From cf8f9d38d0c1984be1b4077b0770293b2f431e4b Mon Sep 17 00:00:00 2001 From: Sondre Lefsaker Date: Mon, 3 Apr 2017 22:17:51 +0200 Subject: [PATCH 001/333] fix: use uri-js to resovle uri's --- lib/compile/resolve.js | 19 +++++++++---------- package.json | 3 ++- spec/resolve.spec.js | 29 +++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/lib/compile/resolve.js b/lib/compile/resolve.js index 7d06afab8..cdd1a59ef 100644 --- a/lib/compile/resolve.js +++ b/lib/compile/resolve.js @@ -1,6 +1,6 @@ 'use strict'; -var url = require('url') +var URI = require('uri-js') , equal = require('fast-deep-equal') , util = require('./util') , SchemaObject = require('./schema_obj') @@ -67,7 +67,7 @@ function resolve(compile, root, ref) { */ function resolveSchema(root, ref) { /* jshint validthis: true */ - var p = url.parse(ref, false, true) + var p = URI.parse(ref) , refPath = _getFullPath(p) , baseId = getFullPath(this._getId(root.schema)); if (refPath !== baseId) { @@ -115,9 +115,9 @@ var PREVENT_SCOPE_CHANGE = util.toHash(['properties', 'patternProperties', 'enum /* @this Ajv */ function getJsonPointer(parsedRef, baseId, schema, root) { /* jshint validthis: true */ - parsedRef.hash = parsedRef.hash || ''; - if (parsedRef.hash.slice(0,2) != '#/') return; - var parts = parsedRef.hash.split('/'); + parsedRef.fragment = parsedRef.fragment || ''; + if (parsedRef.fragment.slice(0,1) != '/') return; + var parts = parsedRef.fragment.split('/'); for (var i = 1; i < parts.length; i++) { var part = parts[i]; @@ -206,14 +206,13 @@ function countKeys(schema) { function getFullPath(id, normalize) { if (normalize !== false) id = normalizeId(id); - var p = url.parse(id, false, true); + var p = URI.parse(id); return _getFullPath(p); } function _getFullPath(p) { - var protocolSeparator = p.protocol || p.href.slice(0,2) == '//' ? '//' : ''; - return (p.protocol||'') + protocolSeparator + (p.host||'') + (p.path||'') + '#'; + return URI.serialize(p).split('#')[0] + '#'; } @@ -225,7 +224,7 @@ function normalizeId(id) { function resolveUrl(baseId, id) { id = normalizeId(id); - return url.resolve(baseId, id); + return URI.resolve(baseId, id); } @@ -246,7 +245,7 @@ function resolveIds(schema) { fullPath += '/' + (typeof keyIndex == 'number' ? keyIndex : util.escapeFragment(keyIndex)); if (typeof id == 'string') { - id = baseId = normalizeId(baseId ? url.resolve(baseId, id) : id); + id = baseId = normalizeId(baseId ? URI.resolve(baseId, id) : id); var refVal = self._refs[id]; if (typeof refVal == 'string') refVal = self._refs[refVal]; diff --git a/package.json b/package.json index eb5db3280..e38a10294 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,8 @@ "co": "^4.6.0", "fast-deep-equal": "^1.0.0", "json-schema-traverse": "^0.3.0", - "json-stable-stringify": "^1.0.1" + "json-stable-stringify": "^1.0.1", + "uri-js": "^3.0.2" }, "devDependencies": { "ajv-async": "^0.1.0", diff --git a/spec/resolve.spec.js b/spec/resolve.spec.js index d1033d987..65d3dd8b5 100644 --- a/spec/resolve.spec.js +++ b/spec/resolve.spec.js @@ -91,6 +91,35 @@ describe('resolve', function () { }); }); }); + + it('should resolve ids defined as urn\'s (issue #423)', function() { + var schema = { + "type": "object", + "properties": { + "ip1": { + "id": "urn:some:ip:prop", + "type": "string", + "format": "ipv4" + }, + "ip2": { + "$ref": "urn:some:ip:prop" + } + }, + "required": [ + "ip1", + "ip2" + ] + }; + + var data = { + "ip1": "0.0.0.0", + "ip2": "0.0.0.0" + }; + instances.forEach(function (ajv) { + var validate = ajv.compile(schema); + validate(data) .should.equal(true); + }); + }); }); From 75c95950a225cadec4136ba7b01577deac41fb85 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Tue, 24 Oct 2017 18:52:13 +0100 Subject: [PATCH 002/333] refactor: use fast-json-stable-stringify, closes #569, closes #579 --- README.md | 4 ++-- lib/ajv.d.ts | 2 +- lib/ajv.js | 4 ++-- lib/compile/index.js | 2 +- package.json | 4 ++-- spec/ajv.spec.js | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 62a52516a..384825cde 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ if (!valid) console.log(ajv.errorsText()); See [API](#api) and [Options](#options) for more details. -Ajv compiles schemas to functions and caches them in all cases (using schema serialized with [json-stable-stringify](https://github.com/substack/json-stable-stringify) or a custom function as a key), so that the next time the same schema is used (not necessarily the same object instance) it won't be compiled again. +Ajv compiles schemas to functions and caches them in all cases (using schema serialized with [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) or a custom function as a key), so that the next time the same schema is used (not necessarily the same object instance) it won't be compiled again. The best performance is achieved when using compiled functions returned by `compile` or `getSchema` methods (there is no additional function call). @@ -1177,7 +1177,7 @@ Defaults: - `beautify` that formatted the generated function using [js-beautify](https://github.com/beautify-web/js-beautify). If you want to beautify the generated code pass `require('js-beautify').js_beautify`. - `transpile` that transpiled asynchronous validation function. You can still use `transpile` option with [ajv-async](https://github.com/epoberezkin/ajv-async) package. See [Asynchronous validation](#asynchronous-validation) for more information. - _cache_: an optional instance of cache to store compiled schemas using stable-stringified schema as a key. For example, set-associative cache [sacjs](https://github.com/epoberezkin/sacjs) can be used. If not passed then a simple hash is used which is good enough for the common use case (a limited number of statically defined schemas). Cache should have methods `put(key, value)`, `get(key)`, `del(key)` and `clear()`. -- _serialize_: an optional function to serialize schema to cache key. Pass `false` to use schema itself as a key (e.g., if WeakMap used as a cache). By default [json-stable-stringify](https://github.com/substack/json-stable-stringify) is used. +- _serialize_: an optional function to serialize schema to cache key. Pass `false` to use schema itself as a key (e.g., if WeakMap used as a cache). By default [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) is used. ## Validation errors diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index 2a8ee1898..60cd2775b 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -10,7 +10,7 @@ declare namespace ajv { interface Ajv { /** * Validate data using schema - * Schema will be compiled and cached (using serialized JSON as key, [json-stable-stringify](https://github.com/substack/json-stable-stringify) is used to serialize by default). + * Schema will be compiled and cached (using serialized JSON as key, [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) is used to serialize by default). * @param {String|Object|Boolean} schemaKeyRef key, ref or schema object * @param {Any} data to be validated * @return {Boolean} validation result. Errors from the last validation will be available in `ajv.errors` (and also in compiled schema: `schema.errors`). diff --git a/lib/ajv.js b/lib/ajv.js index 14095599e..ab187b88e 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -4,7 +4,7 @@ var compileSchema = require('./compile') , resolve = require('./compile/resolve') , Cache = require('./cache') , SchemaObject = require('./compile/schema_obj') - , stableStringify = require('json-stable-stringify') + , stableStringify = require('fast-json-stable-stringify') , formats = require('./compile/formats') , rules = require('./compile/rules') , $dataMetaSchema = require('./$data') @@ -81,7 +81,7 @@ function Ajv(opts) { /** * Validate data using schema - * Schema will be compiled and cached (using serialized JSON as key. [json-stable-stringify](https://github.com/substack/json-stable-stringify) is used to serialize. + * Schema will be compiled and cached (using serialized JSON as key. [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) is used to serialize. * @this Ajv * @param {String|Object} schemaKeyRef key, ref or schema object * @param {Any} data to be validated diff --git a/lib/compile/index.js b/lib/compile/index.js index 45e35d905..e6d15f469 100644 --- a/lib/compile/index.js +++ b/lib/compile/index.js @@ -3,7 +3,7 @@ var resolve = require('./resolve') , util = require('./util') , errorClasses = require('./error_classes') - , stableStringify = require('json-stable-stringify'); + , stableStringify = require('fast-json-stable-stringify'); var validateGenerator = require('../dotjs/validate'); diff --git a/package.json b/package.json index 9a4a30702..66119efff 100644 --- a/package.json +++ b/package.json @@ -65,8 +65,8 @@ "dependencies": { "co": "^4.6.0", "fast-deep-equal": "^1.0.0", - "json-schema-traverse": "^0.3.0", - "json-stable-stringify": "^1.0.1" + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" }, "devDependencies": { "ajv-async": "^0.1.0", diff --git a/spec/ajv.spec.js b/spec/ajv.spec.js index d0fb3de3e..c580f3e97 100644 --- a/spec/ajv.spec.js +++ b/spec/ajv.spec.js @@ -2,7 +2,7 @@ var Ajv = require('./ajv') , should = require('./chai').should() - , stableStringify = require('json-stable-stringify'); + , stableStringify = require('fast-json-stable-stringify'); describe('Ajv', function () { From 256100c76a99249ebdf36a1aa7d296348f0af47d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Tue, 24 Oct 2017 19:14:53 +0100 Subject: [PATCH 003/333] 5.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 66119efff..aadd462db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "5.2.5", + "version": "5.3.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 819d49f2b5a5a84df0de78a7c530cf835970151f Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Tue, 24 Oct 2017 20:16:37 +0100 Subject: [PATCH 004/333] docs: update related packages --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 384825cde..9528a874d 100644 --- a/README.md +++ b/README.md @@ -1232,11 +1232,14 @@ Properties of `params` object in errors depend on the keyword that failed valida ## Related packages -- [ajv-cli](https://github.com/epoberezkin/ajv-cli) - command line interface for Ajv +- [ajv-async](https://github.com/epoberezkin/ajv-async) - 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-i18n](https://github.com/epoberezkin/ajv-i18n) - internationalised error messages -- [ajv-merge-patch](https://github.com/epoberezkin/ajv-merge-patch) - keywords $merge and $patch. -- [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) - several custom keywords that can be used with Ajv (typeof, instanceof, range, propertyNames) -- [ajv-errors](https://github.com/epoberezkin/ajv-errors) - custom error messages for Ajv +- [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-pack](https://github.com/epoberezkin/ajv-pack) - produces a compact module exporting validation functions ## Some packages using Ajv From d20da898199ea982c4defbd51a4d1e88e9d216bb Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Thu, 26 Oct 2017 18:10:10 +0100 Subject: [PATCH 005/333] 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 006/333] 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 007/333] 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 5d2f2b8000df45570534b7e6b3cd665932d5600d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 28 Oct 2017 16:39:09 +0100 Subject: [PATCH 008/333] reserve keyword $id --- lib/compile/rules.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compile/rules.js b/lib/compile/rules.js index eaeab77fa..44830ad72 100644 --- a/lib/compile/rules.js +++ b/lib/compile/rules.js @@ -20,7 +20,7 @@ module.exports = function rules() { var ALL = [ 'type' ]; var KEYWORDS = [ - 'additionalItems', '$schema', 'id', 'title', + 'additionalItems', '$schema', '$id', 'id', 'title', 'description', 'default', 'definitions' ]; var TYPES = [ 'number', 'integer', 'string', 'array', 'object', 'boolean', 'null' ]; From 9845928e7a658e0c1f2bebe069c09bb7f8a1023d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 28 Oct 2017 16:51:18 +0100 Subject: [PATCH 009/333] 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 010/333] 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 011/333] 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 802234489daedb5f7646511bcb5afbb89dfa357f Mon Sep 17 00:00:00 2001 From: rsa Date: Sat, 28 Oct 2017 22:23:23 -0400 Subject: [PATCH 012/333] Fix validate template typo --- lib/dot/validate.jst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/dot/validate.jst b/lib/dot/validate.jst index 4ebc599c0..ef1e35a64 100644 --- a/lib/dot/validate.jst +++ b/lib/dot/validate.jst @@ -260,10 +260,10 @@ function $shouldUseRule($rule) { return it.schema[$rule.keyword] !== undefined || - ($rule.implements && $ruleImlementsSomeKeyword($rule)); + ($rule.implements && $ruleImplementsSomeKeyword($rule)); } - function $ruleImlementsSomeKeyword($rule) { + function $ruleImplementsSomeKeyword($rule) { var impl = $rule.implements; for (var i=0; i < impl.length; i++) if (it.schema[impl[i]] !== undefined) From 5e17f9b4eed702e016d8b810e19a40852104b342 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 29 Oct 2017 18:20:24 +0000 Subject: [PATCH 013/333] 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 014/333] 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 015/333] 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 016/333] 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 32b01a83d2a25321fd687a866666a6155a06eb56 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 30 Oct 2017 21:03:47 +0000 Subject: [PATCH 017/333] test: failing test for #612 --- spec/async_validate.spec.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/spec/async_validate.spec.js b/spec/async_validate.spec.js index fdfdf5dde..55e81baab 100644 --- a/spec/async_validate.spec.js +++ b/spec/async_validate.spec.js @@ -250,6 +250,32 @@ describe('async schemas, formats and keywords', function() { return recursiveTest(schema); }); + it.skip('should validate recursive ref to async sub-schema, issue #612', function() { + var schema = { + $async: true, + type: 'object', + properties: { + foo: { + $async: true, + anyOf: [ + { + type: 'string', + format: 'english_word' + }, + { + type: 'object', + properties: { + foo: { $ref: '#/properties/foo' } + } + } + ] + } + } + }; + + return recursiveTest(schema); + }); + it('should validate ref from referenced async schema to root schema', function() { var schema = { $async: true, From bddda60e9042c2cafdc613609f14d851f7915d11 Mon Sep 17 00:00:00 2001 From: Krittanan Pingclasai Date: Wed, 1 Nov 2017 11:41:36 +0700 Subject: [PATCH 018/333] make available types more noticeable --- KEYWORDS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KEYWORDS.md b/KEYWORDS.md index c53e15e95..ffc696dfd 100644 --- a/KEYWORDS.md +++ b/KEYWORDS.md @@ -49,7 +49,7 @@ The keywords and their values define what rules the data should satisfy to be va `type` keyword requires that the data is of certain type (or some of types). Its value can be a string (the allowed type) or an array of strings (multiple allowed types). -Type can be: number, integer, string, boolean, array, object or null. +Type can be: `number`, `integer`, `string`, `boolean`, `array`, `object` or `null`. __Examples__ From 1d44f1451a960f8e1163f3ad0443bcc86733a895 Mon Sep 17 00:00:00 2001 From: Brendan Abbott Date: Fri, 3 Nov 2017 02:18:41 +1000 Subject: [PATCH 019/333] 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 020/333] 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 021/333] 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 022/333] 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 023/333] 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 024/333] 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 025/333] 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 026/333] 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 027/333] 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 028/333] 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 029/333] 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 030/333] 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 031/333] 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 032/333] 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 033/333] =?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 034/333] 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 035/333] 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 036/333] 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 0cafcf6dce165f1fe2bb6c356232db92c27f882e Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 12 Nov 2017 10:59:51 +0000 Subject: [PATCH 037/333] docs: version 6 beta --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9528a874d..a40f89ace 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,16 @@ The fastest JSON Schema validator for Node.js and browser with draft 6 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@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) -[![Code Climate](https://codeclimate.com/github/epoberezkin/ajv/badges/gpa.svg)](https://codeclimate.com/github/epoberezkin/ajv) [![Coverage Status](https://coveralls.io/repos/epoberezkin/ajv/badge.svg?branch=master&service=github)](https://coveralls.io/github/epoberezkin/ajv?branch=master) [![Greenkeeper badge](https://badges.greenkeeper.io/epoberezkin/ajv.svg)](https://greenkeeper.io/) [![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv) +__Please note__: Ajv [version 6](https://github.com/epoberezkin/ajv/tree/beta) with [JSON Schema draft-07](http://json-schema.org/work-in-progress) support is released. Use `npm install ajv@beta` to install. + + ## Using version 5 [JSON Schema draft-06](https://trac.tools.ietf.org/html/draft-wright-json-schema-validation-01) is published. @@ -107,6 +110,12 @@ Currently Ajv is the only validator that passes all the tests from [JSON Schema npm install ajv ``` +or to install [version 6](https://github.com/epoberezkin/ajv/tree/beta): + +``` +npm install ajv@beta +``` + ## Getting started From 0196611fb70c731121a2ea750eaf7b4073c424be Mon Sep 17 00:00:00 2001 From: "Stuart P. Bentley" Date: Mon, 13 Nov 2017 19:02:25 -0800 Subject: [PATCH 038/333] Update draft-06 meta-schema See json-schema-org/json-schema-spec#481 --- lib/refs/json-schema-draft-06.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/refs/json-schema-draft-06.json b/lib/refs/json-schema-draft-06.json index 621cc5102..5656240b9 100644 --- a/lib/refs/json-schema-draft-06.json +++ b/lib/refs/json-schema-draft-06.json @@ -57,6 +57,10 @@ "type": "string" }, "default": {}, + "examples": { + "type": "array", + "items": {} + }, "multipleOf": { "type": "number", "exclusiveMinimum": 0 From 91374acba36e3a75db0907e21f0e6d71fb885fa1 Mon Sep 17 00:00:00 2001 From: Meir Rotstein Date: Sun, 19 Nov 2017 11:18:40 +0200 Subject: [PATCH 039/333] add logger option --- lib/ajv.js | 15 ++++++++++---- lib/compile/index.js | 8 +++++--- lib/compile/logger.js | 47 +++++++++++++++++++++++++++++++++++++++++++ lib/dot/format.jst | 2 +- lib/dot/ref.jst | 4 ++-- lib/dot/validate.jst | 4 ++-- 6 files changed, 68 insertions(+), 12 deletions(-) create mode 100644 lib/compile/logger.js diff --git a/lib/ajv.js b/lib/ajv.js index ab187b88e..fb812f0b0 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -10,6 +10,7 @@ var compileSchema = require('./compile') , $dataMetaSchema = require('./$data') , patternGroups = require('./patternGroups') , util = require('./compile/util') + , logger = require('./compile/logger') , co = require('co'); module.exports = Ajv; @@ -75,6 +76,7 @@ function Ajv(opts) { if (typeof opts.meta == 'object') this.addMetaSchema(opts.meta); addInitialSchemas(this); if (opts.patternGroups) patternGroups(this); + if (opts.logger !== undefined) setLogger(this); } @@ -166,7 +168,7 @@ function validateSchema(schema, throwOrLogError) { throw new Error('$schema must be a string'); $schema = $schema || this._opts.defaultMeta || defaultMeta(this); if (!$schema) { - console.warn('meta-schema not available'); + logger.warn('meta-schema not available'); this.errors = null; return true; } @@ -179,7 +181,7 @@ function validateSchema(schema, throwOrLogError) { finally { this._formats.uri = currentUriFormat; } if (!valid && throwOrLogError) { var message = 'schema is invalid: ' + this.errorsText(); - if (this._opts.validateSchema == 'log') console.error(message); + if (this._opts.validateSchema == 'log') logger.error(message); else throw new Error(message); } return valid; @@ -380,13 +382,13 @@ function chooseGetId(opts) { function _getId(schema) { - if (schema.$id) console.warn('schema $id ignored', schema.$id); + if (schema.$id) logger.warn('schema $id ignored', schema.$id); return schema.id; } function _get$Id(schema) { - if (schema.id) console.warn('schema id ignored', schema.id); + if (schema.id) logger.warn('schema id ignored', schema.id); return schema.$id; } @@ -475,3 +477,8 @@ function getMetaSchemaOptions(self) { delete metaOpts[META_IGNORE_OPTIONS[i]]; return metaOpts; } + +function setLogger(self) { + if(self._opts.logger === false) logger.set(null); + if(typeof self._opts.logger === 'object') logger.set(self._opts.logger); +} diff --git a/lib/compile/index.js b/lib/compile/index.js index e6d15f469..3da36abbc 100644 --- a/lib/compile/index.js +++ b/lib/compile/index.js @@ -3,7 +3,8 @@ var resolve = require('./resolve') , util = require('./util') , errorClasses = require('./error_classes') - , stableStringify = require('fast-json-stable-stringify'); + , stableStringify = require('fast-json-stable-stringify') + , logger = require('./logger'); var validateGenerator = require('../dotjs/validate'); @@ -104,6 +105,7 @@ function compile(schema, root, localRefs, baseId) { useCustomRule: useCustomRule, opts: opts, formats: formats, + logger: logger, self: self }); @@ -146,7 +148,7 @@ function compile(schema, root, localRefs, baseId) { refVal[0] = validate; } catch(e) { - console.error('Error compiling schema, function code:', sourceCode); + logger.error('Error compiling schema, function code:', sourceCode); throw e; } @@ -260,7 +262,7 @@ function compile(schema, root, localRefs, baseId) { var valid = validateSchema(schema); if (!valid) { var message = 'keyword schema is invalid: ' + self.errorsText(validateSchema.errors); - if (self._opts.validateSchema == 'log') console.error(message); + if (self._opts.validateSchema == 'log') logger.error(message); else throw new Error(message); } } diff --git a/lib/compile/logger.js b/lib/compile/logger.js new file mode 100644 index 000000000..7cad56b2b --- /dev/null +++ b/lib/compile/logger.js @@ -0,0 +1,47 @@ +'use strict'; + +var logger; + +_reset(); + +module.exports = { + log: _logFunc('log'), + warn: _logFunc('warn'), + error: _logFunc('error'), + set: _setLogger, + reset: _reset +}; + +/** + * @param {String} level log level - log, warn or error + * @return {Function} log function + * @private + */ +function _logFunc(level) { + /** + * @this Ajv + */ + return function() { + if(logger && logger[level]) logger[level].apply(this, arguments); + }; +} + +/** + * Set actual logger object, in order to disable the logger send non object or null + * @param {*} loggerObj logger object, expected to implement log, warn and error, if not it gets noop implementation. + */ +function _setLogger(loggerObj) { + if(loggerObj && typeof loggerObj === 'object') + logger = loggerObj; + else + logger = null; +} + +/** + * Reset logger to global console + */ +function _reset() { + logger = typeof console === 'object' ? console : {log: noop, warn: noop, error: noop}; +} + +function noop() {} \ No newline at end of file diff --git a/lib/dot/format.jst b/lib/dot/format.jst index 074d16c31..484dddf79 100644 --- a/lib/dot/format.jst +++ b/lib/dot/format.jst @@ -71,7 +71,7 @@ {{ var $format = it.formats[$schema]; }} {{? !$format }} {{? $unknownFormats == 'ignore' }} - {{ console.warn('unknown format "' + $schema + '" ignored in schema at path "' + it.errSchemaPath + '"'); }} + {{ it.logger.warn('unknown format "' + $schema + '" ignored in schema at path "' + it.errSchemaPath + '"'); }} {{# def.skipFormat }} {{?? $allowUnknown && $unknownFormats.indexOf($schema) >= 0 }} {{# def.skipFormat }} diff --git a/lib/dot/ref.jst b/lib/dot/ref.jst index 4a0889686..036bc2905 100644 --- a/lib/dot/ref.jst +++ b/lib/dot/ref.jst @@ -27,11 +27,11 @@ {{? $refVal === undefined }} {{ var $message = it.MissingRefError.message(it.baseId, $schema); }} {{? it.opts.missingRefs == 'fail' }} - {{ console.error($message); }} + {{ it.logger.error($message); }} {{# def.error:'$ref' }} {{? $breakOnError }} if (false) { {{?}} {{?? it.opts.missingRefs == 'ignore' }} - {{ console.warn($message); }} + {{ it.logger.warn($message); }} {{? $breakOnError }} if (true) { {{?}} {{??}} {{ throw new it.MissingRefError(it.baseId, $schema, $message); }} diff --git a/lib/dot/validate.jst b/lib/dot/validate.jst index ef1e35a64..273e4c37e 100644 --- a/lib/dot/validate.jst +++ b/lib/dot/validate.jst @@ -140,7 +140,7 @@ {{?? it.opts.extendRefs !== true }} {{ $refKeywords = false; - console.warn('$ref: keywords ignored in schema at path "' + it.errSchemaPath + '"'); + it.logger.warn('$ref: keywords ignored in schema at path "' + it.errSchemaPath + '"'); }} {{?}} {{?}} @@ -177,7 +177,7 @@ {{?}} {{??}} {{? it.opts.v5 && it.schema.patternGroups }} - {{ console.warn('keyword "patternGroups" is deprecated and disabled. Use option patternGroups: true to enable.'); }} + {{ it.logger.warn('keyword "patternGroups" is deprecated and disabled. Use option patternGroups: true to enable.'); }} {{?}} {{~ it.RULES:$rulesGroup }} {{? $shouldUseGroup($rulesGroup) }} From b0e28ee3447aeceb51d316f00e0ece5efd0a13d8 Mon Sep 17 00:00:00 2001 From: Meir Rotstein Date: Sun, 19 Nov 2017 11:23:56 +0200 Subject: [PATCH 040/333] logger component tests --- spec/.eslintrc.yml | 1 + spec/logger.spec.js | 103 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 spec/logger.spec.js 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/logger.spec.js b/spec/logger.spec.js new file mode 100644 index 000000000..2b2b80f51 --- /dev/null +++ b/spec/logger.spec.js @@ -0,0 +1,103 @@ +'use strict'; + +var logger = require('../lib/compile/logger') + , should = require('./chai').should(); + +describe('logger object tests', function() { + var origConsoleWarn = console.warn; + var origConsoleLog = console.log; + var origConsoleError = console.error; + + var consoleWarnCalled = false; + var consoleLogCalled = false; + var consoleErrorCalled = false; + + beforeEach(function() { + console.warn = function() { + consoleWarnCalled = true; + origConsoleWarn.apply(console, arguments); + }; + console.log = function() { + consoleLogCalled = true; + origConsoleLog.apply(console, arguments); + }; + console.error = function() { + consoleErrorCalled = true; + origConsoleError.apply(console, arguments); + return 'boo'; + }; + }); + + afterEach(function() { + console.warn = origConsoleWarn; + console.log = origConsoleLog; + console.error = origConsoleError; + logger.reset(); + consoleErrorCalled = consoleLogCalled = consoleWarnCalled = false; + }); + + it('logger should log into global console by default', function() { + logger.log('42'); + logger.warn('42'); + logger.error('42'); + + should.equal(consoleWarnCalled, true); + should.equal(consoleLogCalled, true); + should.equal(consoleErrorCalled, true); + }); + + it('logger should log only into a custom logger if given console by default', function() { + + var customWarnCalled = false; + var customLogCalled = false; + var customErrorCalled = false; + + var customLogger = { + warn: function() { + customWarnCalled = true; + }, + log: function() { + customLogCalled = true; + }, + error: function() { + customErrorCalled = true; + } + }; + + logger.set(customLogger); + logger.log('42'); + logger.warn('42'); + logger.error('42'); + + should.equal(consoleWarnCalled, false); + should.equal(consoleLogCalled, false); + should.equal(consoleErrorCalled, false); + + should.equal(customWarnCalled, true); + should.equal(customLogCalled, true); + should.equal(customErrorCalled, true); + }); + + it('if a custom logger is given without basic logging functions implementations it should not leads to an exception', function() { + logger.set({}); + logger.log('42'); + logger.warn('42'); + logger.error('42'); + + should.equal(consoleWarnCalled, false); + should.equal(consoleLogCalled, false); + should.equal(consoleErrorCalled, false); + }); + + it('if a custom logger is set to null logging should be disabled', function() { + logger.set(null); + logger.log('42'); + logger.warn('42'); + logger.error('42'); + + should.equal(consoleWarnCalled, false); + should.equal(consoleLogCalled, false); + should.equal(consoleErrorCalled, false); + }); + +}); \ No newline at end of file From ceb552abd4c7f8cd8fe7a0d31b25ade7d4d8a595 Mon Sep 17 00:00:00 2001 From: Meir Rotstein Date: Sun, 19 Nov 2017 11:37:27 +0200 Subject: [PATCH 041/333] logger option tests --- spec/options.spec.js | 92 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/spec/options.spec.js b/spec/options.spec.js index dc58f4267..3fdf18c63 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -1219,4 +1219,96 @@ describe('Ajv Options', function () { }); }); }); + + describe('logger', function() { + + /** + * The logger option tests are based on the meta scenario which writes into the logger.warn + */ + + var origConsoleWarn = console.warn; + var consoleCalled = false; + + beforeEach(function() { + console.warn = function() { + consoleCalled = true; + }; + }); + + afterEach(function() { + console.warn = origConsoleWarn; + consoleCalled = false; + }); + + it('logger is undefined - global console should be in use', function() { + + var ajv = new Ajv({ + meta: false + }); + + ajv.compile({ + schema: { type: 'number' }, + minimum: 1 + }); + + should.equal(consoleCalled, true); + }); + + it('logger is an object - logs should only reported to it', function() { + + var loggerCalled = false; + + var logger = { + warn: function() { + loggerCalled = true; + } + }; + + var ajv = new Ajv({ + meta: false, + logger: logger + }); + + ajv.compile({ + schema: { type: 'number' }, + minimum: 1 + }); + + should.equal(loggerCalled, true); + should.equal(consoleCalled, false); + }); + + it('logger is an object but not implements the basic functions - make sure that it not leads to an error', function() { + + var logger = {}; + + var ajv = new Ajv({ + meta: false, + logger: logger + }); + + ajv.compile({ + schema: { type: 'number' }, + minimum: 1 + }); + + should.equal(consoleCalled, false); + }); + + it('logger option is false - no logs should be reported', function() { + + var ajv = new Ajv({ + meta: false, + logger: false + }); + + ajv.compile({ + schema: { type: 'number' }, + minimum: 1 + }); + + should.equal(consoleCalled, false); + }); + + }); }); From 4fe1c21eaa085748752065559b9d53d19ffd1a88 Mon Sep 17 00:00:00 2001 From: Meir Rotstein Date: Sun, 19 Nov 2017 11:42:07 +0200 Subject: [PATCH 042/333] update readme with logger option --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index a40f89ace..d756f278e 100644 --- a/README.md +++ b/README.md @@ -1060,6 +1060,7 @@ Defaults: formats: {}, unknownFormats: true, schemas: {}, + logger: undefined, // referenced schema options: schemaId: undefined // recommended '$id' missingRefs: true, @@ -1105,6 +1106,16 @@ Defaults: - `[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. - _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. deafult is the global `console` object. Option values: + - custom logger object, should implement the following methods: + ``` + { + log: function() {...}, + warn: function() {...}, + error: function() {...} + } + ``` + - `false` - logging is disabled ##### Referenced schema options From e0c7eac50dfbfa6052704f088377089f74380ab7 Mon Sep 17 00:00:00 2001 From: Meir Rotstein Date: Sun, 19 Nov 2017 17:34:42 +0200 Subject: [PATCH 043/333] create logger instance per Ajv instance --- lib/ajv.js | 24 +++++++++-------- lib/compile/index.js | 9 +++---- lib/compile/logger.js | 60 +++++++++++++++++++------------------------ spec/logger.spec.js | 21 ++++++++++----- spec/options.spec.js | 6 ++--- 5 files changed, 62 insertions(+), 58 deletions(-) diff --git a/lib/ajv.js b/lib/ajv.js index fb812f0b0..2fe5e7e44 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -10,7 +10,7 @@ var compileSchema = require('./compile') , $dataMetaSchema = require('./$data') , patternGroups = require('./patternGroups') , util = require('./compile/util') - , logger = require('./compile/logger') + , Logger = require('./compile/logger') , co = require('co'); module.exports = Ajv; @@ -53,6 +53,7 @@ var META_SUPPORT_DATA = ['/properties']; function Ajv(opts) { if (!(this instanceof Ajv)) return new Ajv(opts); opts = this._opts = util.copy(opts) || {}; + setLogger(this); this._schemas = {}; this._refs = {}; this._fragments = {}; @@ -76,7 +77,6 @@ function Ajv(opts) { if (typeof opts.meta == 'object') this.addMetaSchema(opts.meta); addInitialSchemas(this); if (opts.patternGroups) patternGroups(this); - if (opts.logger !== undefined) setLogger(this); } @@ -168,7 +168,7 @@ function validateSchema(schema, throwOrLogError) { throw new Error('$schema must be a string'); $schema = $schema || this._opts.defaultMeta || defaultMeta(this); if (!$schema) { - logger.warn('meta-schema not available'); + this.logger.warn('meta-schema not available'); this.errors = null; return true; } @@ -181,7 +181,7 @@ function validateSchema(schema, throwOrLogError) { finally { this._formats.uri = currentUriFormat; } if (!valid && throwOrLogError) { var message = 'schema is invalid: ' + this.errorsText(); - if (this._opts.validateSchema == 'log') logger.error(message); + if (this._opts.validateSchema == 'log') this.logger.error(message); else throw new Error(message); } return valid; @@ -380,15 +380,15 @@ function chooseGetId(opts) { } } - +/* @this Ajv */ function _getId(schema) { - if (schema.$id) logger.warn('schema $id ignored', schema.$id); + if (schema.$id) this.logger.warn('schema $id ignored', schema.$id); return schema.id; } - +/* @this Ajv */ function _get$Id(schema) { - if (schema.id) logger.warn('schema id ignored', schema.id); + if (schema.id) this.logger.warn('schema id ignored', schema.id); return schema.$id; } @@ -479,6 +479,10 @@ function getMetaSchemaOptions(self) { } function setLogger(self) { - if(self._opts.logger === false) logger.set(null); - if(typeof self._opts.logger === 'object') logger.set(self._opts.logger); + var loggerObj; + + if(self._opts.logger === false) loggerObj = null; + if(typeof self._opts.logger === 'object') loggerObj = self._opts.logger; + + self.logger = new Logger(loggerObj); } diff --git a/lib/compile/index.js b/lib/compile/index.js index 3da36abbc..cf4f5b86b 100644 --- a/lib/compile/index.js +++ b/lib/compile/index.js @@ -3,8 +3,7 @@ var resolve = require('./resolve') , util = require('./util') , errorClasses = require('./error_classes') - , stableStringify = require('fast-json-stable-stringify') - , logger = require('./logger'); + , stableStringify = require('fast-json-stable-stringify'); var validateGenerator = require('../dotjs/validate'); @@ -105,7 +104,7 @@ function compile(schema, root, localRefs, baseId) { useCustomRule: useCustomRule, opts: opts, formats: formats, - logger: logger, + logger: self.logger, self: self }); @@ -148,7 +147,7 @@ function compile(schema, root, localRefs, baseId) { refVal[0] = validate; } catch(e) { - logger.error('Error compiling schema, function code:', sourceCode); + self.logger.error('Error compiling schema, function code:', sourceCode); throw e; } @@ -262,7 +261,7 @@ function compile(schema, root, localRefs, baseId) { var valid = validateSchema(schema); if (!valid) { var message = 'keyword schema is invalid: ' + self.errorsText(validateSchema.errors); - if (self._opts.validateSchema == 'log') logger.error(message); + if (self._opts.validateSchema == 'log') self.logger.error(message); else throw new Error(message); } } diff --git a/lib/compile/logger.js b/lib/compile/logger.js index 7cad56b2b..133b824e0 100644 --- a/lib/compile/logger.js +++ b/lib/compile/logger.js @@ -1,47 +1,41 @@ 'use strict'; -var logger; - -_reset(); - -module.exports = { - log: _logFunc('log'), - warn: _logFunc('warn'), - error: _logFunc('error'), - set: _setLogger, - reset: _reset -}; - -/** - * @param {String} level log level - log, warn or error - * @return {Function} log function - * @private - */ -function _logFunc(level) { - /** - * @this Ajv - */ - return function() { - if(logger && logger[level]) logger[level].apply(this, arguments); - }; -} +module.exports = Logger; /** - * Set actual logger object, in order to disable the logger send non object or null - * @param {*} loggerObj logger object, expected to implement log, warn and error, if not it gets noop implementation. + * @constructor + * @this Logger + * @param {*=} loggerObj logger object, expected to implement log, warn and error, if not given global console will be taken. */ -function _setLogger(loggerObj) { +function Logger(loggerObj) { if(loggerObj && typeof loggerObj === 'object') - logger = loggerObj; + this.logger = loggerObj; + else if(typeof loggerObj === 'undefined') + this.logger = typeof console === 'object' ? console : {log: noop, warn: noop, error: noop}; else - logger = null; + this.logger = null; } +Logger.prototype.log = function() { + _logFunc.call(this, 'log', arguments); +}; + +Logger.prototype.warn = function() { + _logFunc.call(this, 'warn', arguments); +}; + +Logger.prototype.error = function() { + _logFunc.call(this, 'error', arguments); +}; + /** - * Reset logger to global console + * @this Logger + * @param {String} level log level - log, warn or error + * @param {Array} args log argumets + * @private */ -function _reset() { - logger = typeof console === 'object' ? console : {log: noop, warn: noop, error: noop}; +function _logFunc(level, args) { + if(this.logger && this.logger[level]) this.logger[level].apply(this, args); } function noop() {} \ No newline at end of file diff --git a/spec/logger.spec.js b/spec/logger.spec.js index 2b2b80f51..c6bb2fb77 100644 --- a/spec/logger.spec.js +++ b/spec/logger.spec.js @@ -1,9 +1,12 @@ 'use strict'; -var logger = require('../lib/compile/logger') +var Logger = require('../lib/compile/logger') , should = require('./chai').should(); describe('logger object tests', function() { + + var logger; + var origConsoleWarn = console.warn; var origConsoleLog = console.log; var origConsoleError = console.error; @@ -32,11 +35,13 @@ describe('logger object tests', function() { console.warn = origConsoleWarn; console.log = origConsoleLog; console.error = origConsoleError; - logger.reset(); consoleErrorCalled = consoleLogCalled = consoleWarnCalled = false; }); it('logger should log into global console by default', function() { + + logger = new Logger(); + logger.log('42'); logger.warn('42'); logger.error('42'); @@ -46,8 +51,7 @@ describe('logger object tests', function() { should.equal(consoleErrorCalled, true); }); - it('logger should log only into a custom logger if given console by default', function() { - + it('logger should log only into a custom logger if given', function() { var customWarnCalled = false; var customLogCalled = false; var customErrorCalled = false; @@ -64,7 +68,8 @@ describe('logger object tests', function() { } }; - logger.set(customLogger); + logger = new Logger(customLogger); + logger.log('42'); logger.warn('42'); logger.error('42'); @@ -79,7 +84,8 @@ describe('logger object tests', function() { }); it('if a custom logger is given without basic logging functions implementations it should not leads to an exception', function() { - logger.set({}); + logger = new Logger({}); + logger.log('42'); logger.warn('42'); logger.error('42'); @@ -90,7 +96,8 @@ describe('logger object tests', function() { }); it('if a custom logger is set to null logging should be disabled', function() { - logger.set(null); + logger = new Logger(null); + logger.log('42'); logger.warn('42'); logger.error('42'); diff --git a/spec/options.spec.js b/spec/options.spec.js index 3fdf18c63..ea30f9412 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -1240,7 +1240,7 @@ describe('Ajv Options', function () { consoleCalled = false; }); - it('logger is undefined - global console should be in use', function() { + it('no custom logger is given - global console should be in use', function() { var ajv = new Ajv({ meta: false @@ -1254,7 +1254,7 @@ describe('Ajv Options', function () { should.equal(consoleCalled, true); }); - it('logger is an object - logs should only reported to it', function() { + it('custom logger is an object - logs should only reported to it', function() { var loggerCalled = false; @@ -1278,7 +1278,7 @@ describe('Ajv Options', function () { should.equal(consoleCalled, false); }); - it('logger is an object but not implements the basic functions - make sure that it not leads to an error', function() { + it('custom logger is an object but not implements the basic functions - make sure that it not leads to an error', function() { var logger = {}; From 5ba22a36aa98910c57574b3745a63dd6bd2e1ea3 Mon Sep 17 00:00:00 2001 From: Meir Rotstein Date: Sun, 19 Nov 2017 23:02:08 +0200 Subject: [PATCH 044/333] remove Logger class, resolve logger object on Ajv ctor instead --- lib/ajv.js | 18 ++++--- lib/compile/logger.js | 41 ---------------- spec/logger.spec.js | 110 ------------------------------------------ spec/options.spec.js | 33 ++++++------- 4 files changed, 26 insertions(+), 176 deletions(-) delete mode 100644 lib/compile/logger.js delete mode 100644 spec/logger.spec.js diff --git a/lib/ajv.js b/lib/ajv.js index 2fe5e7e44..6f6b00f5e 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -10,7 +10,6 @@ var compileSchema = require('./compile') , $dataMetaSchema = require('./$data') , patternGroups = require('./patternGroups') , util = require('./compile/util') - , Logger = require('./compile/logger') , co = require('co'); module.exports = Ajv; @@ -479,10 +478,15 @@ function getMetaSchemaOptions(self) { } function setLogger(self) { - var loggerObj; - - if(self._opts.logger === false) loggerObj = null; - if(typeof self._opts.logger === 'object') loggerObj = self._opts.logger; - - self.logger = new Logger(loggerObj); + var noop = function(){}; + + if(self._opts.logger === false) { + self.logger = {log: noop, warn: noop, error: noop}; + } else if(typeof self._opts.logger === 'object') { + if (!(self._opts.logger.log && self._opts.logger.warn && self._opts.logger.error)) + throw new Error('logger must implement log, warn and error functions'); + self.logger = self._opts.logger; + } else { + self.logger = console; + } } diff --git a/lib/compile/logger.js b/lib/compile/logger.js deleted file mode 100644 index 133b824e0..000000000 --- a/lib/compile/logger.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict'; - -module.exports = Logger; - -/** - * @constructor - * @this Logger - * @param {*=} loggerObj logger object, expected to implement log, warn and error, if not given global console will be taken. - */ -function Logger(loggerObj) { - if(loggerObj && typeof loggerObj === 'object') - this.logger = loggerObj; - else if(typeof loggerObj === 'undefined') - this.logger = typeof console === 'object' ? console : {log: noop, warn: noop, error: noop}; - else - this.logger = null; -} - -Logger.prototype.log = function() { - _logFunc.call(this, 'log', arguments); -}; - -Logger.prototype.warn = function() { - _logFunc.call(this, 'warn', arguments); -}; - -Logger.prototype.error = function() { - _logFunc.call(this, 'error', arguments); -}; - -/** - * @this Logger - * @param {String} level log level - log, warn or error - * @param {Array} args log argumets - * @private - */ -function _logFunc(level, args) { - if(this.logger && this.logger[level]) this.logger[level].apply(this, args); -} - -function noop() {} \ No newline at end of file diff --git a/spec/logger.spec.js b/spec/logger.spec.js deleted file mode 100644 index c6bb2fb77..000000000 --- a/spec/logger.spec.js +++ /dev/null @@ -1,110 +0,0 @@ -'use strict'; - -var Logger = require('../lib/compile/logger') - , should = require('./chai').should(); - -describe('logger object tests', function() { - - var logger; - - var origConsoleWarn = console.warn; - var origConsoleLog = console.log; - var origConsoleError = console.error; - - var consoleWarnCalled = false; - var consoleLogCalled = false; - var consoleErrorCalled = false; - - beforeEach(function() { - console.warn = function() { - consoleWarnCalled = true; - origConsoleWarn.apply(console, arguments); - }; - console.log = function() { - consoleLogCalled = true; - origConsoleLog.apply(console, arguments); - }; - console.error = function() { - consoleErrorCalled = true; - origConsoleError.apply(console, arguments); - return 'boo'; - }; - }); - - afterEach(function() { - console.warn = origConsoleWarn; - console.log = origConsoleLog; - console.error = origConsoleError; - consoleErrorCalled = consoleLogCalled = consoleWarnCalled = false; - }); - - it('logger should log into global console by default', function() { - - logger = new Logger(); - - logger.log('42'); - logger.warn('42'); - logger.error('42'); - - should.equal(consoleWarnCalled, true); - should.equal(consoleLogCalled, true); - should.equal(consoleErrorCalled, true); - }); - - it('logger should log only into a custom logger if given', function() { - var customWarnCalled = false; - var customLogCalled = false; - var customErrorCalled = false; - - var customLogger = { - warn: function() { - customWarnCalled = true; - }, - log: function() { - customLogCalled = true; - }, - error: function() { - customErrorCalled = true; - } - }; - - logger = new Logger(customLogger); - - logger.log('42'); - logger.warn('42'); - logger.error('42'); - - should.equal(consoleWarnCalled, false); - should.equal(consoleLogCalled, false); - should.equal(consoleErrorCalled, false); - - should.equal(customWarnCalled, true); - should.equal(customLogCalled, true); - should.equal(customErrorCalled, true); - }); - - it('if a custom logger is given without basic logging functions implementations it should not leads to an exception', function() { - logger = new Logger({}); - - logger.log('42'); - logger.warn('42'); - logger.error('42'); - - should.equal(consoleWarnCalled, false); - should.equal(consoleLogCalled, false); - should.equal(consoleErrorCalled, false); - }); - - it('if a custom logger is set to null logging should be disabled', function() { - logger = new Logger(null); - - logger.log('42'); - logger.warn('42'); - logger.error('42'); - - should.equal(consoleWarnCalled, false); - should.equal(consoleLogCalled, false); - should.equal(consoleErrorCalled, false); - }); - -}); \ No newline at end of file diff --git a/spec/options.spec.js b/spec/options.spec.js index ea30f9412..0f5ba027a 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -1261,6 +1261,12 @@ describe('Ajv Options', function () { var logger = { warn: function() { loggerCalled = true; + }, + log: function() { + loggerCalled = true; + }, + error: function() { + loggerCalled = true; } }; @@ -1278,25 +1284,7 @@ describe('Ajv Options', function () { should.equal(consoleCalled, false); }); - it('custom logger is an object but not implements the basic functions - make sure that it not leads to an error', function() { - - var logger = {}; - - var ajv = new Ajv({ - meta: false, - logger: logger - }); - - ajv.compile({ - schema: { type: 'number' }, - minimum: 1 - }); - - should.equal(consoleCalled, false); - }); - it('logger option is false - no logs should be reported', function() { - var ajv = new Ajv({ meta: false, logger: false @@ -1310,5 +1298,14 @@ describe('Ajv Options', function () { should.equal(consoleCalled, false); }); + it('logger option is an object but not implmemting the console functions - an error should be raised', function() { + (function(){ + new Ajv({ + meta: false, + logger: {} + }); + }).should.throw(Error, /logger must implement log, warn and error function/); + }); + }); }); From 89a80cabf85d8f8ba37f94ff2470a5cb2ed8ef0a Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 20 Nov 2017 20:10:57 +0000 Subject: [PATCH 045/333] check that console has log, warn and error methods as well --- README.md | 13 +++---------- lib/ajv.js | 18 ++++++++++-------- spec/options.spec.js | 37 ++++++++++++++++--------------------- 3 files changed, 29 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index d756f278e..ec778c377 100644 --- a/README.md +++ b/README.md @@ -1106,16 +1106,9 @@ Defaults: - `[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. - _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. deafult is the global `console` object. Option values: - - custom logger object, should implement the following methods: - ``` - { - log: function() {...}, - warn: function() {...}, - error: function() {...} - } - ``` - - `false` - logging is disabled +- _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. + - `false` - logging is disabled. ##### Referenced schema options diff --git a/lib/ajv.js b/lib/ajv.js index 6f6b00f5e..033caa6f3 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -477,16 +477,18 @@ function getMetaSchemaOptions(self) { return metaOpts; } -function setLogger(self) { - var noop = function(){}; - if(self._opts.logger === false) { +function setLogger(self) { + var logger = self._opts.logger; + if (logger === false) { self.logger = {log: noop, warn: noop, error: noop}; - } else if(typeof self._opts.logger === 'object') { - if (!(self._opts.logger.log && self._opts.logger.warn && self._opts.logger.error)) - throw new Error('logger must implement log, warn and error functions'); - self.logger = self._opts.logger; } else { - self.logger = console; + if (logger === undefined) logger = console; + if (!(typeof logger == 'object' && logger.log && logger.warn && logger.error)) + throw new Error('logger must implement log, warn and error methods'); + self.logger = logger; } } + + +function noop() {} diff --git a/spec/options.spec.js b/spec/options.spec.js index 0f5ba027a..58e56d54b 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -1227,9 +1227,10 @@ describe('Ajv Options', function () { */ var origConsoleWarn = console.warn; - var consoleCalled = false; + var consoleCalled; beforeEach(function() { + consoleCalled = false; console.warn = function() { consoleCalled = true; }; @@ -1237,37 +1238,28 @@ describe('Ajv Options', function () { afterEach(function() { console.warn = origConsoleWarn; - consoleCalled = false; }); - it('no custom logger is given - global console should be in use', function() { - + it('no custom logger is given - global console should be used', function() { var ajv = new Ajv({ meta: false }); ajv.compile({ - schema: { type: 'number' }, + type: 'number', minimum: 1 }); should.equal(consoleCalled, true); }); - it('custom logger is an object - logs should only reported to it', function() { - + it('custom logger is an object - logs should only report to it', function() { var loggerCalled = false; var logger = { - warn: function() { - loggerCalled = true; - }, - log: function() { - loggerCalled = true; - }, - error: function() { - loggerCalled = true; - } + warn: log, + log: log, + error: log }; var ajv = new Ajv({ @@ -1276,12 +1268,16 @@ describe('Ajv Options', function () { }); ajv.compile({ - schema: { type: 'number' }, + type: 'number', minimum: 1 }); should.equal(loggerCalled, true); should.equal(consoleCalled, false); + + function log() { + loggerCalled = true; + } }); it('logger option is false - no logs should be reported', function() { @@ -1291,21 +1287,20 @@ describe('Ajv Options', function () { }); ajv.compile({ - schema: { type: 'number' }, + type: 'number', minimum: 1 }); should.equal(consoleCalled, false); }); - it('logger option is an object but not implmemting the console functions - an error should be raised', function() { + it('logger option is an object without required methods - an error should be thrown', function() { (function(){ new Ajv({ meta: false, logger: {} }); - }).should.throw(Error, /logger must implement log, warn and error function/); + }).should.throw(Error, /logger must implement log, warn and error methods/); }); - }); }); From f336cdaaaea089e03d3901f95181707125a15edd Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 20 Nov 2017 20:54:59 +0000 Subject: [PATCH 046/333] 5.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aadd462db..57e0f9d2c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "5.3.0", + "version": "5.4.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From b68b269bb457e162cdb61cc9f0b3512ca0f350f2 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 20 Nov 2017 21:04:45 +0000 Subject: [PATCH 047/333] 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 c9a6f995a9e9ebb84580ffdbd925fe3806bad92c Mon Sep 17 00:00:00 2001 From: Philipp Thuerwaechter Date: Wed, 22 Nov 2017 08:37:42 +0100 Subject: [PATCH 048/333] Add method chaining to all add* and remove* methods According to issue #625 this enables crazy things like `new Ajv().addSchema(mySchema).validate(schema, data)` --- lib/ajv.d.ts | 18 ++++++++++++------ lib/ajv.js | 14 +++++++++++--- lib/keyword.js | 5 +++++ spec/ajv.spec.js | 20 ++++++++++++++++++-- spec/custom.spec.js | 16 ++++++++++++++++ 5 files changed, 62 insertions(+), 11 deletions(-) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index 60cd2775b..b687f7814 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -36,15 +36,17 @@ declare namespace ajv { * Adds schema to the instance. * @param {Object|Array} schema schema or array of schemas. If array is passed, `key` and other parameters will be ignored. * @param {String} key Optional schema key. Can be passed to `validate` method instead of schema object or id/ref. One schema per instance can have empty `id` and `key`. + * @return {Ajv} this for method chaining */ - addSchema(schema: Array | Object, key?: string): void; + addSchema(schema: Array | Object, key?: string): Ajv; /** * Add schema that will be used to validate other schemas * options in META_IGNORE_OPTIONS are alway set to false * @param {Object} schema schema object * @param {String} key optional schema key + * @return {Ajv} this for method chaining */ - addMetaSchema(schema: Object, key?: string): void; + addMetaSchema(schema: Object, key?: string): Ajv; /** * Validate schema * @param {Object|Boolean} schema schema to validate @@ -63,21 +65,24 @@ declare namespace ajv { * If RegExp is passed all schemas with key/id matching pattern but meta-schemas are removed. * Even if schema is referenced by other schemas it still can be removed as other schemas have local references. * @param {String|Object|RegExp|Boolean} schemaKeyRef key, ref, pattern to match key/ref or schema object + * @return {Ajv} this for method chaining */ - removeSchema(schemaKeyRef?: Object | string | RegExp | boolean): void; + removeSchema(schemaKeyRef?: Object | string | RegExp | boolean): Ajv; /** * Add custom format * @param {String} name format name * @param {String|RegExp|Function} format string is converted to RegExp; function should return boolean (true when valid) + * @return {Ajv} this for method chaining */ - addFormat(name: string, format: FormatValidator | FormatDefinition): void; + addFormat(name: string, format: FormatValidator | FormatDefinition): Ajv; /** * Define custom keyword * @this Ajv * @param {String} keyword custom keyword, should be a valid identifier, should be different from all standard, custom and macro keywords. * @param {Object} definition keyword definition object with properties `type` (type(s) which the keyword applies to), `validate` or `compile`. + * @return {Ajv} this for method chaining */ - addKeyword(keyword: string, definition: KeywordDefinition): void; + addKeyword(keyword: string, definition: KeywordDefinition): Ajv; /** * Get keyword definition * @this Ajv @@ -89,8 +94,9 @@ declare namespace ajv { * Remove keyword * @this Ajv * @param {String} keyword pre-defined or custom keyword. + * @return {Ajv} this for method chaining */ - removeKeyword(keyword: string): void; + removeKeyword(keyword: string): Ajv; /** * Convert array of error message objects to string * @param {Array} errors optional array of validation errors, if not passed errors from the instance are used. diff --git a/lib/ajv.js b/lib/ajv.js index 033caa6f3..cbee2adbc 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -126,6 +126,7 @@ function compile(schema, _meta) { * @param {String} key Optional schema key. Can be passed to `validate` method instead of schema object or id/ref. One schema per instance can have empty `id` and `key`. * @param {Boolean} _skipValidation true to skip schema validation. Used internally, option validateSchema should be used instead. * @param {Boolean} _meta true if schema is a meta-schema. Used internally, addMetaSchema should be used instead. + * @return {Ajv} this for method chaining */ function addSchema(schema, key, _skipValidation, _meta) { if (Array.isArray(schema)){ @@ -138,6 +139,7 @@ function addSchema(schema, key, _skipValidation, _meta) { key = resolve.normalizeId(key || id); checkUnique(this, key); this._schemas[key] = this._addSchema(schema, _skipValidation, _meta, true); + return this; } @@ -148,9 +150,11 @@ function addSchema(schema, key, _skipValidation, _meta) { * @param {Object} schema schema object * @param {String} key optional schema key * @param {Boolean} skipValidation true to skip schema validation, can be used to override validateSchema option for meta-schema + * @return {Ajv} this for method chaining */ function addMetaSchema(schema, key, skipValidation) { this.addSchema(schema, key, skipValidation, true); + return this; } @@ -247,25 +251,26 @@ function _getSchemaObj(self, keyRef) { * Even if schema is referenced by other schemas it still can be removed as other schemas have local references. * @this Ajv * @param {String|Object|RegExp} schemaKeyRef key, ref, pattern to match key/ref or schema object + * @return {Ajv} this for method chaining */ function removeSchema(schemaKeyRef) { if (schemaKeyRef instanceof RegExp) { _removeAllSchemas(this, this._schemas, schemaKeyRef); _removeAllSchemas(this, this._refs, schemaKeyRef); - return; + return this; } switch (typeof schemaKeyRef) { case 'undefined': _removeAllSchemas(this, this._schemas); _removeAllSchemas(this, this._refs); this._cache.clear(); - return; + return this; case 'string': var schemaObj = _getSchemaObj(this, schemaKeyRef); if (schemaObj) this._cache.del(schemaObj.cacheKey); delete this._schemas[schemaKeyRef]; delete this._refs[schemaKeyRef]; - return; + return this; case 'object': var serialize = this._opts.serialize; var cacheKey = serialize ? serialize(schemaKeyRef) : schemaKeyRef; @@ -277,6 +282,7 @@ function removeSchema(schemaKeyRef) { delete this._refs[id]; } } + return this; } @@ -427,10 +433,12 @@ function errorsText(errors, options) { * @this Ajv * @param {String} name format name * @param {String|RegExp|Function} format string is converted to RegExp; function should return boolean (true when valid) + * @return {Ajv} this for method chaining */ function addFormat(name, format) { if (typeof format == 'string') format = new RegExp(format); this._formats[name] = format; + return this; } diff --git a/lib/keyword.js b/lib/keyword.js index 85e64c600..5fcfb75fc 100644 --- a/lib/keyword.js +++ b/lib/keyword.js @@ -14,6 +14,7 @@ module.exports = { * @this Ajv * @param {String} keyword custom keyword, should be unique (including different from all standard, custom and macro keywords). * @param {Object} definition keyword definition object with properties `type` (type(s) which the keyword applies to), `validate` or `compile`. + * @return {Ajv} this for method chaining */ function addKeyword(keyword, definition) { /* jshint validthis: true */ @@ -91,6 +92,8 @@ function addKeyword(keyword, definition) { function checkDataType(dataType) { if (!RULES.types[dataType]) throw new Error('Unknown type ' + dataType); } + + return this; } @@ -111,6 +114,7 @@ function getKeyword(keyword) { * Remove keyword * @this Ajv * @param {String} keyword pre-defined or custom keyword. + * @return {Ajv} this for method chaining */ function removeKeyword(keyword) { /* jshint validthis: true */ @@ -127,4 +131,5 @@ function removeKeyword(keyword) { } } } + return this; } diff --git a/spec/ajv.spec.js b/spec/ajv.spec.js index c580f3e97..b947942c7 100644 --- a/spec/ajv.spec.js +++ b/spec/ajv.spec.js @@ -119,8 +119,7 @@ describe('Ajv', function () { describe('addSchema method', function() { it('should add and compile schema with key', function() { - var res = ajv.addSchema({ type: 'integer' }, 'int'); - should.not.exist(res); + ajv.addSchema({ type: 'integer' }, 'int'); var validate = ajv.getSchema('int'); validate .should.be.a('function'); @@ -217,6 +216,11 @@ describe('Ajv', function () { e.message .should.equal('schema id must be string'); } }); + + it('should return instance of itself', function() { + var res = ajv.addSchema({ type: 'integer' }, 'int'); + res.should.equal(ajv); + }); }); @@ -382,6 +386,13 @@ describe('Ajv', function () { should.not.exist(ajv._cache.get(str2)); ajv._cache.get(str3) .should.be.an('object'); }); + + it('should return instance of itself', function() { + var res = ajv + .addSchema({ type: 'integer' }, 'int') + .removeSchema('int'); + res.should.equal(ajv); + }); }); @@ -408,6 +419,11 @@ describe('Ajv', function () { testFormat(); }); + it('should return instance of itself', function() { + var res = ajv.addFormat('identifier', /^[a-z_$][a-z0-9_$]*$/i); + res.should.equal(ajv); + }); + function testFormat() { var validate = ajv.compile({ format: 'identifier' }); validate('Abc1') .should.equal(true); diff --git a/spec/custom.spec.js b/spec/custom.spec.js index 4ed6e1355..e7d7efb94 100644 --- a/spec/custom.spec.js +++ b/spec/custom.spec.js @@ -939,6 +939,13 @@ describe('Custom keywords', function () { }); }); + it('should return instance of itself', function() { + var res = ajv.addKeyword('any', { + validate: function() { return true; } + }); + res.should.equal(ajv); + }); + it('should throw if unknown type is passed', function() { should.throw(function() { addKeyword('custom1', 'wrongtype'); @@ -1041,6 +1048,15 @@ describe('Custom keywords', function () { validate(1) .should.equal(false); validate(2) .should.equal(true); }); + + it('should return instance of itself', function() { + var res = ajv + .addKeyword('any', { + validate: function() { return true; } + }) + .removeKeyword('any'); + res.should.equal(ajv); + }); }); From 8fe46ccc21cab4d973350f8ebf0a30c60ab4cbbc Mon Sep 17 00:00:00 2001 From: Philipp Thuerwaechter Date: Wed, 22 Nov 2017 17:32:46 +0100 Subject: [PATCH 049/333] Add method chaining syntax to API docu --- README.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ec778c377..2ae16a5f5 100644 --- a/README.md +++ b/README.md @@ -914,7 +914,7 @@ __Please note__: every time this method is called the errors are overwritten so If the schema is asynchronous (has `$async` keyword on the top level) this method returns a Promise. See [Asynchronous validation](#asynchronous-validation). -##### .addSchema(Array<Object>|Object schema [, String key]) +##### .addSchema(Array<Object>|Object schema [, String key]) -> Ajv Add schema(s) to validator instance. This method does not compile schemas (but it still validates them). Because of that dependencies can be added in any order and circular dependencies are supported. It also prevents unnecessary compilation of schemas that are containers for other schemas but not used as a whole. @@ -929,8 +929,14 @@ Although `addSchema` does not compile schemas, explicit compilation is not requi By default the schema is validated against meta-schema before it is added, and if the schema does not pass validation the exception is thrown. This behaviour is controlled by `validateSchema` option. +__Please note__: Ajv uses the [method chaining syntax](https://en.wikipedia.org/wiki/Method_chaining) for all methods with the prefix `add*` and `remove*`. +This allows you to do nice things like the following. -##### .addMetaSchema(Array<Object>|Object schema [, String key]) +```javascript +var validate = new Ajv().addSchema(schema).addFormat(name, regex).getSchema(uri); +``` + +##### .addMetaSchema(Array<Object>|Object schema [, String key]) -> Ajv 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). @@ -955,7 +961,7 @@ Errors will be available at `ajv.errors`. Retrieve compiled schema previously added with `addSchema` by the key passed to `addSchema` or by its full reference (id). The returned validating function has `schema` property with the reference to the original schema. -##### .removeSchema([Object schema|String key|String ref|RegExp pattern]) +##### .removeSchema([Object schema|String key|String ref|RegExp pattern]) -> Ajv Remove added/cached schema. Even if schema is referenced by other schemas it can be safely removed as dependent schemas have local references. @@ -968,7 +974,7 @@ Schema can be removed using: If no parameter is passed all schemas but meta-schemas will be removed and the cache will be cleared. -##### .addFormat(String name, String|RegExp|Function|Object format) +##### .addFormat(String name, String|RegExp|Function|Object format) -> Ajv Add custom format to validate strings or numbers. It can also be used to replace pre-defined formats for Ajv instance. @@ -986,7 +992,7 @@ If object is passed it should have properties `validate`, `compare` and `async`: Custom formats can be also added via `formats` option. -##### .addKeyword(String keyword, Object definition) +##### .addKeyword(String keyword, Object definition) -> Ajv Add custom validation keyword to Ajv instance. @@ -1027,7 +1033,7 @@ See [Defining custom keywords](#defining-custom-keywords) for more details. Returns custom keyword definition, `true` for pre-defined keywords and `false` if the keyword is unknown. -##### .removeKeyword(String keyword) +##### .removeKeyword(String keyword) -> Ajv Removes custom or pre-defined keyword so you can redefine them. From 3ca95ed54801d312b4afcc33e31797962c2a77cf Mon Sep 17 00:00:00 2001 From: Philipp Thuerwaechter Date: Wed, 22 Nov 2017 18:06:02 +0100 Subject: [PATCH 050/333] Add example for method chaining to README --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 2ae16a5f5..616b4d28c 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,17 @@ if (!valid) console.log(ajv.errorsText()); // ... ``` +or with method chaining + +```javascript +// ... +var validate = new Ajv() + .addSchema(cardSchema) + .getSchema('/card#/definitions/organisation'); +var valid = validate(data); +// ... +``` + See [API](#api) and [Options](#options) for more details. Ajv compiles schemas to functions and caches them in all cases (using schema serialized with [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) or a custom function as a key), so that the next time the same schema is used (not necessarily the same object instance) it won't be compiled again. From 4a179bd41e7916d4f9af7b1226c3304d05f8932f Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Wed, 22 Nov 2017 23:39:29 +0000 Subject: [PATCH 051/333] chore(package): update regenerator to version 0.11.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 57e0f9d2c..61f878889 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "nyc": "^11.0.2", "phantomjs-prebuilt": "^2.1.4", "pre-commit": "^1.1.1", - "regenerator": "0.10.0", + "regenerator": "0.11.1", "require-globify": "^1.3.0", "typescript": "^2.0.3", "uglify-js": "^3.1.5", From f936ef55e38ea0a5d2fd0fa225c3cf80081f71f7 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 24 Nov 2017 19:06:00 +0000 Subject: [PATCH 052/333] docs: more chaining examples --- CUSTOM.md | 131 ++++++++++++++++++++++++++++++++++++------------------ README.md | 38 +++++++--------- 2 files changed, 102 insertions(+), 67 deletions(-) diff --git a/CUSTOM.md b/CUSTOM.md index 6d32f40bf..f487ee4f2 100644 --- a/CUSTOM.md +++ b/CUSTOM.md @@ -42,18 +42,27 @@ __Please note__: In cases when validation flow is different depending on the sch Example. `constant` keyword (a synonym for draft6 keyword `const`, it is equivalent to `enum` keyword with one item): ```javascript -ajv.addKeyword('constant', { validate: function (schema, data) { - return typeof schema == 'object' && schema !== null - ? deepEqual(schema, data) - : schema === data; -}, errors: false }); +ajv.addKeyword('constant', { + validate: function (schema, data) { + return typeof schema == 'object' && schema !== null + ? deepEqual(schema, data) + : schema === data; + }, + errors: false +}); -var schema = { "constant": 2 }; +var schema = { + "constant": 2 +}; var validate = ajv.compile(schema); console.log(validate(2)); // true console.log(validate(3)); // false -var schema = { "constant": { "foo": "bar" } }; +var schema = { + "constant": { + "foo": "bar" + } +}; var validate = ajv.compile(schema); console.log(validate({foo: 'bar'})); // true console.log(validate({foo: 'baz'})); // false @@ -86,20 +95,31 @@ All custom keywords types can have an optional `metaSchema` property in their de Example. `range` and `exclusiveRange` keywords using compiled schema: ```javascript -ajv.addKeyword('range', { type: 'number', compile: function (sch, parentSchema) { - var min = sch[0]; - var max = sch[1]; - - return parentSchema.exclusiveRange === true - ? function (data) { return data > min && data < max; } - : function (data) { return data >= min && data <= max; } -}, errors: false, metaSchema: { - type: 'array', - items: [ { type: 'number' }, { type: 'number' } ], - additionalItems: false -} }); +ajv.addKeyword('range', { + type: 'number', + compile: function (sch, parentSchema) { + var min = sch[0]; + var max = sch[1]; + + return parentSchema.exclusiveRange === true + ? function (data) { return data > min && data < max; } + : function (data) { return data >= min && data <= max; } + }, + errors: false, + metaSchema: { + type: 'array', + items: [ + { type: 'number' }, + { type: 'number' } + ], + additionalItems: false + } +}); -var schema = { "range": [2, 4], "exclusiveRange": true }; +var schema = { + "range": [2, 4], + "exclusiveRange": true +}; var validate = ajv.compile(schema); console.log(validate(2.01)); // true console.log(validate(3.99)); // true @@ -122,27 +142,30 @@ In addition to the errors from the expanded schema macro keyword will add its ow Example. `range` and `exclusiveRange` keywords from the previous example defined with macro: ```javascript -ajv.addKeyword('range', { type: 'number', macro: function (schema, parentSchema) { - return { - minimum: schema[0], - maximum: schema[1], - exclusiveMinimum: !!parentSchema.exclusiveRange, - exclusiveMaximum: !!parentSchema.exclusiveRange - }; -}, metaSchema: { - type: 'array', - items: [ { type: 'number' }, { type: 'number' } ], - additionalItems: false -} }); +ajv.addKeyword('range', { + type: 'number', + macro: function (schema, parentSchema) { + return { + minimum: schema[0], + maximum: schema[1], + exclusiveMinimum: !!parentSchema.exclusiveRange, + exclusiveMaximum: !!parentSchema.exclusiveRange + }; + }, + metaSchema: { + type: 'array', + items: [ + { type: 'number' }, + { type: 'number' } + ], + additionalItems: false + } +}); ``` Example. `contains` keyword from version 5 proposals that requires that the array has at least one item matching schema (see https://github.com/json-schema/json-schema/wiki/contains-(v5-proposal)): ```javascript -ajv.addKeyword('contains', { type: 'array', macro: function (schema) { - return { "not": { "items": { "not": schema } } }; -} }); - var schema = { "contains": { "type": "number", @@ -151,7 +174,20 @@ var schema = { } }; -var validate = ajv.compile(schema); +var validate = ajv.addKeyword('contains', { + type: 'array', + macro: function (schema) { + return { + "not": { + "items": { + "not": schema + } + } + }; + } +}) +.compile(schema); + console.log(validate([1,2,3])); // false console.log(validate([2,3,4])); // false console.log(validate([3,4,5])); // true, number 5 matches schema inside "contains" @@ -177,14 +213,18 @@ While it can be more challenging to define keywords with "inline" functions, it Example `even` keyword: ```javascript -ajv.addKeyword('even', { type: 'number', inline: function (it, keyword, schema) { - var op = schema ? '===' : '!=='; - return 'data' + (it.dataLevel || '') + ' % 2 ' + op + ' 0'; -}, metaSchema: { type: 'boolean' } }); - var schema = { "even": true }; -var validate = ajv.compile(schema); +var validate = ajv.addKeyword('even', { + type: 'number', + inline: function (it, keyword, schema) { + var op = schema ? '===' : '!=='; + return 'data' + (it.dataLevel || '') + ' % 2 ' + op + ' 0'; + }, + metaSchema: { type: 'boolean' } +}) +.compile(schema); + console.log(validate(2)); // true console.log(validate(3)); // false ``` @@ -214,7 +254,10 @@ ajv.addKeyword('range', { statements: true, metaSchema: { type: 'array', - items: [ { type: 'number' }, { type: 'number' } ], + items: [ + { type: 'number' }, + { type: 'number' } + ], additionalItems: false } }); diff --git a/README.md b/README.md index 616b4d28c..fdf6144a2 100644 --- a/README.md +++ b/README.md @@ -145,23 +145,12 @@ or ```javascript // ... -ajv.addSchema(schema, 'mySchema'); -var valid = ajv.validate('mySchema', data); +var valid = ajv.addSchema(schema, 'mySchema') + .validate('mySchema', data); if (!valid) console.log(ajv.errorsText()); // ... ``` -or with method chaining - -```javascript -// ... -var validate = new Ajv() - .addSchema(cardSchema) - .getSchema('/card#/definitions/organisation'); -var valid = validate(data); -// ... -``` - See [API](#api) and [Options](#options) for more details. Ajv compiles schemas to functions and caches them in all cases (using schema serialized with [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) or a custom function as a key), so that the next time the same schema is used (not necessarily the same object instance) it won't be compiled again. @@ -294,8 +283,8 @@ or use `addSchema` method: ```javascript var ajv = new Ajv; -ajv.addSchema(defsSchema); -var validate = ajv.compile(schema); +var validate = ajv.addSchema(defsSchema) + .compile(schema); ``` See [Options](#options) and [addSchema](#api) method. @@ -451,14 +440,17 @@ Ajv allows defining keywords with: Example. `range` and `exclusiveRange` keywords using compiled schema: ```javascript -ajv.addKeyword('range', { type: 'number', compile: function (sch, parentSchema) { - var min = sch[0]; - var max = sch[1]; - - return parentSchema.exclusiveRange === true - ? function (data) { return data > min && data < max; } - : function (data) { return data >= min && data <= max; } -} }); +ajv.addKeyword('range', { + type: 'number', + compile: function (sch, parentSchema) { + var min = sch[0]; + var max = sch[1]; + + return parentSchema.exclusiveRange === true + ? function (data) { return data > min && data < max; } + : function (data) { return data >= min && data <= max; } + } +}); var schema = { "range": [2, 4], "exclusiveRange": true }; var validate = ajv.compile(schema); From a53d4e05034bca465640bc235fd97a125e81e1dd Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 24 Nov 2017 19:58:28 +0000 Subject: [PATCH 053/333] 5.5.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 61f878889..c093913cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "5.4.0", + "version": "5.5.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 05e2b38d67b3771631b5c4229bad77eaa641e209 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 24 Nov 2017 22:27:56 +0000 Subject: [PATCH 054/333] 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 055/333] 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 056/333] 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 057/333] 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 058/333] 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 059/333] 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 060/333] 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 061/333] 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 062/333] 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 b3b5b57d8eaf2dc98f03e1cf60cbbf21d4fab92a Mon Sep 17 00:00:00 2001 From: Travis Date: Fri, 1 Dec 2017 21:17:19 -0500 Subject: [PATCH 063/333] Make addSchema chainable when submitting an array of schemas. --- lib/ajv.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ajv.js b/lib/ajv.js index cbee2adbc..3148b8e37 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -131,7 +131,7 @@ function compile(schema, _meta) { function addSchema(schema, key, _skipValidation, _meta) { if (Array.isArray(schema)){ for (var i=0; i Date: Sat, 2 Dec 2017 10:24:27 +0000 Subject: [PATCH 064/333] 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 8aadb5d0f264543d9eaa64b8fb145c899356a287 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Dec 2017 10:41:40 +0000 Subject: [PATCH 065/333] 5.5.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c093913cf..7e9dfa200 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "5.5.0", + "version": "5.5.1", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From ce62f117ee1e485aca2bef662615f936e1449c19 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Dec 2017 12:32:45 +0000 Subject: [PATCH 066/333] 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 81b810f421c0f6b6bdb9ced06a84f038acab05ce Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Dec 2017 12:58:13 +0000 Subject: [PATCH 067/333] 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 7e9dfa200..4d831494b 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ ".tonic_example.js" ], "scripts": { - "eslint": "if-node-version \">=4\" eslint lib/*.js lib/compile/*.js spec scripts", + "eslint": "if-node-version \">=4\" eslint lib/*.js lib/compile/*.js spec/*.js scripts", "jshint": "jshint lib/*.js lib/**/*.js --exclude lib/dotjs/**/*", "test-spec": "mocha spec/*.spec.js -R spec $(if-node-version 7 echo --harmony-async-await)", "test-fast": "AJV_FAST_TEST=true npm run test-spec", diff --git a/spec/JSON-Schema-Test-Suite b/spec/JSON-Schema-Test-Suite index 8758156cb..870b9f7eb 160000 --- a/spec/JSON-Schema-Test-Suite +++ b/spec/JSON-Schema-Test-Suite @@ -1 +1 @@ -Subproject commit 8758156cb3bae615e5e75abcab6e757883d10669 +Subproject commit 870b9f7ebd35a238ba9d609424db51c7ca027d4a diff --git a/spec/json-schema.spec.js b/spec/json-schema.spec.js index bde31a2d9..e6262bfdc 100644 --- a/spec/json-schema.spec.js +++ b/spec/json-schema.spec.js @@ -10,13 +10,19 @@ var remoteRefs = { 'http://localhost:1234/integer.json': require('./JSON-Schema-Test-Suite/remotes/integer.json'), 'http://localhost:1234/subSchemas.json': require('./JSON-Schema-Test-Suite/remotes/subSchemas.json'), 'http://localhost:1234/folder/folderInteger.json': require('./JSON-Schema-Test-Suite/remotes/folder/folderInteger.json'), + 'http://localhost:1234/name.json': require('./JSON-Schema-Test-Suite/remotes/name.json') }; runTest(getAjvInstances(options, {meta: false}), 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, { + format: 'full', + formats: { + 'json-pointer': /^(?:\/(?:[^~/]|~0|~1)*)*$/ + } +}), 6, typeof window == 'object' ? suite(require('./JSON-Schema-Test-Suite/tests/draft6/{**/,}*.json', {mode: 'list'})) : './JSON-Schema-Test-Suite/tests/draft6/{**/,}*.json'); From 846e080c1781bcd2532bfabb274b6167a20753f1 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Dec 2017 13:25:19 +0000 Subject: [PATCH 068/333] 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 70916a3099e7cc7db3032c5b8841fb25138b524e Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Sat, 2 Dec 2017 15:43:46 +0000 Subject: [PATCH 069/333] chore(package): update json-schema-test to version 2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4d831494b..a1c5edb1e 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,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", From da43adb6aebd3c129a66081e4b579c026d3f9ca6 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Dec 2017 17:17:21 +0000 Subject: [PATCH 070/333] chore: update JSON-Schema-Test-Suite --- spec/JSON-Schema-Test-Suite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From a38c000e723ea393a63853d485e64a4513e50ee8 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Dec 2017 17:40:01 +0000 Subject: [PATCH 071/333] 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 072/333] 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 073/333] 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 074/333] 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 075/333] 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 076/333] 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 2e75568941c83db33bb5e54412aa426ee45da880 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Thu, 7 Dec 2017 19:37:42 +0000 Subject: [PATCH 077/333] chore(package): update regenerator to version 0.12.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a1c5edb1e..72f386fe6 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "nyc": "^11.0.2", "phantomjs-prebuilt": "^2.1.4", "pre-commit": "^1.1.1", - "regenerator": "0.11.1", + "regenerator": "0.12.2", "require-globify": "^1.3.0", "typescript": "^2.0.3", "uglify-js": "^3.1.5", From 88b3018b1a7b886e7993a8fe84a0300b3c1da316 Mon Sep 17 00:00:00 2001 From: Vohmyanin Sergey Vasilevich Date: Fri, 8 Dec 2017 21:22:45 +0300 Subject: [PATCH 078/333] replace Object to object due (Object=any), add CompilationContext interface --- lib/ajv.d.ts | 126 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 78 insertions(+), 48 deletions(-) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index b687f7814..b815fda5a 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -3,7 +3,7 @@ declare var ajv: { new (options?: ajv.Options): ajv.Ajv; ValidationError: ValidationError; MissingRefError: MissingRefError; - $dataMetaSchema: Object; + $dataMetaSchema: object; } declare namespace ajv { @@ -11,51 +11,51 @@ declare namespace ajv { /** * Validate data using schema * Schema will be compiled and cached (using serialized JSON as key, [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) is used to serialize by default). - * @param {String|Object|Boolean} schemaKeyRef key, ref or schema object + * @param {string|object|Boolean} schemaKeyRef key, ref or schema object * @param {Any} data to be validated * @return {Boolean} validation result. Errors from the last validation will be available in `ajv.errors` (and also in compiled schema: `schema.errors`). */ - validate(schemaKeyRef: Object | string | boolean, data: any): boolean | Thenable; + validate(schemaKeyRef: object | string | boolean, data: any): boolean | Thenable; /** * Create validating function for passed schema. - * @param {Object|Boolean} schema schema object + * @param {object|Boolean} schema schema object * @return {Function} validating function */ - compile(schema: Object | boolean): ValidateFunction; + compile(schema: object | boolean): ValidateFunction; /** * Creates validating function for passed schema with asynchronous loading of missing schemas. * `loadSchema` option should be a function that accepts schema uri and node-style callback. * @this Ajv - * @param {Object|Boolean} schema schema object + * @param {object|Boolean} schema schema object * @param {Boolean} meta optional true to compile meta-schema; this parameter can be skipped * @param {Function} callback optional node-style callback, it is always called with 2 parameters: error (or null) and validating function. * @return {Thenable} validating function */ - compileAsync(schema: Object | boolean, meta?: Boolean, callback?: (err: Error, validate: ValidateFunction) => any): Thenable; + compileAsync(schema: object | boolean, meta?: Boolean, callback?: (err: Error, validate: ValidateFunction) => any): Thenable; /** * Adds schema to the instance. - * @param {Object|Array} schema schema or array of schemas. If array is passed, `key` and other parameters will be ignored. - * @param {String} key Optional schema key. Can be passed to `validate` method instead of schema object or id/ref. One schema per instance can have empty `id` and `key`. + * @param {object|Array} schema schema or array of schemas. If array is passed, `key` and other parameters will be ignored. + * @param {string} key Optional schema key. Can be passed to `validate` method instead of schema object or id/ref. One schema per instance can have empty `id` and `key`. * @return {Ajv} this for method chaining */ - addSchema(schema: Array | Object, key?: string): Ajv; + addSchema(schema: Array | object, key?: string): Ajv; /** * Add schema that will be used to validate other schemas * options in META_IGNORE_OPTIONS are alway set to false - * @param {Object} schema schema object - * @param {String} key optional schema key + * @param {object} schema schema object + * @param {string} key optional schema key * @return {Ajv} this for method chaining */ - addMetaSchema(schema: Object, key?: string): Ajv; + addMetaSchema(schema: object, key?: string): Ajv; /** * Validate schema - * @param {Object|Boolean} schema schema to validate + * @param {object|Boolean} schema schema to validate * @return {Boolean} true if schema is valid */ - validateSchema(schema: Object | boolean): boolean; + validateSchema(schema: object | boolean): boolean; /** * Get compiled schema from the instance by `key` or `ref`. - * @param {String} keyRef `key` that was passed to `addSchema` or full schema reference (`schema.id` or resolved id). + * @param {string} keyRef `key` that was passed to `addSchema` or full schema reference (`schema.id` or resolved id). * @return {Function} schema validating function (with property `schema`). */ getSchema(keyRef: string): ValidateFunction; @@ -64,44 +64,44 @@ declare namespace ajv { * If no parameter is passed all schemas but meta-schemas are removed. * If RegExp is passed all schemas with key/id matching pattern but meta-schemas are removed. * Even if schema is referenced by other schemas it still can be removed as other schemas have local references. - * @param {String|Object|RegExp|Boolean} schemaKeyRef key, ref, pattern to match key/ref or schema object + * @param {string|object|RegExp|Boolean} schemaKeyRef key, ref, pattern to match key/ref or schema object * @return {Ajv} this for method chaining */ - removeSchema(schemaKeyRef?: Object | string | RegExp | boolean): Ajv; + removeSchema(schemaKeyRef?: object | string | RegExp | boolean): Ajv; /** * Add custom format - * @param {String} name format name - * @param {String|RegExp|Function} format string is converted to RegExp; function should return boolean (true when valid) + * @param {string} name format name + * @param {string|RegExp|Function} format string is converted to RegExp; function should return boolean (true when valid) * @return {Ajv} this for method chaining */ addFormat(name: string, format: FormatValidator | FormatDefinition): Ajv; /** * Define custom keyword * @this Ajv - * @param {String} keyword custom keyword, should be a valid identifier, should be different from all standard, custom and macro keywords. - * @param {Object} definition keyword definition object with properties `type` (type(s) which the keyword applies to), `validate` or `compile`. + * @param {string} keyword custom keyword, should be a valid identifier, should be different from all standard, custom and macro keywords. + * @param {object} definition keyword definition object with properties `type` (type(s) which the keyword applies to), `validate` or `compile`. * @return {Ajv} this for method chaining */ addKeyword(keyword: string, definition: KeywordDefinition): Ajv; /** * Get keyword definition * @this Ajv - * @param {String} keyword pre-defined or custom keyword. - * @return {Object|Boolean} custom keyword definition, `true` if it is a predefined keyword, `false` otherwise. + * @param {string} keyword pre-defined or custom keyword. + * @return {object|Boolean} custom keyword definition, `true` if it is a predefined keyword, `false` otherwise. */ - getKeyword(keyword: string): Object | boolean; + getKeyword(keyword: string): object | boolean; /** * Remove keyword * @this Ajv - * @param {String} keyword pre-defined or custom keyword. + * @param {string} keyword pre-defined or custom keyword. * @return {Ajv} this for method chaining */ removeKeyword(keyword: string): Ajv; /** * Convert array of error message objects to string - * @param {Array} errors optional array of validation errors, if not passed errors from the instance are used. - * @param {Object} options optional options with properties `separator` and `dataVar`. - * @return {String} human readable string with all errors descriptions + * @param {Array} errors optional array of validation errors, if not passed errors from the instance are used. + * @param {object} options optional options with properties `separator` and `dataVar`. + * @return {string} human readable string with all errors descriptions */ errorsText(errors?: Array, options?: ErrorsTextOptions): string; errors?: Array; @@ -115,17 +115,17 @@ declare namespace ajv { ( data: any, dataPath?: string, - parentData?: Object | Array, + parentData?: object | Array, parentDataProperty?: string | number, - rootData?: Object | Array + rootData?: object | Array ): boolean | Thenable; - schema?: Object | boolean; + schema?: object | boolean; errors?: null | Array; - refs?: Object; + refs?: object; refVal?: Array; - root?: ValidateFunction | Object; + root?: ValidateFunction | object; $async?: true; - source?: Object; + source?: object; } interface Options { @@ -136,19 +136,19 @@ declare namespace ajv { uniqueItems?: boolean; unicode?: boolean; format?: string; - formats?: Object; + formats?: object; unknownFormats?: true | string[] | 'ignore'; - schemas?: Array | Object; + schemas?: Array | object; schemaId?: '$id' | 'id'; missingRefs?: true | 'ignore' | 'fail'; extendRefs?: true | 'ignore' | 'fail'; - loadSchema?: (uri: string, cb?: (err: Error, schema: Object) => void) => Thenable; + loadSchema?: (uri: string, cb?: (err: Error, schema: object) => void) => Thenable; removeAdditional?: boolean | 'all' | 'failing'; useDefaults?: boolean | 'shared'; coerceTypes?: boolean | 'array'; async?: boolean | string; transpile?: string | ((code: string) => string); - meta?: boolean | Object; + meta?: boolean | object; validateSchema?: boolean | 'log'; addUsedSchema?: boolean; inlineRefs?: boolean | number; @@ -160,7 +160,7 @@ declare namespace ajv { messages?: boolean; sourceCode?: boolean; processCode?: (code: string) => string; - cache?: Object; + cache?: object; } type FormatValidator = string | RegExp | ((data: string) => boolean | Thenable); @@ -176,27 +176,57 @@ declare namespace ajv { async?: boolean; $data?: boolean; errors?: boolean | string; - metaSchema?: Object; + metaSchema?: object; // schema: false makes validate not to expect schema (ValidateFunction) schema?: boolean; modifying?: boolean; valid?: boolean; // one and only one of the following properties should be present validate?: SchemaValidateFunction | ValidateFunction; - compile?: (schema: any, parentSchema: Object) => ValidateFunction; - macro?: (schema: any, parentSchema: Object) => Object | boolean; - inline?: (it: Object, keyword: string, schema: any, parentSchema: Object) => string; + compile?: (schema: any, parentSchema: object, it: CompilationContext) => ValidateFunction; + macro?: (schema: any, parentSchema: object, it: CompilationContext) => object | boolean; + inline?: (it: CompilationContext, keyword: string, schema: any, parentSchema: object) => string; + } + + interface CompilationContext { + level: number; + dataLevel: number; + schema: any; + schemaPath: string; + baseId: string; + async: boolean; + opts: Options; + formats: { + [index: string]: FormatDefinition | undefined; + }; + compositeRule: boolean; + validate: (schema: object) => boolean; + util: { + copy(obj: any, target?: any): any; + toHash(source: string[]): { [index: string]: true | undefined }; + equal(obj: any, target: any): boolean; + getProperty(str: string): string; + schemaHasRules(schema: object, rules: any): string; + escapeQuotes(str: string): string; + toQuotedString(str: string): string; + getData(jsonPointer: string, dataLevel: number, paths: string[]): string; + escapeJsonPointer(str: string): string; + unescapeJsonPointer(str: string): string; + escapeFragment(str: string): string; + unescapeFragment(str: string): string; + }; + self: Ajv; } interface SchemaValidateFunction { ( schema: any, data: any, - parentSchema?: Object, + parentSchema?: object, dataPath?: string, - parentData?: Object | Array, + parentData?: object | Array, parentDataProperty?: string | number, - rootData?: Object | Array + rootData?: object | Array ): boolean | Thenable; errors?: Array; } @@ -217,7 +247,7 @@ declare namespace ajv { message?: string; // These are added with the `verbose` option. schema?: any; - parentSchema?: Object; + parentSchema?: object; data?: any; } From 7a7812f4efe4612df700a23ca131432151a1d51c Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 9 Dec 2017 13:05:43 +0000 Subject: [PATCH 079/333] chore: regenerator semver --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 72f386fe6..e4424e090 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "nyc": "^11.0.2", "phantomjs-prebuilt": "^2.1.4", "pre-commit": "^1.1.1", - "regenerator": "0.12.2", + "regenerator": "^0.12.2", "require-globify": "^1.3.0", "typescript": "^2.0.3", "uglify-js": "^3.1.5", From 90bd2816a2f2579a85dd1a025c7bf44a33b9d825 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 11 Dec 2017 19:40:11 +0000 Subject: [PATCH 080/333] chore: update typescript --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e4424e090..81dd9d357 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "pre-commit": "^1.1.1", "regenerator": "^0.12.2", "require-globify": "^1.3.0", - "typescript": "^2.0.3", + "typescript": "^2.6.2", "uglify-js": "^3.1.5", "watch": "^1.0.0" } From b915832018e7da88895490710e1bcfd24212d488 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 11 Dec 2017 19:51:55 +0000 Subject: [PATCH 081/333] docs: update performance chart --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fdf6144a2..387c81d8c 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Currently Ajv is the fastest and the most standard compliant validator according Performance of different validators by [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark): -[![performance](https://chart.googleapis.com/chart?chxt=x,y&cht=bhs&chco=76A4FB&chls=2.0&chbh=32,4,1&chs=600x416&chxl=-1:%7Cajv%7Cis-my-json-valid%7Cjsen%7Cschemasaurus%7Cthemis%7Cz-schema%7Cjsck%7Cjsonschema%7Cskeemas%7Ctv4%7Cjayschema&chd=t:100,68,61,22.8,17.6,6.6,2.7,0.9,0.7,0.4,0.1)](https://github.com/ebdrup/json-schema-benchmark/blob/master/README.md#performance) +[![performance](https://chart.googleapis.com/chart?chxt=x,y&cht=bhs&chco=76A4FB&chls=2.0&chbh=32,4,1&chs=600x416&chxl=-1:|djv|ajv|json-schema-validator-generator|jsen|is-my-json-valid|themis|z-schema|jsck|skeemas|json-schema-library|tv4&chd=t:100,98,72.1,66.8,50.1,15.1,6.1,3.8,1.2,0.7,0.2)](https://github.com/ebdrup/json-schema-benchmark/blob/master/README.md#performance) ## Features From cecd4ecca66abee0441a8277c647856b09454f82 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 16 Dec 2017 20:01:47 +0000 Subject: [PATCH 082/333] 5.5.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 81dd9d357..5b2ba14d2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "5.5.1", + "version": "5.5.2", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 184f0f449e685ea4b6b14631a9f5127898fd57d8 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Thu, 21 Dec 2017 16:43:24 +0000 Subject: [PATCH 083/333] chore(package): update karma to version 2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5b2ba14d2..a68ee2381 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "js-beautify": "^1.7.3", "jshint": "^2.9.4", "json-schema-test": "^2.0.0", - "karma": "^1.0.0", + "karma": "^2.0.0", "karma-chrome-launcher": "^2.0.0", "karma-mocha": "^1.1.1", "karma-phantomjs-launcher": "^1.0.0", From cd0c9c35ffdb5adfb2eb199a8d5ab774bd1e06b0 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Sun, 24 Dec 2017 11:52:36 +0000 Subject: [PATCH 084/333] chore: pin uglify-js to 3.2.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5b2ba14d2..0f26a185c 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "regenerator": "^0.12.2", "require-globify": "^1.3.0", "typescript": "^2.6.2", - "uglify-js": "^3.1.5", + "uglify-js": "3.2.2", "watch": "^1.0.0" } } From f7ff65b22860bedbffa01d1eaad91f6bfa7b7208 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 24 Dec 2017 13:56:40 +0000 Subject: [PATCH 085/333] chore: update eslint --- .eslintrc.yml | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index dfd66150d..9fe920d6a 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -5,7 +5,7 @@ env: rules: block-scoped-var: 2 callback-return: 2 - complexity: [2, 13] + complexity: [2, 16] curly: [2, multi-or-nest, consistent] dot-location: [2, property] dot-notation: 2 diff --git a/package.json b/package.json index 5b2ba14d2..b2bda969d 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "coveralls": "^3.0.0", "del-cli": "^1.1.0", "dot": "^1.0.3", - "eslint": "^4.1.0", + "eslint": "^4.14.0", "gh-pages-generator": "^0.2.0", "glob": "^7.0.0", "if-node-version": "^1.0.0", From db8e9702f221c57ba488bcd91d9573301b2d65aa Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Sun, 24 Dec 2017 20:25:54 +0000 Subject: [PATCH 086/333] chore(package): update uglify-js to version 3.3.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6e0b8d581..5566a4a5b 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "regenerator": "^0.12.2", "require-globify": "^1.3.0", "typescript": "^2.6.2", - "uglify-js": "3.2.2", + "uglify-js": "3.3.1", "watch": "^1.0.0" } } From 7f064165263e6b1b49c12bbe2e2bf0d50c700eac Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Wed, 3 Jan 2018 15:47:04 +0000 Subject: [PATCH 087/333] chore: update uglify-js version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 54963ba02..678799c82 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "regenerator": "^0.12.2", "require-globify": "^1.3.0", "typescript": "^2.6.2", - "uglify-js": "3.3.1", + "uglify-js": "^3.3.1", "watch": "^1.0.0" } } From a5a0f0bf4d065fe026eff881e808205ff01a7e8b Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Thu, 4 Jan 2018 19:00:54 +0000 Subject: [PATCH 088/333] chore(package): update browserify to version 15.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 678799c82..c79c69bb8 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "ajv-async": "^0.1.0", "bluebird": "^3.1.5", "brfs": "^1.4.3", - "browserify": "^14.1.0", + "browserify": "^15.0.0", "chai": "^4.0.1", "coveralls": "^3.0.0", "del-cli": "^1.1.0", From c56519b9f485ad8743c36a4d2f4767419459135e Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 7 Jan 2018 12:58:38 +0000 Subject: [PATCH 089/333] 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", From 02a202cfbc4bc78cca5632f6f09ba96a727302c3 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 7 Jan 2018 15:40:35 +0000 Subject: [PATCH 090/333] docs: remove "beta" from readme --- README.md | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 9f66915d2..1ff44c5d3 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,11 @@ # Ajv: Another JSON Schema Validator -The fastest JSON Schema validator for Node.js and browser with draft-07 support. +The fastest JSON Schema validator for Node.js and browser. Supports draft-04/06/07. [![Build Status](https://travis-ci.org/epoberezkin/ajv.svg?branch=master)](https://travis-ci.org/epoberezkin/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) [![Greenkeeper badge](https://badges.greenkeeper.io/epoberezkin/ajv.svg)](https://greenkeeper.io/) @@ -18,7 +17,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/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). +[Ajv version 6.0.0](https://github.com/epoberezkin/ajv/releases/tag/v6.0.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 schemas you need to explicitly add the meta-schema to the validator instance: @@ -118,12 +117,6 @@ Currently Ajv is the only validator that passes all the tests from [JSON Schema npm install ajv ``` -or to install [version 6](https://github.com/epoberezkin/ajv/tree/beta): - -``` -npm install ajv@beta -``` - ## Getting started @@ -1282,15 +1275,15 @@ Please see [Contributing guidelines](https://github.com/epoberezkin/ajv/blob/mas See https://github.com/epoberezkin/ajv/releases -__Please note__: [Changes in version 5.0.0](https://github.com/epoberezkin/ajv/releases/tag/5.0.0). +__Please note__: [Changes in version 6.0.0](https://github.com/epoberezkin/ajv/releases/tag/v6.0.0). -[Changes in version 4.6.0](https://github.com/epoberezkin/ajv/releases/tag/4.6.0). +[Version 5.0.0](https://github.com/epoberezkin/ajv/releases/tag/5.0.0). -[Changes in version 4.0.0](https://github.com/epoberezkin/ajv/releases/tag/4.0.0). +[Version 4.0.0](https://github.com/epoberezkin/ajv/releases/tag/4.0.0). -[Changes in version 3.0.0](https://github.com/epoberezkin/ajv/releases/tag/3.0.0). +[Version 3.0.0](https://github.com/epoberezkin/ajv/releases/tag/3.0.0). -[Changes in version 2.0.0](https://github.com/epoberezkin/ajv/releases/tag/2.0.0). +[Version 2.0.0](https://github.com/epoberezkin/ajv/releases/tag/2.0.0). ## License From 06e86d2e91bb29da22de814b72c12f077b3a27bf Mon Sep 17 00:00:00 2001 From: Danila Shutov Date: Thu, 28 Dec 2017 14:08:43 +0300 Subject: [PATCH 091/333] Add publishing scripts --- .travis.yml | 1 + scripts/publish-built-version | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100755 scripts/publish-built-version diff --git a/.travis.yml b/.travis.yml index 0ada0bbab..c0e4ef775 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ after_script: - codeclimate-test-reporter < coverage/lcov.info - coveralls < coverage/lcov.info - scripts/travis-gh-pages + - scripts/publish-built-version notifications: webhooks: urls: diff --git a/scripts/publish-built-version b/scripts/publish-built-version new file mode 100755 index 000000000..53eeae5d9 --- /dev/null +++ b/scripts/publish-built-version @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +set -e + +if [[ -n $TRAVIS_TAG && $TRAVIS_JOB_NUMBER =~ ".3" ]]; then + echo "About to publish $TRAVIS_TAG to ajv-dist..." + + git config user.email "$GIT_USER_EMAIL" + git config user.name "$GIT_USER_NAME" + + git clone https://${GITHUB_TOKEN}@github.com/dcbrwn/ajv-dist.git ../ajv-dist + + mkdir -p ../ajv-dist/dist + cp ./dist/* ../ajv-dist/dist + cp ./bower.json ../ajv-dist + cd ../ajv-dist + + if [[ `git status --porcelain` ]]; then + echo "Changes detected. Updating master branch..." + git add -A + git commit -m "updated by travis build #$TRAVIS_BUILD_NUMBER" + git push --quiet origin master > /dev/null 2>&1 + fi + + echo "Publishing tag..." + + git tag $TRAVIS_TAG + git push --tags > /dev/null 2>&1 + + echo "Done" +fi From 94d39a46f957f16053d8bea56d69d3f38686a14c Mon Sep 17 00:00:00 2001 From: Danila Shutov Date: Tue, 9 Jan 2018 12:12:02 +0300 Subject: [PATCH 092/333] Change target repo --- scripts/publish-built-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/publish-built-version b/scripts/publish-built-version index 53eeae5d9..bbac30258 100755 --- a/scripts/publish-built-version +++ b/scripts/publish-built-version @@ -8,7 +8,7 @@ if [[ -n $TRAVIS_TAG && $TRAVIS_JOB_NUMBER =~ ".3" ]]; then git config user.email "$GIT_USER_EMAIL" git config user.name "$GIT_USER_NAME" - git clone https://${GITHUB_TOKEN}@github.com/dcbrwn/ajv-dist.git ../ajv-dist + git clone https://${GITHUB_TOKEN}@github.com/epoberezkin/ajv-dist.git ../ajv-dist mkdir -p ../ajv-dist/dist cp ./dist/* ../ajv-dist/dist From b7cee43bbeb4313cfa8f2c6f2e3df6418616ccbf Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Thu, 11 Jan 2018 21:43:42 +0000 Subject: [PATCH 093/333] update draft-07 meta-schema --- lib/refs/json-schema-draft-07.json | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/refs/json-schema-draft-07.json b/lib/refs/json-schema-draft-07.json index 554c5d010..5bee90ec1 100644 --- a/lib/refs/json-schema-draft-07.json +++ b/lib/refs/json-schema-draft-07.json @@ -59,14 +59,14 @@ "description": { "type": "string" }, - "default": {}, + "default": true, "readOnly": { "type": "boolean", "default": false }, "examples": { "type": "array", - "items": {} + "items": true }, "multipleOf": { "type": "number", @@ -96,7 +96,7 @@ { "$ref": "#" }, { "$ref": "#/definitions/schemaArray" } ], - "default": {} + "default": true }, "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, @@ -135,9 +135,10 @@ } }, "propertyNames": { "$ref": "#" }, - "const": {}, + "const": true, "enum": { "type": "array", + "items": true, "minItems": 1, "uniqueItems": true }, @@ -163,5 +164,5 @@ "oneOf": { "$ref": "#/definitions/schemaArray" }, "not": { "$ref": "#" } }, - "default": {} + "default": true } From cea2e39b8e27bdc4bdad631b7be666c2baeba934 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Thu, 11 Jan 2018 21:46:02 +0000 Subject: [PATCH 094/333] 6.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0fb67aa98..12521ba99 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.0.0", + "version": "6.0.1", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From ef40fbbda6f165d95a82dddb9103d6475b0fd130 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Fri, 12 Jan 2018 08:32:54 +0000 Subject: [PATCH 095/333] docs: mark errorDataPath option as deprecated --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1ff44c5d3..a17eb5e1d 100644 --- a/README.md +++ b/README.md @@ -1053,7 +1053,7 @@ Defaults: loopRequired: Infinity, ownProperties: false, multipleOfPrecision: false, - errorDataPath: 'object', + errorDataPath: 'object', // deprecated messages: true, sourceCode: false, processCode: undefined, // function (str: string): string {} @@ -1144,7 +1144,7 @@ Defaults: - _loopRequired_: by default `required` keyword is compiled into a single expression (or a sequence of statements in `allErrors` mode). In case of a very large number of properties in this keyword it may result in a very big validation function. Pass integer to set the number of properties above which `required` keyword will be validated in a loop - smaller validation function size but also worse performance. - _ownProperties_: by default Ajv iterates over all enumerable object properties; when this option is `true` only own enumerable object properties (i.e. found directly on the object rather than on its prototype) are iterated. Contributed by @mbroadst. - _multipleOfPrecision_: by default `multipleOf` keyword is validated by comparing the result of division with parseInt() of that result. It works for dividers that are bigger than 1. For small dividers such as 0.01 the result of the division is usually not integer (even when it should be integer, see issue [#84](https://github.com/epoberezkin/ajv/issues/84)). If you need to use fractional dividers set this option to some positive integer N to have `multipleOf` validated using this formula: `Math.abs(Math.round(division) - division) < 1e-N` (it is slower but allows for float arithmetics deviations). -- _errorDataPath_: set `dataPath` to point to 'object' (default) or to 'property' when validating keywords `required`, `additionalProperties` and `dependencies`. +- _errorDataPath_ (deprecated): set `dataPath` to point to 'object' (default) or to 'property' when validating keywords `required`, `additionalProperties` and `dependencies`. - _messages_: Include human-readable messages in errors. `true` by default. `false` can be passed when custom messages are used (e.g. with [ajv-i18n](https://github.com/epoberezkin/ajv-i18n)). - _sourceCode_: add `sourceCode` property to validating function (for debugging; this code can be different from the result of toString call). - _processCode_: an optional function to process generated code before it is passed to Function constructor. It can be used to either beautify (the validating function is generated without line-breaks) or to transpile code. Starting from version 5.0.0 this option replaced options: From 8962d23bcb4aa9c1925e7a5674f9673509d5a4f6 Mon Sep 17 00:00:00 2001 From: Mike Ralphson Date: Fri, 12 Jan 2018 09:44:49 +0000 Subject: [PATCH 096/333] README.md; fix two typos --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a17eb5e1d..93b52cd41 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ __Please note__: To use Ajv with draft-06 schemas you need to explicitly add the ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json')); ``` -To use Ajv with draft-04 schemas in addition to explicitely adding meta-schema you also need to use option schemaId: +To use Ajv with draft-04 schemas in addition to explicitly adding meta-schema you also need to use option schemaId: ```javascript var ajv = new Ajv({schemaId: 'id'}); @@ -231,7 +231,7 @@ JSON Schema specification defines several annotation keywords that describe sche - `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. +__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 keywords from the instance. ## Formats From e0586f6cda1159b39cc2be128c15ef6d415ce819 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Thu, 18 Jan 2018 02:12:11 +0000 Subject: [PATCH 097/333] chore(package): update mocha to version 5.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 12521ba99..10496b8e7 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "karma-mocha": "^1.1.1", "karma-phantomjs-launcher": "^1.0.0", "karma-sauce-launcher": "^1.1.0", - "mocha": "^4.0.0", + "mocha": "^5.0.0", "nyc": "^11.0.2", "phantomjs-prebuilt": "^2.1.4", "pre-commit": "^1.1.1", From 315ac7dacf4527d16869a47c384c7b3aced3062f Mon Sep 17 00:00:00 2001 From: Kevin Partington Date: Wed, 17 Jan 2018 22:26:36 -0600 Subject: [PATCH 098/333] Add ESLint to "Some packages using Ajv" section --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a17eb5e1d..1ad5cfa0b 100644 --- a/README.md +++ b/README.md @@ -1250,6 +1250,7 @@ If you have published a useful plugin please submit a PR to add it to the next s - [electron-builder](https://github.com/electron-userland/electron-builder) - a solution to package and build a ready for distribution Electron app - [addons-linter](https://github.com/mozilla/addons-linter) - Mozilla Add-ons Linter - [gh-pages-generator](https://github.com/epoberezkin/gh-pages-generator) - multi-page site generator converting markdown files to GitHub pages +- [ESLint](https://github.com/eslint/eslint) - the pluggable linting utility for JavaScript and JSX ## Tests From 4a12ca3b9214d6df9caa7019d80a043b44c6c9b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Lehni?= Date: Wed, 24 Jan 2018 23:38:14 +0100 Subject: [PATCH 099/333] Fix additionalProperties error with errorDataPath --- lib/dot/errors.def | 2 +- spec/errors.spec.js | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/dot/errors.def b/lib/dot/errors.def index 855b8821d..e8df5ddb0 100644 --- a/lib/dot/errors.def +++ b/lib/dot/errors.def @@ -94,7 +94,7 @@ 'false schema': "'boolean schema is false'", $ref: "'can\\\'t resolve reference {{=it.util.escapeQuotes($schema)}}'", additionalItems: "'should NOT have more than {{=$schema.length}} items'", - additionalProperties: "'should NOT have additional properties'", + additionalProperties: "'{{? it.opts._errorDataPathProperty }}is an invalid additional property{{??}}should NOT have additional properties{{?}}'", anyOf: "'should match some schema in anyOf'", const: "'should be equal to constant'", contains: "'should contain a valid item'", diff --git a/spec/errors.spec.js b/spec/errors.spec.js index 0a7a5fb57..e0b5783e3 100644 --- a/spec/errors.spec.js +++ b/spec/errors.spec.js @@ -64,22 +64,23 @@ describe('Validation errors', function () { , invalidData = { foo: 1, bar: 2, baz: 3, quux: 4 }; var path = pathFunc(errorDataPath); + var msg = additionalFunc(errorDataPath); var validate = ajv.compile(schema); shouldBeValid(validate, data); shouldBeInvalid(validate, invalidData); - shouldBeError(validate.errors[0], 'additionalProperties', '#/additionalProperties', path("['baz']"), undefined, { additionalProperty: 'baz' }); + shouldBeError(validate.errors[0], 'additionalProperties', '#/additionalProperties', path("['baz']"), msg, { additionalProperty: 'baz' }); var validateJP = ajvJP.compile(schema); shouldBeValid(validateJP, data); shouldBeInvalid(validateJP, invalidData); - shouldBeError(validateJP.errors[0], 'additionalProperties', '#/additionalProperties', path("/baz"), undefined, { additionalProperty: 'baz' }); + shouldBeError(validateJP.errors[0], 'additionalProperties', '#/additionalProperties', path("/baz"), msg, { additionalProperty: 'baz' }); var fullValidate = fullAjv.compile(schema); shouldBeValid(fullValidate, data); shouldBeInvalid(fullValidate, invalidData, 2); - shouldBeError(fullValidate.errors[0], 'additionalProperties', '#/additionalProperties', path('/baz'), undefined, { additionalProperty: 'baz' }); - shouldBeError(fullValidate.errors[1], 'additionalProperties', '#/additionalProperties', path('/quux'), undefined, { additionalProperty: 'quux' }); + shouldBeError(fullValidate.errors[0], 'additionalProperties', '#/additionalProperties', path('/baz'), msg, { additionalProperty: 'baz' }); + shouldBeError(fullValidate.errors[1], 'additionalProperties', '#/additionalProperties', path('/quux'), msg, { additionalProperty: 'quux' }); if (errorDataPath == 'property') { fullValidate.errors @@ -184,7 +185,7 @@ describe('Validation errors', function () { delete invalidData2[98]; var path = pathFunc(errorDataPath); - var msg = msgFunc(errorDataPath); + var msg = requiredFunc(errorDataPath); test(); @@ -368,7 +369,7 @@ describe('Validation errors', function () { , invalidData2 = { bar: 2 }; var path = pathFunc(errorDataPath); - var msg = msgFunc(errorDataPath); + var msg = requiredFunc(errorDataPath); var validate = ajv.compile(schema); shouldBeValid(validate, data); @@ -399,7 +400,7 @@ describe('Validation errors', function () { }; } - function msgFunc(errorDataPath) { + function requiredFunc(errorDataPath) { return function (prop) { return errorDataPath == 'property' ? 'is a required property' @@ -407,6 +408,12 @@ describe('Validation errors', function () { }; } + function additionalFunc(errorDataPath) { + return errorDataPath == 'property' + ? 'is an invalid additional property' + : 'should NOT have additional properties'; + } + it('"items" errors should include item index without quotes in dataPath (#48)', function() { var schema1 = { From 5a1c6802e67fabca610f8d0bd805b4791c1c7475 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 26 Jan 2018 18:44:54 +0000 Subject: [PATCH 100/333] 6.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 10496b8e7..178fb9de4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.0.1", + "version": "6.1.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 74bb715725768bf7d9eeb1d7cd98870661490659 Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Mon, 29 Jan 2018 15:22:57 +0300 Subject: [PATCH 101/333] Added 'auto' into schemaId type definition #668 --- lib/ajv.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index ba5677c4c..68e855deb 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -139,7 +139,7 @@ declare namespace ajv { formats?: object; unknownFormats?: true | string[] | 'ignore'; schemas?: Array | object; - schemaId?: '$id' | 'id'; + schemaId?: '$id' | 'id' | 'auto'; missingRefs?: true | 'ignore' | 'fail'; extendRefs?: true | 'ignore' | 'fail'; loadSchema?: (uri: string, cb?: (err: Error, schema: object) => void) => Thenable; From 4ec60cf49113e8e00774a1fc6d52c8269231a7bc Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 29 Jan 2018 21:27:51 +0000 Subject: [PATCH 102/333] 6.1.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 178fb9de4..fb4108e3f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.1.0", + "version": "6.1.1", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 3200ab0c5a8eefe5ca8334a20d824c856fb526fe Mon Sep 17 00:00:00 2001 From: Danila Shutov Date: Fri, 2 Feb 2018 12:13:27 +0300 Subject: [PATCH 103/333] Clean up dist folder before copying new files --- scripts/publish-built-version | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/publish-built-version b/scripts/publish-built-version index bbac30258..2796ed25f 100755 --- a/scripts/publish-built-version +++ b/scripts/publish-built-version @@ -10,9 +10,10 @@ if [[ -n $TRAVIS_TAG && $TRAVIS_JOB_NUMBER =~ ".3" ]]; then git clone https://${GITHUB_TOKEN}@github.com/epoberezkin/ajv-dist.git ../ajv-dist - mkdir -p ../ajv-dist/dist - cp ./dist/* ../ajv-dist/dist - cp ./bower.json ../ajv-dist + rm -rf ../ajv-dist/dist + mkdir ../ajv-dist/dist + cp ./dist/ajv.* ../ajv-dist/dist + cat bower.json | sed 's/"name": "ajv"/"name": "ajv-dist"/' > ../ajv-dist/bower.json cd ../ajv-dist if [[ `git status --porcelain` ]]; then From cb85eea789dbf69b2122d703bf33a2f26556e747 Mon Sep 17 00:00:00 2001 From: Igor Savin Date: Fri, 2 Feb 2018 23:27:06 +0200 Subject: [PATCH 104/333] Failing test for integer coercion refs #693 --- spec/coercion.spec.js | 44 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/spec/coercion.spec.js b/spec/coercion.spec.js index 42f2f586d..1bedfe237 100644 --- a/spec/coercion.spec.js +++ b/spec/coercion.spec.js @@ -260,7 +260,7 @@ describe('Type coercion', function () { }); - it('should coerce to multiple types in order', function() { + it('should coerce to multiple types in order with number type', function() { var schema = { type: 'object', properties: { @@ -302,6 +302,48 @@ describe('Type coercion', function () { }); }); + it('should coerce to multiple types in order with integer type', function() { + var schema = { + type: 'object', + properties: { + foo: { + type: [ 'integer', 'boolean', 'null' ] + } + } + }; + + instances.forEach(function (_ajv) { + var data; + + _ajv.validate(schema, data = { foo: '1' }) .should.equal(true); + data .should.eql({ foo: 1 }); + + _ajv.validate(schema, data = { foo: '1.5' }) .should.equal(true); + data .should.eql({ foo: 1.5 }); + + _ajv.validate(schema, data = { foo: 'false' }) .should.equal(true); + data .should.eql({ foo: false }); + + _ajv.validate(schema, data = { foo: 1 }) .should.equal(true); + data .should.eql({ foo: 1 }); // no coercion + + _ajv.validate(schema, data = { foo: true }) .should.equal(true); + data .should.eql({ foo: true }); // no coercion + + _ajv.validate(schema, data = { foo: null }) .should.equal(true); + data .should.eql({ foo: null }); // no coercion + + _ajv.validate(schema, data = { foo: 'abc' }) .should.equal(false); + data .should.eql({ foo: 'abc' }); // can't coerce + + _ajv.validate(schema, data = { foo: {} }) .should.equal(false); + data .should.eql({ foo: {} }); // can't coerce + + _ajv.validate(schema, data = { foo: [] }) .should.equal(false); + data .should.eql({ foo: [] }); // can't coerce + }); + }); + it('should fail to coerce non-number if multiple properties/items are coerced (issue #152)', function() { var schema = { From a8a4ddc2fc8bb252839f9f1ea1a659f8edef5e1d Mon Sep 17 00:00:00 2001 From: Igor Savin Date: Fri, 2 Feb 2018 23:32:31 +0200 Subject: [PATCH 105/333] test: skip test and remove incorrect check --- spec/coercion.spec.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/spec/coercion.spec.js b/spec/coercion.spec.js index 1bedfe237..a1dbc61ee 100644 --- a/spec/coercion.spec.js +++ b/spec/coercion.spec.js @@ -302,7 +302,7 @@ describe('Type coercion', function () { }); }); - it('should coerce to multiple types in order with integer type', function() { + it.skip('should coerce to multiple types in order with integer type', function() { var schema = { type: 'object', properties: { @@ -318,9 +318,6 @@ describe('Type coercion', function () { _ajv.validate(schema, data = { foo: '1' }) .should.equal(true); data .should.eql({ foo: 1 }); - _ajv.validate(schema, data = { foo: '1.5' }) .should.equal(true); - data .should.eql({ foo: 1.5 }); - _ajv.validate(schema, data = { foo: 'false' }) .should.equal(true); data .should.eql({ foo: false }); From 5e23353a3711e74b75cdb88be9aa891d99532652 Mon Sep 17 00:00:00 2001 From: Oliver Clark Date: Tue, 6 Feb 2018 07:56:26 -0800 Subject: [PATCH 106/333] Update FAQ.md typo --- FAQ.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FAQ.md b/FAQ.md index 6e8e8a3f7..2d0901098 100644 --- a/FAQ.md +++ b/FAQ.md @@ -77,7 +77,7 @@ See [#22](https://github.com/epoberezkin/ajv/issues/22), [#125](https://github.c 2. When schemas are recursive (or mutually recursive) resolving references would result in self-referencing recursive data-structures that can be difficult to process. 3. There are cases when such inlining would also require adding (or modyfing) `id` attribute in the inlined schema fragment to make the resulting schema equivalent. -There were many conversations about the meaning of `$ref` in [JSON Schema GitHub organisation](https://github.com/json-schema-org). The consesus is that while it is possible to treat `$ref` as schema inclusion with two caveats (above), this interpretation is unnecessary complex. A more efficient approach is to treat `$ref` as a delegation, i.e. a special keyword that validates the current data instance against the referenced schema. The analogy with programming languages is that `$ref` is a function call rather than a macro. See [here](https://github.com/json-schema-org/json-schema-spec/issues/279), for example. +There were many conversations about the meaning of `$ref` in [JSON Schema GitHub organisation](https://github.com/json-schema-org). The consensus is that while it is possible to treat `$ref` as schema inclusion with two caveats (above), this interpretation is unnecessary complex. A more efficient approach is to treat `$ref` as a delegation, i.e. a special keyword that validates the current data instance against the referenced schema. The analogy with programming languages is that `$ref` is a function call rather than a macro. See [here](https://github.com/json-schema-org/json-schema-spec/issues/279), for example. ##### How can I generate a schema where `$ref` keywords are replaced with referenced schemas? From 1b5523978a6c515f1b5c32574d1f9d50a84a9dc7 Mon Sep 17 00:00:00 2001 From: Igor Savin Date: Wed, 7 Feb 2018 10:59:42 +0200 Subject: [PATCH 107/333] tests: un-skip passing test --- spec/coercion.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/coercion.spec.js b/spec/coercion.spec.js index a1dbc61ee..a9f13de5c 100644 --- a/spec/coercion.spec.js +++ b/spec/coercion.spec.js @@ -302,7 +302,7 @@ describe('Type coercion', function () { }); }); - it.skip('should coerce to multiple types in order with integer type', function() { + it('should coerce to multiple types in order with integer type', function() { var schema = { type: 'object', properties: { From ce35fce211ad0ef07d1eb04f663512299d2a8f69 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Wed, 7 Feb 2018 09:49:19 +0000 Subject: [PATCH 108/333] chore(package): update browserify to version 16.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fb4108e3f..e2bb69f03 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "ajv-async": "^1.0.0", "bluebird": "^3.1.5", "brfs": "^1.4.3", - "browserify": "^15.0.0", + "browserify": "^16.0.0", "chai": "^4.0.1", "coveralls": "^3.0.0", "del-cli": "^1.1.0", From 24e1c7512ebe2b73ae5c300761369762b1b1f87c Mon Sep 17 00:00:00 2001 From: Wilson Date: Wed, 21 Feb 2018 23:44:42 -0800 Subject: [PATCH 109/333] [Doc] Make .compile API clearer --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cd1d7f357..4a7e1c3b6 100644 --- a/README.md +++ b/README.md @@ -846,9 +846,10 @@ Create Ajv instance. Generate validating function and cache the compiled schema for future use. -Validating function returns boolean and has properties `errors` with the errors from the last validation (`null` if there were no errors) and `schema` with the reference to the original schema. +Validating function returns a boolean value and has two properties: `errors`, `schema`. Any errors returned from the last validation are set to `errors` +while the value of `errors` is set to `null` to indicate no errors. `schema` contains the reference to the original schema. -Unless the option `validateSchema` is false, the schema will be validated against meta-schema and if schema is invalid the error will be thrown. See [options](#options). +The schema will be validated against meta-schema unless `validateSchema` option is set to false. If schema is invalid, an error will be thrown. See [options](#options). ##### .compileAsync(Object schema [, Boolean meta] [, Function callback]) -> Promise @@ -899,7 +900,7 @@ This allows you to do nice things like the following. ```javascript var validate = new Ajv().addSchema(schema).addFormat(name, regex).getSchema(uri); -``` +``` ##### .addMetaSchema(Array<Object>|Object schema [, String key]) -> Ajv From aabd9959deae10cad66178a0f4ec033949bb3121 Mon Sep 17 00:00:00 2001 From: Marshall Cottrell Date: Fri, 23 Feb 2018 11:43:01 -0600 Subject: [PATCH 110/333] feat: allowedValue param in const keyword error, closes #713 --- lib/dot/errors.def | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dot/errors.def b/lib/dot/errors.def index e8df5ddb0..4d7f84840 100644 --- a/lib/dot/errors.def +++ b/lib/dot/errors.def @@ -167,7 +167,7 @@ additionalItems: "{ limit: {{=$schema.length}} }", additionalProperties: "{ additionalProperty: '{{=$additionalProperty}}' }", anyOf: "{}", - const: "{}", + const: "{ allowedValue: schema{{=$lvl}} }", contains: "{}", 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}} }", From a6f134537747699561a50676ce8e1c818011c892 Mon Sep 17 00:00:00 2001 From: Marshall Cottrell Date: Fri, 23 Feb 2018 11:52:42 -0600 Subject: [PATCH 111/333] docs: allowedValue param in const keyword error --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cd1d7f357..004b82ff4 100644 --- a/README.md +++ b/README.md @@ -899,7 +899,7 @@ This allows you to do nice things like the following. ```javascript var validate = new Ajv().addSchema(schema).addFormat(name, regex).getSchema(uri); -``` +``` ##### .addMetaSchema(Array<Object>|Object schema [, String key]) -> Ajv @@ -1199,6 +1199,7 @@ Properties of `params` object in errors depend on the keyword that failed valida - `patternRequired` (in ajv-keywords) - property `missingPattern` (required pattern that did not match any property). - `type` - property `type` (required type(s), a string, can be a comma-separated list) - `uniqueItems` - properties `i` and `j` (indices of duplicate items). +- `const` - property `allowedValue` pointing to the value (the schema of the keyword). - `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). From 0a39f1de29bb22ed1f6e5beb7fecc9f0904c6a0d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 25 Feb 2018 20:57:29 +0000 Subject: [PATCH 112/333] docs: compile method --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ff8015f1b..3fcb92a8c 100644 --- a/README.md +++ b/README.md @@ -846,10 +846,9 @@ Create Ajv instance. Generate validating function and cache the compiled schema for future use. -Validating function returns a boolean value and has two properties: `errors`, `schema`. Any errors returned from the last validation are set to `errors` -while the value of `errors` is set to `null` to indicate no errors. `schema` contains the reference to the original schema. +Validating function returns a boolean value. This function has properties `errors` and `schema`. Errors encountered during the last validation are assigned to `errors` property (it is assigned `null` if there was no errors). `schema` property contains the reference to the original schema. -The schema will be validated against meta-schema unless `validateSchema` option is set to false. If schema is invalid, an error will be thrown. See [options](#options). +The schema passed to this method will be validated against meta-schema unless `validateSchema` option is false. If schema is invalid, an error will be thrown. See [options](#options). ##### .compileAsync(Object schema [, Boolean meta] [, Function callback]) -> Promise From 2abd9919fa69112d76e91942753f6288121437ba Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 25 Feb 2018 21:32:14 +0000 Subject: [PATCH 113/333] 6.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e2bb69f03..4813d69a4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.1.1", + "version": "6.2.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 6d62c71daa025be9a8ec6bbcbc4a1a892ce855fa Mon Sep 17 00:00:00 2001 From: Dominik Moritz Date: Wed, 28 Feb 2018 14:12:48 -0800 Subject: [PATCH 114/333] failing test for #725 --- spec/tests/rules/uniqueItems.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/tests/rules/uniqueItems.json b/spec/tests/rules/uniqueItems.json index f2ce5d024..4b359f828 100644 --- a/spec/tests/rules/uniqueItems.json +++ b/spec/tests/rules/uniqueItems.json @@ -11,6 +11,11 @@ "data": ["foo", "bar", "baz"], "valid": true }, + { + "description": "array of unique items with strings that are properties of has are valid", + "data": ["toString", "foo"], + "valid": true + }, { "description": "array of non-unique strings are invalid", "data": ["foo", "bar", "bar"], From 352034c9e607ed28397e9a9eab7440b32b5e0ba2 Mon Sep 17 00:00:00 2001 From: Dominik Moritz Date: Wed, 28 Feb 2018 19:39:01 -0800 Subject: [PATCH 115/333] Support unique items that are properties of hash. Fixes #725. --- lib/dot/uniqueItems.jst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dot/uniqueItems.jst b/lib/dot/uniqueItems.jst index 28327509f..77f0edec3 100644 --- a/lib/dot/uniqueItems.jst +++ b/lib/dot/uniqueItems.jst @@ -34,7 +34,7 @@ for (;i--;) { var item = {{=$data}}[i]; if (typeof item != '{{=$itemType}}') continue; - if (itemIndices[item] !== undefined) { + if (typeof itemIndices[item] == 'number') { {{=$valid}} = false; j = itemIndices[item]; break; From c6de7746cd1f29940c1d1002942fba8324712135 Mon Sep 17 00:00:00 2001 From: Dominik Moritz Date: Thu, 1 Mar 2018 01:41:02 -0800 Subject: [PATCH 116/333] Fix typo --- spec/tests/rules/uniqueItems.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/tests/rules/uniqueItems.json b/spec/tests/rules/uniqueItems.json index 4b359f828..2618ac541 100644 --- a/spec/tests/rules/uniqueItems.json +++ b/spec/tests/rules/uniqueItems.json @@ -12,7 +12,7 @@ "valid": true }, { - "description": "array of unique items with strings that are properties of has are valid", + "description": "array of unique items with strings that are properties of hash are valid", "data": ["toString", "foo"], "valid": true }, From 8bbc1a21072a455a8f8fd6ef6c3ea0f44e117f84 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 3 Mar 2018 10:35:40 +0000 Subject: [PATCH 117/333] fix: uniqueItems when item type is array of types, closes #727 --- lib/dot/uniqueItems.jst | 14 +++-- spec/tests/rules/uniqueItems.json | 88 +++++++++++++++++++++++++++++-- 2 files changed, 96 insertions(+), 6 deletions(-) diff --git a/lib/dot/uniqueItems.jst b/lib/dot/uniqueItems.jst index 77f0edec3..22f82f99d 100644 --- a/lib/dot/uniqueItems.jst +++ b/lib/dot/uniqueItems.jst @@ -18,8 +18,12 @@ , {{=$valid}} = true , j; if (i > 1) { - {{ var $itemType = it.schema.items && it.schema.items.type; }} - {{? !$itemType || $itemType == 'object' || $itemType == 'array' }} + {{ + var $itemType = it.schema.items && it.schema.items.type + , $typeIsArray = Array.isArray($itemType); + }} + {{? !$itemType || $itemType == 'object' || $itemType == 'array' || + ($typeIsArray && ($itemType.indexOf('object') >= 0 || $itemType.indexOf('array') >= 0)) }} outer: for (;i--;) { for (j = i; j--;) { @@ -33,7 +37,11 @@ var itemIndices = {}, item; for (;i--;) { var item = {{=$data}}[i]; - if (typeof item != '{{=$itemType}}') continue; + {{ var $method = 'checkDataType' + ($typeIsArray ? 's' : ''); }} + if ({{= it.util[$method]($itemType, 'item', true) }}) continue; + {{? $typeIsArray}} + if (typeof item == 'string') item = '"' + item; + {{?}} if (typeof itemIndices[item] == 'number') { {{=$valid}} = false; j = itemIndices[item]; diff --git a/spec/tests/rules/uniqueItems.json b/spec/tests/rules/uniqueItems.json index 2618ac541..d283d2045 100644 --- a/spec/tests/rules/uniqueItems.json +++ b/spec/tests/rules/uniqueItems.json @@ -7,17 +7,17 @@ }, "tests": [ { - "description": "array of unique strings are valid", + "description": "array of unique strings is valid", "data": ["foo", "bar", "baz"], "valid": true }, { - "description": "array of unique items with strings that are properties of hash are valid", + "description": "array of unique items with strings that are properties of hash is valid", "data": ["toString", "foo"], "valid": true }, { - "description": "array of non-unique strings are invalid", + "description": "array of non-unique strings is invalid", "data": ["foo", "bar", "bar"], "valid": false }, @@ -27,5 +27,87 @@ "valid": false } ] + }, + { + "description": "uniqueItems with multiple types when the list of types includes array", + "schema": { + "items": { "type": ["array", "string"] }, + "uniqueItems": true + }, + "tests": [ + { + "description": "array of unique items is valid", + "data": [[1], [2], "foo"], + "valid": true + }, + { + "description": "array of non-unique items is invalid", + "data": [[1], [1], "foo"], + "valid": false + }, + { + "description": "array with incorrect type is invalid", + "data": [{}, 1, 2], + "valid": false + } + ] + }, + { + "description": "uniqueItems with multiple types when the list of types includes object", + "schema": { + "items": { "type": ["object", "string"] }, + "uniqueItems": true + }, + "tests": [ + { + "description": "array of unique items is valid", + "data": [{"a": 1}, {"b": 2}, "foo"], + "valid": true + }, + { + "description": "array of non-unique items is invalid", + "data": [{"a": 1}, {"a": 1}, "foo"], + "valid": false + }, + { + "description": "array with incorrect type is invalid", + "data": [[], 1, 2], + "valid": false + } + ] + }, + { + "description": "uniqueItems with multiple types when all types are scalar", + "schema": { + "items": { "type": ["number", "string", "boolean", "null"] }, + "uniqueItems": true + }, + "tests": [ + { + "description": "array of unique items is valid (string/number)", + "data": ["1", 1, 2], + "valid": true + }, + { + "description": "array of unique items is valid (string/boolean)", + "data": ["true", true, false], + "valid": true + }, + { + "description": "array of unique items is valid (string/null)", + "data": ["null", null, 0], + "valid": true + }, + { + "description": "array of non-unique items is invalid", + "data": [1, 1, 2], + "valid": false + }, + { + "description": "array with incorrect type is invalid", + "data": [[], 1, 2], + "valid": false + } + ] } ] From db56b51fff7834df60925c8166bddf6dab4a27c8 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 3 Mar 2018 13:41:50 +0000 Subject: [PATCH 118/333] fix: error messages for exclusiveMaximum/Minimum wint $data, closes #722 --- lib/dot/_limit.jst | 8 ++++++++ spec/errors.spec.js | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/lib/dot/_limit.jst b/lib/dot/_limit.jst index 13e7649b3..e10806fd3 100644 --- a/lib/dot/_limit.jst +++ b/lib/dot/_limit.jst @@ -50,6 +50,14 @@ ) || {{=$data}} !== {{=$data}}) { var op{{=$lvl}} = {{=$exclusive}} ? '{{=$op}}' : '{{=$op}}='; + {{ + if ($schema === undefined) { + $errorKeyword = $exclusiveKeyword; + $errSchemaPath = it.errSchemaPath + '/' + $exclusiveKeyword; + $schemaValue = $schemaValueExcl; + $isData = $isDataExcl; + } + }} {{??}} {{ var $exclIsNumber = typeof $schemaExcl == 'number' diff --git a/spec/errors.spec.js b/spec/errors.spec.js index e0b5783e3..6291b15ae 100644 --- a/spec/errors.spec.js +++ b/spec/errors.spec.js @@ -711,6 +711,42 @@ describe('Validation errors', function () { } }); }); + + it('should include limits in error message with $data', function() { + var schema = { + "properties": { + "smaller": { + "type": "number", + "exclusiveMaximum": { "$data": "1/larger" } + }, + "larger": { "type": "number" } + } + }; + + ajv = new Ajv({$data: true}); + fullAjv = new Ajv({$data: true, allErrors: true, verbose: true, jsonPointers: true}); + + [ajv, fullAjv].forEach(function (_ajv) { + var validate = _ajv.compile(schema); + shouldBeValid(validate, {smaller: 2, larger: 4}); + shouldBeValid(validate, {smaller: 3, larger: 4}); + + shouldBeInvalid(validate, {smaller: 4, larger: 4}); + testError(); + + shouldBeInvalid(validate, {smaller: 5, larger: 4}); + testError(); + + function testError() { + var err = validate.errors[0]; + shouldBeError(err, 'exclusiveMaximum', + '#/properties/smaller/exclusiveMaximum', + _ajv._opts.jsonPointers ? '/smaller' : '.smaller', + 'should be < 4', + {comparison: '<', limit: 4, exclusive: true}); + } + }); + }); }); From f0836de250bcd57dc0a5730be7ecd3693ecee1eb Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 3 Mar 2018 14:26:32 +0000 Subject: [PATCH 119/333] 6.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4813d69a4..0caab4448 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.2.0", + "version": "6.2.1", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 15e8d62b86f69ffa52f87307620b6c0f0041877a Mon Sep 17 00:00:00 2001 From: Stan Goldmann Date: Mon, 5 Mar 2018 11:00:23 +0100 Subject: [PATCH 120/333] Replace `Thenable` with official `PromiseLike` declaration --- lib/ajv.d.ts | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index 68e855deb..c48d42859 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -15,7 +15,7 @@ declare namespace ajv { * @param {Any} data to be validated * @return {Boolean} validation result. Errors from the last validation will be available in `ajv.errors` (and also in compiled schema: `schema.errors`). */ - validate(schemaKeyRef: object | string | boolean, data: any): boolean | Thenable; + validate(schemaKeyRef: object | string | boolean, data: any): boolean | PromiseLike; /** * Create validating function for passed schema. * @param {object|Boolean} schema schema object @@ -29,9 +29,9 @@ declare namespace ajv { * @param {object|Boolean} schema schema object * @param {Boolean} meta optional true to compile meta-schema; this parameter can be skipped * @param {Function} callback optional node-style callback, it is always called with 2 parameters: error (or null) and validating function. - * @return {Thenable} validating function + * @return {PromiseLike} validating function */ - compileAsync(schema: object | boolean, meta?: Boolean, callback?: (err: Error, validate: ValidateFunction) => any): Thenable; + compileAsync(schema: object | boolean, meta?: Boolean, callback?: (err: Error, validate: ValidateFunction) => any): PromiseLike; /** * Adds schema to the instance. * @param {object|Array} schema schema or array of schemas. If array is passed, `key` and other parameters will be ignored. @@ -107,10 +107,6 @@ declare namespace ajv { errors?: Array; } - interface Thenable { - then (onFulfilled?: (value: R) => U | Thenable, onRejected?: (error: any) => U | Thenable): Thenable; - } - interface ValidateFunction { ( data: any, @@ -118,7 +114,7 @@ declare namespace ajv { parentData?: object | Array, parentDataProperty?: string | number, rootData?: object | Array - ): boolean | Thenable; + ): boolean | PromiseLike; schema?: object | boolean; errors?: null | Array; refs?: object; @@ -142,7 +138,7 @@ declare namespace ajv { schemaId?: '$id' | 'id' | 'auto'; missingRefs?: true | 'ignore' | 'fail'; extendRefs?: true | 'ignore' | 'fail'; - loadSchema?: (uri: string, cb?: (err: Error, schema: object) => void) => Thenable; + loadSchema?: (uri: string, cb?: (err: Error, schema: object) => void) => PromiseLike; removeAdditional?: boolean | 'all' | 'failing'; useDefaults?: boolean | 'shared'; coerceTypes?: boolean | 'array'; @@ -163,7 +159,7 @@ declare namespace ajv { cache?: object; } - type FormatValidator = string | RegExp | ((data: string) => boolean | Thenable); + type FormatValidator = string | RegExp | ((data: string) => boolean | PromiseLike); interface FormatDefinition { validate: FormatValidator; @@ -227,7 +223,7 @@ declare namespace ajv { parentData?: object | Array, parentDataProperty?: string | number, rootData?: object | Array - ): boolean | Thenable; + ): boolean | PromiseLike; errors?: Array; } From d9c02e0146529aa24ad6243e6c39ce80b4a5c68f Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 17 Mar 2018 18:19:23 +0000 Subject: [PATCH 121/333] docs: use multiple issue templates --- .github/ISSUE_TEMPLATE/change.md | 15 +++++++++++++++ .github/ISSUE_TEMPLATE/compatibility.md | 19 +++++++++++++++++++ CONTRIBUTING.md | 4 ++-- 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/change.md create mode 100644 .github/ISSUE_TEMPLATE/compatibility.md diff --git a/.github/ISSUE_TEMPLATE/change.md b/.github/ISSUE_TEMPLATE/change.md new file mode 100644 index 000000000..965bd02ff --- /dev/null +++ b/.github/ISSUE_TEMPLATE/change.md @@ -0,0 +1,15 @@ + + +**What version of Ajv you are you using?** + +**What problem do you want to solve?** + +**What do you think is the correct solution to problem?** + +**Will you be able to implement it?** diff --git a/.github/ISSUE_TEMPLATE/compatibility.md b/.github/ISSUE_TEMPLATE/compatibility.md new file mode 100644 index 000000000..4203ad43f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/compatibility.md @@ -0,0 +1,19 @@ + + +**The version of Ajv you are using** + +**The environment you have the problem with.** + +**Your code (please make it as small as possible to reproduce the issue).** + +**If your issue is in the browser, please list the other packages loaded in the page in the order they are loaded. Please check if the issue gets resolved (or results change) if you move Ajv bundle closer to the top.** + +**Results in node.js v4.** + +**Results and error messages in your platform.** diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dd78a8a1e..6082d0d88 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,7 +43,7 @@ Please make sure to include the following information in the issue: #### Change proposals -[Create a proposal](https://github.com/epoberezkin/ajv/issues/new?labels=suggestion&body=**What%20version%20of%20Ajv%20you%20are%20you%20using%3F**%0A%0A**What%20problem%20do%20you%20want%20to%20solve%3F**%0A%0A**What%20do%20you%20think%20is%20the%20correct%20solution%20to%20problem?**%0A%0A**Will%20you%20be%20able%20to%20implement%20it%3F**%0A%0A) for a new feature, option or some other improvement. +[Create a proposal](https://github.com/epoberezkin/ajv/issues/new?template=change.md) for a new feature, option or some other improvement. Please include this information: @@ -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 environment (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?template=compatibility.md) 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: From 80d249ac7fc92c125d2217f53e29b6e24f3566c1 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 17 Mar 2018 18:51:13 +0000 Subject: [PATCH 122/333] docs: using JSON Schema in FAQ --- FAQ.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/FAQ.md b/FAQ.md index 2d0901098..472a5edb0 100644 --- a/FAQ.md +++ b/FAQ.md @@ -3,6 +3,27 @@ The purpose of this document is to help find answers quicker. I am happy to continue the discussion about these issues, so please comment on some of the issues mentioned below or create a new issue if it seems more appropriate. + +## Using JSON schema + +Ajv implements JSON schema specification. Before submitting the issue about the behaviour of any validation keywords please review them in: + +- [JSON Schema specification](https://tools.ietf.org/html/draft-handrews-json-schema-validation-00) (draft-07) +- [Validation keywords](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md) in Ajv documentation +- [JSON Schema tutorial](https://spacetelescope.github.io/understanding-json-schema/) (for draft-04) + + +##### Why Ajv validates empty object as valid? + +"properties" keyword does not require the presence of any properties, you need to use "required" keyword. It also doesn't require that the data is an object, so any other type of data will also be valid. To require a specific type use "type" keyword. + + +##### Why Ajv validates only the first item of the array? + +"items" keyword support [two syntaxes](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#items) - 1) when the schema applies to all items; 2) when there is a different schema for each item in the beginning of the array. This problem means you are using the second syntax. + + + ## Ajv API for returning validation errors See [#65](https://github.com/epoberezkin/ajv/issues/65), [#212](https://github.com/epoberezkin/ajv/issues/212), [#236](https://github.com/epoberezkin/ajv/issues/236), [#242](https://github.com/epoberezkin/ajv/issues/242), [#256](https://github.com/epoberezkin/ajv/issues/256). @@ -32,6 +53,7 @@ Doing this would create a precedent where validated data is used in error messag 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. See [#127](https://github.com/epoberezkin/ajv/issues/127), [#129](https://github.com/epoberezkin/ajv/issues/129), [#134](https://github.com/epoberezkin/ajv/issues/134), [#140](https://github.com/epoberezkin/ajv/issues/140), [#193](https://github.com/epoberezkin/ajv/issues/193), [#205](https://github.com/epoberezkin/ajv/issues/205), [#238](https://github.com/epoberezkin/ajv/issues/238), [#264](https://github.com/epoberezkin/ajv/issues/264). @@ -66,6 +88,7 @@ This problem is related to the problem explained above - properties treated as a See the exemple in [Filtering Data](https://github.com/epoberezkin/ajv#filtering-data) section of readme. + ## Generating schemas with resolved references ($ref) See [#22](https://github.com/epoberezkin/ajv/issues/22), [#125](https://github.com/epoberezkin/ajv/issues/125), [#146](https://github.com/epoberezkin/ajv/issues/146), [#228](https://github.com/epoberezkin/ajv/issues/228), [#336](https://github.com/epoberezkin/ajv/issues/336), [#454](https://github.com/epoberezkin/ajv/issues/454). From 1dac91c60cd372c4e0db1230217fb11d9a182cde Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 17 Mar 2018 18:56:43 +0000 Subject: [PATCH 123/333] docs: corrections, uri-reference format --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3fcb92a8c..5986d6b9e 100644 --- a/README.md +++ b/README.md @@ -102,8 +102,8 @@ Performance of different validators by [json-schema-benchmark](https://github.co - [assigning defaults](#assigning-defaults) to missing properties and items - [coercing data](#coercing-data-types) to the types specified in `type` keywords - [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). +- draft-06/07 keywords `const`, `contains`, `propertyNames` and `if/then/else` +- draft-06 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 - [$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 @@ -241,9 +241,10 @@ The following formats are supported for string validation with "format" keyword: - _date_: full-date according to [RFC3339](http://tools.ietf.org/html/rfc3339#section-5.6). - _time_: time with optional time-zone. - _date-time_: date-time from the same source (time-zone is mandatory). `date`, `time` and `date-time` validate ranges in `full` mode and only regexp in `fast` mode (see [options](#options)). -- _uri_: full uri with optional protocol. -- _url_: [URL record](https://url.spec.whatwg.org/#concept-url). +- _uri_: full URI. +- _uri-reference_: URI reference, including full and relative URIs. - _uri-template_: URI template according to [RFC6570](https://tools.ietf.org/html/rfc6570) +- _url_: [URL record](https://url.spec.whatwg.org/#concept-url). - _email_: email address. - _hostname_: host name according to [RFC1034](http://tools.ietf.org/html/rfc1034#section-3.5). - _ipv4_: IP address v4. @@ -253,9 +254,9 @@ 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. +__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 to mention this plugin here. -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. +There are two modes of format validation: `fast` and `full`. This mode affects formats `date`, `time`, `date-time`, `uri`, `uri-reference`, `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 5c063d8cf6dba00ce43d1054e1d394de7330f576 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 17 Mar 2018 19:18:28 +0000 Subject: [PATCH 124/333] 6.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0caab4448..350e3397a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.2.1", + "version": "6.3.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From cad7dc94e8ac9b9f4066d216666412753d03be67 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 24 Mar 2018 22:18:09 +0000 Subject: [PATCH 125/333] fix: check only own properties of schema, change property limit for equility check from 5 to 8, fixes #743 --- lib/dot/properties.jst | 4 ++-- spec/issues.spec.js | 37 +++++++++++++++++++++++++++++++++++++ spec/options.spec.js | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/lib/dot/properties.jst b/lib/dot/properties.jst index dc8ab7bae..8b1442b1d 100644 --- a/lib/dot/properties.jst +++ b/lib/dot/properties.jst @@ -58,8 +58,8 @@ var {{=$nextValid}} = true; {{? $someProperties }} var isAdditional{{=$lvl}} = !(false {{? $schemaKeys.length }} - {{? $schemaKeys.length > 5 }} - || validate.schema{{=$schemaPath}}[{{=$key}}] + {{? $schemaKeys.length > 8 }} + || validate.schema{{=$schemaPath}}.hasOwnProperty({{=$key}}) {{??}} {{~ $schemaKeys:$propertyKey }} || {{=$key}} == {{= it.util.toQuotedString($propertyKey) }} diff --git a/spec/issues.spec.js b/spec/issues.spec.js index ee181bb94..87c21a449 100644 --- a/spec/issues.spec.js +++ b/spec/issues.spec.js @@ -666,3 +666,40 @@ describe('full date format validation should understand leap years', function () ajv.validate(schema, invalidDate) .should.equal(false); }); }); + + +describe('property __proto__ should be removed with removeAdditional option, issue #743', function() { + it('should remove additional properties', function() { + var ajv = new Ajv({removeAdditional: true}); + + var schema = { + properties: { + obj: { + additionalProperties: false, + properties: { + a: { type: 'string' }, + b: { type: 'string' }, + c: { type: 'string' }, + d: { type: 'string' }, + e: { type: 'string' }, + f: { type: 'string' }, + g: { type: 'string' }, + h: { type: 'string' }, + i: { type: 'string' } + } + } + } + }; + + var obj= Object.create(null); + obj.__proto__ = null; // should be removed + obj.additional = 'will be removed'; + obj.a = 'valid'; + obj.b = 'valid'; + + var data = {obj: obj}; + + ajv.validate(schema, data) .should.equal(true); + Object.keys(data.obj) .should.eql(['a', 'b']); + }); +}); diff --git a/spec/options.spec.js b/spec/options.spec.js index f0f949dcc..511c75986 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -46,6 +46,46 @@ describe('Ajv Options', function () { }); + it('should remove properties that would error when `additionalProperties = false` (many properties, boolean schema)', function() { + var ajv = new Ajv({removeAdditional: true}); + + var schema = { + properties: { + obj: { + additionalProperties: false, + properties: { + a: { type: 'string' }, + b: false, + c: { type: 'string' }, + d: { type: 'string' }, + e: { type: 'string' }, + f: { type: 'string' }, + g: { type: 'string' }, + h: { type: 'string' }, + i: { type: 'string' } + } + } + } + }; + + var data = { + obj: { + a: 'valid', + b: 'should not be removed', + additional: 'will be removed' + } + }; + + ajv.validate(schema, data) .should.equal(false); + data .should.eql({ + obj: { + a: 'valid', + b: 'should not be removed' + } + }); + }); + + it('should remove properties that would error when `additionalProperties` is a schema', function() { var ajv = new Ajv({ removeAdditional: 'failing' }); From 72b1cbbf22646039fdddfc9f46f8dff01ac7e32e Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 24 Mar 2018 22:21:54 +0000 Subject: [PATCH 126/333] fix: option v5 used instead of $data --- lib/dot/properties.jst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dot/properties.jst b/lib/dot/properties.jst index 8b1442b1d..862067e75 100644 --- a/lib/dot/properties.jst +++ b/lib/dot/properties.jst @@ -42,7 +42,7 @@ , $currentBaseId = it.baseId; var $required = it.schema.required; - if ($required && !(it.opts.v5 && $required.$data) && $required.length < it.opts.loopRequired) + if ($required && !(it.opts.$data && $required.$data) && $required.length < it.opts.loopRequired) var $requiredHash = it.util.toHash($required); }} From c38089e516530ebf4c2acd1f59b58e757f55d1f6 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 24 Mar 2018 22:44:48 +0000 Subject: [PATCH 127/333] test: run browser tests in node 8 only --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 350e3397a..35d57cfbb 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "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 && 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", + "test": "npm run jshint && npm run eslint && npm run test-ts && npm run build && npm run test-cov && if-node-version 8 npm run test-browser", "prepublish": "npm run build && npm run bundle", "watch": "watch 'npm run build' ./lib/dot" }, From 3834eb4a50a5b37a0fc4c03ca359e29fb3d90bdc Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 24 Mar 2018 22:47:02 +0000 Subject: [PATCH 128/333] test: remove unused node version checks --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 35d57cfbb..f5fdd57c3 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,9 @@ ".tonic_example.js" ], "scripts": { - "eslint": "if-node-version \">=4\" eslint lib/*.js lib/compile/*.js spec/*.js scripts", + "eslint": "eslint lib/*.js lib/compile/*.js spec/*.js scripts", "jshint": "jshint lib/*.js lib/**/*.js --exclude lib/dotjs/**/*", - "test-spec": "mocha spec/*.spec.js -R spec $(if-node-version 7 echo --harmony-async-await)", + "test-spec": "mocha spec/*.spec.js -R spec", "test-fast": "AJV_FAST_TEST=true npm run test-spec", "test-debug": "mocha spec/*.spec.js --debug-brk -R spec", "test-cov": "nyc npm run test-spec", From 60f31aebbe789740a8c3d6435fd54e296565e4f7 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 25 Mar 2018 11:20:59 +0100 Subject: [PATCH 129/333] chore: do not create package-lock.json --- .npmrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 .npmrc diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..43c97e719 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false From 2a6367426e7dfd5501290891b22ddad8dc628186 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 25 Mar 2018 11:21:59 +0100 Subject: [PATCH 130/333] 6.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f2888128e..a2144a75e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.3.0", + "version": "6.4.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From a42a14016fccaa71e28018802c8f8bec42b343db Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Wed, 11 Apr 2018 00:18:15 +0000 Subject: [PATCH 131/333] fix(package): update uri-js to version 4.2.1 Closes #751 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a2144a75e..f3b35fe10 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "fast-deep-equal": "^1.0.0", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.3.0", - "uri-js": "^3.0.2" + "uri-js": "^4.2.1" }, "devDependencies": { "ajv-async": "^1.0.0", From dfaee84f1965c8de08cfc8cca8ffac19b0043e2c Mon Sep 17 00:00:00 2001 From: Jason Walton Date: Wed, 18 Apr 2018 15:13:21 -0400 Subject: [PATCH 132/333] Fix typescript defs to allow numeric validators. --- lib/ajv.d.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index c48d42859..a77702139 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -1,4 +1,4 @@ -declare var ajv: { +declare var ajv: { (options?: ajv.Options): ajv.Ajv; new (options?: ajv.Options): ajv.Ajv; ValidationError: ValidationError; @@ -160,13 +160,24 @@ declare namespace ajv { } type FormatValidator = string | RegExp | ((data: string) => boolean | PromiseLike); + type NumberFormatValidator = ((data: number) => boolean | PromiseLike); - interface FormatDefinition { + interface NumberFormatDefinition { + type: "number", + validate: NumberFormatValidator; + compare?: (data1: number, data2: number) => number; + async?: boolean; + } + + interface StringFormatDefinition { + type?: "string", validate: FormatValidator; - compare: (data1: string, data2: string) => number; + compare?: (data1: string, data2: string) => number; async?: boolean; } + type FormatDefinition = NumberFormatDefinition | StringFormatDefinition; + interface KeywordDefinition { type?: string | Array; async?: boolean; From bd678919637637bee5dc3c42b454ccd7ed5a3d4c Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Thu, 26 Apr 2018 17:17:50 +0000 Subject: [PATCH 133/333] Update to node 10 in .travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 0bd461203..8d052eb19 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ node_js: - "6" - "8" - "9" + - "10" after_script: - codeclimate-test-reporter < coverage/lcov.info - coveralls < coverage/lcov.info From 7ce638b19fd576d6c024d37050b1b3c196fd3a64 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Sat, 28 Apr 2018 10:04:02 +0000 Subject: [PATCH 134/333] fix(package): update fast-deep-equal to version 2.0.1 Closes #772 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a2144a75e..da584b464 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "homepage": "https://github.com/epoberezkin/ajv", "tonicExampleFilename": ".tonic_example.js", "dependencies": { - "fast-deep-equal": "^1.0.0", + "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.3.0", "uri-js": "^3.0.2" From 5ebfe2c1f4074a5c460aa5e680f1b46ff87d45e8 Mon Sep 17 00:00:00 2001 From: Vadim Cebaniuc Date: Sat, 28 Apr 2018 01:18:38 +0300 Subject: [PATCH 135/333] passContext in recursive $ref --- lib/ajv.js | 4 +- lib/compile/index.js | 4 +- spec/issues.spec.js | 110 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 2 deletions(-) diff --git a/lib/ajv.js b/lib/ajv.js index b37950867..3f7100120 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -363,9 +363,11 @@ function _compile(schemaObj, root) { return v; + /* @this {*} - custom context, see passContext option */ function callValidate() { + /* jshint validthis: true */ var _validate = schemaObj.validate; - var result = _validate.apply(null, arguments); + var result = _validate.apply(this, arguments); callValidate.errors = _validate.errors; return result; } diff --git a/lib/compile/index.js b/lib/compile/index.js index 8af3a365f..8e369db3d 100644 --- a/lib/compile/index.js +++ b/lib/compile/index.js @@ -69,9 +69,11 @@ function compile(schema, root, localRefs, baseId) { endCompiling.call(this, schema, root, baseId); } + /* @this {*} - custom context, see passContext option */ function callValidate() { + /* jshint validthis: true */ var validate = compilation.validate; - var result = validate.apply(null, arguments); + var result = validate.apply(this, arguments); callValidate.errors = validate.errors; return result; } diff --git a/spec/issues.spec.js b/spec/issues.spec.js index 87c21a449..0a6e57078 100644 --- a/spec/issues.spec.js +++ b/spec/issues.spec.js @@ -703,3 +703,113 @@ describe('property __proto__ should be removed with removeAdditional option, iss Object.keys(data.obj) .should.eql(['a', 'b']); }); }); + + +describe('issue #768, fix passContext in recursive $ref', function() { + var ajv, contexts; + + beforeEach(function() { + contexts = []; + }); + + describe('passContext = true', function() { + it('should pass this value as context to custom keyword validation function', function() { + var validate = getValidate(true); + var self = {}; + validate.call(self, { bar: 'a', baz: { bar: 'b' } }); + contexts .should.have.length(2); + contexts.forEach(function(ctx) { + ctx .should.equal(self); + }); + }); + }); + + describe('passContext = false', function() { + it('should pass ajv instance as context to custom keyword validation function', function() { + var validate = getValidate(false); + validate({ bar: 'a', baz: { bar: 'b' } }); + contexts .should.have.length(2); + contexts.forEach(function(ctx) { + ctx .should.equal(ajv); + }); + }); + }); + + describe('ref is fragment and passContext = true', function() { + it('should pass this value as context to custom keyword validation function', function() { + var validate = getValidateFragments(true); + var self = {}; + validate.call(self, { baz: { corge: 'a', quux: { baz: { corge: 'b' } } } }); + contexts .should.have.length(2); + contexts.forEach(function(ctx) { + ctx .should.equal(self); + }); + }); + }); + + describe('ref is fragment and passContext = false', function() { + it('should pass ajv instance as context to custom keyword validation function', function() { + var validate = getValidateFragments(false); + validate({ baz: { corge: 'a', quux: { baz: { corge: 'b' } } } }); + contexts .should.have.length(2); + contexts.forEach(function(ctx) { + ctx .should.equal(ajv); + }); + }); + }); + + function getValidate(passContext) { + ajv = new Ajv({ passContext: passContext }); + ajv.addKeyword('testValidate', { validate: storeContext }); + + var schema = { + "$id" : "foo", + "type": "object", + "required": ["bar"], + "properties": { + "bar": { "testValidate": true }, + "baz": { + "$ref": "foo" + } + } + }; + + return ajv.compile(schema); + } + + + function getValidateFragments(passContext) { + ajv = new Ajv({ passContext: passContext }); + ajv.addKeyword('testValidate', { validate: storeContext }); + + ajv.addSchema({ + "$id" : "foo", + "definitions": { + "bar": { + "properties": { + "baz": { + "$ref": "boo" + } + } + } + } + }); + + ajv.addSchema({ + "$id" : "boo", + "type": "object", + "required": ["corge"], + "properties": { + "quux": { "$ref": "foo#/definitions/bar" }, + "corge": { "testValidate": true } + } + }); + + return ajv.compile({ "$ref": "foo#/definitions/bar" }); + } + + function storeContext() { + contexts.push(this); + return true; + } +}); From 83314deedb1ad2192b3be54347a18e500e3f4343 Mon Sep 17 00:00:00 2001 From: jonganc Date: Mon, 7 May 2018 13:56:20 -0400 Subject: [PATCH 136/333] Fix small typos I hope you mind me fixing some small typos. Awesome project. --- KEYWORDS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/KEYWORDS.md b/KEYWORDS.md index e5b330e3d..32740dc41 100644 --- a/KEYWORDS.md +++ b/KEYWORDS.md @@ -267,7 +267,7 @@ The value of the keyword should be an object or an array of objects. If the keyword value is an object, then for the data array to be valid each item of the array should be valid according to the schema in this value. In this case the "additionalItems" keyword is ignored. -If the keyword value is an array, then items with indeces less than the number of items in the keyword should be valid according to the schemas with the same indeces. Whether additional items are valid will depend on "additionalItems" keyword. +If the keyword value is an array, then items with indices less than the number of items in the keyword should be valid according to the schemas with the same indices. Whether additional items are valid will depend on "additionalItems" keyword. __Examples__ @@ -307,7 +307,7 @@ If the length of data array is bigger than the length of "items" keyword value t - `false`: data is invalid - `true`: data is valid -- an object: data is valid if all additional items (i.e. items with indeces greater or equal than "items" keyword value length) are valid according to the schema in "assitionalItems" keyword. +- an object: data is valid if all additional items (i.e. items with indices greater or equal than "items" keyword value length) are valid according to the schema in "additionalItems" keyword. __Examples__ From b41f940f557aac0c68bdbc0c34209d5fd81bf2a9 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Tue, 8 May 2018 21:18:06 +0100 Subject: [PATCH 137/333] 6.5.0 --- package.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index b9e8c028e..06fe7de02 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.4.0", + "version": "6.5.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", @@ -68,31 +68,31 @@ "devDependencies": { "ajv-async": "^1.0.0", "bluebird": "^3.1.5", - "brfs": "^1.4.3", - "browserify": "^16.0.0", + "brfs": "^1.6.1", + "browserify": "^16.2.0", "chai": "^4.0.1", - "coveralls": "^3.0.0", + "coveralls": "^3.0.1", "del-cli": "^1.1.0", "dot": "^1.0.3", "eslint": "^4.14.0", - "gh-pages-generator": "^0.2.0", + "gh-pages-generator": "^0.2.3", "glob": "^7.0.0", "if-node-version": "^1.0.0", "js-beautify": "^1.7.3", "jshint": "^2.9.4", "json-schema-test": "^2.0.0", - "karma": "^2.0.0", + "karma": "^2.0.2", "karma-chrome-launcher": "^2.0.0", "karma-mocha": "^1.1.1", "karma-phantomjs-launcher": "^1.0.0", "karma-sauce-launcher": "^1.1.0", - "mocha": "^5.0.0", - "nyc": "^11.0.2", + "mocha": "^5.1.1", + "nyc": "^11.7.1", "phantomjs-prebuilt": "^2.1.4", "pre-commit": "^1.1.1", "require-globify": "^1.3.0", - "typescript": "^2.6.2", - "uglify-js": "^3.3.1", + "typescript": "^2.8.3", + "uglify-js": "^3.3.24", "watch": "^1.0.0" } } From 3165ee36dd1bd9a3851176670345e50e41e88530 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Fri, 1 Jun 2018 21:29:11 +0000 Subject: [PATCH 138/333] chore(package): update nyc to version 12.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 06fe7de02..b9a385317 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "karma-phantomjs-launcher": "^1.0.0", "karma-sauce-launcher": "^1.1.0", "mocha": "^5.1.1", - "nyc": "^11.7.1", + "nyc": "^12.0.1", "phantomjs-prebuilt": "^2.1.4", "pre-commit": "^1.1.1", "require-globify": "^1.3.0", From ede27152d2881ccc69bb41045272dd65f726563e Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Sun, 10 Jun 2018 08:42:13 +0000 Subject: [PATCH 139/333] fix(package): update json-schema-traverse to version 0.4.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 06fe7de02..288e0ce89 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "dependencies": { "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0", + "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.1" }, "devDependencies": { From 15c6d07fe575bfbf4aafe022a79d8391664965c9 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 10 Jun 2018 10:03:14 +0100 Subject: [PATCH 140/333] failing test for compileAsync, #801 --- spec/async.spec.js | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/spec/async.spec.js b/spec/async.spec.js index e6fcba940..beb8412da 100644 --- a/spec/async.spec.js +++ b/spec/async.spec.js @@ -60,6 +60,24 @@ describe('compileAsync method', function() { "enum": ["foo", "bar"] } } + }, + "http://example.com/foo.json": { + "$id": "http://example.com/foo.json", + "type": "object", + "properties": { + "bar": {"$ref": "bar.json"}, + "other": {"$ref": "other.json"} + } + }, + "http://example.com/bar.json": { + "$id": "http://example.com/bar.json", + "type": "object", + "properties": { + "foo": {"$ref": "foo.json"} + } + }, + "http://example.com/other.json": { + "$id": "http://example.com/other.json" } }; @@ -412,6 +430,23 @@ describe('compileAsync method', function() { }); + describe.skip('schema with multiple remote properties, the first is recursive schema (#801)', function() { + it('should validate data', function() { + var schema = { + "$id": "http://example.com/list.json", + "type": "object", + "properties": { + "foo": {"$ref": "foo.json"} + } + }; + + return ajv.compileAsync(schema).then(function (validate) { + validate({foo: {}}) .should.equal(true); + }); + }); + }); + + function loadSchema(uri) { loadCallCount++; return new Promise(function (resolve, reject) { From 32651b5cfd2916366dea40d986dfe624f792d3b6 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 10 Jun 2018 10:58:30 +0100 Subject: [PATCH 141/333] fix: compileAsync schema with multiple remote schemas and some recursive, fixes #801 --- lib/ajv.js | 4 ++++ spec/async.spec.js | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/ajv.js b/lib/ajv.js index 3f7100120..4740eb6a7 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -351,6 +351,10 @@ function _compile(schemaObj, root) { var v; try { v = compileSchema.call(this, schemaObj.schema, root, schemaObj.localRefs); } + catch(e) { + delete schemaObj.validate; + throw e; + } finally { schemaObj.compiling = false; if (schemaObj.meta) this._opts = currentOpts; diff --git a/spec/async.spec.js b/spec/async.spec.js index beb8412da..5b4c5ff6e 100644 --- a/spec/async.spec.js +++ b/spec/async.spec.js @@ -430,7 +430,7 @@ describe('compileAsync method', function() { }); - describe.skip('schema with multiple remote properties, the first is recursive schema (#801)', function() { + describe('schema with multiple remote properties, the first is recursive schema (#801)', function() { it('should validate data', function() { var schema = { "$id": "http://example.com/list.json", From 0020556493f3d443002596697768f3f047782198 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 10 Jun 2018 11:45:50 +0100 Subject: [PATCH 142/333] 6.5.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 061e7f467..7f635d4c4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.5.0", + "version": "6.5.1", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From f954bd9b9538cc58cf9167b5ab6b8c84bae5b8e3 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Fri, 22 Jun 2018 11:16:19 +0000 Subject: [PATCH 143/333] chore(package): update brfs to version 2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7f635d4c4..cb1324f9c 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "devDependencies": { "ajv-async": "^1.0.0", "bluebird": "^3.1.5", - "brfs": "^1.6.1", + "brfs": "^2.0.0", "browserify": "^16.2.0", "chai": "^4.0.1", "coveralls": "^3.0.1", From c092118ec11ee5ace0e1ced4a51a99548672ece9 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Sat, 23 Jun 2018 05:51:30 +0000 Subject: [PATCH 144/333] chore(package): update eslint to version 5.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7f635d4c4..4987d47da 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "coveralls": "^3.0.1", "del-cli": "^1.1.0", "dot": "^1.0.3", - "eslint": "^4.14.0", + "eslint": "^5.0.0", "gh-pages-generator": "^0.2.3", "glob": "^7.0.0", "if-node-version": "^1.0.0", From 4865e6e894fbfa0ebe9d5a1fead2a15d2bf4458e Mon Sep 17 00:00:00 2001 From: Jonathan Stewmon Date: Thu, 21 Jun 2018 15:57:39 -0500 Subject: [PATCH 145/333] support resolving internal refs from macro keywords --- lib/compile/resolve.js | 2 +- spec/custom.spec.js | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/lib/compile/resolve.js b/lib/compile/resolve.js index cdd1a59ef..66f2aee9b 100644 --- a/lib/compile/resolve.js +++ b/lib/compile/resolve.js @@ -70,7 +70,7 @@ function resolveSchema(root, ref) { var p = URI.parse(ref) , refPath = _getFullPath(p) , baseId = getFullPath(this._getId(root.schema)); - if (refPath !== baseId) { + if (Object.keys(root.schema).length === 0 || refPath !== baseId) { var id = normalizeId(refPath); var refVal = this._refs[id]; if (typeof refVal == 'string') { diff --git a/spec/custom.spec.js b/spec/custom.spec.js index e7d7efb94..21635bc25 100644 --- a/spec/custom.spec.js +++ b/spec/custom.spec.js @@ -209,6 +209,44 @@ describe('Custom keywords', function () { testMultipleRangeKeyword({ type: 'number', macro: macroRange }, 2); }); + it('should support resolving $ref without id or $id', function () { + instances.forEach(function (_ajv) { + _ajv.addKeyword('macroRef', { + macro: function (schema, parentSchema, it) { + it.baseId .should.equal('#'); + var ref = schema.$ref; + var validate = _ajv.getSchema(ref); + if (validate) return validate.schema; + throw new ajv.constructor.MissingRefError(it.baseId, ref); + }, + metaSchema: { + "type": "object", + "required": [ "$ref" ], + "additionalProperties": false, + "properties": { + "$ref": { + "type": "string" + } + } + } + }); + var schema = { + "macroRef": { + "$ref": "#/definitions/schema" + }, + "definitions": { + "schema": { + "type": "string" + } + } + }; + var validate; + (function compileMacroRef () { validate = _ajv.compile(schema); }).should.not.throw(); + shouldBeValid(validate, 'foo'); + shouldBeInvalid(validate, 1, 2); + }); + }); + it('should recursively expand macro keywords', function() { instances.forEach(function (_ajv) { _ajv.addKeyword('deepProperties', { type: 'object', macro: macroDeepProperties }); From 558ddfe05b270b7e1a37592ae440cdf2a8f91c1d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 30 Jun 2018 17:40:03 +0100 Subject: [PATCH 146/333] chore: remove node 4 from travis test --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8d052eb19..c0cfe4295 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ before_script: - git submodule update --init - npm install -g codeclimate-test-reporter node_js: - - "4" - "6" - "8" - "9" From 88d57fafa25543fd26b11ccf5687c3bd3544f3e8 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 30 Jun 2018 19:11:00 +0100 Subject: [PATCH 147/333] 6.5.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9e52e8874..942c1065e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.5.1", + "version": "6.5.2", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 9c04afb5faa15946484eba6d28439049a34d5557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20D=C3=BCrst?= Date: Tue, 7 Aug 2018 10:33:12 +0200 Subject: [PATCH 148/333] Upgrade uri-js dependency to the ES5 fixed version Updates the dependency to uri-js to version 4.2.2 which fixes the esnext/es5 issue and therefore makes ajv compatible with IE11 again. For more info about this issue see this issue over at uri-js: https://github.com/garycourt/uri-js/issues/24 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 942c1065e..0eea77302 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.1" + "uri-js": "^4.2.2" }, "devDependencies": { "ajv-async": "^1.0.0", From 4074aeeee869b128be148e6771643cdbdd6bbab4 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Thu, 9 Aug 2018 20:44:15 +0000 Subject: [PATCH 149/333] chore(package): update karma to version 3.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 942c1065e..c1bc43d82 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "js-beautify": "^1.7.3", "jshint": "^2.9.4", "json-schema-test": "^2.0.0", - "karma": "^2.0.2", + "karma": "^3.0.0", "karma-chrome-launcher": "^2.0.0", "karma-mocha": "^1.1.1", "karma-phantomjs-launcher": "^1.0.0", From 0dba01b8f201d7cb0ca537469ba842661d573139 Mon Sep 17 00:00:00 2001 From: Nathan Date: Fri, 10 Aug 2018 18:28:39 +1000 Subject: [PATCH 150/333] add bsonType plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MongoDB’s implementation of JSON Schema includes the addition of the bsonType keyword, which allows you to use all BSON types in their validator --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5986d6b9e..97dc4da60 100644 --- a/README.md +++ b/README.md @@ -1221,6 +1221,7 @@ If you have published a useful plugin please submit a PR to add it to the next s ## Related packages - [ajv-async](https://github.com/epoberezkin/ajv-async) - plugin to configure async validation mode +- [ajv-bsontype](https://github.com/BoLaMN/ajv-bsontype) - plugin to validate mongodb's bsonType formats - [ajv-cli](https://github.com/jessedc/ajv-cli) - command line interface - [ajv-errors](https://github.com/epoberezkin/ajv-errors) - plugin for custom error messages - [ajv-i18n](https://github.com/epoberezkin/ajv-i18n) - internationalised error messages From cd53f365e4e14ab0015b0019ec7f54d1e1c1e931 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 17 Aug 2018 10:50:08 +0100 Subject: [PATCH 151/333] fix types --- lib/ajv.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index a77702139..a6510c0ea 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -103,7 +103,7 @@ declare namespace ajv { * @param {object} options optional options with properties `separator` and `dataVar`. * @return {string} human readable string with all errors descriptions */ - errorsText(errors?: Array, options?: ErrorsTextOptions): string; + errorsText(errors?: Array | null, options?: ErrorsTextOptions): string; errors?: Array; } From 8e7f47a9f29fc4ba487720c301142d8706a38a8a Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 17 Aug 2018 12:33:03 +0100 Subject: [PATCH 152/333] 6.5.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 16319a18e..1d80a5e36 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.5.2", + "version": "6.5.3", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 9aa65f75154ffc855d0e3ebd32eb2a835584de00 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Mon, 3 Sep 2018 16:52:44 +0000 Subject: [PATCH 153/333] fix: pin bluebird to 3.5.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1d80a5e36..c6158a292 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ }, "devDependencies": { "ajv-async": "^1.0.0", - "bluebird": "^3.1.5", + "bluebird": "3.5.1", "brfs": "^2.0.0", "browserify": "^16.2.0", "chai": "^4.0.1", From 85b7f52f5cdfaeaeebb4fda0100c839c81ad5a7a Mon Sep 17 00:00:00 2001 From: ossdev Date: Wed, 19 Sep 2018 14:20:51 +0000 Subject: [PATCH 154/333] replacing phantomjs with headless chrome --- karma.conf.js | 9 +++++++-- package.json | 6 ++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index ab2d0defd..155d0874e 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -53,8 +53,13 @@ module.exports = function(config) { // - Safari (only Mac) // - PhantomJS // - IE (only Windows) - browsers: ['Chrome'], - + browsers: ['HeadlessChrome'], + customLaunchers: { + HeadlessChrome:{ + base: 'ChromeHeadless', + flags: ['--no-sandbox'] + }, + }, // Continuous Integration mode // if true, Karma captures browsers, runs the tests and exits diff --git a/package.json b/package.json index c6158a292..e339c078d 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "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-karma": "karma start", "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 8 npm run test-browser", "prepublish": "npm run build && npm run bundle", @@ -82,13 +82,11 @@ "jshint": "^2.9.4", "json-schema-test": "^2.0.0", "karma": "^3.0.0", - "karma-chrome-launcher": "^2.0.0", + "karma-chrome-launcher": "^2.2.0", "karma-mocha": "^1.1.1", - "karma-phantomjs-launcher": "^1.0.0", "karma-sauce-launcher": "^1.1.0", "mocha": "^5.1.1", "nyc": "^12.0.1", - "phantomjs-prebuilt": "^2.1.4", "pre-commit": "^1.1.1", "require-globify": "^1.3.0", "typescript": "^2.8.3", From f01e92a15ad3b38bf7b38efa0c7d726e89b32d8a Mon Sep 17 00:00:00 2001 From: billytrend Date: Fri, 21 Sep 2018 15:56:02 -0700 Subject: [PATCH 155/333] Fixes grammar --- lib/dot/errors.def | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/dot/errors.def b/lib/dot/errors.def index 4d7f84840..5c5752cb0 100644 --- a/lib/dot/errors.def +++ b/lib/dot/errors.def @@ -104,9 +104,9 @@ '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'", + _limitItems: "'should NOT have {{?$keyword=='maxItems'}}more{{??}}fewer{{?}} than {{#def.concatSchema}} items'", _limitLength: "'should NOT be {{?$keyword=='maxLength'}}longer{{??}}shorter{{?}} than {{#def.concatSchema}} characters'", - _limitProperties:"'should NOT have {{?$keyword=='maxProperties'}}more{{??}}less{{?}} than {{#def.concatSchema}} properties'", + _limitProperties:"'should NOT have {{?$keyword=='maxProperties'}}more{{??}}fewer{{?}} than {{#def.concatSchema}} properties'", multipleOf: "'should be multiple of {{#def.appendSchema}}", not: "'should NOT be valid'", oneOf: "'should match exactly one schema in oneOf'", From 70362b952922e5713b267941878e292c4753f709 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 23 Sep 2018 11:28:49 +0100 Subject: [PATCH 156/333] test: failing test for #861 --- .../tests/issues/861_empty_propertynames.json | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 spec/tests/issues/861_empty_propertynames.json diff --git a/spec/tests/issues/861_empty_propertynames.json b/spec/tests/issues/861_empty_propertynames.json new file mode 100644 index 000000000..d9e34f689 --- /dev/null +++ b/spec/tests/issues/861_empty_propertynames.json @@ -0,0 +1,24 @@ +[ + { + "skip": true, + "description": "propertyNames with empty schema (#861)", + "schema": { + "properties": { + "foo": {"type": "string"} + }, + "propertyNames": {} + }, + "tests": [ + { + "description": "valid", + "data": {"foo": "bar"}, + "valid": true + }, + { + "description": "invalid", + "data": {"foo": 1}, + "valid": false + } + ] + } +] From c1f929bcc8b8bbfab541749023c156e70880f5f0 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 23 Sep 2018 11:33:11 +0100 Subject: [PATCH 157/333] fix: propertyNames with empty schema, closes #861 --- lib/dot/propertyNames.jst | 4 ++-- spec/tests/issues/861_empty_propertynames.json | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/dot/propertyNames.jst b/lib/dot/propertyNames.jst index 51caffc20..ee52b2151 100644 --- a/lib/dot/propertyNames.jst +++ b/lib/dot/propertyNames.jst @@ -3,6 +3,8 @@ {{# def.setupKeyword }} {{# def.setupNextLevel }} +var {{=$errs}} = errors; + {{? {{# def.nonEmptySchema:$schema }} }} {{ $it.schema = $schema; @@ -22,8 +24,6 @@ , $currentBaseId = it.baseId; }} - var {{=$errs}} = errors; - {{? $ownProperties }} var {{=$dataProperties}} = undefined; {{?}} diff --git a/spec/tests/issues/861_empty_propertynames.json b/spec/tests/issues/861_empty_propertynames.json index d9e34f689..55ce3501f 100644 --- a/spec/tests/issues/861_empty_propertynames.json +++ b/spec/tests/issues/861_empty_propertynames.json @@ -1,6 +1,5 @@ [ { - "skip": true, "description": "propertyNames with empty schema (#861)", "schema": { "properties": { From 8578816435590a9b9f0fc167e677e2ac82b142c0 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 23 Sep 2018 11:50:32 +0100 Subject: [PATCH 158/333] 6.5.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c6158a292..94af09903 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.5.3", + "version": "6.5.4", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From ae684165eec39ed0f2088d9b81f1f0512f153795 Mon Sep 17 00:00:00 2001 From: jsdevel Date: Tue, 2 Oct 2018 21:43:53 -0700 Subject: [PATCH 159/333] Adding logger to typescript Options declaration. --- lib/ajv.d.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index a6510c0ea..341b0cff8 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -107,6 +107,12 @@ declare namespace ajv { errors?: Array; } + interface CustomLogger { + log(...args: any[]): any; + warn(...args: any[]): any; + error(...args: any[]): any; + } + interface ValidateFunction { ( data: any, @@ -157,6 +163,7 @@ declare namespace ajv { sourceCode?: boolean; processCode?: (code: string) => string; cache?: object; + logger?: CustomLogger | false } type FormatValidator = string | RegExp | ((data: string) => boolean | PromiseLike); From 8d769b68910289b158c52bf9826959b325524d92 Mon Sep 17 00:00:00 2001 From: Nathan Woltman Date: Mon, 15 Oct 2018 20:32:00 -0400 Subject: [PATCH 160/333] Remove duplicated character in email regex The `'` character appears twice in a character class part of the "full" email regex, which is redundant. This removes on of the `'`s. --- lib/compile/formats.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compile/formats.js b/lib/compile/formats.js index 97aca1a15..5c0fb9ef5 100644 --- a/lib/compile/formats.js +++ b/lib/compile/formats.js @@ -69,7 +69,7 @@ formats.full = { 'uri-reference': URIREF, 'uri-template': URITEMPLATE, url: URL, - email: /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&''*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i, + email: /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i, hostname: hostname, ipv4: /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/, ipv6: /^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i, From 494026e44c3a2f68ae0dae8615549f567b42ba95 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 4 Nov 2018 21:30:14 +0000 Subject: [PATCH 161/333] 6.5.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 36d718809..5ab39c470 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.5.4", + "version": "6.5.5", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From f2010f40f2046d5c2a9232d9e40601f1300a678d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 10 Nov 2018 21:03:51 +0000 Subject: [PATCH 162/333] feat: keyword "nullable", #486, closes epoberezkin/ajv-keywords#32 --- README.md | 2 + lib/ajv.d.ts | 3 +- lib/ajv.js | 1 + lib/dot/validate.jst | 10 +++++ spec/options.spec.js | 87 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 102 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 97dc4da60..44b5081c3 100644 --- a/README.md +++ b/README.md @@ -1029,6 +1029,7 @@ Defaults: jsonPointers: false, uniqueItems: true, unicode: true, + nullable: false, format: 'fast', formats: {}, unknownFormats: true, @@ -1075,6 +1076,7 @@ Defaults: - _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. +- _nullable_: support keyword "nullable" from [Open API 3 specification](https://swagger.io/docs/specification/data-models/data-types/). - _format_: formats validation mode ('fast' by default). Pass 'full' for more correct and slow validation or `false` not to validate formats at all. E.g., 25:00:00 and 2015/14/33 will be invalid time and date in 'full' mode but it will be valid in 'fast' mode. - _formats_: an object with custom formats. Keys and values will be passed to `addFormat` method. - _unknownFormats_: handling of unknown formats. Option values: diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index 341b0cff8..2c26417e1 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -163,7 +163,8 @@ declare namespace ajv { sourceCode?: boolean; processCode?: (code: string) => string; cache?: object; - logger?: CustomLogger | false + logger?: CustomLogger | false; + nullable?: boolean; } type FormatValidator = string | RegExp | ((data: string) => boolean | PromiseLike); diff --git a/lib/ajv.js b/lib/ajv.js index 4740eb6a7..a50dc1a14 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -72,6 +72,7 @@ function Ajv(opts) { if (opts.formats) addInitialFormats(this); addDraft6MetaSchema(this); if (typeof opts.meta == 'object') this.addMetaSchema(opts.meta); + if (opts.nullable) this.addKeyword('nullable', {metaSchema: {const: true}}); addInitialSchemas(this); } diff --git a/lib/dot/validate.jst b/lib/dot/validate.jst index 27393cf30..89a5b3b49 100644 --- a/lib/dot/validate.jst +++ b/lib/dot/validate.jst @@ -100,6 +100,16 @@ var $typeSchema = it.schema.type , $typeIsArray = Array.isArray($typeSchema); + if ($typeSchema && it.opts.nullable && it.schema.nullable === true) { + if ($typeIsArray) { + if ($typeSchema.indexOf('null') == -1) + $typeSchema = $typeSchema.concat('null'); + } else if ($typeSchema != 'null') { + $typeSchema = [$typeSchema, 'null']; + $typeIsArray = true; + } + } + if ($typeIsArray && $typeSchema.length == 1) { $typeSchema = $typeSchema[0]; $typeIsArray = false; diff --git a/spec/options.spec.js b/spec/options.spec.js index 511c75986..84bd4812d 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -1412,4 +1412,91 @@ describe('Ajv Options', function () { }).should.throw(Error, /logger must implement log, warn and error methods/); }); }); + + + describe('nullable', function() { + var ajv; + + describe('= true', function() { + beforeEach(function () { + ajv = new Ajv({ + nullable: true + }); + }); + + it('should add keyword "nullable"', function() { + testNullable({ + type: 'number', + nullable: true + }); + + testNullable({ + type: ['number'], + nullable: true + }); + + testNullable({ + type: ['number', 'null'] + }); + + testNullable({ + type: ['number', 'null'], + nullable: true + }); + + testNotNullable({type: 'number'}); + + testNotNullable({type: ['number']}); + }); + + it('"nullable" keyword must be "true" if present', function() { + should.throw(function() { + ajv.compile({nullable: false}); + }); + }); + }); + + describe('without option "nullable"', function() { + it('should ignore keyword nullable', function() { + ajv = new Ajv; + + testNotNullable({ + type: 'number', + nullable: true + }); + + testNotNullable({ + type: ['number'], + nullable: true + }); + + testNullable({ + type: ['number', 'null'], + }); + + testNullable({ + type: ['number', 'null'], + nullable: true + }); + + should.not.throw(function () { + ajv.compile({nullable: false}); + }); + }); + }); + + function testNullable(schema) { + var validate = ajv.compile(schema); + validate(1) .should.equal(true); + validate(null) .should.equal(true); + validate('1') .should.equal(false); + } + + function testNotNullable(schema) { + var validate = ajv.compile(schema); + validate(1) .should.equal(true); + validate(null) .should.equal(false); + validate('1') .should.equal(false); + } + }); }); From c8a32771e44f14807b22c0d75af02f45c8e36aa0 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 10 Nov 2018 21:28:01 +0000 Subject: [PATCH 163/333] docs: type definition for serialize option, #880 --- lib/ajv.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index 2c26417e1..60fbb5602 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -165,6 +165,7 @@ declare namespace ajv { cache?: object; logger?: CustomLogger | false; nullable?: boolean; + serialize?: ((schema: object | boolean) => any) | false; } type FormatValidator = string | RegExp | ((data: string) => boolean | PromiseLike); From d298a4802d7d7b5702a47935f8d8fe443fc0c29e Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 11 Nov 2018 11:06:11 +0000 Subject: [PATCH 164/333] fix types --- lib/ajv.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index 60fbb5602..763a09be6 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -104,7 +104,7 @@ declare namespace ajv { * @return {string} human readable string with all errors descriptions */ errorsText(errors?: Array | null, options?: ErrorsTextOptions): string; - errors?: Array; + errors?: Array | null; } interface CustomLogger { From f5937d93a957283ec713fba8202326e5f1e9c45d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 11 Nov 2018 11:49:20 +0000 Subject: [PATCH 165/333] remove old draft-6 mentions --- lib/ajv.js | 4 ++-- spec/options.spec.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ajv.js b/lib/ajv.js index a50dc1a14..9f16723aa 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -70,7 +70,7 @@ function Ajv(opts) { this._metaOpts = getMetaSchemaOptions(this); if (opts.formats) addInitialFormats(this); - addDraft6MetaSchema(this); + addDefaultMetaSchema(this); if (typeof opts.meta == 'object') this.addMetaSchema(opts.meta); if (opts.nullable) this.addKeyword('nullable', {metaSchema: {const: true}}); addInitialSchemas(this); @@ -444,7 +444,7 @@ function addFormat(name, format) { } -function addDraft6MetaSchema(self) { +function addDefaultMetaSchema(self) { var $dataSchema; if (self._opts.$data) { $dataSchema = require('./refs/data.json'); diff --git a/spec/options.spec.js b/spec/options.spec.js index 84bd4812d..00d8392ba 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -298,7 +298,7 @@ describe('Ajv Options', function () { }); describe('meta and validateSchema', function() { - it('should add draft-6 meta schema by default', function() { + it('should add draft-7 meta schema by default', function() { testOptionMeta(new Ajv); testOptionMeta(new Ajv({ meta: true })); From f9fcc504a4aaee3653af0f61c7681e2e80557f67 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 11 Nov 2018 11:59:09 +0000 Subject: [PATCH 166/333] refactor: remove "equal" file --- lib/compile/equal.js | 3 --- spec/custom.spec.js | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 lib/compile/equal.js diff --git a/lib/compile/equal.js b/lib/compile/equal.js deleted file mode 100644 index 911774c85..000000000 --- a/lib/compile/equal.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -module.exports = require('fast-deep-equal'); diff --git a/spec/custom.spec.js b/spec/custom.spec.js index 21635bc25..5b2511617 100644 --- a/spec/custom.spec.js +++ b/spec/custom.spec.js @@ -2,7 +2,7 @@ var getAjvInstances = require('./ajv_instances') , should = require('./chai').should() - , equal = require('../lib/compile/equal') + , equal = require('fast-deep-equal') , customRules = require('./custom_rules'); From 9fd1c2a64b6fad3dae5c24625efb5fbaecb02b5f Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Thu, 29 Nov 2018 06:47:30 +0000 Subject: [PATCH 167/333] chore: un-pin bluebird --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5ab39c470..d9177196f 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ }, "devDependencies": { "ajv-async": "^1.0.0", - "bluebird": "3.5.1", + "bluebird": "^3.5.3", "brfs": "^2.0.0", "browserify": "^16.2.0", "chai": "^4.0.1", From d665aab9c9ca9f7ac2eee9c90d268aaa15292038 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Thu, 29 Nov 2018 06:51:39 +0000 Subject: [PATCH 168/333] 6.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d9177196f..c0a18e634 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.5.5", + "version": "6.6.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From bf0fc0301e2a61cea17fe1f7b57c100594b846fb Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Thu, 29 Nov 2018 08:51:17 +0000 Subject: [PATCH 169/333] Revert "refactor: remove "equal" file" This reverts commit f9fcc504a4aaee3653af0f61c7681e2e80557f67. --- lib/compile/equal.js | 3 +++ spec/custom.spec.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 lib/compile/equal.js diff --git a/lib/compile/equal.js b/lib/compile/equal.js new file mode 100644 index 000000000..911774c85 --- /dev/null +++ b/lib/compile/equal.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('fast-deep-equal'); diff --git a/spec/custom.spec.js b/spec/custom.spec.js index 5b2511617..21635bc25 100644 --- a/spec/custom.spec.js +++ b/spec/custom.spec.js @@ -2,7 +2,7 @@ var getAjvInstances = require('./ajv_instances') , should = require('./chai').should() - , equal = require('fast-deep-equal') + , equal = require('../lib/compile/equal') , customRules = require('./custom_rules'); From 1de2a5f5f2f9d412b123a18a0020fb19606456c1 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Thu, 29 Nov 2018 08:51:56 +0000 Subject: [PATCH 170/333] 6.6.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c0a18e634..064697167 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.6.0", + "version": "6.6.1", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From a37ed7dc6fd4685e53fadc1fce195d2e1420f70e Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Tue, 11 Dec 2018 17:30:16 +0000 Subject: [PATCH 171/333] chore(package): update karma-sauce-launcher to version 2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 064697167..eeb4e49c2 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "karma": "^3.0.0", "karma-chrome-launcher": "^2.2.0", "karma-mocha": "^1.1.1", - "karma-sauce-launcher": "^1.1.0", + "karma-sauce-launcher": "^2.0.0", "mocha": "^5.1.1", "nyc": "^12.0.1", "pre-commit": "^1.1.1", From ad434b236d46ada11137f303d6d70c2e9bca6782 Mon Sep 17 00:00:00 2001 From: Simon Chan Date: Thu, 13 Dec 2018 17:57:52 +0800 Subject: [PATCH 172/333] docs: fix type definition for errors --- lib/ajv.d.ts | 68 ++++++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index 763a09be6..7ef76d335 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -1,12 +1,36 @@ declare var ajv: { (options?: ajv.Options): ajv.Ajv; - new (options?: ajv.Options): ajv.Ajv; - ValidationError: ValidationError; - MissingRefError: MissingRefError; + new(options?: ajv.Options): ajv.Ajv; + ValidationError: typeof AjvErrors.ValidationError; + MissingRefError: typeof AjvErrors.MissingRefError; $dataMetaSchema: object; } +declare namespace AjvErrors { + class ValidationError extends Error { + constructor(errors: Array); + + message: string; + errors: Array; + ajv: true; + validation: true; + } + + class MissingRefError extends Error { + constructor(baseId: string, ref: string, message?: string); + static message: (baseId: string, ref: string) => string; + + message: string; + missingRef: string; + missingSchema: string; + } +} + declare namespace ajv { + type ValidationError = AjvErrors.ValidationError; + + type MissingRefError = AjvErrors.MissingRefError; + interface Ajv { /** * Validate data using schema @@ -15,7 +39,7 @@ declare namespace ajv { * @param {Any} data to be validated * @return {Boolean} validation result. Errors from the last validation will be available in `ajv.errors` (and also in compiled schema: `schema.errors`). */ - validate(schemaKeyRef: object | string | boolean, data: any): boolean | PromiseLike; + validate(schemaKeyRef: object | string | boolean, data: T): boolean | PromiseLike; /** * Create validating function for passed schema. * @param {object|Boolean} schema schema object @@ -114,13 +138,13 @@ declare namespace ajv { } interface ValidateFunction { - ( - data: any, + ( + data: T, dataPath?: string, parentData?: object | Array, parentDataProperty?: string | number, rootData?: object | Array - ): boolean | PromiseLike; + ): boolean | PromiseLike; schema?: object | boolean; errors?: null | Array; refs?: object; @@ -268,11 +292,11 @@ declare namespace ajv { } type ErrorParameters = RefParams | LimitParams | AdditionalPropertiesParams | - DependenciesParams | FormatParams | ComparisonParams | - MultipleOfParams | PatternParams | RequiredParams | - TypeParams | UniqueItemsParams | CustomParams | - PatternRequiredParams | PropertyNamesParams | - IfParams | SwitchParams | NoParams | EnumParams; + DependenciesParams | FormatParams | ComparisonParams | + MultipleOfParams | PatternParams | RequiredParams | + TypeParams | UniqueItemsParams | CustomParams | + PatternRequiredParams | PropertyNamesParams | + IfParams | SwitchParams | NoParams | EnumParams; interface RefParams { ref: string; @@ -344,29 +368,11 @@ declare namespace ajv { caseIndex: number; } - interface NoParams {} + interface NoParams { } interface EnumParams { allowedValues: Array; } } -declare class ValidationError extends Error { - constructor(errors: Array); - - message: string; - errors: Array; - ajv: true; - validation: true; -} - -declare class MissingRefError extends Error { - constructor(baseId: string, ref: string, message?: string); - static message: (baseId: string, ref: string) => string; - - message: string; - missingRef: string; - missingSchema: string; -} - export = ajv; From d048cfc979dc24494e2d2d18b9d2645b02b4f3ea Mon Sep 17 00:00:00 2001 From: Simon Chan Date: Thu, 13 Dec 2018 17:55:00 +0800 Subject: [PATCH 173/333] test: add tests for typescript definition --- package.json | 2 +- spec/typescript/index.ts | 60 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 spec/typescript/index.ts diff --git a/package.json b/package.json index 064697167..6fbbd53ea 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "test-fast": "AJV_FAST_TEST=true npm run test-spec", "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", + "test-ts": "tsc --target ES5 --noImplicitAny --noEmit spec/typescript/index.ts", "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", diff --git a/spec/typescript/index.ts b/spec/typescript/index.ts new file mode 100644 index 000000000..0858180e9 --- /dev/null +++ b/spec/typescript/index.ts @@ -0,0 +1,60 @@ +import ajv = require("../.."); + +// #region new() +const options: ajv.Options = { + verbose: true, +}; + +let instance: ajv.Ajv; + +instance = ajv(); +instance = ajv(options); + +instance = new ajv(); +instance = new ajv(options); +// #endregion new() + +// #region validate() +let data = { + foo: 42, +} + +let result = instance.validate("", data); + +if (typeof result === "boolean") { + // sync + console.log(result); +} else { + // async + result.then(value => { + data = value; + }); +} +// #endregion validate() + +// #region compile() +const validator = instance.compile({}); +result = validator(data); + +if (typeof result === "boolean") { + // sync + console.log(result); +} else { + // async + result.then(value => { + data = value; + }); +} +// #endregion compile() + +// #region errors +const validationError: ajv.ValidationError = new ajv.ValidationError([]); +validationError instanceof ajv.ValidationError; +validationError.ajv === true; +validationError.validation === true; + +ajv.MissingRefError.message("", ""); +const missingRefError: ajv.MissingRefError = new ajv.MissingRefError("", "", ""); +missingRefError instanceof ajv.MissingRefError; +missingRefError.missingRef; +// #endregion From 3df9b7302f3b8495bb28d04eb1bbf6098a04f1f6 Mon Sep 17 00:00:00 2001 From: xdu Date: Fri, 14 Dec 2018 23:46:47 -0800 Subject: [PATCH 174/333] Adding example to load official draft-04 to README --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 44b5081c3..39909cb71 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,21 @@ var ajv = new Ajv({schemaId: 'id'}); ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); ``` +In case the above meta schema gives you validation error on `id` or `$schema` see [issue](https://github.com/epoberezkin/ajv/issues/904) you can load the official meta schema json instead. Example with `axios` (feel free to replace `axios` with the your preferred http request package): + +```javascript +// inside an async function. +const axios = require('axios'); +const util = require('util'); + +const response = await axios.get('http://json-schema.org/draft-04/schema#'); +if (response.status != 200) { + throw new Error(util.format('could not load draft-04 schema. status=%s', response.status)); +} + +ajv.addMetaSchema(response.data); +``` + ## Contents From 8f24e34158b1057771ac07fc5c19ff7557d2d340 Mon Sep 17 00:00:00 2001 From: Xing Du Date: Sat, 15 Dec 2018 19:10:57 -0800 Subject: [PATCH 175/333] Replace lib/refs/json-schema-draft-04.json with a copy of the official one And revert the previous README change --- README.md | 15 --------------- lib/refs/json-schema-draft-04.json | 7 +++---- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 39909cb71..44b5081c3 100644 --- a/README.md +++ b/README.md @@ -34,21 +34,6 @@ var ajv = new Ajv({schemaId: 'id'}); ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); ``` -In case the above meta schema gives you validation error on `id` or `$schema` see [issue](https://github.com/epoberezkin/ajv/issues/904) you can load the official meta schema json instead. Example with `axios` (feel free to replace `axios` with the your preferred http request package): - -```javascript -// inside an async function. -const axios = require('axios'); -const util = require('util'); - -const response = await axios.get('http://json-schema.org/draft-04/schema#'); -if (response.status != 200) { - throw new Error(util.format('could not load draft-04 schema. status=%s', response.status)); -} - -ajv.addMetaSchema(response.data); -``` - ## Contents diff --git a/lib/refs/json-schema-draft-04.json b/lib/refs/json-schema-draft-04.json index 85eb502a6..bcbb84743 100644 --- a/lib/refs/json-schema-draft-04.json +++ b/lib/refs/json-schema-draft-04.json @@ -28,12 +28,10 @@ "type": "object", "properties": { "id": { - "type": "string", - "format": "uri" + "type": "string" }, "$schema": { - "type": "string", - "format": "uri" + "type": "string" }, "title": { "type": "string" @@ -137,6 +135,7 @@ } ] }, + "format": { "type": "string" }, "allOf": { "$ref": "#/definitions/schemaArray" }, "anyOf": { "$ref": "#/definitions/schemaArray" }, "oneOf": { "$ref": "#/definitions/schemaArray" }, From 223058beb07e46ede4b8e344361b696069b9ba95 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 16 Dec 2018 20:46:30 +0000 Subject: [PATCH 176/333] refactor: remove uri format change during schema validation, closes #906 --- lib/ajv.js | 10 +--------- spec/options.spec.js | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/ajv.js b/lib/ajv.js index 9f16723aa..7a83e3b5e 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -55,8 +55,6 @@ function Ajv(opts) { this._refs = {}; this._fragments = {}; this._formats = formats(opts.format); - var schemaUriFormat = this._schemaUriFormat = this._formats['uri-reference']; - this._schemaUriFormatFunc = function (str) { return schemaUriFormat.test(str); }; this._cache = opts.cache || new Cache; this._loadingSchemas = {}; @@ -171,13 +169,7 @@ function validateSchema(schema, throwOrLogError) { this.errors = null; return true; } - var currentUriFormat = this._formats.uri; - this._formats.uri = typeof currentUriFormat == 'function' - ? this._schemaUriFormatFunc - : this._schemaUriFormat; - var valid; - try { valid = this.validate($schema, schema); } - finally { this._formats.uri = currentUriFormat; } + var valid = this.validate($schema, schema); if (!valid && throwOrLogError) { var message = 'schema is invalid: ' + this.errorsText(); if (this._opts.validateSchema == 'log') this.logger.error(message); diff --git a/spec/options.spec.js b/spec/options.spec.js index 00d8392ba..1754fa8fd 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -1194,7 +1194,7 @@ describe('Ajv Options', function () { describe('= "id"', function() { it('should use id and ignore $id', function() { var ajv = new Ajv({schemaId: 'id', meta: false}); - ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); + ajv.addMetaSchema(require('../lib/refs/json-schema-draft-04.json')); ajv._opts.defaultMeta = 'http://json-schema.org/draft-04/schema#'; ajv.addSchema({ id: 'mySchema1', type: 'string' }); From b4c0af9d299ff8892811723b0f98ef8eee816024 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 16 Dec 2018 20:49:44 +0000 Subject: [PATCH 177/333] docs: mark url format as deprecated --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 44b5081c3..15b1e6a18 100644 --- a/README.md +++ b/README.md @@ -244,7 +244,7 @@ The following formats are supported for string validation with "format" keyword: - _uri_: full URI. - _uri-reference_: URI reference, including full and relative URIs. - _uri-template_: URI template according to [RFC6570](https://tools.ietf.org/html/rfc6570) -- _url_: [URL record](https://url.spec.whatwg.org/#concept-url). +- _url_ (deprecated): [URL record](https://url.spec.whatwg.org/#concept-url). - _email_: email address. - _hostname_: host name according to [RFC1034](http://tools.ietf.org/html/rfc1034#section-3.5). - _ipv4_: IP address v4. From 78b77b67290f6cafb2066e2a0e8681d81ca74b0c Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 16 Dec 2018 20:54:54 +0000 Subject: [PATCH 178/333] 6.6.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eeb4e49c2..0388bd2cc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.6.1", + "version": "6.6.2", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 0b57989f36883697ad3d7eca48c420737e931cc4 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Fri, 28 Dec 2018 13:02:56 +0000 Subject: [PATCH 179/333] docs: equal.js file is needed, closes #890 --- lib/compile/equal.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/compile/equal.js b/lib/compile/equal.js index 911774c85..4b271d543 100644 --- a/lib/compile/equal.js +++ b/lib/compile/equal.js @@ -1,3 +1,5 @@ 'use strict'; +// do NOT remove this file - it would break pre-compiled schemas +// https://github.com/epoberezkin/ajv/issues/889 module.exports = require('fast-deep-equal'); From af24ccf58702105c45a517b4df9e6ef3d9a18aa5 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 2 Jan 2019 23:24:44 +0000 Subject: [PATCH 180/333] Update issue templates --- .github/ISSUE_TEMPLATE/bug-or-error-report.md | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug-or-error-report.md diff --git a/.github/ISSUE_TEMPLATE/bug-or-error-report.md b/.github/ISSUE_TEMPLATE/bug-or-error-report.md new file mode 100644 index 000000000..7f102d1e0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-or-error-report.md @@ -0,0 +1,73 @@ +--- +name: Bug or error report +about: Please use for issues with incorrect validation behaviour +title: '' +labels: '' +assignees: '' + +--- + +**What version of Ajv are you using? Does the issue happen if you use the latest version?** + + + +**Ajv options object** + + + +```javascript + + +``` + + +**JSON Schema** + + + +```json + + +``` + + +**Sample data** + + + +```json + + +``` + + +**Your code** + + + +```javascript + + +``` + + +**Validation result, data AFTER validation, error messages** + +``` + + +``` + +**What results did you expect?** + + +**Are you going to resolve the issue?** From 8c7ce5997f9be17a15feaf604c940adaefdd7a33 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Wed, 2 Jan 2019 23:40:41 +0000 Subject: [PATCH 181/333] update issue templates --- .github/ISSUE_TEMPLATE.md | 2 +- .github/ISSUE_TEMPLATE/bug-or-error-report.md | 10 +++++++++- .github/ISSUE_TEMPLATE/change.md | 9 +++++++++ .github/ISSUE_TEMPLATE/compatibility.md | 19 ++++++++++++++----- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 1deda1e23..bac24aca4 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -2,7 +2,7 @@ Frequently Asked Questions: https://github.com/epoberezkin/ajv/blob/master/FAQ.md Please provide all info and reduce your schema and data to the smallest possible size. -This template is for bug reports. For other issues please use: +This template is for bug or error reports. For other issues please use: - a new feature/improvement: http://epoberezkin.github.io/ajv/contribute.html#changes - browser/compatibility issues: http://epoberezkin.github.io/ajv/contribute.html#compatibility - JSON-Schema standard: http://epoberezkin.github.io/ajv/contribute.html#json-schema diff --git a/.github/ISSUE_TEMPLATE/bug-or-error-report.md b/.github/ISSUE_TEMPLATE/bug-or-error-report.md index 7f102d1e0..3da68b71e 100644 --- a/.github/ISSUE_TEMPLATE/bug-or-error-report.md +++ b/.github/ISSUE_TEMPLATE/bug-or-error-report.md @@ -1,12 +1,20 @@ --- name: Bug or error report -about: Please use for issues with incorrect validation behaviour +about: Please use for issues related to incorrect validation behaviour title: '' labels: '' assignees: '' --- + + **What version of Ajv are you using? Does the issue happen if you use the latest version?** diff --git a/.github/ISSUE_TEMPLATE/change.md b/.github/ISSUE_TEMPLATE/change.md index 965bd02ff..7306708c4 100644 --- a/.github/ISSUE_TEMPLATE/change.md +++ b/.github/ISSUE_TEMPLATE/change.md @@ -1,3 +1,12 @@ +--- +name: Feature or change proposal +about: For proposals of new features, options or some other improvements +title: '' +labels: 'enhancement' +assignees: '' + +--- + + +**The version of Ajv you are using** + +**Operating system and node.js version** + +**Package manager and its version** + +**Link to (or contents of) package.json** + +**Error messages** + +**The output of `npm ls`** diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6082d0d88..897f63485 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,6 +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) + - [Installation and dependency issues](#installation) - [JSON Schema standard](#json-schema) - [Ajv usage questions](#usage) - [Code](#code) @@ -63,7 +64,7 @@ Please include as much details as possible. #### Browser and compatibility issues -[Create an issue](https://github.com/epoberezkin/ajv/issues/new?template=compatibility.md) 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). +[Create an issue](https://github.com/epoberezkin/ajv/issues/new?template=compatibility.md) to report a compatibility problem that only happens in a particular environment (when your code works correctly in node.js v8+ in linux systems but fails in some other environment). Please include this information: @@ -71,10 +72,29 @@ Please include this information: 2. The environment you have the problem with. 3. Your code (please make it as small as possible to reproduce the issue). 4. If your issue is in the browser, please list the other packages loaded in the page in the order they are loaded. Please check if the issue gets resolved (or results change) if you move Ajv bundle closer to the top. -5. Results in node.js v4. +5. Results in node.js v8+. 6. Results and error messages in your platform. +#### Installation and dependency issues + +[Create an issue](https://github.com/epoberezkin/ajv/issues/new?template=installation.md) to report problems that happen during Ajv installation or when Ajv is missing some dependency. + +Before submitting the issue, please try the following: +- use the latest stable Node.js and `npm` +- use `yarn` instead of `npm` - the issue can be related to https://github.com/npm/npm/issues/19877 +- remove `node_modules` and `package-lock.json` and run install again + +If nothing helps, please submit: + +1. The version of Ajv you are using +2. Operating system and node.js version +3. Package manager and its version +4. Link to (or contents of) package.json +5. Error messages +6. The output of `npm ls` + + #### Using JSON Schema standard Ajv implements JSON Schema standard draft-04 and draft-06/07. From b65d61aa524d1928c634a250c6a363da58893a1d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Thu, 3 Jan 2019 20:34:39 +0000 Subject: [PATCH 185/333] remove generic argument type --- lib/ajv.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index 7ef76d335..6593d0788 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -39,7 +39,7 @@ declare namespace ajv { * @param {Any} data to be validated * @return {Boolean} validation result. Errors from the last validation will be available in `ajv.errors` (and also in compiled schema: `schema.errors`). */ - validate(schemaKeyRef: object | string | boolean, data: T): boolean | PromiseLike; + validate(schemaKeyRef: object | string | boolean, data: any): boolean | PromiseLike; /** * Create validating function for passed schema. * @param {object|Boolean} schema schema object @@ -138,13 +138,13 @@ declare namespace ajv { } interface ValidateFunction { - ( - data: T, + ( + data: any, dataPath?: string, parentData?: object | Array, parentDataProperty?: string | number, rootData?: object | Array - ): boolean | PromiseLike; + ): boolean | PromiseLike; schema?: object | boolean; errors?: null | Array; refs?: object; From 9c0a36515e9ce85d9cbb4601cab7aba0230a3778 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Thu, 3 Jan 2019 20:48:54 +0000 Subject: [PATCH 186/333] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 15b1e6a18..e2b1ad3d3 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); - [Validation errors](#validation-errors) - [Plugins](#plugins) - [Related packages](#related-packages) -- [Packages using Ajv](#some-packages-using-ajv) +- [Some packages using Ajv](#some-packages-using-ajv) - [Tests, Contributing, History, License](#tests) @@ -1228,7 +1228,7 @@ If you have published a useful plugin please submit a PR to add it to the next s - [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) - 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-keywords](https://github.com/epoberezkin/ajv-keywords) - plugin with custom validation keywords (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 95edb4958dd3a93b5dd2179aa71e517a386c7d12 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Thu, 3 Jan 2019 21:47:28 +0000 Subject: [PATCH 187/333] useDefaults: "empty" (#916) * test: new value "empty" for useDefaults option * feat: option useDefaults: "empty", closes #912 --- README.md | 39 ++++-------------------- lib/dot/defaults.def | 7 ++++- spec/options.spec.js | 71 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index e2b1ad3d3..af9b83782 100644 --- a/README.md +++ b/README.md @@ -699,13 +699,11 @@ The schema above is also more efficient - it will compile into a faster function With [option `useDefaults`](#options) Ajv will assign values from `default` keyword in the schemas of `properties` and `items` (when it is the array of schemas) to the missing properties and items. -This option modifies original data. - -__Please note__: by default the default value is inserted in the generated validation code as a literal (starting from v4.0), so the value inserted in the data will be the deep clone of the default in the schema. +With the option value `"empty"` properties and items equal to `null` or `""` (empty string) will be considered missing and assigned defaults. -If you need to insert the default value in the data by reference pass the option `useDefaults: "shared"`. +This option modifies original data. -Inserting defaults by reference can be faster (in case you have an object in `default`) and it allows to have dynamic values in defaults, e.g. timestamp, without recompiling the schema. The side effect is that modifying the default value in any validated data instance will change the default in the schema and in other validated data instances. See example 3 below. +__Please note__: the default value is inserted in the generated validation code as a literal, so the value inserted in the data will be the deep clone of the default in the schema. Example 1 (`default` in `properties`): @@ -748,32 +746,6 @@ console.log(validate(data)); // true console.log(data); // [ 1, "foo" ] ``` -Example 3 (inserting "defaults" by reference): - -```javascript -var ajv = new Ajv({ useDefaults: 'shared' }); - -var schema = { - properties: { - foo: { - default: { bar: 1 } - } - } -} - -var validate = ajv.compile(schema); - -var data = {}; -console.log(validate(data)); // true -console.log(data); // { foo: { bar: 1 } } - -data.foo.bar = 2; - -var data2 = {}; -console.log(validate(data2)); // true -console.log(data2); // { foo: { bar: 2 } } -``` - `default` keywords in other cases are ignored: - not in `properties` or `items` subschemas @@ -1115,8 +1087,9 @@ Defaults: - `"failing"` - additional properties that fail schema validation will be removed (where `additionalProperties` keyword is `false` or schema). - _useDefaults_: replace missing properties and items with the values from corresponding `default` keywords. Default behaviour is to ignore `default` keywords. This option is not used if schema is added with `addMetaSchema` method. See examples in [Assigning defaults](#assigning-defaults). Option values: - `false` (default) - do not use defaults - - `true` - insert defaults by value (safer and slower, object literal is used). - - `"shared"` - insert defaults by reference (faster). If the default is an object, it will be shared by all instances of validated data. If you modify the inserted default in the validated data, it will be modified in the schema as well. + - `true` - insert defaults by value (object literal is used). + - `"empty"` - use defaults for properties and items that are present and equal to `null` or `""` (an empty string). + - `"shared"` (deprecated) - insert defaults by reference. If the default is an object, it will be shared by all instances of validated data. If you modify the inserted default in the validated data, it will be modified in the schema as well. - _coerceTypes_: change data type of data to match `type` keyword. See the example in [Coercing data types](#coercing-data-types) and [coercion rules](https://github.com/epoberezkin/ajv/blob/master/COERCION.md). Option values: - `false` (default) - no type coercion. - `true` - coerce scalar data types. diff --git a/lib/dot/defaults.def b/lib/dot/defaults.def index 5ad8d1d2d..f100cc4bf 100644 --- a/lib/dot/defaults.def +++ b/lib/dot/defaults.def @@ -1,5 +1,10 @@ {{## def.assignDefault: - if ({{=$passData}} === undefined) + if ({{=$passData}} === undefined + {{? it.opts.useDefaults == 'empty' }} + || {{=$passData}} === null + || {{=$passData}} === '' + {{?}} + ) {{=$passData}} = {{? it.opts.useDefaults == 'shared' }} {{= it.useDefault($sch.default) }} {{??}} diff --git a/spec/options.spec.js b/spec/options.spec.js index 1754fa8fd..991f831d6 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -695,6 +695,77 @@ describe('Ajv Options', function () { throw new Error('unknown useDefaults mode'); } }); + + + describe('defaults with "empty" values', function() { + var schema, data; + + beforeEach(function() { + schema = { + properties: { + obj: { + properties: { + str: {default: 'foo'}, + n1: {default: 1}, + n2: {default: 2}, + n3: {default: 3} + } + }, + arr: { + items: [ + {default: 'foo'}, + {default: 1}, + {default: 2}, + {default: 3} + ] + } + } + }; + + data = { + obj: { + str: '', + n1: null, + n2: undefined + }, + arr: ['', null, undefined] + }; + }); + + it('should NOT assign defaults when useDefaults is true/"shared"', function() { + test(new Ajv({useDefaults: true})); + test(new Ajv({useDefaults: 'shared'})); + + function test(ajv) { + var validate = ajv.compile(schema); + validate(data) .should.equal(true); + data .should.eql({ + obj: { + str: '', + n1: null, + n2: 2, + n3: 3 + }, + arr: ['', null, 2, 3] + }); + } + }); + + it('should assign defaults when useDefaults = "empty"', function() { + var ajv = new Ajv({useDefaults: 'empty'}); + var validate = ajv.compile(schema); + validate(data) .should.equal(true); + data .should.eql({ + obj: { + str: 'foo', + n1: 1, + n2: 2, + n3: 3 + }, + arr: ['foo', 1, 2, 3] + }); + }); + }); }); From 38a9ad4cf9498532190f49fac7245930e649fe58 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 13 Jan 2019 17:50:05 +0000 Subject: [PATCH 188/333] 6.7.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 38b8a6d43..5db7efa6a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.6.2", + "version": "6.7.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 0c31c1e2a81e315511c60a0dd7420a72cb181e61 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 19 Jan 2019 18:14:21 +0000 Subject: [PATCH 189/333] docs: transfer notice --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index af9b83782..fca515ee3 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@ The fastest JSON Schema validator for Node.js and browser. Supports draft-04/06/07. - [![Build Status](https://travis-ci.org/epoberezkin/ajv.svg?branch=master)](https://travis-ci.org/epoberezkin/ajv) [![npm](https://img.shields.io/npm/v/ajv.svg)](https://www.npmjs.com/package/ajv) [![npm downloads](https://img.shields.io/npm/dm/ajv.svg)](https://www.npmjs.com/package/ajv) @@ -12,6 +11,7 @@ The fastest JSON Schema validator for Node.js and browser. Supports draft-04/06/ [![Greenkeeper badge](https://badges.greenkeeper.io/epoberezkin/ajv.svg)](https://greenkeeper.io/) [![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv) +### _Ajv and [related repositories](#related-packages) will be transfered to [ajv-validator](https://github.com/ajv-validator) org_ ## Using version 6 From 0bf7d38bc85e4b8925a75f9355b7b1dfb4fd0b66 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Feb 2019 15:16:42 +0000 Subject: [PATCH 190/333] docs: option defaults for "empty" properties, closes #927 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fca515ee3..a66cc0178 100644 --- a/README.md +++ b/README.md @@ -1085,10 +1085,10 @@ Defaults: - `"all"` - all additional properties are removed, regardless of `additionalProperties` keyword in schema (and no validation is made for them). - `true` - only additional properties with `additionalProperties` keyword equal to `false` are removed. - `"failing"` - additional properties that fail schema validation will be removed (where `additionalProperties` keyword is `false` or schema). -- _useDefaults_: replace missing properties and items with the values from corresponding `default` keywords. Default behaviour is to ignore `default` keywords. This option is not used if schema is added with `addMetaSchema` method. See examples in [Assigning defaults](#assigning-defaults). Option values: +- _useDefaults_: replace missing or undefined properties and items with the values from corresponding `default` keywords. Default behaviour is to ignore `default` keywords. This option is not used if schema is added with `addMetaSchema` method. See examples in [Assigning defaults](#assigning-defaults). Option values: - `false` (default) - do not use defaults - `true` - insert defaults by value (object literal is used). - - `"empty"` - use defaults for properties and items that are present and equal to `null` or `""` (an empty string). + - `"empty"` - in addition to missing or undefined, use defaults for properties and items that are equal to `null` or `""` (an empty string). - `"shared"` (deprecated) - insert defaults by reference. If the default is an object, it will be shared by all instances of validated data. If you modify the inserted default in the validated data, it will be modified in the schema as well. - _coerceTypes_: change data type of data to match `type` keyword. See the example in [Coercing data types](#coercing-data-types) and [coercion rules](https://github.com/epoberezkin/ajv/blob/master/COERCION.md). Option values: - `false` (default) - no type coercion. From 334071a380c37e4d24b37de79e7ed7cc4c63a7e5 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Feb 2019 18:31:00 +0000 Subject: [PATCH 191/333] docs: security considerations --- README.md | 49 +++++++++++++++ lib/refs/json-schema-secure.json | 100 +++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 lib/refs/json-schema-secure.json diff --git a/README.md b/README.md index a66cc0178..1e394e78d 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); - [Defining custom keywords](#defining-custom-keywords) - [Asynchronous schema compilation](#asynchronous-schema-compilation) - [Asynchronous validation](#asynchronous-validation) + - [Security considerations](#security-considerations) - Modifying data during validation - [Filtering data](#filtering-data) - [Assigning defaults](#assigning-defaults) @@ -605,6 +606,54 @@ validate(data).then(successFunc).catch(errorFunc); See [Options](#options). +## Security considerations + +##### Untrusted schemas + +Ajv treats JSON schemas as trusted as your application code. This security model is based on the most common use case, when the schemas are static and bundled together with the application. + +If your schemas are received from untrusted sources (or generated from untrusted data) there may be several scenarios you may want to prevent: +- compiling schemas can cause stack overflow (if they are too deep) +- compiling schemas can be slow (e.g. [#557](https://github.com/epoberezkin/ajv/issues/557)) +- validating certain data can be slow + +It is difficult to predict all the scenarios, but at the very least it is recommended to limit the size of untrusted JSON Schemas (e.g. as JSON string length) and the maximum schema object depth (that can be high for relatively small JSON strings). Even that would not prevent slow regular expressions in schemas. + +Regardless the measures you take, using untrusted schemas increases security risks. + + +##### Circular references in JavaScript objects + +Ajv does not support schemas and validated data that have circular references in objects. See [issue #802](https://github.com/epoberezkin/ajv/issues/802). + +An attempt to compile such schemas or validate such data would cause stack overflow (or will not complete in case of asynchronous validation). Untrusted data can lead to circular references, depending on the parser you use. + + +##### Security risks of trusted schemas + +Some keywords in JSON Schemas can lead to very slow validation for certain data. These keywords include (but, most likely, not limited to): + +- `pattern` and `format` for large strings - use `maxLength` to mitigate +- `uniqueItems` for large non-scalar arrays - use `maxItems` to mitigate +- `patternProperties` for large property names - use `propertyNames` to mitigate + +__Please note__: The suggestions above to prevent slow validation would only work if you do NOT use `allErrors: true` in production code (using it would continue validation after validation errors). + +You can validate your JSON schemas against [this meta-schema](https://github.com/epoberezkin/ajv/blob/master/lib/refs/json-schema-secure.json) to check that these recommendations are followed: + +``` +const isSchemaSecure = ajv.compile(require('ajv/lib/refs/json-schema-secure.json')); + +const schema1 = {format: 'email'}; +isSchemaSecure(schema1); // false + +const schema2 = {format: 'email', maxLength: 256}; +isSchemaSecure(schema2); // true +``` + +__Please note__: even following all these recommendation is not a guarantee that validation of untrusted data is absolutely safe - it can still lead to some undesirable situations. + + ## Filtering data With [option `removeAdditional`](#options) (added by [andyscott](https://github.com/andyscott)) you can filter data during the validation. diff --git a/lib/refs/json-schema-secure.json b/lib/refs/json-schema-secure.json new file mode 100644 index 000000000..d6a8e4c42 --- /dev/null +++ b/lib/refs/json-schema-secure.json @@ -0,0 +1,100 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-secure.json#", + "title": "Meta-schema for the security assessment of JSON Schemas", + "description": "If a JSON Schema fails validation against this meta-schema, it may be unsafe to validate untrusted data", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": {"$ref": "#"} + } + }, + "allOf": [ + { + "description": "prevent slow validation of large property names", + "if": { + "required": ["patternProperties"] + }, + "then": { + "required": ["propertyNames"], + "properties": { + "propertyNames": { + "required": ["maxLength"] + } + } + } + }, + { + "description": "prevent slow validation of large non-scalar arrays", + "if": { + "required": ["uniqueItems"], + "properties": { + "uniqueItems": {"const": true}, + "type": { + "anyOf": [ + { + "enum": ["object", "array"] + }, + { + "type": "array", + "contains": {"enum": ["object", "array"]} + } + ] + } + } + }, + "then": { + "required": ["maxItems"] + } + }, + { + "description": "prevent slow validation of large strings", + "if": { + "anyOf": [ + {"required": ["pattern"]}, + {"required": ["format"]} + ] + }, + "then": { + "required": ["maxLength"] + } + } + ], + "properties": { + "additionalItems": {"$ref": "#"}, + "additionalProperties": {"$ref": "#"}, + "dependencies": { + "additionalProperties": { + "anyOf": [ + {"type": "array"}, + {"$ref": "#"} + ] + } + }, + "items": { + "anyOf": [ + {"$ref": "#"}, + {"$ref": "#/definitions/schemaArray"} + ] + }, + "definitions": { + "additionalProperties": {"$ref": "#"} + }, + "patternProperties": { + "additionalProperties": {"$ref": "#"} + }, + "properties": { + "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": "#"} + } +} From 4031b6ac04199bcedbb6c33d5d8f6c25de9f00d4 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Feb 2019 19:26:59 +0000 Subject: [PATCH 192/333] test: schema security --- README.md | 13 ++-- lib/refs/json-schema-secure.json | 22 ++++--- spec/security.spec.js | 27 ++++++++ spec/security/array.json | 104 +++++++++++++++++++++++++++++++ spec/security/object.json | 29 +++++++++ spec/security/string.json | 44 +++++++++++++ 6 files changed, 225 insertions(+), 14 deletions(-) create mode 100644 spec/security.spec.js create mode 100644 spec/security/array.json create mode 100644 spec/security/object.json create mode 100644 spec/security/string.json diff --git a/README.md b/README.md index 1e394e78d..a5acea42f 100644 --- a/README.md +++ b/README.md @@ -608,16 +608,19 @@ See [Options](#options). ## Security considerations +JSON Schema, if properly used, can replace data sanitisation. It doesn't replace other API security considerations. It also introduces additional security aspects to consider. + + ##### Untrusted schemas Ajv treats JSON schemas as trusted as your application code. This security model is based on the most common use case, when the schemas are static and bundled together with the application. -If your schemas are received from untrusted sources (or generated from untrusted data) there may be several scenarios you may want to prevent: +If your schemas are received from untrusted sources (or generated from untrusted data) there are several scenarios you need to prevent: - compiling schemas can cause stack overflow (if they are too deep) - compiling schemas can be slow (e.g. [#557](https://github.com/epoberezkin/ajv/issues/557)) - validating certain data can be slow -It is difficult to predict all the scenarios, but at the very least it is recommended to limit the size of untrusted JSON Schemas (e.g. as JSON string length) and the maximum schema object depth (that can be high for relatively small JSON strings). Even that would not prevent slow regular expressions in schemas. +It is difficult to predict all the scenarios, but at the very least it may help to limit the size of untrusted schemas (e.g. limit JSON string length) and also the maximum schema object depth (that can be high for relatively small JSON strings). You also may want to mitigate slow regular expressions in `pattern` and `patternProperties` keywords. Regardless the measures you take, using untrusted schemas increases security risks. @@ -626,12 +629,12 @@ Regardless the measures you take, using untrusted schemas increases security ris Ajv does not support schemas and validated data that have circular references in objects. See [issue #802](https://github.com/epoberezkin/ajv/issues/802). -An attempt to compile such schemas or validate such data would cause stack overflow (or will not complete in case of asynchronous validation). Untrusted data can lead to circular references, depending on the parser you use. +An attempt to compile such schemas or validate such data would cause stack overflow (or will not complete in case of asynchronous validation). Depending on the parser you use, untrusted data can lead to circular references. ##### Security risks of trusted schemas -Some keywords in JSON Schemas can lead to very slow validation for certain data. These keywords include (but, most likely, not limited to): +Some keywords in JSON Schemas can lead to very slow validation for certain data. These keywords include (but may be not limited to): - `pattern` and `format` for large strings - use `maxLength` to mitigate - `uniqueItems` for large non-scalar arrays - use `maxItems` to mitigate @@ -651,7 +654,7 @@ const schema2 = {format: 'email', maxLength: 256}; isSchemaSecure(schema2); // true ``` -__Please note__: even following all these recommendation is not a guarantee that validation of untrusted data is absolutely safe - it can still lead to some undesirable situations. +__Please note__: even following all these recommendation is not a guarantee that validation of untrusted data is absolutely safe - it can still lead to some undesirable results. ## Filtering data diff --git a/lib/refs/json-schema-secure.json b/lib/refs/json-schema-secure.json index d6a8e4c42..fea4d04fc 100644 --- a/lib/refs/json-schema-secure.json +++ b/lib/refs/json-schema-secure.json @@ -31,16 +31,20 @@ "required": ["uniqueItems"], "properties": { "uniqueItems": {"const": true}, - "type": { - "anyOf": [ - { - "enum": ["object", "array"] - }, - { - "type": "array", - "contains": {"enum": ["object", "array"]} + "items": { + "properties": { + "type": { + "anyOf": [ + { + "enum": ["object", "array"] + }, + { + "type": "array", + "contains": {"enum": ["object", "array"]} + } + ] } - ] + } } } }, diff --git a/spec/security.spec.js b/spec/security.spec.js new file mode 100644 index 000000000..182e8b89f --- /dev/null +++ b/spec/security.spec.js @@ -0,0 +1,27 @@ +'use strict'; + +var jsonSchemaTest = require('json-schema-test') + , getAjvInstances = require('./ajv_instances') + , options = require('./ajv_options') + , suite = require('./browser_test_suite') + , after = require('./after_test'); + +var instances = getAjvInstances(options, { + schemas: [require('../lib/refs/json-schema-secure.json')] +}); + + +jsonSchemaTest(instances, { + description: 'Secure schemas tests of ' + instances.length + ' ajv instances with different options', + suites: { + 'security': typeof window == 'object' + ? suite(require('./security/{**/,}*.json', {mode: 'list'})) + : './security/{**/,}*.json' + }, + assert: require('./chai').assert, + afterError: after.error, + afterEach: after.each, + cwd: __dirname, + hideFolder: 'security/', + timeout: 90000 +}); diff --git a/spec/security/array.json b/spec/security/array.json new file mode 100644 index 000000000..b089a49bb --- /dev/null +++ b/spec/security/array.json @@ -0,0 +1,104 @@ +[ + { + "description": "uniqueItems without type keyword should be used together with maxItems", + "schema": {"$ref": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-secure.json#"}, + "tests": [ + { + "description": "uniqueItems keyword used without maxItems is unsafe", + "data": { + "uniqueItems": true + }, + "valid": false + }, + { + "description": "uniqueItems keyword used with maxItems is safe", + "data": { + "uniqueItems": true, + "maxItems": "10" + }, + "valid": true + }, + { + "description": "uniqueItems: false is ignored (and safe)", + "data": { + "uniqueItems": false + }, + "valid": true + } + ] + }, + { + "description": "uniqueItems with scalar type(s) is safe to use without maxItems", + "schema": {"$ref": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-secure.json#"}, + "tests": [ + { + "description": "uniqueItems keyword with a single scalar type is safe", + "data": { + "uniqueItems": true, + "items": { + "type": "number" + } + }, + "valid": true + }, + { + "description": "uniqueItems keyword with multiple scalar types is safe", + "data": { + "uniqueItems": true, + "items": { + "type": ["number", "string"] + } + }, + "valid": true + } + ] + }, + { + "description": "uniqueItems with compound type(s) should be used together with maxItems", + "schema": {"$ref": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-secure.json#"}, + "tests": [ + { + "description": "uniqueItems keyword with a single compound type and without maxItems is unsafe", + "data": { + "uniqueItems": true, + "items": { + "type": "object" + } + }, + "valid": false + }, + { + "description": "uniqueItems keyword with a single compound type and with maxItems is safe", + "data": { + "uniqueItems": true, + "maxItems": "10", + "items": { + "type": "object" + } + }, + "valid": true + }, + { + "description": "uniqueItems keyword with multiple types including compound type and without maxItems is unsafe", + "data": { + "uniqueItems": true, + "items": { + "type": ["array","number"] + } + }, + "valid": false + }, + { + "description": "uniqueItems keyword with multiple types including compound type and with maxItems is safe", + "data": { + "uniqueItems": true, + "maxItems": "10", + "items": { + "type": ["array","number"] + } + }, + "valid": true + } + ] + } +] diff --git a/spec/security/object.json b/spec/security/object.json new file mode 100644 index 000000000..e8efedecb --- /dev/null +++ b/spec/security/object.json @@ -0,0 +1,29 @@ +[ + { + "description": "patternProperties keyword should be used together with propertyNames", + "schema": { "$ref": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-secure.json#" }, + "tests": [ + { + "description": "patternProperties keyword used without propertyNames is unsafe", + "data": { + "patternProperties": { + ".*": {} + } + }, + "valid": false + }, + { + "description": "patternProperties keyword used with propertyNames is safe", + "data": { + "patternProperties": { + ".*": {} + }, + "propertyNames": { + "maxLength": "256" + } + }, + "valid": true + } + ] + } +] diff --git a/spec/security/string.json b/spec/security/string.json new file mode 100644 index 000000000..6aa4f861e --- /dev/null +++ b/spec/security/string.json @@ -0,0 +1,44 @@ +[ + { + "description": "pattern keyword should be used together with maxLength", + "schema": { "$ref": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-secure.json#" }, + "tests": [ + { + "description": "pattern keyword used without maxLength is unsafe", + "data": { + "pattern": ".*" + }, + "valid": false + }, + { + "description": "pattern keyword used with maxLength is safe", + "data": { + "pattern": ".*", + "maxLength": "256" + }, + "valid": true + } + ] + }, + { + "description": "format keyword should be used together with maxLength", + "schema": { "$ref": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-secure.json#" }, + "tests": [ + { + "description": "format keyword used without maxLength is unsafe", + "data": { + "format": "email" + }, + "valid": false + }, + { + "description": "format keyword used with maxLength is safe", + "data": { + "format": "email", + "maxLength": "256" + }, + "valid": true + } + ] + } +] From a9e03c5916ed3a42629fcce4c0a0d97f8a122ad3 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Feb 2019 19:29:14 +0000 Subject: [PATCH 193/333] docs: js syntax highlight --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a5acea42f..04d112dc3 100644 --- a/README.md +++ b/README.md @@ -644,7 +644,7 @@ __Please note__: The suggestions above to prevent slow validation would only wor You can validate your JSON schemas against [this meta-schema](https://github.com/epoberezkin/ajv/blob/master/lib/refs/json-schema-secure.json) to check that these recommendations are followed: -``` +```javascript const isSchemaSecure = ajv.compile(require('ajv/lib/refs/json-schema-secure.json')); const schema1 = {format: 'email'}; From aced0cc56ed60832db2332f7922d3129e4531630 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Feb 2019 19:46:59 +0000 Subject: [PATCH 194/333] refactor: security meta-schema --- lib/refs/json-schema-secure.json | 40 ++++++++++++-------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/lib/refs/json-schema-secure.json b/lib/refs/json-schema-secure.json index fea4d04fc..66faf84f8 100644 --- a/lib/refs/json-schema-secure.json +++ b/lib/refs/json-schema-secure.json @@ -10,25 +10,19 @@ "items": {"$ref": "#"} } }, - "allOf": [ - { + "dependencies": { + "patternProperties": { "description": "prevent slow validation of large property names", - "if": { - "required": ["patternProperties"] - }, - "then": { - "required": ["propertyNames"], - "properties": { - "propertyNames": { - "required": ["maxLength"] - } + "required": ["propertyNames"], + "properties": { + "propertyNames": { + "required": ["maxLength"] } } }, - { + "uniqueItems": { "description": "prevent slow validation of large non-scalar arrays", "if": { - "required": ["uniqueItems"], "properties": { "uniqueItems": {"const": true}, "items": { @@ -52,19 +46,15 @@ "required": ["maxItems"] } }, - { - "description": "prevent slow validation of large strings", - "if": { - "anyOf": [ - {"required": ["pattern"]}, - {"required": ["format"]} - ] - }, - "then": { - "required": ["maxLength"] - } + "pattern": { + "description": "prevent slow pattern matching of large strings", + "required": ["maxLength"] + }, + "format": { + "description": "prevent slow format validation of large strings", + "required": ["maxLength"] } - ], + }, "properties": { "additionalItems": {"$ref": "#"}, "additionalProperties": {"$ref": "#"}, From e6c1e531e2a9497dd5e789b4835752eddacfa9cc Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Feb 2019 19:49:34 +0000 Subject: [PATCH 195/333] docs: security --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 04d112dc3..93edfdc10 100644 --- a/README.md +++ b/README.md @@ -654,7 +654,7 @@ const schema2 = {format: 'email', maxLength: 256}; isSchemaSecure(schema2); // true ``` -__Please note__: even following all these recommendation is not a guarantee that validation of untrusted data is absolutely safe - it can still lead to some undesirable results. +__Please note__: following all these recommendation is not a guarantee that validation of untrusted data is safe - it can still lead to some undesirable results. ## Filtering data From b9f85073382e03fc4d0d6444c42e1673e26d492d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Feb 2019 19:52:37 +0000 Subject: [PATCH 196/333] 6.8.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5db7efa6a..dc4e65dc7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.7.0", + "version": "6.8.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 6d4b31e16923ce5b11b56625b79d5bf6f798574a Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Feb 2019 20:05:08 +0000 Subject: [PATCH 197/333] 6.8.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dc4e65dc7..9486b6949 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.8.0", + "version": "6.8.1", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 28c85ada05f77dab80e99571d350b554b3bee441 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Wed, 6 Feb 2019 23:34:14 -0800 Subject: [PATCH 198/333] Allow nullable property of JSON Schema object to be false as well as true. Remove test that asserted failure if nullable was false. --- lib/ajv.js | 2 +- spec/options.spec.js | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/ajv.js b/lib/ajv.js index 7a83e3b5e..3e639e8af 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -70,7 +70,7 @@ function Ajv(opts) { if (opts.formats) addInitialFormats(this); addDefaultMetaSchema(this); if (typeof opts.meta == 'object') this.addMetaSchema(opts.meta); - if (opts.nullable) this.addKeyword('nullable', {metaSchema: {const: true}}); + if (opts.nullable) this.addKeyword('nullable', {metaSchema: {type: "boolean"}}); addInitialSchemas(this); } diff --git a/spec/options.spec.js b/spec/options.spec.js index 991f831d6..351f165c6 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -1519,12 +1519,6 @@ describe('Ajv Options', function () { testNotNullable({type: ['number']}); }); - - it('"nullable" keyword must be "true" if present', function() { - should.throw(function() { - ajv.compile({nullable: false}); - }); - }); }); describe('without option "nullable"', function() { From 859259eeca909360e85a0449cbd05dae525bc6c9 Mon Sep 17 00:00:00 2001 From: Mathew Polzin Date: Thu, 7 Feb 2019 08:39:32 -0800 Subject: [PATCH 199/333] Add tests that show that with nullable option on but 'nullable' keyword set to false an object is not nullable. --- spec/options.spec.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/options.spec.js b/spec/options.spec.js index 351f165c6..72c18aa2e 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -1519,6 +1519,18 @@ describe('Ajv Options', function () { testNotNullable({type: ['number']}); }); + + it('should respect "nullable" == false with opts.nullable == true', function() { + testNotNullable({ + type: 'number', + nullable: false + }); + + testNotNullable({ + type: ['number'], + nullable: false + }); + }); }); describe('without option "nullable"', function() { From 58879a03f0cf0719b560d50d444534f0d31eb79f Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 9 Feb 2019 08:43:55 +0000 Subject: [PATCH 200/333] fix: pin jshint to 2.9.7 (#939) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9486b6949..b25b48967 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "glob": "^7.0.0", "if-node-version": "^1.0.0", "js-beautify": "^1.7.3", - "jshint": "^2.9.4", + "jshint": "2.9.7", "json-schema-test": "^2.0.0", "karma": "^3.0.0", "karma-chrome-launcher": "^2.2.0", From ac2221a4b55a721756b5cb73e789b43858ed6258 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 9 Feb 2019 11:40:40 +0000 Subject: [PATCH 201/333] style fix --- lib/ajv.js | 2 +- spec/options.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ajv.js b/lib/ajv.js index 3e639e8af..0a97367fe 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -70,7 +70,7 @@ function Ajv(opts) { if (opts.formats) addInitialFormats(this); addDefaultMetaSchema(this); if (typeof opts.meta == 'object') this.addMetaSchema(opts.meta); - if (opts.nullable) this.addKeyword('nullable', {metaSchema: {type: "boolean"}}); + if (opts.nullable) this.addKeyword('nullable', {metaSchema: {type: 'boolean'}}); addInitialSchemas(this); } diff --git a/spec/options.spec.js b/spec/options.spec.js index 72c18aa2e..2ca1b4ec6 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -1519,7 +1519,7 @@ describe('Ajv Options', function () { testNotNullable({type: ['number']}); }); - + it('should respect "nullable" == false with opts.nullable == true', function() { testNotNullable({ type: 'number', From 51e858e67c3f832b9da8c7703b130e6a2122af5c Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 9 Feb 2019 12:49:52 +0000 Subject: [PATCH 202/333] docs: clarify "format" option values --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 93edfdc10..e2d068e1d 100644 --- a/README.md +++ b/README.md @@ -263,7 +263,7 @@ You can add additional formats and replace any of the formats above using [addFo The option `unknownFormats` allows changing the default behaviour when an unknown format is encountered. In this case Ajv can either fail schema compilation (default) or ignore it (default in versions before 5.0.0). You also can whitelist specific format(s) to be ignored. See [Options](#options) for details. -You can find patterns used for format validation and the sources that were used in [formats.js](https://github.com/epoberezkin/ajv/blob/master/lib/compile/formats.js). +You can find regular expressions used for format validation and the sources that were used in [formats.js](https://github.com/epoberezkin/ajv/blob/master/lib/compile/formats.js). ## Combining schemas with $ref @@ -1101,7 +1101,10 @@ Defaults: - _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. - _nullable_: support keyword "nullable" from [Open API 3 specification](https://swagger.io/docs/specification/data-models/data-types/). -- _format_: formats validation mode ('fast' by default). Pass 'full' for more correct and slow validation or `false` not to validate formats at all. E.g., 25:00:00 and 2015/14/33 will be invalid time and date in 'full' mode but it will be valid in 'fast' mode. +- _format_: formats validation mode. Option values: + - `'fast'` (default) - simplified and fast validation (see [Formats](#formats) for details of which formats are available and affected by this option). + - `'full'` - more restrictive and slow validation. E.g., 25:00:00 and 2015/14/33 will be invalid time and date in 'full' mode but it will be valid in 'fast' mode. + - `false` - ignore all format keywords. - _formats_: an object with custom formats. Keys and values will be passed to `addFormat` method. - _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. From f080c91e9adccad49f191f82902cf6fb6ccafa8e Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 9 Feb 2019 12:52:47 +0000 Subject: [PATCH 203/333] docs: double quotes --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e2d068e1d..ee12e366c 100644 --- a/README.md +++ b/README.md @@ -1102,8 +1102,8 @@ Defaults: - _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. - _nullable_: support keyword "nullable" from [Open API 3 specification](https://swagger.io/docs/specification/data-models/data-types/). - _format_: formats validation mode. Option values: - - `'fast'` (default) - simplified and fast validation (see [Formats](#formats) for details of which formats are available and affected by this option). - - `'full'` - more restrictive and slow validation. E.g., 25:00:00 and 2015/14/33 will be invalid time and date in 'full' mode but it will be valid in 'fast' mode. + - `"fast"` (default) - simplified and fast validation (see [Formats](#formats) for details of which formats are available and affected by this option). + - `"full"` - more restrictive and slow validation. E.g., 25:00:00 and 2015/14/33 will be invalid time and date in 'full' mode but it will be valid in 'fast' mode. - `false` - ignore all format keywords. - _formats_: an object with custom formats. Keys and values will be passed to `addFormat` method. - _unknownFormats_: handling of unknown formats. Option values: From fdfbd4402a82ce63b220323794f64c51900d31d1 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 9 Feb 2019 20:28:33 +0000 Subject: [PATCH 204/333] feat: support for required dependencies of custom keyword (keywords that must be present in the same schema) --- CUSTOM.md | 2 ++ README.md | 1 + lib/compile/index.js | 23 +++++++++++++++------- lib/keyword.js | 10 +++++++++- spec/custom.spec.js | 46 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 8 deletions(-) diff --git a/CUSTOM.md b/CUSTOM.md index 6a5cbe8f1..4d3065e37 100644 --- a/CUSTOM.md +++ b/CUSTOM.md @@ -92,6 +92,8 @@ In some cases it is the best approach to define keywords, but it has the perform 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. +Custom keyword can also have an optional `dependencies` property in their definitions - it is a list of required keywords in a containing (parent) schema. + Example. `range` and `exclusiveRange` keywords using compiled schema: ```javascript diff --git a/README.md b/README.md index ee12e366c..547c374f7 100644 --- a/README.md +++ b/README.md @@ -1005,6 +1005,7 @@ Keyword definition is an object with the following properties: - _inline_: compiling function that returns code (as string) - _schema_: an optional `false` value used with "validate" keyword to not pass schema - _metaSchema_: an optional meta-schema for keyword schema +- _dependencies_: an optional list of properties that must be present in the parent schema - it will be checked during schema compilation - _modifying_: `true` MUST be passed if keyword modifies data - _valid_: pass `true`/`false` to pre-define validation result, the result returned from validation function will be ignored. This option cannot be used with macro keywords. - _$data_: an optional `true` value to support [$data reference](#data-reference) as the value of custom keyword. The reference will be resolved at validation time. If the keyword has meta-schema it would be extended to allow $data and it will be used to validate the resolved value. Supporting $data reference requires that keyword has validating function (as the only option or in addition to compile, macro or inline function). diff --git a/lib/compile/index.js b/lib/compile/index.js index 8e369db3d..bac562706 100644 --- a/lib/compile/index.js +++ b/lib/compile/index.js @@ -255,13 +255,22 @@ function compile(schema, root, localRefs, baseId) { } function useCustomRule(rule, schema, parentSchema, it) { - var validateSchema = rule.definition.validateSchema; - if (validateSchema && self._opts.validateSchema !== false) { - var valid = validateSchema(schema); - if (!valid) { - var message = 'keyword schema is invalid: ' + self.errorsText(validateSchema.errors); - if (self._opts.validateSchema == 'log') self.logger.error(message); - else throw new Error(message); + if (self._opts.validateSchema !== false) { + var deps = rule.definition.dependencies; + if (deps && !deps.every(function(keyword) { + return Object.prototype.hasOwnProperty.call(parentSchema, keyword); + })) { + throw new Error('parent schema must have all required keywords: ' + deps.join(',')); + } + + var validateSchema = rule.definition.validateSchema; + if (validateSchema) { + var valid = validateSchema(schema); + if (!valid) { + var message = 'keyword schema is invalid: ' + self.errorsText(validateSchema.errors); + if (self._opts.validateSchema == 'log') self.logger.error(message); + else throw new Error(message); + } } } diff --git a/lib/keyword.js b/lib/keyword.js index 6ed84c10a..950dceb50 100644 --- a/lib/keyword.js +++ b/lib/keyword.js @@ -24,7 +24,7 @@ function addKeyword(keyword, definition) { if (RULES.keywords[keyword]) throw new Error('Keyword ' + keyword + ' is already defined'); - if (!IDENTIFIER.test(keyword)) + if (!isIdentifier(keyword)) throw new Error('Keyword ' + keyword + ' is not a valid identifier'); if (definition) { @@ -45,6 +45,10 @@ function addKeyword(keyword, definition) { if ($data && !definition.validate) throw new Error('$data support: "validate" function is not defined'); + var deps = definition.dependencies; + if (deps && !(Array.isArray(deps) && deps.every(isIdentifier))) + throw new Error('"dependencies" option should be a list of valid identifiers'); + var metaSchema = definition.metaSchema; if (metaSchema) { if ($data) { @@ -93,6 +97,10 @@ function addKeyword(keyword, definition) { if (!RULES.types[dataType]) throw new Error('Unknown type ' + dataType); } + function isIdentifier(name) { + return typeof name == 'string' && IDENTIFIER.test(name); + } + return this; } diff --git a/spec/custom.spec.js b/spec/custom.spec.js index 21635bc25..d2891da78 100644 --- a/spec/custom.spec.js +++ b/spec/custom.spec.js @@ -1182,4 +1182,50 @@ describe('Custom keywords', function () { }); }); }); + + + describe('"dependencies" in keyword definition', function() { + it("should require properties in the parent schema", function() { + ajv.addKeyword('allRequired', { + macro: function(schema, parentSchema) { + return schema ? {required: Object.keys(parentSchema.properties)} : true; + }, + metaSchema: {type: 'boolean'}, + dependencies: ['properties'] + }); + + var invalidSchema = { + allRequired: true + }; + + should.throw(function () { + ajv.compile(invalidSchema); + }); + + var schema = { + properties: { + foo: true + }, + allRequired: true + }; + + var v = ajv.compile(schema); + v({foo: 1}) .should.equal(true); + v({}) .should.equal(false); + }); + + it("'dependencies'should be array of valid strings", function() { + ajv.addKeyword('newKeyword1', { + metaSchema: {type: 'boolean'}, + dependencies: ['dep1', 'dep-2'] + }); + + should.throw(function () { + ajv.addKeyword('newKeyword2', { + metaSchema: {type: 'boolean'}, + dependencies: ['dep.1'] + }); + }); + }); + }); }); From 33d1ac468297b12127eed28e65e28f0b4e4192f3 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 9 Feb 2019 20:35:12 +0000 Subject: [PATCH 205/333] style fix --- .eslintrc.yml | 2 +- lib/compile/index.js | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index 9fe920d6a..df53e9be5 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -5,7 +5,7 @@ env: rules: block-scoped-var: 2 callback-return: 2 - complexity: [2, 16] + complexity: [2, 18] curly: [2, multi-or-nest, consistent] dot-location: [2, property] dot-notation: 2 diff --git a/lib/compile/index.js b/lib/compile/index.js index bac562706..f4d3f0d58 100644 --- a/lib/compile/index.js +++ b/lib/compile/index.js @@ -258,10 +258,9 @@ function compile(schema, root, localRefs, baseId) { if (self._opts.validateSchema !== false) { var deps = rule.definition.dependencies; if (deps && !deps.every(function(keyword) { - return Object.prototype.hasOwnProperty.call(parentSchema, keyword); - })) { + return Object.prototype.hasOwnProperty.call(parentSchema, keyword); + })) throw new Error('parent schema must have all required keywords: ' + deps.join(',')); - } var validateSchema = rule.definition.validateSchema; if (validateSchema) { From 47c8fc90ad5737d2243de1b9986783f5fe51b703 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 9 Feb 2019 21:56:28 +0000 Subject: [PATCH 206/333] refactor: use json schema to validate custom keyword definition --- README.md | 1 + lib/keyword.js | 65 ++++++++++++++++++++++++++++----------------- spec/custom.spec.js | 4 +-- 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 547c374f7..7bc1dcaeb 100644 --- a/README.md +++ b/README.md @@ -1007,6 +1007,7 @@ Keyword definition is an object with the following properties: - _metaSchema_: an optional meta-schema for keyword schema - _dependencies_: an optional list of properties that must be present in the parent schema - it will be checked during schema compilation - _modifying_: `true` MUST be passed if keyword modifies data +- _statements_: `true` can be passed in case inline keyword generates statements (as opposed to expression) - _valid_: pass `true`/`false` to pre-define validation result, the result returned from validation function will be ignored. This option cannot be used with macro keywords. - _$data_: an optional `true` value to support [$data reference](#data-reference) as the value of custom keyword. The reference will be resolved at validation time. If the keyword has meta-schema it would be extended to allow $data and it will be used to validate the resolved value. Supporting $data reference requires that keyword has validating function (as the only option or in addition to compile, macro or inline function). - _async_: an optional `true` value if the validation function is asynchronous (whether it is compiled or passed in _validate_ property); in this case it should return a promise that resolves with a value `true` or `false`. This option is ignored in case of "macro" and "inline" keywords. diff --git a/lib/keyword.js b/lib/keyword.js index 950dceb50..6419e447f 100644 --- a/lib/keyword.js +++ b/lib/keyword.js @@ -2,6 +2,7 @@ var IDENTIFIER = /^[a-z_$][a-z0-9_$-]*$/i; var customRuleCode = require('./dotjs/custom'); +var metaSchema = require('./refs/json-schema-draft-07.json'); module.exports = { add: addKeyword, @@ -9,6 +10,37 @@ module.exports = { remove: removeKeyword }; +var definitionSchema = { + definitions: { + simpleTypes: metaSchema.definitions.simpleTypes + }, + type: 'object', + dependencies: { + schema: ['validate'], + $data: ['validate'], + statements: ['inline'], + valid: {not: {required: ['macro']}} + }, + properties: { + type: metaSchema.properties.type, + schema: {type: 'boolean'}, + statements: {type: 'boolean'}, + dependencies: { + type: 'array', + items: {type: 'string'} + }, + metaSchema: {type: 'object'}, + modifying: {type: 'boolean'}, + valid: {type: 'boolean'}, + $data: {type: 'boolean'}, + async: {type: 'boolean'}, + errors: {type: 'boolean'}, + validateSchema: true + } +}; + +var validateDefinition; + /** * Define custom keyword * @this Ajv @@ -24,34 +56,26 @@ function addKeyword(keyword, definition) { if (RULES.keywords[keyword]) throw new Error('Keyword ' + keyword + ' is already defined'); - if (!isIdentifier(keyword)) + if (!IDENTIFIER.test(keyword)) throw new Error('Keyword ' + keyword + ' is not a valid identifier'); if (definition) { - if (definition.macro && definition.valid !== undefined) - throw new Error('"valid" option cannot be used with macro keywords'); + validateDefinition = validateDefinition || this.compile(definitionSchema); + + if (!validateDefinition(definition)) + throw new Error('custom keyword definition is invalid: ' + this.errorsText(validateDefinition.errors)); var dataType = definition.type; if (Array.isArray(dataType)) { - var i, len = dataType.length; - for (i=0; i Date: Sat, 9 Feb 2019 22:06:43 +0000 Subject: [PATCH 207/333] eslint option --- .eslintrc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index df53e9be5..9fe920d6a 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -5,7 +5,7 @@ env: rules: block-scoped-var: 2 callback-return: 2 - complexity: [2, 18] + complexity: [2, 16] curly: [2, multi-or-nest, consistent] dot-location: [2, property] dot-notation: 2 From 7079aed19599fd13c95412b181fd2c08feb1debb Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 9 Feb 2019 22:11:59 +0000 Subject: [PATCH 208/333] remove property in custom keyword definition schema --- lib/keyword.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/keyword.js b/lib/keyword.js index 6419e447f..44d3fec56 100644 --- a/lib/keyword.js +++ b/lib/keyword.js @@ -34,8 +34,7 @@ var definitionSchema = { valid: {type: 'boolean'}, $data: {type: 'boolean'}, async: {type: 'boolean'}, - errors: {type: 'boolean'}, - validateSchema: true + errors: {type: 'boolean'} } }; From cd404c4c777670b3195a03b9ff6946e35de382db Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 9 Feb 2019 22:12:27 +0000 Subject: [PATCH 209/333] 6.9.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b25b48967..1be049078 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.8.1", + "version": "6.9.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From fe7f91d06b8507d8bfe2e21552b9c02ce9a8431c Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 10 Feb 2019 08:39:18 +0000 Subject: [PATCH 210/333] fix: incorrect custom keyword definition schema, #941 --- README.md | 2 +- lib/keyword.js | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7bc1dcaeb..5ddefa134 100644 --- a/README.md +++ b/README.md @@ -1011,7 +1011,7 @@ Keyword definition is an object with the following properties: - _valid_: pass `true`/`false` to pre-define validation result, the result returned from validation function will be ignored. This option cannot be used with macro keywords. - _$data_: an optional `true` value to support [$data reference](#data-reference) as the value of custom keyword. The reference will be resolved at validation time. If the keyword has meta-schema it would be extended to allow $data and it will be used to validate the resolved value. Supporting $data reference requires that keyword has validating function (as the only option or in addition to compile, macro or inline function). - _async_: an optional `true` value if the validation function is asynchronous (whether it is compiled or passed in _validate_ property); in this case it should return a promise that resolves with a value `true` or `false`. This option is ignored in case of "macro" and "inline" keywords. -- _errors_: an optional boolean indicating whether keyword returns errors. If this property is not set Ajv will determine if the errors were set in case of failed validation. +- _errors_: an optional boolean or string `"full"` indicating whether keyword returns errors. If this property is not set Ajv will determine if the errors were set in case of failed validation. _compile_, _macro_ and _inline_ are mutually exclusive, only one should be used at a time. _validate_ can be used separately or in addition to them to support $data reference. diff --git a/lib/keyword.js b/lib/keyword.js index 44d3fec56..53d95c69d 100644 --- a/lib/keyword.js +++ b/lib/keyword.js @@ -34,7 +34,12 @@ var definitionSchema = { valid: {type: 'boolean'}, $data: {type: 'boolean'}, async: {type: 'boolean'}, - errors: {type: 'boolean'} + errors: { + anyOf: [ + {type: 'boolean'}, + {const: 'full'} + ] + } } }; From 2fc78ab32ff5311dd110817feabcfdb526d152b6 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 10 Feb 2019 08:40:43 +0000 Subject: [PATCH 211/333] 6.9.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1be049078..5aeecd6af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.9.0", + "version": "6.9.1", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 51685b8eef82588d419a206873bf403422e99d2c Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Sun, 10 Feb 2019 10:14:15 +0000 Subject: [PATCH 212/333] chore(package): update nyc to version 13.2.0 (#930) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5aeecd6af..f4d00500b 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "karma-mocha": "^1.1.1", "karma-sauce-launcher": "^2.0.0", "mocha": "^5.1.1", - "nyc": "^12.0.1", + "nyc": "^13.2.0", "pre-commit": "^1.1.1", "require-globify": "^1.3.0", "typescript": "^2.8.3", From 71dc5dc27d954b76f81e4b9c3d7ce827acd6e6e3 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 10 Feb 2019 12:05:05 +0000 Subject: [PATCH 213/333] refactor: split options.spec.js file --- package.json | 6 +- spec/options.spec.js | 1579 ---------------------- spec/options/comment.spec.js | 92 ++ spec/options/meta_validateSchema.spec.js | 79 ++ spec/options/nullable.spec.js | 97 ++ spec/options/options_add_schemas.spec.js | 130 ++ spec/options/options_code.spec.js | 115 ++ spec/options/options_refs.spec.js | 167 +++ spec/options/options_reporting.spec.js | 158 +++ spec/options/options_validation.spec.js | 94 ++ spec/options/ownProperties.spec.js | 178 +++ spec/options/removeAdditional.spec.js | 122 ++ spec/options/schemaId.spec.js | 72 + spec/options/unknownFormats.spec.js | 108 ++ spec/options/useDefaults.spec.js | 223 +++ 15 files changed, 1638 insertions(+), 1582 deletions(-) delete mode 100644 spec/options.spec.js create mode 100644 spec/options/comment.spec.js create mode 100644 spec/options/meta_validateSchema.spec.js create mode 100644 spec/options/nullable.spec.js create mode 100644 spec/options/options_add_schemas.spec.js create mode 100644 spec/options/options_code.spec.js create mode 100644 spec/options/options_refs.spec.js create mode 100644 spec/options/options_reporting.spec.js create mode 100644 spec/options/options_validation.spec.js create mode 100644 spec/options/ownProperties.spec.js create mode 100644 spec/options/removeAdditional.spec.js create mode 100644 spec/options/schemaId.spec.js create mode 100644 spec/options/unknownFormats.spec.js create mode 100644 spec/options/useDefaults.spec.js diff --git a/package.json b/package.json index f4d00500b..7767cfca8 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,9 @@ ".tonic_example.js" ], "scripts": { - "eslint": "eslint lib/*.js lib/compile/*.js spec/*.js scripts", - "jshint": "jshint lib/*.js lib/**/*.js --exclude lib/dotjs/**/*", - "test-spec": "mocha spec/*.spec.js -R spec", + "eslint": "eslint lib/{compile/,}*.js spec/{**/,}*.js scripts --ignore-pattern spec/JSON-Schema-Test-Suite", + "jshint": "jshint lib/{compile/,}*.js", + "test-spec": "mocha spec/{**/,}*.spec.js -R spec", "test-fast": "AJV_FAST_TEST=true npm run test-spec", "test-debug": "mocha spec/*.spec.js --debug-brk -R spec", "test-cov": "nyc npm run test-spec", diff --git a/spec/options.spec.js b/spec/options.spec.js deleted file mode 100644 index 2ca1b4ec6..000000000 --- a/spec/options.spec.js +++ /dev/null @@ -1,1579 +0,0 @@ -'use strict'; - -var Ajv = require('./ajv') - , getAjvInstances = require('./ajv_instances') - , should = require('./chai').should(); - - -describe('Ajv Options', function () { - describe('removeAdditional', function() { - it('should remove all additional properties', function() { - var ajv = new Ajv({ removeAdditional: 'all' }); - - ajv.addSchema({ - $id: '//test/fooBar', - properties: { foo: { type: 'string' }, bar: { type: 'string' } } - }); - - var object = { - foo: 'foo', bar: 'bar', baz: 'baz-to-be-removed' - }; - - ajv.validate('//test/fooBar', object).should.equal(true); - object.should.have.property('foo'); - object.should.have.property('bar'); - object.should.not.have.property('baz'); - }); - - - it('should remove properties that would error when `additionalProperties = false`', function() { - var ajv = new Ajv({ removeAdditional: true }); - - ajv.addSchema({ - $id: '//test/fooBar', - properties: { foo: { type: 'string' }, bar: { type: 'string' } }, - additionalProperties: false - }); - - var object = { - foo: 'foo', bar: 'bar', baz: 'baz-to-be-removed' - }; - - ajv.validate('//test/fooBar', object).should.equal(true); - object.should.have.property('foo'); - object.should.have.property('bar'); - object.should.not.have.property('baz'); - }); - - - it('should remove properties that would error when `additionalProperties = false` (many properties, boolean schema)', function() { - var ajv = new Ajv({removeAdditional: true}); - - var schema = { - properties: { - obj: { - additionalProperties: false, - properties: { - a: { type: 'string' }, - b: false, - c: { type: 'string' }, - d: { type: 'string' }, - e: { type: 'string' }, - f: { type: 'string' }, - g: { type: 'string' }, - h: { type: 'string' }, - i: { type: 'string' } - } - } - } - }; - - var data = { - obj: { - a: 'valid', - b: 'should not be removed', - additional: 'will be removed' - } - }; - - ajv.validate(schema, data) .should.equal(false); - data .should.eql({ - obj: { - a: 'valid', - b: 'should not be removed' - } - }); - }); - - - it('should remove properties that would error when `additionalProperties` is a schema', function() { - var ajv = new Ajv({ removeAdditional: 'failing' }); - - ajv.addSchema({ - $id: '//test/fooBar', - properties: { foo: { type: 'string' }, bar: { type: 'string' } }, - additionalProperties: { type: 'string' } - }); - - var object = { - foo: 'foo', bar: 'bar', baz: 'baz-to-be-kept', fizz: 1000 - }; - - ajv.validate('//test/fooBar', object).should.equal(true); - object.should.have.property('foo'); - object.should.have.property('bar'); - object.should.have.property('baz'); - object.should.not.have.property('fizz'); - - ajv.addSchema({ - $id: '//test/fooBar2', - properties: { foo: { type: 'string' }, bar: { type: 'string' } }, - additionalProperties: { type: 'string', pattern: '^to-be-', maxLength: 10 } - }); - - object = { - foo: 'foo', bar: 'bar', baz: 'to-be-kept', quux: 'to-be-removed', fizz: 1000 - }; - - ajv.validate('//test/fooBar2', object).should.equal(true); - object.should.have.property('foo'); - object.should.have.property('bar'); - object.should.have.property('baz'); - object.should.not.have.property('fizz'); - }); - }); - - - describe('ownProperties', function() { - var ajv, ajvOP, ajvOP1; - - beforeEach(function() { - ajv = new Ajv({ allErrors: true }); - ajvOP = new Ajv({ ownProperties: true, allErrors: true }); - ajvOP1 = new Ajv({ ownProperties: true }); - }); - - it('should only validate own properties with additionalProperties', function() { - var schema = { - properties: { a: { type: 'number' } }, - additionalProperties: false - }; - - var obj = { a: 1 }; - var proto = { b: 2 }; - test(schema, obj, proto); - }); - - it('should only validate own properties with properties keyword', function() { - var schema = { - properties: { - a: { type: 'number' }, - b: { type: 'number' } - } - }; - - var obj = { a: 1 }; - var proto = { b: 'not a number' }; - test(schema, obj, proto); - }); - - it('should only validate own properties with required keyword', function() { - var schema = { - required: ['a', 'b'] - }; - - var obj = { a: 1 }; - var proto = { b: 2 }; - test(schema, obj, proto, 1, true); - }); - - it('should only validate own properties with required keyword - many properties', function() { - ajv = new Ajv({ allErrors: true, loopRequired: 1 }); - ajvOP = new Ajv({ ownProperties: true, allErrors: true, loopRequired: 1 }); - ajvOP1 = new Ajv({ ownProperties: true, loopRequired: 1 }); - - var schema = { - required: ['a', 'b', 'c', 'd'] - }; - - var obj = { a: 1, b: 2 }; - var proto = { c: 3, d: 4 }; - test(schema, obj, proto, 2, true); - }); - - it('should only validate own properties with required keyword as $data', function() { - ajv = new Ajv({ allErrors: true, $data: true }); - ajvOP = new Ajv({ ownProperties: true, allErrors: true, $data: true }); - ajvOP1 = new Ajv({ ownProperties: true, $data: true }); - - var schema = { - required: { $data: '0/req' }, - properties: { - req: { - type: 'array', - items: { type: 'string' } - } - } - }; - - var obj = { - req: ['a', 'b'], - a: 1 - }; - var proto = { b: 2 }; - test(schema, obj, proto, 1, true); - }); - - it('should only validate own properties with properties and required keyword', function() { - var schema = { - properties: { - a: { type: 'number' }, - b: { type: 'number' } - }, - required: ['a', 'b'] - }; - - var obj = { a: 1 }; - var proto = { b: 2 }; - test(schema, obj, proto, 1, true); - }); - - it('should only validate own properties with dependencies keyword', function() { - var schema = { - dependencies: { - a: ['c'], - b: ['d'] - } - }; - - var obj = { a: 1, c: 3 }; - var proto = { b: 2 }; - test(schema, obj, proto); - - obj = { a: 1, b: 2, c: 3 }; - proto = { d: 4 }; - test(schema, obj, proto, 1, true); - }); - - it('should only validate own properties with schema dependencies', function() { - var schema = { - dependencies: { - a: { not: { required: ['c'] } }, - b: { not: { required: ['d'] } } - } - }; - - var obj = { a: 1, d: 3 }; - var proto = { b: 2 }; - test(schema, obj, proto); - - obj = { a: 1, b: 2 }; - proto = { d: 4 }; - test(schema, obj, proto); - }); - - it('should only validate own properties with patternProperties', function() { - var schema = { - patternProperties: { 'f.*o': { 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: { - format: 'email' - } - }; - - var obj = { 'e@example.com': 2 }; - var proto = { 'not email': 1 }; - test(schema, obj, proto, 2); - }); - - function test(schema, obj, proto, errors, reverse) { - errors = errors || 1; - var validate = ajv.compile(schema); - var validateOP = ajvOP.compile(schema); - var validateOP1 = ajvOP1.compile(schema); - var data = Object.create(proto); - for (var key in obj) data[key] = obj[key]; - - if (reverse) { - validate(data) .should.equal(true); - validateOP(data) .should.equal(false); - validateOP.errors .should.have.length(errors); - validateOP1(data) .should.equal(false); - validateOP1.errors .should.have.length(1); - } else { - validate(data) .should.equal(false); - validate.errors .should.have.length(errors); - validateOP(data) .should.equal(true); - validateOP1(data) .should.equal(true); - } - } - }); - - describe('meta and validateSchema', function() { - it('should add draft-7 meta schema by default', function() { - testOptionMeta(new Ajv); - testOptionMeta(new Ajv({ meta: true })); - - function testOptionMeta(ajv) { - 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' }); }); - should.throw(function() { ajv.addSchema({ type: 123 }); }); - } - }); - - 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-07/schema')); - should.not.throw(function() { ajv.addSchema({ type: 'wrong_type' }, 'integer'); }); - }); - - it('should skip schema validation with validateSchema: false', function() { - var ajv = new Ajv; - should.throw(function() { ajv.addSchema({ type: 123 }, 'integer'); }); - - ajv = new Ajv({ validateSchema: false }); - should.not.throw(function() { ajv.addSchema({ type: 123 }, 'integer'); }); - - ajv = new Ajv({ validateSchema: false, meta: false }); - should.not.throw(function() { ajv.addSchema({ type: 123 }, 'integer'); }); - }); - - it('should not throw on invalid schema with validateSchema: "log"', function() { - var logError = console.error; - var loggedError = false; - console.error = function() { loggedError = true; logError.apply(console, arguments); }; - - var ajv = new Ajv({ validateSchema: 'log' }); - should.not.throw(function() { ajv.addSchema({ type: 123 }, 'integer'); }); - loggedError .should.equal(true); - - loggedError = false; - ajv = new Ajv({ validateSchema: 'log', meta: false }); - should.not.throw(function() { ajv.addSchema({ type: 123 }, 'integer'); }); - loggedError .should.equal(false); - console.error = logError; - }); - - it('should validate v6 schema', function() { - var ajv = new Ajv; - ajv.validateSchema({ contains: { minimum: 2 } }) .should.equal(true); - ajv.validateSchema({ contains: 2 }). should.equal(false); - }); - - it('should use option meta as default meta schema', function() { - var meta = { - $schema: 'http://json-schema.org/draft-07/schema', - properties: { - myKeyword: { type: 'boolean' } - } - }; - var ajv = new Ajv({ meta: meta }); - ajv.validateSchema({ myKeyword: true }) .should.equal(true); - ajv.validateSchema({ myKeyword: 2 }) .should.equal(false); - ajv.validateSchema({ - $schema: 'http://json-schema.org/draft-07/schema', - myKeyword: 2 - }) .should.equal(true); - - ajv = new Ajv; - ajv.validateSchema({ myKeyword: true }) .should.equal(true); - ajv.validateSchema({ myKeyword: 2 }) .should.equal(true); - }); - }); - - - describe('schemas', function() { - it('should add schemas from object', function() { - var ajv = new Ajv({ schemas: { - int: { type: 'integer' }, - str: { type: 'string' } - }}); - - ajv.validate('int', 123) .should.equal(true); - ajv.validate('int', 'foo') .should.equal(false); - ajv.validate('str', 'foo') .should.equal(true); - ajv.validate('str', 123) .should.equal(false); - }); - - 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' } } } - ]}); - - ajv.validate('obj', { int: 123, str: 'foo' }) .should.equal(true); - ajv.validate('obj', { int: 'foo', str: 'bar' }) .should.equal(false); - ajv.validate('obj', { int: 123, str: 456 }) .should.equal(false); - }); - }); - - - describe('format', function() { - it('should not validate formats if option format == false', function() { - var ajv = new Ajv - , ajvFF = new Ajv({ format: false }); - - var schema = { format: 'date-time' }; - var invalideDateTime = '06/19/1963 08:30:06 PST'; - - ajv.validate(schema, invalideDateTime) .should.equal(false); - ajvFF.validate(schema, invalideDateTime) .should.equal(true); - }); - }); - - - describe('formats', function() { - it('should add formats from options', function() { - var ajv = new Ajv({ formats: { - identifier: /^[a-z_$][a-z0-9_$]*$/i - }}); - - var validate = ajv.compile({ format: 'identifier' }); - validate('Abc1') .should.equal(true); - validate('123') .should.equal(false); - validate(123) .should.equal(true); - }); - }); - - - describe('missingRefs', function() { - it('should throw if ref is missing without this option', function() { - var ajv = new Ajv; - should.throw(function() { - ajv.compile({ $ref: 'missing_reference' }); - }); - }); - - it('should not throw and pass validation with missingRef == "ignore"', function() { - testMissingRefsIgnore(new Ajv({ missingRefs: 'ignore' })); - testMissingRefsIgnore(new Ajv({ missingRefs: 'ignore', allErrors: true })); - - function testMissingRefsIgnore(ajv) { - var validate = ajv.compile({ $ref: 'missing_reference' }); - validate({}) .should.equal(true); - } - }); - - it('should not throw and fail validation with missingRef == "fail" if the ref is used', function() { - testMissingRefsFail(new Ajv({ missingRefs: 'fail' })); - testMissingRefsFail(new Ajv({ missingRefs: 'fail', verbose: true })); - testMissingRefsFail(new Ajv({ missingRefs: 'fail', allErrors: true })); - testMissingRefsFail(new Ajv({ missingRefs: 'fail', allErrors: true, verbose: true })); - - function testMissingRefsFail(ajv) { - var validate = ajv.compile({ - anyOf: [ - { type: 'number' }, - { $ref: 'missing_reference' } - ] - }); - validate(123) .should.equal(true); - validate('foo') .should.equal(false); - - validate = ajv.compile({ $ref: 'missing_reference' }); - validate({}) .should.equal(false); - } - }); - }); - - - describe('uniqueItems', function() { - it('should not validate uniqueItems with uniqueItems option == false', function() { - testUniqueItems(new Ajv({ uniqueItems: false })); - testUniqueItems(new Ajv({ uniqueItems: false, allErrors: true })); - - function testUniqueItems(ajv) { - var validate = ajv.compile({ uniqueItems: true }); - validate([1,2,3]) .should.equal(true); - validate([1,1,1]) .should.equal(true); - } - }); - }); - - - describe('unicode', function() { - it('should use String.prototype.length with unicode option == false', function() { - var ajvUnicode = new Ajv; - testUnicode(new Ajv({ unicode: false })); - testUnicode(new Ajv({ unicode: false, allErrors: true })); - - function testUnicode(ajv) { - var validateWithUnicode = ajvUnicode.compile({ minLength: 2 }); - var validate = ajv.compile({ minLength: 2 }); - - validateWithUnicode('😀') .should.equal(false); - validate('😀') .should.equal(true); - - validateWithUnicode = ajvUnicode.compile({ maxLength: 1 }); - validate = ajv.compile({ maxLength: 1 }); - - validateWithUnicode('😀') .should.equal(true); - validate('😀') .should.equal(false); - } - }); - }); - - - describe('verbose', function() { - it('should add schema, parentSchema and data to errors with verbose option == true', function() { - testVerbose(new Ajv({ verbose: true })); - testVerbose(new Ajv({ verbose: true, allErrors: true })); - - function testVerbose(ajv) { - var schema = { properties: { foo: { minimum: 5 } } }; - var validate = ajv.compile(schema); - - var data = { foo: 3 }; - validate(data) .should.equal(false); - validate.errors .should.have.length(1); - var err = validate.errors[0]; - - should.equal(err.schema, 5); - err.parentSchema .should.eql({ minimum: 5 }); - err.parentSchema .should.equal(schema.properties.foo); // by reference - should.equal(err.data, 3); - } - }); - }); - - - describe('multipleOfPrecision', function() { - it('should allow for some deviation from 0 when validating multipleOf with value < 1', function() { - test(new Ajv({ multipleOfPrecision: 7 })); - test(new Ajv({ multipleOfPrecision: 7, allErrors: true })); - - function test(ajv) { - var schema = { multipleOf: 0.01 }; - var validate = ajv.compile(schema); - - validate(4.18) .should.equal(true); - validate(4.181) .should.equal(false); - - schema = { multipleOf: 0.0000001 }; - validate = ajv.compile(schema); - - validate(53.198098) .should.equal(true); - validate(53.1980981) .should.equal(true); - validate(53.19809811) .should.equal(false); - } - }); - }); - - - describe('useDefaults', function() { - it('should replace undefined property with default value', function() { - var instances = getAjvInstances({ - allErrors: true, - loopRequired: 3 - }, { useDefaults: true }); - - instances.forEach(test); - - - function test(ajv) { - var schema = { - properties: { - foo: { type: 'string', default: 'abc' }, - bar: { type: 'number', default: 1 }, - baz: { type: 'boolean', default: false }, - nil: { type: 'null', default: null }, - obj: { type: 'object', default: {} }, - arr: { type: 'array', default: [] } - }, - required: ['foo', 'bar', 'baz', 'nil', 'obj', 'arr'], - minProperties: 6 - }; - - var validate = ajv.compile(schema); - - var data = {}; - validate(data) .should.equal(true); - data .should.eql({ foo: 'abc', bar: 1, baz: false, nil: null, obj: {}, arr:[] }); - - data = { foo: 'foo', bar: 2, obj: { test: true } }; - validate(data) .should.equal(true); - data .should.eql({ foo: 'foo', bar: 2, baz: false, nil: null, obj: { test: true }, arr:[] }); - } - }); - - it('should replace undefined item with default value', function() { - test(new Ajv({ useDefaults: true })); - test(new Ajv({ useDefaults: true, allErrors: true })); - - function test(ajv) { - var schema = { - items: [ - { type: 'string', default: 'abc' }, - { type: 'number', default: 1 }, - { type: 'boolean', default: false } - ], - minItems: 3 - }; - - var validate = ajv.compile(schema); - - var data = []; - validate(data) .should.equal(true); - data .should.eql([ 'abc', 1, false ]); - - data = [ 'foo' ]; - validate(data) .should.equal(true); - data .should.eql([ 'foo', 1, false ]); - - data = ['foo', 2,'false']; - validate(data) .should.equal(false); - validate.errors .should.have.length(1); - data .should.eql([ 'foo', 2, 'false' ]); - } - }); - - 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() { - it('should NOT modify underlying defaults when modifying validated data', function() { - test('value', new Ajv({ useDefaults: true })); - test('value', new Ajv({ useDefaults: true, allErrors: true })); - }); - }); - - describe('using by reference', function() { - it('should modify underlying defaults when modifying validated data', function() { - test('reference', new Ajv({ useDefaults: 'shared' })); - test('reference', new Ajv({ useDefaults: 'shared', allErrors: true })); - }); - }); - - function test(useDefaultsMode, ajv) { - var schema = { - properties: { - items: { - type: 'array', - default: ['a-default'] - } - } - }; - - var validate = ajv.compile(schema); - - var data = {}; - validate(data) .should.equal(true); - data.items .should.eql([ 'a-default' ]); - - data.items.push('another-value'); - data.items .should.eql([ 'a-default', 'another-value' ]); - - var data2 = {}; - validate(data2) .should.equal(true); - - if (useDefaultsMode == 'reference') - data2.items .should.eql([ 'a-default', 'another-value' ]); - else if (useDefaultsMode == 'value') - data2.items .should.eql([ 'a-default' ]); - else - throw new Error('unknown useDefaults mode'); - } - }); - - - describe('defaults with "empty" values', function() { - var schema, data; - - beforeEach(function() { - schema = { - properties: { - obj: { - properties: { - str: {default: 'foo'}, - n1: {default: 1}, - n2: {default: 2}, - n3: {default: 3} - } - }, - arr: { - items: [ - {default: 'foo'}, - {default: 1}, - {default: 2}, - {default: 3} - ] - } - } - }; - - data = { - obj: { - str: '', - n1: null, - n2: undefined - }, - arr: ['', null, undefined] - }; - }); - - it('should NOT assign defaults when useDefaults is true/"shared"', function() { - test(new Ajv({useDefaults: true})); - test(new Ajv({useDefaults: 'shared'})); - - function test(ajv) { - var validate = ajv.compile(schema); - validate(data) .should.equal(true); - data .should.eql({ - obj: { - str: '', - n1: null, - n2: 2, - n3: 3 - }, - arr: ['', null, 2, 3] - }); - } - }); - - it('should assign defaults when useDefaults = "empty"', function() { - var ajv = new Ajv({useDefaults: 'empty'}); - var validate = ajv.compile(schema); - validate(data) .should.equal(true); - data .should.eql({ - obj: { - str: 'foo', - n1: 1, - n2: 2, - n3: 3 - }, - arr: ['foo', 1, 2, 3] - }); - }); - }); - }); - - - describe('addUsedSchema', function() { - [true, undefined].forEach(function (optionValue) { - describe('= ' + optionValue, function() { - var ajv; - - beforeEach(function() { - ajv = new Ajv({ addUsedSchema: optionValue }); - }); - - describe('compile and validate', function() { - it('should add schema', function() { - 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' }; - 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' }); - should.throw(function() { - ajv.compile({ $id: 'str', minLength: 2 }); - }); - - 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); - }); - }); - }); - }); - }); - - describe('= false', function() { - var ajv; - - beforeEach(function() { - ajv = new Ajv({ addUsedSchema: false }); - }); - - - describe('compile and validate', function() { - it('should NOT add schema', function() { - 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' }; - 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' }); - should.not.throw(function() { - ajv.compile({ $id: 'str', minLength: 2 }); - }); - - 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); - }); - }); - }); - }); - }); - - - describe('passContext', function() { - var ajv, contexts; - - beforeEach(function() { - contexts = []; - }); - - describe('= true', function() { - it('should pass this value as context to custom keyword validation function', function() { - var validate = getValidate(true); - var self = {}; - validate.call(self, {}); - contexts .should.have.length(4); - contexts.forEach(function(ctx) { - ctx .should.equal(self); - }); - }); - }); - - describe('= false', function() { - it('should pass ajv instance as context to custom keyword validation function', function() { - var validate = getValidate(false); - var self = {}; - validate.call(self, {}); - contexts .should.have.length(4); - contexts.forEach(function(ctx) { - ctx .should.equal(ajv); - }); - }); - }); - - function getValidate(passContext) { - ajv = new Ajv({ passContext: passContext, inlineRefs: false }); - ajv.addKeyword('testValidate', { validate: storeContext }); - ajv.addKeyword('testCompile', { compile: compileTestValidate }); - - var schema = { - definitions: { - test1: { - testValidate: true, - testCompile: true, - }, - test2: { - allOf: [ { $ref: '#/definitions/test1' } ] - } - }, - allOf: [ - { $ref: '#/definitions/test1' }, - { $ref: '#/definitions/test2' } - ] - }; - - return ajv.compile(schema); - } - - function storeContext() { - contexts.push(this); - return true; - } - - function compileTestValidate() { - return storeContext; - } - }); - - - describe('allErrors', function() { - it('should be disabled inside "not" keyword', function() { - test(new Ajv, false); - test(new Ajv({ allErrors: true }), true); - - function test(ajv, allErrors) { - var format1called = false - , format2called = false; - - ajv.addFormat('format1', function() { - format1called = true; - return false; - }); - - ajv.addFormat('format2', function() { - format2called = true; - return false; - }); - - var schema1 = { - allOf: [ - { format: 'format1' }, - { format: 'format2' } - ] - }; - - ajv.validate(schema1, 'abc') .should.equal(false); - ajv.errors .should.have.length(allErrors ? 2 : 1); - format1called .should.equal(true); - format2called .should.equal(allErrors); - - var schema2 = { - not: schema1 - }; - - format1called = format2called = false; - ajv.validate(schema2, 'abc') .should.equal(true); - should.equal(ajv.errors, null); - format1called .should.equal(true); - format2called .should.equal(false); - } - }); - }); - - - describe('extendRefs', function() { - describe('= true', function() { - it('should allow extending $ref with other keywords', function() { - test(new Ajv({ extendRefs: true }), true); - }); - - it('should NOT log warning if extendRefs is true', function() { - testWarning(new Ajv({ extendRefs: true })); - }); - }); - - describe('= "ignore" and default', function() { - it('should ignore other keywords when $ref is used', function() { - test(new Ajv); - test(new Ajv({ extendRefs: 'ignore' }), false); - }); - - it('should log warning when other keywords are used with $ref', function() { - testWarning(new Ajv, /keywords\signored/); - testWarning(new Ajv({ extendRefs: 'ignore' }), /keywords\signored/); - }); - }); - - describe('= "fail"', function() { - it('should fail schema compilation if other keywords are used with $ref', function() { - testFail(new Ajv({ extendRefs: 'fail' })); - - function testFail(ajv) { - should.throw(function() { - var schema = { - "definitions": { - "int": { "type": "integer" } - }, - "$ref": "#/definitions/int", - "minimum": 10 - }; - ajv.compile(schema); - }); - - should.not.throw(function() { - var schema = { - "definitions": { - "int": { "type": "integer" } - }, - "allOf": [ - { "$ref": "#/definitions/int" }, - { "minimum": 10 } - ] - }; - ajv.compile(schema); - }); - } - }); - }); - - function test(ajv, shouldExtendRef) { - var schema = { - "definitions": { - "int": { "type": "integer" } - }, - "$ref": "#/definitions/int", - "minimum": 10 - }; - - var validate = ajv.compile(schema); - validate(10) .should.equal(true); - validate(1) .should.equal(!shouldExtendRef); - - schema = { - "definitions": { - "int": { "type": "integer" } - }, - "type": "object", - "properties": { - "foo": { - "$ref": "#/definitions/int", - "minimum": 10 - }, - "bar": { - "allOf": [ - { "$ref": "#/definitions/int" }, - { "minimum": 10 } - ] - } - } - }; - - validate = ajv.compile(schema); - validate({ foo: 10, bar: 10 }) .should.equal(true); - validate({ foo: 1, bar: 10 }) .should.equal(!shouldExtendRef); - validate({ foo: 10, bar: 1 }) .should.equal(false); - } - - function testWarning(ajv, msgPattern) { - var oldConsole; - try { - oldConsole = console.warn; - var consoleMsg; - console.warn = function() { - consoleMsg = Array.prototype.join.call(arguments, ' '); - }; - - var schema = { - "definitions": { - "int": { "type": "integer" } - }, - "$ref": "#/definitions/int", - "minimum": 10 - }; - - ajv.compile(schema); - if (msgPattern) consoleMsg .should.match(msgPattern); - else should.not.exist(consoleMsg); - } finally { - console.warn = oldConsole; - } - } - }); - - - describe('sourceCode', function() { - describe('= true', function() { - it('should add source.code property', function() { - test(new Ajv({sourceCode: true})); - - function test(ajv) { - var validate = ajv.compile({ "type": "number" }); - validate.source.code .should.be.a('string'); - } - }); - }); - - describe('= false and default', function() { - it('should not add source and sourceCode properties', function() { - test(new Ajv); - test(new Ajv({sourceCode: false})); - - function test(ajv) { - var validate = ajv.compile({ "type": "number" }); - should.not.exist(validate.source); - should.not.exist(validate.sourceCode); - } - }); - }); - }); - - - describe('unknownFormats', function() { - describe('= true (default)', function() { - it('should fail schema compilation if unknown format is used', function() { - test(new Ajv); - test(new Ajv({unknownFormats: true})); - - function test(ajv) { - should.throw(function() { - ajv.compile({ format: 'unknown' }); - }); - } - }); - - it('should fail validation if unknown format is used via $data', function() { - test(new Ajv({$data: true})); - test(new Ajv({$data: true, unknownFormats: true})); - - function test(ajv) { - var validate = ajv.compile({ - properties: { - foo: { format: { $data: '1/bar' } }, - bar: { type: 'string' } - } - }); - - validate({foo: 1, bar: 'unknown'}) .should.equal(false); - validate({foo: '2016-10-16', bar: 'date'}) .should.equal(true); - validate({foo: '20161016', bar: 'date'}) .should.equal(false); - validate({foo: '20161016'}) .should.equal(true); - - validate({foo: '2016-10-16', bar: 'unknown'}) .should.equal(false); - } - }); - }); - - describe('= "ignore (default before 5.0.0)"', function() { - it('should pass schema compilation and be valid if unknown format is used', function() { - test(new Ajv({unknownFormats: 'ignore'})); - - function test(ajv) { - var validate = ajv.compile({ format: 'unknown' }); - validate('anything') .should.equal(true); - } - }); - - it('should be valid if unknown format is used via $data', function() { - test(new Ajv({$data: true, unknownFormats: 'ignore'})); - - function test(ajv) { - var validate = ajv.compile({ - properties: { - foo: { format: { $data: '1/bar' } }, - bar: { type: 'string' } - } - }); - - validate({foo: 1, bar: 'unknown'}) .should.equal(true); - validate({foo: '2016-10-16', bar: 'date'}) .should.equal(true); - validate({foo: '20161016', bar: 'date'}) .should.equal(false); - validate({foo: '20161016'}) .should.equal(true); - validate({foo: '2016-10-16', bar: 'unknown'}) .should.equal(true); - } - }); - }); - - describe('= [String]', function() { - it('should pass schema compilation and be valid if whitelisted unknown format is used', function() { - test(new Ajv({unknownFormats: ['allowed']})); - - function test(ajv) { - var validate = ajv.compile({ format: 'allowed' }); - validate('anything') .should.equal(true); - - should.throw(function() { - ajv.compile({ format: 'unknown' }); - }); - } - }); - - it('should be valid if whitelisted unknown format is used via $data', function() { - test(new Ajv({$data: true, unknownFormats: ['allowed']})); - - function test(ajv) { - var validate = ajv.compile({ - properties: { - foo: { format: { $data: '1/bar' } }, - bar: { type: 'string' } - } - }); - - validate({foo: 1, bar: 'allowed'}) .should.equal(true); - validate({foo: 1, bar: 'unknown'}) .should.equal(false); - validate({foo: '2016-10-16', bar: 'date'}) .should.equal(true); - validate({foo: '20161016', bar: 'date'}) .should.equal(false); - validate({foo: '20161016'}) .should.equal(true); - - validate({foo: '2016-10-16', bar: 'allowed'}) .should.equal(true); - validate({foo: '2016-10-16', bar: 'unknown'}) .should.equal(false); - } - }); - }); - }); - - - describe('processCode', function() { - it('should process generated code', function() { - var ajv = new Ajv; - var validate = ajv.compile({type: 'string'}); - validate.toString().split('\n').length .should.equal(1); - - var beautify = require('js-beautify').js_beautify; - var ajvPC = new Ajv({processCode: beautify}); - validate = ajvPC.compile({type: 'string'}); - validate.toString().split('\n').length .should.be.above(1); - validate('foo') .should.equal(true); - validate(1) .should.equal(false); - }); - }); - - - describe('serialize', function() { - var serializeCalled; - - it('should use custom function to serialize schema to string', function() { - serializeCalled = undefined; - var ajv = new Ajv({ serialize: serialize }); - ajv.addSchema({ type: 'string' }); - should.equal(serializeCalled, true); - }); - - function serialize(schema) { - serializeCalled = true; - return JSON.stringify(schema); - } - }); - - - describe('schemaId', function() { - describe('= "$id" (default)', function() { - it('should use $id and ignore id', function() { - test(new Ajv); - test(new Ajv({schemaId: '$id'})); - - function test(ajv) { - 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')); - } - }); - }); - - describe('= "id"', function() { - it('should use id and ignore $id', function() { - var ajv = new Ajv({schemaId: 'id', meta: false}); - ajv.addMetaSchema(require('../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'); - validate('foo') .should.equal(true); - validate(1) .should.equal(false); - - validate = ajv.compile({ $id: 'mySchema2', type: 'string' }); - should.not.exist(ajv.getSchema('mySchema2')); - }); - }); - - 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); - - 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' - }); - }); - }); - }); - }); - - - 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); - } - }); - }); - }); - }); - - - describe('logger', function() { - - /** - * The logger option tests are based on the meta scenario which writes into the logger.warn - */ - - var origConsoleWarn = console.warn; - var consoleCalled; - - beforeEach(function() { - consoleCalled = false; - console.warn = function() { - consoleCalled = true; - }; - }); - - afterEach(function() { - console.warn = origConsoleWarn; - }); - - it('no custom logger is given - global console should be used', function() { - var ajv = new Ajv({ - meta: false - }); - - ajv.compile({ - type: 'number', - minimum: 1 - }); - - should.equal(consoleCalled, true); - }); - - it('custom logger is an object - logs should only report to it', function() { - var loggerCalled = false; - - var logger = { - warn: log, - log: log, - error: log - }; - - var ajv = new Ajv({ - meta: false, - logger: logger - }); - - ajv.compile({ - type: 'number', - minimum: 1 - }); - - should.equal(loggerCalled, true); - should.equal(consoleCalled, false); - - function log() { - loggerCalled = true; - } - }); - - it('logger option is false - no logs should be reported', function() { - var ajv = new Ajv({ - meta: false, - logger: false - }); - - ajv.compile({ - type: 'number', - minimum: 1 - }); - - should.equal(consoleCalled, false); - }); - - it('logger option is an object without required methods - an error should be thrown', function() { - (function(){ - new Ajv({ - meta: false, - logger: {} - }); - }).should.throw(Error, /logger must implement log, warn and error methods/); - }); - }); - - - describe('nullable', function() { - var ajv; - - describe('= true', function() { - beforeEach(function () { - ajv = new Ajv({ - nullable: true - }); - }); - - it('should add keyword "nullable"', function() { - testNullable({ - type: 'number', - nullable: true - }); - - testNullable({ - type: ['number'], - nullable: true - }); - - testNullable({ - type: ['number', 'null'] - }); - - testNullable({ - type: ['number', 'null'], - nullable: true - }); - - testNotNullable({type: 'number'}); - - testNotNullable({type: ['number']}); - }); - - it('should respect "nullable" == false with opts.nullable == true', function() { - testNotNullable({ - type: 'number', - nullable: false - }); - - testNotNullable({ - type: ['number'], - nullable: false - }); - }); - }); - - describe('without option "nullable"', function() { - it('should ignore keyword nullable', function() { - ajv = new Ajv; - - testNotNullable({ - type: 'number', - nullable: true - }); - - testNotNullable({ - type: ['number'], - nullable: true - }); - - testNullable({ - type: ['number', 'null'], - }); - - testNullable({ - type: ['number', 'null'], - nullable: true - }); - - should.not.throw(function () { - ajv.compile({nullable: false}); - }); - }); - }); - - function testNullable(schema) { - var validate = ajv.compile(schema); - validate(1) .should.equal(true); - validate(null) .should.equal(true); - validate('1') .should.equal(false); - } - - function testNotNullable(schema) { - var validate = ajv.compile(schema); - validate(1) .should.equal(true); - validate(null) .should.equal(false); - validate('1') .should.equal(false); - } - }); -}); diff --git a/spec/options/comment.spec.js b/spec/options/comment.spec.js new file mode 100644 index 000000000..efdc98b5e --- /dev/null +++ b/spec/options/comment.spec.js @@ -0,0 +1,92 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +describe('$comment option', 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/options/meta_validateSchema.spec.js b/spec/options/meta_validateSchema.spec.js new file mode 100644 index 000000000..2e287da75 --- /dev/null +++ b/spec/options/meta_validateSchema.spec.js @@ -0,0 +1,79 @@ +'use strict'; + +var Ajv = require('../ajv'); +var should = require('../chai').should(); + + +describe('meta and validateSchema options', function() { + it('should add draft-7 meta schema by default', function() { + testOptionMeta(new Ajv); + testOptionMeta(new Ajv({ meta: true })); + + function testOptionMeta(ajv) { + 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' }); }); + should.throw(function() { ajv.addSchema({ type: 123 }); }); + } + }); + + 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-07/schema')); + should.not.throw(function() { ajv.addSchema({ type: 'wrong_type' }, 'integer'); }); + }); + + it('should skip schema validation with validateSchema: false', function() { + var ajv = new Ajv; + should.throw(function() { ajv.addSchema({ type: 123 }, 'integer'); }); + + ajv = new Ajv({ validateSchema: false }); + should.not.throw(function() { ajv.addSchema({ type: 123 }, 'integer'); }); + + ajv = new Ajv({ validateSchema: false, meta: false }); + should.not.throw(function() { ajv.addSchema({ type: 123 }, 'integer'); }); + }); + + it('should not throw on invalid schema with validateSchema: "log"', function() { + var logError = console.error; + var loggedError = false; + console.error = function() { loggedError = true; logError.apply(console, arguments); }; + + var ajv = new Ajv({ validateSchema: 'log' }); + should.not.throw(function() { ajv.addSchema({ type: 123 }, 'integer'); }); + loggedError .should.equal(true); + + loggedError = false; + ajv = new Ajv({ validateSchema: 'log', meta: false }); + should.not.throw(function() { ajv.addSchema({ type: 123 }, 'integer'); }); + loggedError .should.equal(false); + console.error = logError; + }); + + it('should validate v6 schema', function() { + var ajv = new Ajv; + ajv.validateSchema({ contains: { minimum: 2 } }) .should.equal(true); + ajv.validateSchema({ contains: 2 }). should.equal(false); + }); + + it('should use option meta as default meta schema', function() { + var meta = { + $schema: 'http://json-schema.org/draft-07/schema', + properties: { + myKeyword: { type: 'boolean' } + } + }; + var ajv = new Ajv({ meta: meta }); + ajv.validateSchema({ myKeyword: true }) .should.equal(true); + ajv.validateSchema({ myKeyword: 2 }) .should.equal(false); + ajv.validateSchema({ + $schema: 'http://json-schema.org/draft-07/schema', + myKeyword: 2 + }) .should.equal(true); + + ajv = new Ajv; + ajv.validateSchema({ myKeyword: true }) .should.equal(true); + ajv.validateSchema({ myKeyword: 2 }) .should.equal(true); + }); +}); diff --git a/spec/options/nullable.spec.js b/spec/options/nullable.spec.js new file mode 100644 index 000000000..df1bda5e4 --- /dev/null +++ b/spec/options/nullable.spec.js @@ -0,0 +1,97 @@ +'use strict'; + +var Ajv = require('../ajv'); +var should = require('../chai').should(); + + +describe('nullable option', function() { + var ajv; + + describe('= true', function() { + beforeEach(function () { + ajv = new Ajv({ + nullable: true + }); + }); + + it('should add keyword "nullable"', function() { + testNullable({ + type: 'number', + nullable: true + }); + + testNullable({ + type: ['number'], + nullable: true + }); + + testNullable({ + type: ['number', 'null'] + }); + + testNullable({ + type: ['number', 'null'], + nullable: true + }); + + testNotNullable({type: 'number'}); + + testNotNullable({type: ['number']}); + }); + + it('should respect "nullable" == false with opts.nullable == true', function() { + testNotNullable({ + type: 'number', + nullable: false + }); + + testNotNullable({ + type: ['number'], + nullable: false + }); + }); + }); + + describe('without option "nullable"', function() { + it('should ignore keyword nullable', function() { + ajv = new Ajv; + + testNotNullable({ + type: 'number', + nullable: true + }); + + testNotNullable({ + type: ['number'], + nullable: true + }); + + testNullable({ + type: ['number', 'null'], + }); + + testNullable({ + type: ['number', 'null'], + nullable: true + }); + + should.not.throw(function () { + ajv.compile({nullable: false}); + }); + }); + }); + + function testNullable(schema) { + var validate = ajv.compile(schema); + validate(1) .should.equal(true); + validate(null) .should.equal(true); + validate('1') .should.equal(false); + } + + function testNotNullable(schema) { + var validate = ajv.compile(schema); + validate(1) .should.equal(true); + validate(null) .should.equal(false); + validate('1') .should.equal(false); + } +}); diff --git a/spec/options/options_add_schemas.spec.js b/spec/options/options_add_schemas.spec.js new file mode 100644 index 000000000..e1a59f236 --- /dev/null +++ b/spec/options/options_add_schemas.spec.js @@ -0,0 +1,130 @@ +'use strict'; + +var Ajv = require('../ajv'); +var should = require('../chai').should(); + + +describe('options to add schemas', function() { + describe('schemas', function() { + it('should add schemas from object', function() { + var ajv = new Ajv({ schemas: { + int: { type: 'integer' }, + str: { type: 'string' } + }}); + + ajv.validate('int', 123) .should.equal(true); + ajv.validate('int', 'foo') .should.equal(false); + ajv.validate('str', 'foo') .should.equal(true); + ajv.validate('str', 123) .should.equal(false); + }); + + 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' } } } + ]}); + + ajv.validate('obj', { int: 123, str: 'foo' }) .should.equal(true); + ajv.validate('obj', { int: 'foo', str: 'bar' }) .should.equal(false); + ajv.validate('obj', { int: 123, str: 456 }) .should.equal(false); + }); + }); + + + describe('addUsedSchema', function() { + [true, undefined].forEach(function (optionValue) { + describe('= ' + optionValue, function() { + var ajv; + + beforeEach(function() { + ajv = new Ajv({ addUsedSchema: optionValue }); + }); + + describe('compile and validate', function() { + it('should add schema', function() { + 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' }; + 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' }); + should.throw(function() { + ajv.compile({ $id: 'str', minLength: 2 }); + }); + + 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); + }); + }); + }); + }); + }); + + describe('= false', function() { + var ajv; + + beforeEach(function() { + ajv = new Ajv({ addUsedSchema: false }); + }); + + + describe('compile and validate', function() { + it('should NOT add schema', function() { + 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' }; + 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' }); + should.not.throw(function() { + ajv.compile({ $id: 'str', minLength: 2 }); + }); + + 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); + }); + }); + }); + }); + }); + + + describe('serialize', function() { + var serializeCalled; + + it('should use custom function to serialize schema to string', function() { + serializeCalled = undefined; + var ajv = new Ajv({ serialize: serialize }); + ajv.addSchema({ type: 'string' }); + should.equal(serializeCalled, true); + }); + + function serialize(schema) { + serializeCalled = true; + return JSON.stringify(schema); + } + }); +}); diff --git a/spec/options/options_code.spec.js b/spec/options/options_code.spec.js new file mode 100644 index 000000000..8884c8c4c --- /dev/null +++ b/spec/options/options_code.spec.js @@ -0,0 +1,115 @@ +'use strict'; + +var Ajv = require('../ajv'); +var should = require('../chai').should(); + + +describe('code generation options', function () { + describe('sourceCode', function() { + describe('= true', function() { + it('should add source.code property', function() { + test(new Ajv({sourceCode: true})); + + function test(ajv) { + var validate = ajv.compile({ "type": "number" }); + validate.source.code .should.be.a('string'); + } + }); + }); + + describe('= false and default', function() { + it('should not add source and sourceCode properties', function() { + test(new Ajv); + test(new Ajv({sourceCode: false})); + + function test(ajv) { + var validate = ajv.compile({ "type": "number" }); + should.not.exist(validate.source); + should.not.exist(validate.sourceCode); + } + }); + }); + }); + + + describe('processCode', function() { + it('should process generated code', function() { + var ajv = new Ajv; + var validate = ajv.compile({type: 'string'}); + validate.toString().split('\n').length .should.equal(1); + + var beautify = require('js-beautify').js_beautify; + var ajvPC = new Ajv({processCode: beautify}); + validate = ajvPC.compile({type: 'string'}); + validate.toString().split('\n').length .should.be.above(1); + validate('foo') .should.equal(true); + validate(1) .should.equal(false); + }); + }); + + + describe('passContext option', function() { + var ajv, contexts; + + beforeEach(function() { + contexts = []; + }); + + describe('= true', function() { + it('should pass this value as context to custom keyword validation function', function() { + var validate = getValidate(true); + var self = {}; + validate.call(self, {}); + contexts .should.have.length(4); + contexts.forEach(function(ctx) { + ctx .should.equal(self); + }); + }); + }); + + describe('= false', function() { + it('should pass ajv instance as context to custom keyword validation function', function() { + var validate = getValidate(false); + var self = {}; + validate.call(self, {}); + contexts .should.have.length(4); + contexts.forEach(function(ctx) { + ctx .should.equal(ajv); + }); + }); + }); + + function getValidate(passContext) { + ajv = new Ajv({ passContext: passContext, inlineRefs: false }); + ajv.addKeyword('testValidate', { validate: storeContext }); + ajv.addKeyword('testCompile', { compile: compileTestValidate }); + + var schema = { + definitions: { + test1: { + testValidate: true, + testCompile: true, + }, + test2: { + allOf: [ { $ref: '#/definitions/test1' } ] + } + }, + allOf: [ + { $ref: '#/definitions/test1' }, + { $ref: '#/definitions/test2' } + ] + }; + + return ajv.compile(schema); + } + + function storeContext() { + contexts.push(this); + return true; + } + + function compileTestValidate() { + return storeContext; + } + }); +}); diff --git a/spec/options/options_refs.spec.js b/spec/options/options_refs.spec.js new file mode 100644 index 000000000..1f1d20a75 --- /dev/null +++ b/spec/options/options_refs.spec.js @@ -0,0 +1,167 @@ +'use strict'; + +var Ajv = require('../ajv'); +var should = require('../chai').should(); + + +describe('referenced schema options', function() { + describe('extendRefs', function() { + describe('= true', function() { + it('should allow extending $ref with other keywords', function() { + test(new Ajv({ extendRefs: true }), true); + }); + + it('should NOT log warning if extendRefs is true', function() { + testWarning(new Ajv({ extendRefs: true })); + }); + }); + + describe('= "ignore" and default', function() { + it('should ignore other keywords when $ref is used', function() { + test(new Ajv); + test(new Ajv({ extendRefs: 'ignore' }), false); + }); + + it('should log warning when other keywords are used with $ref', function() { + testWarning(new Ajv, /keywords\signored/); + testWarning(new Ajv({ extendRefs: 'ignore' }), /keywords\signored/); + }); + }); + + describe('= "fail"', function() { + it('should fail schema compilation if other keywords are used with $ref', function() { + testFail(new Ajv({ extendRefs: 'fail' })); + + function testFail(ajv) { + should.throw(function() { + var schema = { + "definitions": { + "int": { "type": "integer" } + }, + "$ref": "#/definitions/int", + "minimum": 10 + }; + ajv.compile(schema); + }); + + should.not.throw(function() { + var schema = { + "definitions": { + "int": { "type": "integer" } + }, + "allOf": [ + { "$ref": "#/definitions/int" }, + { "minimum": 10 } + ] + }; + ajv.compile(schema); + }); + } + }); + }); + + function test(ajv, shouldExtendRef) { + var schema = { + "definitions": { + "int": { "type": "integer" } + }, + "$ref": "#/definitions/int", + "minimum": 10 + }; + + var validate = ajv.compile(schema); + validate(10) .should.equal(true); + validate(1) .should.equal(!shouldExtendRef); + + schema = { + "definitions": { + "int": { "type": "integer" } + }, + "type": "object", + "properties": { + "foo": { + "$ref": "#/definitions/int", + "minimum": 10 + }, + "bar": { + "allOf": [ + { "$ref": "#/definitions/int" }, + { "minimum": 10 } + ] + } + } + }; + + validate = ajv.compile(schema); + validate({ foo: 10, bar: 10 }) .should.equal(true); + validate({ foo: 1, bar: 10 }) .should.equal(!shouldExtendRef); + validate({ foo: 10, bar: 1 }) .should.equal(false); + } + + function testWarning(ajv, msgPattern) { + var oldConsole; + try { + oldConsole = console.warn; + var consoleMsg; + console.warn = function() { + consoleMsg = Array.prototype.join.call(arguments, ' '); + }; + + var schema = { + "definitions": { + "int": { "type": "integer" } + }, + "$ref": "#/definitions/int", + "minimum": 10 + }; + + ajv.compile(schema); + if (msgPattern) consoleMsg .should.match(msgPattern); + else should.not.exist(consoleMsg); + } finally { + console.warn = oldConsole; + } + } + }); + + + describe('missingRefs', function() { + it('should throw if ref is missing without this option', function() { + var ajv = new Ajv; + should.throw(function() { + ajv.compile({ $ref: 'missing_reference' }); + }); + }); + + it('should not throw and pass validation with missingRef == "ignore"', function() { + testMissingRefsIgnore(new Ajv({ missingRefs: 'ignore' })); + testMissingRefsIgnore(new Ajv({ missingRefs: 'ignore', allErrors: true })); + + function testMissingRefsIgnore(ajv) { + var validate = ajv.compile({ $ref: 'missing_reference' }); + validate({}) .should.equal(true); + } + }); + + it('should not throw and fail validation with missingRef == "fail" if the ref is used', function() { + testMissingRefsFail(new Ajv({ missingRefs: 'fail' })); + testMissingRefsFail(new Ajv({ missingRefs: 'fail', verbose: true })); + testMissingRefsFail(new Ajv({ missingRefs: 'fail', allErrors: true })); + testMissingRefsFail(new Ajv({ missingRefs: 'fail', allErrors: true, verbose: true })); + + function testMissingRefsFail(ajv) { + var validate = ajv.compile({ + anyOf: [ + { type: 'number' }, + { $ref: 'missing_reference' } + ] + }); + validate(123) .should.equal(true); + validate('foo') .should.equal(false); + + validate = ajv.compile({ $ref: 'missing_reference' }); + validate({}) .should.equal(false); + } + }); + }); +}); diff --git a/spec/options/options_reporting.spec.js b/spec/options/options_reporting.spec.js new file mode 100644 index 000000000..578611e80 --- /dev/null +++ b/spec/options/options_reporting.spec.js @@ -0,0 +1,158 @@ +'use strict'; + +var Ajv = require('../ajv'); +var should = require('../chai').should(); + + +describe('reporting options', function () { + describe('verbose', function() { + it('should add schema, parentSchema and data to errors with verbose option == true', function() { + testVerbose(new Ajv({ verbose: true })); + testVerbose(new Ajv({ verbose: true, allErrors: true })); + + function testVerbose(ajv) { + var schema = { properties: { foo: { minimum: 5 } } }; + var validate = ajv.compile(schema); + + var data = { foo: 3 }; + validate(data) .should.equal(false); + validate.errors .should.have.length(1); + var err = validate.errors[0]; + + should.equal(err.schema, 5); + err.parentSchema .should.eql({ minimum: 5 }); + err.parentSchema .should.equal(schema.properties.foo); // by reference + should.equal(err.data, 3); + } + }); + }); + + + describe('allErrors', function() { + it('should be disabled inside "not" keyword', function() { + test(new Ajv, false); + test(new Ajv({ allErrors: true }), true); + + function test(ajv, allErrors) { + var format1called = false + , format2called = false; + + ajv.addFormat('format1', function() { + format1called = true; + return false; + }); + + ajv.addFormat('format2', function() { + format2called = true; + return false; + }); + + var schema1 = { + allOf: [ + { format: 'format1' }, + { format: 'format2' } + ] + }; + + ajv.validate(schema1, 'abc') .should.equal(false); + ajv.errors .should.have.length(allErrors ? 2 : 1); + format1called .should.equal(true); + format2called .should.equal(allErrors); + + var schema2 = { + not: schema1 + }; + + format1called = format2called = false; + ajv.validate(schema2, 'abc') .should.equal(true); + should.equal(ajv.errors, null); + format1called .should.equal(true); + format2called .should.equal(false); + } + }); + }); + + + describe('logger', function() { + /** + * The logger option tests are based on the meta scenario which writes into the logger.warn + */ + + var origConsoleWarn = console.warn; + var consoleCalled; + + beforeEach(function() { + consoleCalled = false; + console.warn = function() { + consoleCalled = true; + }; + }); + + afterEach(function() { + console.warn = origConsoleWarn; + }); + + it('no custom logger is given - global console should be used', function() { + var ajv = new Ajv({ + meta: false + }); + + ajv.compile({ + type: 'number', + minimum: 1 + }); + + should.equal(consoleCalled, true); + }); + + it('custom logger is an object - logs should only report to it', function() { + var loggerCalled = false; + + var logger = { + warn: log, + log: log, + error: log + }; + + var ajv = new Ajv({ + meta: false, + logger: logger + }); + + ajv.compile({ + type: 'number', + minimum: 1 + }); + + should.equal(loggerCalled, true); + should.equal(consoleCalled, false); + + function log() { + loggerCalled = true; + } + }); + + it('logger option is false - no logs should be reported', function() { + var ajv = new Ajv({ + meta: false, + logger: false + }); + + ajv.compile({ + type: 'number', + minimum: 1 + }); + + should.equal(consoleCalled, false); + }); + + it('logger option is an object without required methods - an error should be thrown', function() { + (function(){ + new Ajv({ + meta: false, + logger: {} + }); + }).should.throw(Error, /logger must implement log, warn and error methods/); + }); + }); +}); diff --git a/spec/options/options_validation.spec.js b/spec/options/options_validation.spec.js new file mode 100644 index 000000000..e362826bc --- /dev/null +++ b/spec/options/options_validation.spec.js @@ -0,0 +1,94 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +describe('validation options', function() { + describe('format', function() { + it('should not validate formats if option format == false', function() { + var ajv = new Ajv + , ajvFF = new Ajv({ format: false }); + + var schema = { format: 'date-time' }; + var invalideDateTime = '06/19/1963 08:30:06 PST'; + + ajv.validate(schema, invalideDateTime) .should.equal(false); + ajvFF.validate(schema, invalideDateTime) .should.equal(true); + }); + }); + + + describe('formats', function() { + it('should add formats from options', function() { + var ajv = new Ajv({ formats: { + identifier: /^[a-z_$][a-z0-9_$]*$/i + }}); + + var validate = ajv.compile({ format: 'identifier' }); + validate('Abc1') .should.equal(true); + validate('123') .should.equal(false); + validate(123) .should.equal(true); + }); + }); + + + describe('uniqueItems', function() { + it('should not validate uniqueItems with uniqueItems option == false', function() { + testUniqueItems(new Ajv({ uniqueItems: false })); + testUniqueItems(new Ajv({ uniqueItems: false, allErrors: true })); + + function testUniqueItems(ajv) { + var validate = ajv.compile({ uniqueItems: true }); + validate([1,2,3]) .should.equal(true); + validate([1,1,1]) .should.equal(true); + } + }); + }); + + + describe('unicode', function() { + it('should use String.prototype.length with unicode option == false', function() { + var ajvUnicode = new Ajv; + testUnicode(new Ajv({ unicode: false })); + testUnicode(new Ajv({ unicode: false, allErrors: true })); + + function testUnicode(ajv) { + var validateWithUnicode = ajvUnicode.compile({ minLength: 2 }); + var validate = ajv.compile({ minLength: 2 }); + + validateWithUnicode('😀') .should.equal(false); + validate('😀') .should.equal(true); + + validateWithUnicode = ajvUnicode.compile({ maxLength: 1 }); + validate = ajv.compile({ maxLength: 1 }); + + validateWithUnicode('😀') .should.equal(true); + validate('😀') .should.equal(false); + } + }); + }); + + + describe('multipleOfPrecision', function() { + it('should allow for some deviation from 0 when validating multipleOf with value < 1', function() { + test(new Ajv({ multipleOfPrecision: 7 })); + test(new Ajv({ multipleOfPrecision: 7, allErrors: true })); + + function test(ajv) { + var schema = { multipleOf: 0.01 }; + var validate = ajv.compile(schema); + + validate(4.18) .should.equal(true); + validate(4.181) .should.equal(false); + + schema = { multipleOf: 0.0000001 }; + validate = ajv.compile(schema); + + validate(53.198098) .should.equal(true); + validate(53.1980981) .should.equal(true); + validate(53.19809811) .should.equal(false); + } + }); + }); +}); diff --git a/spec/options/ownProperties.spec.js b/spec/options/ownProperties.spec.js new file mode 100644 index 000000000..312579f96 --- /dev/null +++ b/spec/options/ownProperties.spec.js @@ -0,0 +1,178 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +describe('ownProperties option', function() { + var ajv, ajvOP, ajvOP1; + + beforeEach(function() { + ajv = new Ajv({ allErrors: true }); + ajvOP = new Ajv({ ownProperties: true, allErrors: true }); + ajvOP1 = new Ajv({ ownProperties: true }); + }); + + it('should only validate own properties with additionalProperties', function() { + var schema = { + properties: { a: { type: 'number' } }, + additionalProperties: false + }; + + var obj = { a: 1 }; + var proto = { b: 2 }; + test(schema, obj, proto); + }); + + it('should only validate own properties with properties keyword', function() { + var schema = { + properties: { + a: { type: 'number' }, + b: { type: 'number' } + } + }; + + var obj = { a: 1 }; + var proto = { b: 'not a number' }; + test(schema, obj, proto); + }); + + it('should only validate own properties with required keyword', function() { + var schema = { + required: ['a', 'b'] + }; + + var obj = { a: 1 }; + var proto = { b: 2 }; + test(schema, obj, proto, 1, true); + }); + + it('should only validate own properties with required keyword - many properties', function() { + ajv = new Ajv({ allErrors: true, loopRequired: 1 }); + ajvOP = new Ajv({ ownProperties: true, allErrors: true, loopRequired: 1 }); + ajvOP1 = new Ajv({ ownProperties: true, loopRequired: 1 }); + + var schema = { + required: ['a', 'b', 'c', 'd'] + }; + + var obj = { a: 1, b: 2 }; + var proto = { c: 3, d: 4 }; + test(schema, obj, proto, 2, true); + }); + + it('should only validate own properties with required keyword as $data', function() { + ajv = new Ajv({ allErrors: true, $data: true }); + ajvOP = new Ajv({ ownProperties: true, allErrors: true, $data: true }); + ajvOP1 = new Ajv({ ownProperties: true, $data: true }); + + var schema = { + required: { $data: '0/req' }, + properties: { + req: { + type: 'array', + items: { type: 'string' } + } + } + }; + + var obj = { + req: ['a', 'b'], + a: 1 + }; + var proto = { b: 2 }; + test(schema, obj, proto, 1, true); + }); + + it('should only validate own properties with properties and required keyword', function() { + var schema = { + properties: { + a: { type: 'number' }, + b: { type: 'number' } + }, + required: ['a', 'b'] + }; + + var obj = { a: 1 }; + var proto = { b: 2 }; + test(schema, obj, proto, 1, true); + }); + + it('should only validate own properties with dependencies keyword', function() { + var schema = { + dependencies: { + a: ['c'], + b: ['d'] + } + }; + + var obj = { a: 1, c: 3 }; + var proto = { b: 2 }; + test(schema, obj, proto); + + obj = { a: 1, b: 2, c: 3 }; + proto = { d: 4 }; + test(schema, obj, proto, 1, true); + }); + + it('should only validate own properties with schema dependencies', function() { + var schema = { + dependencies: { + a: { not: { required: ['c'] } }, + b: { not: { required: ['d'] } } + } + }; + + var obj = { a: 1, d: 3 }; + var proto = { b: 2 }; + test(schema, obj, proto); + + obj = { a: 1, b: 2 }; + proto = { d: 4 }; + test(schema, obj, proto); + }); + + it('should only validate own properties with patternProperties', function() { + var schema = { + patternProperties: { 'f.*o': { 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: { + format: 'email' + } + }; + + var obj = { 'e@example.com': 2 }; + var proto = { 'not email': 1 }; + test(schema, obj, proto, 2); + }); + + function test(schema, obj, proto, errors, reverse) { + errors = errors || 1; + var validate = ajv.compile(schema); + var validateOP = ajvOP.compile(schema); + var validateOP1 = ajvOP1.compile(schema); + var data = Object.create(proto); + for (var key in obj) data[key] = obj[key]; + + if (reverse) { + validate(data) .should.equal(true); + validateOP(data) .should.equal(false); + validateOP.errors .should.have.length(errors); + validateOP1(data) .should.equal(false); + validateOP1.errors .should.have.length(1); + } else { + validate(data) .should.equal(false); + validate.errors .should.have.length(errors); + validateOP(data) .should.equal(true); + validateOP1(data) .should.equal(true); + } + } +}); diff --git a/spec/options/removeAdditional.spec.js b/spec/options/removeAdditional.spec.js new file mode 100644 index 000000000..1eef0b795 --- /dev/null +++ b/spec/options/removeAdditional.spec.js @@ -0,0 +1,122 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +describe('removeAdditional option', function() { + it('should remove all additional properties', function() { + var ajv = new Ajv({ removeAdditional: 'all' }); + + ajv.addSchema({ + $id: '//test/fooBar', + properties: { foo: { type: 'string' }, bar: { type: 'string' } } + }); + + var object = { + foo: 'foo', bar: 'bar', baz: 'baz-to-be-removed' + }; + + ajv.validate('//test/fooBar', object).should.equal(true); + object.should.have.property('foo'); + object.should.have.property('bar'); + object.should.not.have.property('baz'); + }); + + + it('should remove properties that would error when `additionalProperties = false`', function() { + var ajv = new Ajv({ removeAdditional: true }); + + ajv.addSchema({ + $id: '//test/fooBar', + properties: { foo: { type: 'string' }, bar: { type: 'string' } }, + additionalProperties: false + }); + + var object = { + foo: 'foo', bar: 'bar', baz: 'baz-to-be-removed' + }; + + ajv.validate('//test/fooBar', object).should.equal(true); + object.should.have.property('foo'); + object.should.have.property('bar'); + object.should.not.have.property('baz'); + }); + + + it('should remove properties that would error when `additionalProperties = false` (many properties, boolean schema)', function() { + var ajv = new Ajv({removeAdditional: true}); + + var schema = { + properties: { + obj: { + additionalProperties: false, + properties: { + a: { type: 'string' }, + b: false, + c: { type: 'string' }, + d: { type: 'string' }, + e: { type: 'string' }, + f: { type: 'string' }, + g: { type: 'string' }, + h: { type: 'string' }, + i: { type: 'string' } + } + } + } + }; + + var data = { + obj: { + a: 'valid', + b: 'should not be removed', + additional: 'will be removed' + } + }; + + ajv.validate(schema, data) .should.equal(false); + data .should.eql({ + obj: { + a: 'valid', + b: 'should not be removed' + } + }); + }); + + + it('should remove properties that would error when `additionalProperties` is a schema', function() { + var ajv = new Ajv({ removeAdditional: 'failing' }); + + ajv.addSchema({ + $id: '//test/fooBar', + properties: { foo: { type: 'string' }, bar: { type: 'string' } }, + additionalProperties: { type: 'string' } + }); + + var object = { + foo: 'foo', bar: 'bar', baz: 'baz-to-be-kept', fizz: 1000 + }; + + ajv.validate('//test/fooBar', object).should.equal(true); + object.should.have.property('foo'); + object.should.have.property('bar'); + object.should.have.property('baz'); + object.should.not.have.property('fizz'); + + ajv.addSchema({ + $id: '//test/fooBar2', + properties: { foo: { type: 'string' }, bar: { type: 'string' } }, + additionalProperties: { type: 'string', pattern: '^to-be-', maxLength: 10 } + }); + + object = { + foo: 'foo', bar: 'bar', baz: 'to-be-kept', quux: 'to-be-removed', fizz: 1000 + }; + + ajv.validate('//test/fooBar2', object).should.equal(true); + object.should.have.property('foo'); + object.should.have.property('bar'); + object.should.have.property('baz'); + object.should.not.have.property('fizz'); + }); +}); diff --git a/spec/options/schemaId.spec.js b/spec/options/schemaId.spec.js new file mode 100644 index 000000000..f8871034f --- /dev/null +++ b/spec/options/schemaId.spec.js @@ -0,0 +1,72 @@ +'use strict'; + +var Ajv = require('../ajv'); +var should = require('../chai').should(); + + +describe('schemaId option', function() { + describe('= "$id" (default)', function() { + it('should use $id and ignore id', function() { + test(new Ajv); + test(new Ajv({schemaId: '$id'})); + + function test(ajv) { + 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')); + } + }); + }); + + describe('= "id"', function() { + it('should use id and ignore $id', function() { + var ajv = new Ajv({schemaId: 'id', meta: false}); + ajv.addMetaSchema(require('../../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'); + validate('foo') .should.equal(true); + validate(1) .should.equal(false); + + validate = ajv.compile({ $id: 'mySchema2', type: 'string' }); + should.not.exist(ajv.getSchema('mySchema2')); + }); + }); + + 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); + + 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/options/unknownFormats.spec.js b/spec/options/unknownFormats.spec.js new file mode 100644 index 000000000..81ea32655 --- /dev/null +++ b/spec/options/unknownFormats.spec.js @@ -0,0 +1,108 @@ +'use strict'; + +var Ajv = require('../ajv'); +var should = require('../chai').should(); + + +describe('unknownFormats option', function() { + describe('= true (default)', function() { + it('should fail schema compilation if unknown format is used', function() { + test(new Ajv); + test(new Ajv({unknownFormats: true})); + + function test(ajv) { + should.throw(function() { + ajv.compile({ format: 'unknown' }); + }); + } + }); + + it('should fail validation if unknown format is used via $data', function() { + test(new Ajv({$data: true})); + test(new Ajv({$data: true, unknownFormats: true})); + + function test(ajv) { + var validate = ajv.compile({ + properties: { + foo: { format: { $data: '1/bar' } }, + bar: { type: 'string' } + } + }); + + validate({foo: 1, bar: 'unknown'}) .should.equal(false); + validate({foo: '2016-10-16', bar: 'date'}) .should.equal(true); + validate({foo: '20161016', bar: 'date'}) .should.equal(false); + validate({foo: '20161016'}) .should.equal(true); + + validate({foo: '2016-10-16', bar: 'unknown'}) .should.equal(false); + } + }); + }); + + describe('= "ignore (default before 5.0.0)"', function() { + it('should pass schema compilation and be valid if unknown format is used', function() { + test(new Ajv({unknownFormats: 'ignore'})); + + function test(ajv) { + var validate = ajv.compile({ format: 'unknown' }); + validate('anything') .should.equal(true); + } + }); + + it('should be valid if unknown format is used via $data', function() { + test(new Ajv({$data: true, unknownFormats: 'ignore'})); + + function test(ajv) { + var validate = ajv.compile({ + properties: { + foo: { format: { $data: '1/bar' } }, + bar: { type: 'string' } + } + }); + + validate({foo: 1, bar: 'unknown'}) .should.equal(true); + validate({foo: '2016-10-16', bar: 'date'}) .should.equal(true); + validate({foo: '20161016', bar: 'date'}) .should.equal(false); + validate({foo: '20161016'}) .should.equal(true); + validate({foo: '2016-10-16', bar: 'unknown'}) .should.equal(true); + } + }); + }); + + describe('= [String]', function() { + it('should pass schema compilation and be valid if whitelisted unknown format is used', function() { + test(new Ajv({unknownFormats: ['allowed']})); + + function test(ajv) { + var validate = ajv.compile({ format: 'allowed' }); + validate('anything') .should.equal(true); + + should.throw(function() { + ajv.compile({ format: 'unknown' }); + }); + } + }); + + it('should be valid if whitelisted unknown format is used via $data', function() { + test(new Ajv({$data: true, unknownFormats: ['allowed']})); + + function test(ajv) { + var validate = ajv.compile({ + properties: { + foo: { format: { $data: '1/bar' } }, + bar: { type: 'string' } + } + }); + + validate({foo: 1, bar: 'allowed'}) .should.equal(true); + validate({foo: 1, bar: 'unknown'}) .should.equal(false); + validate({foo: '2016-10-16', bar: 'date'}) .should.equal(true); + validate({foo: '20161016', bar: 'date'}) .should.equal(false); + validate({foo: '20161016'}) .should.equal(true); + + validate({foo: '2016-10-16', bar: 'allowed'}) .should.equal(true); + validate({foo: '2016-10-16', bar: 'unknown'}) .should.equal(false); + } + }); + }); +}); diff --git a/spec/options/useDefaults.spec.js b/spec/options/useDefaults.spec.js new file mode 100644 index 000000000..7a12e8423 --- /dev/null +++ b/spec/options/useDefaults.spec.js @@ -0,0 +1,223 @@ +'use strict'; + +var Ajv = require('../ajv'); +var getAjvInstances = require('../ajv_instances'); +require('../chai').should(); + + +describe('useDefaults options', function() { + it('should replace undefined property with default value', function() { + var instances = getAjvInstances({ + allErrors: true, + loopRequired: 3 + }, { useDefaults: true }); + + instances.forEach(test); + + + function test(ajv) { + var schema = { + properties: { + foo: { type: 'string', default: 'abc' }, + bar: { type: 'number', default: 1 }, + baz: { type: 'boolean', default: false }, + nil: { type: 'null', default: null }, + obj: { type: 'object', default: {} }, + arr: { type: 'array', default: [] } + }, + required: ['foo', 'bar', 'baz', 'nil', 'obj', 'arr'], + minProperties: 6 + }; + + var validate = ajv.compile(schema); + + var data = {}; + validate(data) .should.equal(true); + data .should.eql({ foo: 'abc', bar: 1, baz: false, nil: null, obj: {}, arr:[] }); + + data = { foo: 'foo', bar: 2, obj: { test: true } }; + validate(data) .should.equal(true); + data .should.eql({ foo: 'foo', bar: 2, baz: false, nil: null, obj: { test: true }, arr:[] }); + } + }); + + it('should replace undefined item with default value', function() { + test(new Ajv({ useDefaults: true })); + test(new Ajv({ useDefaults: true, allErrors: true })); + + function test(ajv) { + var schema = { + items: [ + { type: 'string', default: 'abc' }, + { type: 'number', default: 1 }, + { type: 'boolean', default: false } + ], + minItems: 3 + }; + + var validate = ajv.compile(schema); + + var data = []; + validate(data) .should.equal(true); + data .should.eql([ 'abc', 1, false ]); + + data = [ 'foo' ]; + validate(data) .should.equal(true); + data .should.eql([ 'foo', 1, false ]); + + data = ['foo', 2,'false']; + validate(data) .should.equal(false); + validate.errors .should.have.length(1); + data .should.eql([ 'foo', 2, 'false' ]); + } + }); + + 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() { + it('should NOT modify underlying defaults when modifying validated data', function() { + test('value', new Ajv({ useDefaults: true })); + test('value', new Ajv({ useDefaults: true, allErrors: true })); + }); + }); + + describe('using by reference', function() { + it('should modify underlying defaults when modifying validated data', function() { + test('reference', new Ajv({ useDefaults: 'shared' })); + test('reference', new Ajv({ useDefaults: 'shared', allErrors: true })); + }); + }); + + function test(useDefaultsMode, ajv) { + var schema = { + properties: { + items: { + type: 'array', + default: ['a-default'] + } + } + }; + + var validate = ajv.compile(schema); + + var data = {}; + validate(data) .should.equal(true); + data.items .should.eql([ 'a-default' ]); + + data.items.push('another-value'); + data.items .should.eql([ 'a-default', 'another-value' ]); + + var data2 = {}; + validate(data2) .should.equal(true); + + if (useDefaultsMode == 'reference') + data2.items .should.eql([ 'a-default', 'another-value' ]); + else if (useDefaultsMode == 'value') + data2.items .should.eql([ 'a-default' ]); + else + throw new Error('unknown useDefaults mode'); + } + }); + + + describe('defaults with "empty" values', function() { + var schema, data; + + beforeEach(function() { + schema = { + properties: { + obj: { + properties: { + str: {default: 'foo'}, + n1: {default: 1}, + n2: {default: 2}, + n3: {default: 3} + } + }, + arr: { + items: [ + {default: 'foo'}, + {default: 1}, + {default: 2}, + {default: 3} + ] + } + } + }; + + data = { + obj: { + str: '', + n1: null, + n2: undefined + }, + arr: ['', null, undefined] + }; + }); + + it('should NOT assign defaults when useDefaults is true/"shared"', function() { + test(new Ajv({useDefaults: true})); + test(new Ajv({useDefaults: 'shared'})); + + function test(ajv) { + var validate = ajv.compile(schema); + validate(data) .should.equal(true); + data .should.eql({ + obj: { + str: '', + n1: null, + n2: 2, + n3: 3 + }, + arr: ['', null, 2, 3] + }); + } + }); + + it('should assign defaults when useDefaults = "empty"', function() { + var ajv = new Ajv({useDefaults: 'empty'}); + var validate = ajv.compile(schema); + validate(data) .should.equal(true); + data .should.eql({ + obj: { + str: 'foo', + n1: 1, + n2: 2, + n3: 3 + }, + arr: ['foo', 1, 2, 3] + }); + }); + }); +}); From a7f78f2894d184006b5b31e1aa098367c3e033ed Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 10 Feb 2019 12:46:47 +0000 Subject: [PATCH 214/333] refactor: split issues.spec.js file --- spec/issues.spec.js | 815 ------------------ ...1_allErrors_custom_keyword_skipped.spec.js | 58 ++ spec/issues/182_nan_validation.spec.js | 26 + .../204_options_schemas_data_together.spec.js | 23 + spec/issues/210_mutual_recur_frags.spec.js | 79 ++ .../240_mutual_recur_frags_common_ref.spec.js | 210 +++++ .../259_validate_meta_against_itself.spec.js | 13 + .../273_error_schemaPath_refd_schema.spec.js | 31 + .../342_uniqueItems_non-json_objects.spec.js | 33 + spec/issues/388_code_clean-up not.spec.js | 28 + .../485_type_validation_priority.spec.js | 32 + spec/issues/50_refs_with_definitions.spec.js | 49 ++ .../521_wrong_warning_id_property.spec.js | 28 + .../533_missing_ref_error_when_ignore.spec.js | 29 + spec/issues/617_full_format_leap_year.spec.js | 47 + ...3_removeAdditional_to_remove_proto.spec.js | 41 + .../768_passContext_recursive_ref.spec.js | 114 +++ spec/issues/8_shared_refs.spec.js | 40 + 18 files changed, 881 insertions(+), 815 deletions(-) delete mode 100644 spec/issues.spec.js create mode 100644 spec/issues/181_allErrors_custom_keyword_skipped.spec.js create mode 100644 spec/issues/182_nan_validation.spec.js create mode 100644 spec/issues/204_options_schemas_data_together.spec.js create mode 100644 spec/issues/210_mutual_recur_frags.spec.js create mode 100644 spec/issues/240_mutual_recur_frags_common_ref.spec.js create mode 100644 spec/issues/259_validate_meta_against_itself.spec.js create mode 100644 spec/issues/273_error_schemaPath_refd_schema.spec.js create mode 100644 spec/issues/342_uniqueItems_non-json_objects.spec.js create mode 100644 spec/issues/388_code_clean-up not.spec.js create mode 100644 spec/issues/485_type_validation_priority.spec.js create mode 100644 spec/issues/50_refs_with_definitions.spec.js create mode 100644 spec/issues/521_wrong_warning_id_property.spec.js create mode 100644 spec/issues/533_missing_ref_error_when_ignore.spec.js create mode 100644 spec/issues/617_full_format_leap_year.spec.js create mode 100644 spec/issues/743_removeAdditional_to_remove_proto.spec.js create mode 100644 spec/issues/768_passContext_recursive_ref.spec.js create mode 100644 spec/issues/8_shared_refs.spec.js diff --git a/spec/issues.spec.js b/spec/issues.spec.js deleted file mode 100644 index 0a6e57078..000000000 --- a/spec/issues.spec.js +++ /dev/null @@ -1,815 +0,0 @@ -'use strict'; - -var Ajv = require('./ajv') - , should = require('./chai').should(); - - -describe('issue #8: schema with shared references', function() { - it('should be supported by addSchema', spec('addSchema')); - - it('should be supported by compile', spec('compile')); - - function spec(method) { - return function() { - var ajv = new Ajv; - - var propertySchema = { - type: 'string', - maxLength: 4 - }; - - var schema = { - $id: 'obj.json#', - type: 'object', - properties: { - foo: propertySchema, - bar: propertySchema - } - }; - - ajv[method](schema); - - var result = ajv.validate('obj.json#', { foo: 'abc', bar: 'def' }); - result .should.equal(true); - - result = ajv.validate('obj.json#', { foo: 'abcde', bar: 'fghg' }); - result .should.equal(false); - ajv.errors .should.have.length(1); - }; - } -}); - -describe('issue #50: references with "definitions"', function () { - it('should be supported by addSchema', spec('addSchema')); - - it('should be supported by compile', spec('addSchema')); - - function spec(method) { - return function() { - var result; - - var ajv = new Ajv; - - ajv[method]({ - $id: 'http://example.com/test/person.json#', - definitions: { - name: { type: 'string' } - }, - type: 'object', - properties: { - name: { $ref: '#/definitions/name'} - } - }); - - ajv[method]({ - $id: 'http://example.com/test/employee.json#', - type: 'object', - properties: { - person: { $ref: '/test/person.json#' }, - role: { type: 'string' } - } - }); - - result = ajv.validate('http://example.com/test/employee.json#', { - person: { - name: 'Alice' - }, - role: 'Programmer' - }); - - result. should.equal(true); - should.equal(ajv.errors, null); - }; - } -}); - - -describe('issue #182, NaN validation', function() { - it('should not pass minimum/maximum validation', function() { - testNaN({ minimum: 1 }, false); - testNaN({ maximum: 1 }, false); - }); - - it('should pass type: number validation', function() { - testNaN({ type: 'number' }, true); - }); - - it('should not pass type: integer validation', function() { - testNaN({ type: 'integer' }, false); - }); - - function testNaN(schema, NaNisValid) { - var ajv = new Ajv; - var validate = ajv.compile(schema); - validate(NaN) .should.equal(NaNisValid); - } -}); - - -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'}], - $data: true - }); - - var schema = { const: 42 }; - var validate = ajv.compile(schema); - - validate(42) .should.equal(true); - validate(43) .should.equal(false); - - ajv.validate('str', 'foo') .should.equal(true); - ajv.validate('str', 42) .should.equal(false); - }); -}); - - -describe('issue #181, custom keyword is not validated in allErrors mode if there were previous error', function() { - it('should validate custom keyword that doesn\'t create errors', function() { - testCustomKeywordErrors({ - type:'object', - errors: true, - validate: function v(/* value */) { - return false; - } - }); - }); - - it('should validate custom keyword that creates errors', function() { - testCustomKeywordErrors({ - type:'object', - errors: true, - validate: function v(/* value */) { - v.errors = v.errors || []; - v.errors.push({ - keyword: 'alwaysFails', - message: 'alwaysFails error', - params: { - keyword: 'alwaysFails' - } - }); - - return false; - } - }); - }); - - function testCustomKeywordErrors(def) { - var ajv = new Ajv({ allErrors: true }); - - ajv.addKeyword('alwaysFails', def); - - var schema = { - required: ['foo'], - alwaysFails: true - }; - - var validate = ajv.compile(schema); - - validate({ foo: 1 }) .should.equal(false); - validate.errors .should.have.length(1); - validate.errors[0].keyword .should.equal('alwaysFails'); - - validate({}) .should.equal(false); - validate.errors .should.have.length(2); - validate.errors[0].keyword .should.equal('required'); - validate.errors[1].keyword .should.equal('alwaysFails'); - } -}); - - -describe('issue #210, mutual recursive $refs that are schema fragments', function() { - it('should compile and validate schema when one ref is fragment', function() { - var ajv = new Ajv; - - ajv.addSchema({ - "$id" : "foo", - "definitions": { - "bar": { - "properties": { - "baz": { - "anyOf": [ - { "enum": [42] }, - { "$ref": "boo" } - ] - } - } - } - } - }); - - ajv.addSchema({ - "$id" : "boo", - "type": "object", - "required": ["quux"], - "properties": { - "quux": { "$ref": "foo#/definitions/bar" } - } - }); - - var validate = ajv.compile({ "$ref": "foo#/definitions/bar" }); - - validate({ baz: { quux: { baz: 42 } } }) .should.equal(true); - validate({ baz: { quux: { baz: "foo" } } }) .should.equal(false); - }); - - it('should compile and validate schema when both refs are fragments', function() { - var ajv = new Ajv; - - ajv.addSchema({ - "$id" : "foo", - "definitions": { - "bar": { - "properties": { - "baz": { - "anyOf": [ - { "enum": [42] }, - { "$ref": "boo#/definitions/buu" } - ] - } - } - } - } - }); - - ajv.addSchema({ - "$id" : "boo", - "definitions": { - "buu": { - "type": "object", - "required": ["quux"], - "properties": { - "quux": { "$ref": "foo#/definitions/bar" } - } - } - } - }); - - var validate = ajv.compile({ "$ref": "foo#/definitions/bar" }); - - validate({ baz: { quux: { baz: 42 } } }) .should.equal(true); - validate({ baz: { quux: { baz: "foo" } } }) .should.equal(false); - }); -}); - - -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#', - resource: { - $id: '#resource', - properties: { - id: { type: 'string' } - } - }, - resourceIdentifier: { - $id: '#resource_identifier', - properties: { - id: { type: 'string' }, - type: { type: 'string' } - } - } - }; - - var domainSchema = { - $schema: 'http://json-schema.org/draft-07/schema#', - $id: 'schema://domain.schema#', - properties: { - data: { - oneOf: [ - { $ref: 'schema://library.schema#resource_identifier' }, - { $ref: 'schema://catalog_item.schema#resource_identifier' }, - ] - } - } - }; - - it('should compile and validate schema when one ref is fragment', function() { - var ajv = new Ajv; - - var librarySchema = { - $schema: 'http://json-schema.org/draft-07/schema#', - $id: 'schema://library.schema#', - properties: { - name: { type: 'string' }, - links: { - properties: { - catalogItems: { - type: 'array', - items: { $ref: 'schema://catalog_item_resource_identifier.schema#' } - } - } - } - }, - definitions: { - resource_identifier: { - $id: '#resource_identifier', - allOf: [ - { - properties: { - type: { - type: 'string', - 'enum': ['Library'] - } - } - }, - { $ref: 'schema://api.schema#resource_identifier' } - ] - } - } - }; - - var catalogItemSchema = { - $schema: 'http://json-schema.org/draft-07/schema#', - $id: 'schema://catalog_item.schema#', - properties: { - name: { type: 'string' }, - links: { - properties: { - library: { $ref: 'schema://library.schema#resource_identifier' } - } - } - }, - definitions: { - resource_identifier: { - $id: '#resource_identifier', - allOf: [ - { - properties: { - type: { - type: 'string', - 'enum': ['CatalogItem'] - } - } - }, - { $ref: 'schema://api.schema#resource_identifier' } - ] - } - } - }; - - var catalogItemResourceIdentifierSchema = { - $schema: 'http://json-schema.org/draft-07/schema#', - $id: 'schema://catalog_item_resource_identifier.schema#', - allOf: [ - { - properties: { - type: { - type: 'string', - enum: ['CatalogItem'] - } - } - }, - { - $ref: 'schema://api.schema#resource_identifier' - } - ] - }; - - ajv.addSchema(librarySchema); - ajv.addSchema(catalogItemSchema); - ajv.addSchema(catalogItemResourceIdentifierSchema); - ajv.addSchema(apiSchema); - - var validate = ajv.compile(domainSchema); - testSchema(validate); - }); - - it('should compile and validate schema when both refs are fragments', function() { - var ajv = new Ajv; - - var librarySchema = { - $schema: 'http://json-schema.org/draft-07/schema#', - $id: 'schema://library.schema#', - properties: { - name: { type: 'string' }, - links: { - properties: { - catalogItems: { - type: 'array', - items: { $ref: 'schema://catalog_item.schema#resource_identifier' } - } - } - } - }, - definitions: { - resource_identifier: { - $id: '#resource_identifier', - allOf: [ - { - properties: { - type: { - type: 'string', - 'enum': ['Library'] - } - } - }, - { $ref: 'schema://api.schema#resource_identifier' } - ] - } - } - }; - - var catalogItemSchema = { - $schema: 'http://json-schema.org/draft-07/schema#', - $id: 'schema://catalog_item.schema#', - properties: { - name: { type: 'string' }, - links: { - properties: { - library: { $ref: 'schema://library.schema#resource_identifier' } - } - } - }, - definitions: { - resource_identifier: { - $id: '#resource_identifier', - allOf: [ - { - properties: { - type: { - type: 'string', - 'enum': ['CatalogItem'] - } - } - }, - { $ref: 'schema://api.schema#resource_identifier' } - ] - } - } - }; - - ajv.addSchema(librarySchema); - ajv.addSchema(catalogItemSchema); - ajv.addSchema(apiSchema); - - var validate = ajv.compile(domainSchema); - testSchema(validate); - }); - - - function testSchema(validate) { - validate({ data: { type: 'Library', id: '123' } }) .should.equal(true); - validate({ data: { type: 'Library', id: 123 } }) .should.equal(false); - validate({ data: { type: 'CatalogItem', id: '123' } }) .should.equal(true); - validate({ data: { type: 'CatalogItem', id: 123 } }) .should.equal(false); - validate({ data: { type: 'Foo', id: '123' } }) .should.equal(false); - } -}); - - -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; - var hyperSchema = require('./remotes/hyper-schema.json'); - ajv.addMetaSchema(hyperSchema); - }); -}); - - -describe.skip('issue #273, schemaPath in error in referenced schema', function() { - it('should have canonic reference with hash after file name', function() { - test(new Ajv); - test(new Ajv({inlineRefs: false})); - - function test(ajv) { - var schema = { - "properties": { - "a": { "$ref": "int" } - } - }; - - var referencedSchema = { - "id": "int", - "type": "integer" - }; - - ajv.addSchema(referencedSchema); - var validate = ajv.compile(schema); - - validate({ "a": "foo" }) .should.equal(false); - validate.errors[0].schemaPath .should.equal('int#/type'); - } - }); -}); - - -describe('issue #342, support uniqueItems with some non-JSON objects', function() { - var validate; - - before(function() { - var ajv = new Ajv; - validate = ajv.compile({ uniqueItems: true }); - }); - - it('should allow different RegExps', function() { - validate([/foo/, /bar/]) .should.equal(true); - validate([/foo/ig, /foo/gi]) .should.equal(false); - validate([/foo/, {}]) .should.equal(true); - }); - - it('should allow different Dates', function() { - validate([new Date('2016-11-11'), new Date('2016-11-12')]) .should.equal(true); - validate([new Date('2016-11-11'), new Date('2016-11-11')]) .should.equal(false); - validate([new Date('2016-11-11'), {}]) .should.equal(true); - }); - - it('should allow undefined properties', function() { - validate([{}, {foo: undefined}]) .should.equal(true); - validate([{foo: undefined}, {}]) .should.equal(true); - validate([{foo: undefined}, {bar: undefined}]) .should.equal(true); - validate([{foo: undefined}, {foo: undefined}]) .should.equal(false); - }); -}); - - -describe('issue #388, code clean-up not working', function() { - it('should remove assignement to rootData if it is not used', function() { - var ajv = new Ajv; - var validate = ajv.compile({ - type: 'object', - properties: { - foo: { type: 'string' } - } - }); - var code = validate.toString(); - code.match(/rootData/g).length .should.equal(1); - }); - - it('should remove assignement to errors if they are not used', function() { - var ajv = new Ajv; - var validate = ajv.compile({ - type: 'object' - }); - var code = validate.toString(); - should.equal(code.match(/[^.]errors|vErrors/g), null); - }); -}); - - -describe('issue #485, order of type validation', function() { - it('should validate types befor keywords', function() { - var ajv = new Ajv({allErrors: true}); - var validate = ajv.compile({ - type: ['integer', 'string'], - required: ['foo'], - minimum: 2 - }); - - validate(2) .should.equal(true); - validate('foo') .should.equal(true); - - validate(1.5) .should.equal(false); - checkErrors(['type', 'minimum']); - - validate({}) .should.equal(false); - checkErrors(['type', 'required']); - - function checkErrors(expectedErrs) { - validate.errors .should.have.length(expectedErrs.length); - expectedErrs.forEach(function (keyword, i) { - validate.errors[i].keyword .should.equal(keyword); - }); - } - }); -}); - - -describe('issue #521, incorrect warning with "id" property', function() { - it('should not log warning', function() { - var ajv = new Ajv({schemaId: '$id'}); - var consoleWarn = console.warn; - console.warn = function() { - throw new Error('should not log warning'); - }; - - try { - ajv.compile({ - "$id": "http://example.com/schema.json", - "type": "object", - "properties": { - "id": {"type": "string"}, - }, - "required": [ "id"] - }); - } finally { - console.warn = consoleWarn; - } - }); -}); - - -describe('issue #533, throwing missing ref exception with option missingRefs: "ignore"', function() { - var schema = { - "type": "object", - "properties": { - "foo": {"$ref": "#/definitions/missing"}, - "bar": {"$ref": "#/definitions/missing"} - } - }; - - it('should pass validation without throwing exception', function() { - var ajv = new Ajv({missingRefs: 'ignore'}); - var validate = ajv.compile(schema); - validate({foo: 'anything'}) .should.equal(true); - validate({foo: 'anything', bar: 'whatever'}) .should.equal(true); - }); - - it('should throw exception during schema compilation with option missingRefs: true', function() { - var ajv = new Ajv; - should.throw(function() { - ajv.compile(schema); - }); - }); -}); - -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' }); - - 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); - }); -}); - - -describe('property __proto__ should be removed with removeAdditional option, issue #743', function() { - it('should remove additional properties', function() { - var ajv = new Ajv({removeAdditional: true}); - - var schema = { - properties: { - obj: { - additionalProperties: false, - properties: { - a: { type: 'string' }, - b: { type: 'string' }, - c: { type: 'string' }, - d: { type: 'string' }, - e: { type: 'string' }, - f: { type: 'string' }, - g: { type: 'string' }, - h: { type: 'string' }, - i: { type: 'string' } - } - } - } - }; - - var obj= Object.create(null); - obj.__proto__ = null; // should be removed - obj.additional = 'will be removed'; - obj.a = 'valid'; - obj.b = 'valid'; - - var data = {obj: obj}; - - ajv.validate(schema, data) .should.equal(true); - Object.keys(data.obj) .should.eql(['a', 'b']); - }); -}); - - -describe('issue #768, fix passContext in recursive $ref', function() { - var ajv, contexts; - - beforeEach(function() { - contexts = []; - }); - - describe('passContext = true', function() { - it('should pass this value as context to custom keyword validation function', function() { - var validate = getValidate(true); - var self = {}; - validate.call(self, { bar: 'a', baz: { bar: 'b' } }); - contexts .should.have.length(2); - contexts.forEach(function(ctx) { - ctx .should.equal(self); - }); - }); - }); - - describe('passContext = false', function() { - it('should pass ajv instance as context to custom keyword validation function', function() { - var validate = getValidate(false); - validate({ bar: 'a', baz: { bar: 'b' } }); - contexts .should.have.length(2); - contexts.forEach(function(ctx) { - ctx .should.equal(ajv); - }); - }); - }); - - describe('ref is fragment and passContext = true', function() { - it('should pass this value as context to custom keyword validation function', function() { - var validate = getValidateFragments(true); - var self = {}; - validate.call(self, { baz: { corge: 'a', quux: { baz: { corge: 'b' } } } }); - contexts .should.have.length(2); - contexts.forEach(function(ctx) { - ctx .should.equal(self); - }); - }); - }); - - describe('ref is fragment and passContext = false', function() { - it('should pass ajv instance as context to custom keyword validation function', function() { - var validate = getValidateFragments(false); - validate({ baz: { corge: 'a', quux: { baz: { corge: 'b' } } } }); - contexts .should.have.length(2); - contexts.forEach(function(ctx) { - ctx .should.equal(ajv); - }); - }); - }); - - function getValidate(passContext) { - ajv = new Ajv({ passContext: passContext }); - ajv.addKeyword('testValidate', { validate: storeContext }); - - var schema = { - "$id" : "foo", - "type": "object", - "required": ["bar"], - "properties": { - "bar": { "testValidate": true }, - "baz": { - "$ref": "foo" - } - } - }; - - return ajv.compile(schema); - } - - - function getValidateFragments(passContext) { - ajv = new Ajv({ passContext: passContext }); - ajv.addKeyword('testValidate', { validate: storeContext }); - - ajv.addSchema({ - "$id" : "foo", - "definitions": { - "bar": { - "properties": { - "baz": { - "$ref": "boo" - } - } - } - } - }); - - ajv.addSchema({ - "$id" : "boo", - "type": "object", - "required": ["corge"], - "properties": { - "quux": { "$ref": "foo#/definitions/bar" }, - "corge": { "testValidate": true } - } - }); - - return ajv.compile({ "$ref": "foo#/definitions/bar" }); - } - - function storeContext() { - contexts.push(this); - return true; - } -}); diff --git a/spec/issues/181_allErrors_custom_keyword_skipped.spec.js b/spec/issues/181_allErrors_custom_keyword_skipped.spec.js new file mode 100644 index 000000000..aa734aa15 --- /dev/null +++ b/spec/issues/181_allErrors_custom_keyword_skipped.spec.js @@ -0,0 +1,58 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +describe('issue #181, custom keyword is not validated in allErrors mode if there were previous error', function() { + it('should validate custom keyword that doesn\'t create errors', function() { + testCustomKeywordErrors({ + type:'object', + errors: true, + validate: function v(/* value */) { + return false; + } + }); + }); + + it('should validate custom keyword that creates errors', function() { + testCustomKeywordErrors({ + type:'object', + errors: true, + validate: function v(/* value */) { + v.errors = v.errors || []; + v.errors.push({ + keyword: 'alwaysFails', + message: 'alwaysFails error', + params: { + keyword: 'alwaysFails' + } + }); + + return false; + } + }); + }); + + function testCustomKeywordErrors(def) { + var ajv = new Ajv({ allErrors: true }); + + ajv.addKeyword('alwaysFails', def); + + var schema = { + required: ['foo'], + alwaysFails: true + }; + + var validate = ajv.compile(schema); + + validate({ foo: 1 }) .should.equal(false); + validate.errors .should.have.length(1); + validate.errors[0].keyword .should.equal('alwaysFails'); + + validate({}) .should.equal(false); + validate.errors .should.have.length(2); + validate.errors[0].keyword .should.equal('required'); + validate.errors[1].keyword .should.equal('alwaysFails'); + } +}); diff --git a/spec/issues/182_nan_validation.spec.js b/spec/issues/182_nan_validation.spec.js new file mode 100644 index 000000000..4d341a3c9 --- /dev/null +++ b/spec/issues/182_nan_validation.spec.js @@ -0,0 +1,26 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +describe('issue #182, NaN validation', function() { + it('should not pass minimum/maximum validation', function() { + testNaN({ minimum: 1 }, false); + testNaN({ maximum: 1 }, false); + }); + + it('should pass type: number validation', function() { + testNaN({ type: 'number' }, true); + }); + + it('should not pass type: integer validation', function() { + testNaN({ type: 'integer' }, false); + }); + + function testNaN(schema, NaNisValid) { + var ajv = new Ajv; + var validate = ajv.compile(schema); + validate(NaN) .should.equal(NaNisValid); + } +}); diff --git a/spec/issues/204_options_schemas_data_together.spec.js b/spec/issues/204_options_schemas_data_together.spec.js new file mode 100644 index 000000000..73746c17e --- /dev/null +++ b/spec/issues/204_options_schemas_data_together.spec.js @@ -0,0 +1,23 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +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'}], + $data: true + }); + + var schema = { const: 42 }; + var validate = ajv.compile(schema); + + validate(42) .should.equal(true); + validate(43) .should.equal(false); + + ajv.validate('str', 'foo') .should.equal(true); + ajv.validate('str', 42) .should.equal(false); + }); +}); diff --git a/spec/issues/210_mutual_recur_frags.spec.js b/spec/issues/210_mutual_recur_frags.spec.js new file mode 100644 index 000000000..58847f017 --- /dev/null +++ b/spec/issues/210_mutual_recur_frags.spec.js @@ -0,0 +1,79 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +describe('issue #210, mutual recursive $refs that are schema fragments', function() { + it('should compile and validate schema when one ref is fragment', function() { + var ajv = new Ajv; + + ajv.addSchema({ + "$id" : "foo", + "definitions": { + "bar": { + "properties": { + "baz": { + "anyOf": [ + { "enum": [42] }, + { "$ref": "boo" } + ] + } + } + } + } + }); + + ajv.addSchema({ + "$id" : "boo", + "type": "object", + "required": ["quux"], + "properties": { + "quux": { "$ref": "foo#/definitions/bar" } + } + }); + + var validate = ajv.compile({ "$ref": "foo#/definitions/bar" }); + + validate({ baz: { quux: { baz: 42 } } }) .should.equal(true); + validate({ baz: { quux: { baz: "foo" } } }) .should.equal(false); + }); + + it('should compile and validate schema when both refs are fragments', function() { + var ajv = new Ajv; + + ajv.addSchema({ + "$id" : "foo", + "definitions": { + "bar": { + "properties": { + "baz": { + "anyOf": [ + { "enum": [42] }, + { "$ref": "boo#/definitions/buu" } + ] + } + } + } + } + }); + + ajv.addSchema({ + "$id" : "boo", + "definitions": { + "buu": { + "type": "object", + "required": ["quux"], + "properties": { + "quux": { "$ref": "foo#/definitions/bar" } + } + } + } + }); + + var validate = ajv.compile({ "$ref": "foo#/definitions/bar" }); + + validate({ baz: { quux: { baz: 42 } } }) .should.equal(true); + validate({ baz: { quux: { baz: "foo" } } }) .should.equal(false); + }); +}); diff --git a/spec/issues/240_mutual_recur_frags_common_ref.spec.js b/spec/issues/240_mutual_recur_frags_common_ref.spec.js new file mode 100644 index 000000000..d9e40241d --- /dev/null +++ b/spec/issues/240_mutual_recur_frags_common_ref.spec.js @@ -0,0 +1,210 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +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#', + resource: { + $id: '#resource', + properties: { + id: { type: 'string' } + } + }, + resourceIdentifier: { + $id: '#resource_identifier', + properties: { + id: { type: 'string' }, + type: { type: 'string' } + } + } + }; + + var domainSchema = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: 'schema://domain.schema#', + properties: { + data: { + oneOf: [ + { $ref: 'schema://library.schema#resource_identifier' }, + { $ref: 'schema://catalog_item.schema#resource_identifier' }, + ] + } + } + }; + + it('should compile and validate schema when one ref is fragment', function() { + var ajv = new Ajv; + + var librarySchema = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: 'schema://library.schema#', + properties: { + name: { type: 'string' }, + links: { + properties: { + catalogItems: { + type: 'array', + items: { $ref: 'schema://catalog_item_resource_identifier.schema#' } + } + } + } + }, + definitions: { + resource_identifier: { + $id: '#resource_identifier', + allOf: [ + { + properties: { + type: { + type: 'string', + 'enum': ['Library'] + } + } + }, + { $ref: 'schema://api.schema#resource_identifier' } + ] + } + } + }; + + var catalogItemSchema = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: 'schema://catalog_item.schema#', + properties: { + name: { type: 'string' }, + links: { + properties: { + library: { $ref: 'schema://library.schema#resource_identifier' } + } + } + }, + definitions: { + resource_identifier: { + $id: '#resource_identifier', + allOf: [ + { + properties: { + type: { + type: 'string', + 'enum': ['CatalogItem'] + } + } + }, + { $ref: 'schema://api.schema#resource_identifier' } + ] + } + } + }; + + var catalogItemResourceIdentifierSchema = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: 'schema://catalog_item_resource_identifier.schema#', + allOf: [ + { + properties: { + type: { + type: 'string', + enum: ['CatalogItem'] + } + } + }, + { + $ref: 'schema://api.schema#resource_identifier' + } + ] + }; + + ajv.addSchema(librarySchema); + ajv.addSchema(catalogItemSchema); + ajv.addSchema(catalogItemResourceIdentifierSchema); + ajv.addSchema(apiSchema); + + var validate = ajv.compile(domainSchema); + testSchema(validate); + }); + + it('should compile and validate schema when both refs are fragments', function() { + var ajv = new Ajv; + + var librarySchema = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: 'schema://library.schema#', + properties: { + name: { type: 'string' }, + links: { + properties: { + catalogItems: { + type: 'array', + items: { $ref: 'schema://catalog_item.schema#resource_identifier' } + } + } + } + }, + definitions: { + resource_identifier: { + $id: '#resource_identifier', + allOf: [ + { + properties: { + type: { + type: 'string', + 'enum': ['Library'] + } + } + }, + { $ref: 'schema://api.schema#resource_identifier' } + ] + } + } + }; + + var catalogItemSchema = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: 'schema://catalog_item.schema#', + properties: { + name: { type: 'string' }, + links: { + properties: { + library: { $ref: 'schema://library.schema#resource_identifier' } + } + } + }, + definitions: { + resource_identifier: { + $id: '#resource_identifier', + allOf: [ + { + properties: { + type: { + type: 'string', + 'enum': ['CatalogItem'] + } + } + }, + { $ref: 'schema://api.schema#resource_identifier' } + ] + } + } + }; + + ajv.addSchema(librarySchema); + ajv.addSchema(catalogItemSchema); + ajv.addSchema(apiSchema); + + var validate = ajv.compile(domainSchema); + testSchema(validate); + }); + + + function testSchema(validate) { + validate({ data: { type: 'Library', id: '123' } }) .should.equal(true); + validate({ data: { type: 'Library', id: 123 } }) .should.equal(false); + validate({ data: { type: 'CatalogItem', id: '123' } }) .should.equal(true); + validate({ data: { type: 'CatalogItem', id: 123 } }) .should.equal(false); + validate({ data: { type: 'Foo', id: '123' } }) .should.equal(false); + } +}); diff --git a/spec/issues/259_validate_meta_against_itself.spec.js b/spec/issues/259_validate_meta_against_itself.spec.js new file mode 100644 index 000000000..d99fc21ef --- /dev/null +++ b/spec/issues/259_validate_meta_against_itself.spec.js @@ -0,0 +1,13 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +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; + var hyperSchema = require('../remotes/hyper-schema.json'); + ajv.addMetaSchema(hyperSchema); + }); +}); diff --git a/spec/issues/273_error_schemaPath_refd_schema.spec.js b/spec/issues/273_error_schemaPath_refd_schema.spec.js new file mode 100644 index 000000000..927164087 --- /dev/null +++ b/spec/issues/273_error_schemaPath_refd_schema.spec.js @@ -0,0 +1,31 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +describe.skip('issue #273, schemaPath in error in referenced schema', function() { + it('should have canonic reference with hash after file name', function() { + test(new Ajv); + test(new Ajv({inlineRefs: false})); + + function test(ajv) { + var schema = { + "properties": { + "a": { "$ref": "int" } + } + }; + + var referencedSchema = { + "id": "int", + "type": "integer" + }; + + ajv.addSchema(referencedSchema); + var validate = ajv.compile(schema); + + validate({ "a": "foo" }) .should.equal(false); + validate.errors[0].schemaPath .should.equal('int#/type'); + } + }); +}); diff --git a/spec/issues/342_uniqueItems_non-json_objects.spec.js b/spec/issues/342_uniqueItems_non-json_objects.spec.js new file mode 100644 index 000000000..ae7eee0cf --- /dev/null +++ b/spec/issues/342_uniqueItems_non-json_objects.spec.js @@ -0,0 +1,33 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +describe('issue #342, support uniqueItems with some non-JSON objects', function() { + var validate; + + before(function() { + var ajv = new Ajv; + validate = ajv.compile({ uniqueItems: true }); + }); + + it('should allow different RegExps', function() { + validate([/foo/, /bar/]) .should.equal(true); + validate([/foo/ig, /foo/gi]) .should.equal(false); + validate([/foo/, {}]) .should.equal(true); + }); + + it('should allow different Dates', function() { + validate([new Date('2016-11-11'), new Date('2016-11-12')]) .should.equal(true); + validate([new Date('2016-11-11'), new Date('2016-11-11')]) .should.equal(false); + validate([new Date('2016-11-11'), {}]) .should.equal(true); + }); + + it('should allow undefined properties', function() { + validate([{}, {foo: undefined}]) .should.equal(true); + validate([{foo: undefined}, {}]) .should.equal(true); + validate([{foo: undefined}, {bar: undefined}]) .should.equal(true); + validate([{foo: undefined}, {foo: undefined}]) .should.equal(false); + }); +}); diff --git a/spec/issues/388_code_clean-up not.spec.js b/spec/issues/388_code_clean-up not.spec.js new file mode 100644 index 000000000..9a0288362 --- /dev/null +++ b/spec/issues/388_code_clean-up not.spec.js @@ -0,0 +1,28 @@ +'use strict'; + +var Ajv = require('../ajv'); +var should = require('../chai').should(); + + +describe('issue #388, code clean-up not working', function() { + it('should remove assignement to rootData if it is not used', function() { + var ajv = new Ajv; + var validate = ajv.compile({ + type: 'object', + properties: { + foo: { type: 'string' } + } + }); + var code = validate.toString(); + code.match(/rootData/g).length .should.equal(1); + }); + + it('should remove assignement to errors if they are not used', function() { + var ajv = new Ajv; + var validate = ajv.compile({ + type: 'object' + }); + var code = validate.toString(); + should.equal(code.match(/[^.]errors|vErrors/g), null); + }); +}); diff --git a/spec/issues/485_type_validation_priority.spec.js b/spec/issues/485_type_validation_priority.spec.js new file mode 100644 index 000000000..eb2b1e9df --- /dev/null +++ b/spec/issues/485_type_validation_priority.spec.js @@ -0,0 +1,32 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +describe('issue #485, order of type validation', function() { + it('should validate types before keywords', function() { + var ajv = new Ajv({allErrors: true}); + var validate = ajv.compile({ + type: ['integer', 'string'], + required: ['foo'], + minimum: 2 + }); + + validate(2) .should.equal(true); + validate('foo') .should.equal(true); + + validate(1.5) .should.equal(false); + checkErrors(['type', 'minimum']); + + validate({}) .should.equal(false); + checkErrors(['type', 'required']); + + function checkErrors(expectedErrs) { + validate.errors .should.have.length(expectedErrs.length); + expectedErrs.forEach(function (keyword, i) { + validate.errors[i].keyword .should.equal(keyword); + }); + } + }); +}); diff --git a/spec/issues/50_refs_with_definitions.spec.js b/spec/issues/50_refs_with_definitions.spec.js new file mode 100644 index 000000000..26b84a8cc --- /dev/null +++ b/spec/issues/50_refs_with_definitions.spec.js @@ -0,0 +1,49 @@ +'use strict'; + +var Ajv = require('../ajv'); +var should = require('../chai').should(); + + +describe('issue #50: references with "definitions"', function () { + it('should be supported by addSchema', spec('addSchema')); + + it('should be supported by compile', spec('addSchema')); + + function spec(method) { + return function() { + var result; + + var ajv = new Ajv; + + ajv[method]({ + $id: 'http://example.com/test/person.json#', + definitions: { + name: { type: 'string' } + }, + type: 'object', + properties: { + name: { $ref: '#/definitions/name'} + } + }); + + ajv[method]({ + $id: 'http://example.com/test/employee.json#', + type: 'object', + properties: { + person: { $ref: '/test/person.json#' }, + role: { type: 'string' } + } + }); + + result = ajv.validate('http://example.com/test/employee.json#', { + person: { + name: 'Alice' + }, + role: 'Programmer' + }); + + result. should.equal(true); + should.equal(ajv.errors, null); + }; + } +}); diff --git a/spec/issues/521_wrong_warning_id_property.spec.js b/spec/issues/521_wrong_warning_id_property.spec.js new file mode 100644 index 000000000..79fbbce7e --- /dev/null +++ b/spec/issues/521_wrong_warning_id_property.spec.js @@ -0,0 +1,28 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +describe('issue #521, incorrect warning with "id" property', function() { + it('should not log warning', function() { + var ajv = new Ajv({schemaId: '$id'}); + var consoleWarn = console.warn; + console.warn = function() { + throw new Error('should not log warning'); + }; + + try { + ajv.compile({ + "$id": "http://example.com/schema.json", + "type": "object", + "properties": { + "id": {"type": "string"}, + }, + "required": [ "id"] + }); + } finally { + console.warn = consoleWarn; + } + }); +}); diff --git a/spec/issues/533_missing_ref_error_when_ignore.spec.js b/spec/issues/533_missing_ref_error_when_ignore.spec.js new file mode 100644 index 000000000..4e77d5fc6 --- /dev/null +++ b/spec/issues/533_missing_ref_error_when_ignore.spec.js @@ -0,0 +1,29 @@ +'use strict'; + +var Ajv = require('../ajv'); +var should = require('../chai').should(); + + +describe('issue #533, throwing missing ref exception with option missingRefs: "ignore"', function() { + var schema = { + "type": "object", + "properties": { + "foo": {"$ref": "#/definitions/missing"}, + "bar": {"$ref": "#/definitions/missing"} + } + }; + + it('should pass validation without throwing exception', function() { + var ajv = new Ajv({missingRefs: 'ignore'}); + var validate = ajv.compile(schema); + validate({foo: 'anything'}) .should.equal(true); + validate({foo: 'anything', bar: 'whatever'}) .should.equal(true); + }); + + it('should throw exception during schema compilation with option missingRefs: true', function() { + var ajv = new Ajv; + should.throw(function() { + ajv.compile(schema); + }); + }); +}); diff --git a/spec/issues/617_full_format_leap_year.spec.js b/spec/issues/617_full_format_leap_year.spec.js new file mode 100644 index 000000000..995c4ba13 --- /dev/null +++ b/spec/issues/617_full_format_leap_year.spec.js @@ -0,0 +1,47 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +describe('PR #617, 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' }); + + 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); + }); +}); diff --git a/spec/issues/743_removeAdditional_to_remove_proto.spec.js b/spec/issues/743_removeAdditional_to_remove_proto.spec.js new file mode 100644 index 000000000..f5a01926d --- /dev/null +++ b/spec/issues/743_removeAdditional_to_remove_proto.spec.js @@ -0,0 +1,41 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +describe('issue #743, property __proto__ should be removed with removeAdditional option', function() { + it('should remove additional properties', function() { + var ajv = new Ajv({removeAdditional: true}); + + var schema = { + properties: { + obj: { + additionalProperties: false, + properties: { + a: { type: 'string' }, + b: { type: 'string' }, + c: { type: 'string' }, + d: { type: 'string' }, + e: { type: 'string' }, + f: { type: 'string' }, + g: { type: 'string' }, + h: { type: 'string' }, + i: { type: 'string' } + } + } + } + }; + + var obj= Object.create(null); + obj.__proto__ = null; // should be removed + obj.additional = 'will be removed'; + obj.a = 'valid'; + obj.b = 'valid'; + + var data = {obj: obj}; + + ajv.validate(schema, data) .should.equal(true); + Object.keys(data.obj) .should.eql(['a', 'b']); + }); +}); diff --git a/spec/issues/768_passContext_recursive_ref.spec.js b/spec/issues/768_passContext_recursive_ref.spec.js new file mode 100644 index 000000000..3410f7462 --- /dev/null +++ b/spec/issues/768_passContext_recursive_ref.spec.js @@ -0,0 +1,114 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +describe('issue #768, fix passContext in recursive $ref', function() { + var ajv, contexts; + + beforeEach(function() { + contexts = []; + }); + + describe('passContext = true', function() { + it('should pass this value as context to custom keyword validation function', function() { + var validate = getValidate(true); + var self = {}; + validate.call(self, { bar: 'a', baz: { bar: 'b' } }); + contexts .should.have.length(2); + contexts.forEach(function(ctx) { + ctx .should.equal(self); + }); + }); + }); + + describe('passContext = false', function() { + it('should pass ajv instance as context to custom keyword validation function', function() { + var validate = getValidate(false); + validate({ bar: 'a', baz: { bar: 'b' } }); + contexts .should.have.length(2); + contexts.forEach(function(ctx) { + ctx .should.equal(ajv); + }); + }); + }); + + describe('ref is fragment and passContext = true', function() { + it('should pass this value as context to custom keyword validation function', function() { + var validate = getValidateFragments(true); + var self = {}; + validate.call(self, { baz: { corge: 'a', quux: { baz: { corge: 'b' } } } }); + contexts .should.have.length(2); + contexts.forEach(function(ctx) { + ctx .should.equal(self); + }); + }); + }); + + describe('ref is fragment and passContext = false', function() { + it('should pass ajv instance as context to custom keyword validation function', function() { + var validate = getValidateFragments(false); + validate({ baz: { corge: 'a', quux: { baz: { corge: 'b' } } } }); + contexts .should.have.length(2); + contexts.forEach(function(ctx) { + ctx .should.equal(ajv); + }); + }); + }); + + function getValidate(passContext) { + ajv = new Ajv({ passContext: passContext }); + ajv.addKeyword('testValidate', { validate: storeContext }); + + var schema = { + "$id" : "foo", + "type": "object", + "required": ["bar"], + "properties": { + "bar": { "testValidate": true }, + "baz": { + "$ref": "foo" + } + } + }; + + return ajv.compile(schema); + } + + + function getValidateFragments(passContext) { + ajv = new Ajv({ passContext: passContext }); + ajv.addKeyword('testValidate', { validate: storeContext }); + + ajv.addSchema({ + "$id" : "foo", + "definitions": { + "bar": { + "properties": { + "baz": { + "$ref": "boo" + } + } + } + } + }); + + ajv.addSchema({ + "$id" : "boo", + "type": "object", + "required": ["corge"], + "properties": { + "quux": { "$ref": "foo#/definitions/bar" }, + "corge": { "testValidate": true } + } + }); + + return ajv.compile({ "$ref": "foo#/definitions/bar" }); + } + + function storeContext() { + contexts.push(this); + return true; + } +}); diff --git a/spec/issues/8_shared_refs.spec.js b/spec/issues/8_shared_refs.spec.js new file mode 100644 index 000000000..2208f452e --- /dev/null +++ b/spec/issues/8_shared_refs.spec.js @@ -0,0 +1,40 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +describe('issue #8: schema with shared references', function() { + it('should be supported by addSchema', spec('addSchema')); + + it('should be supported by compile', spec('compile')); + + function spec(method) { + return function() { + var ajv = new Ajv; + + var propertySchema = { + type: 'string', + maxLength: 4 + }; + + var schema = { + $id: 'obj.json#', + type: 'object', + properties: { + foo: propertySchema, + bar: propertySchema + } + }; + + ajv[method](schema); + + var result = ajv.validate('obj.json#', { foo: 'abc', bar: 'def' }); + result .should.equal(true); + + result = ajv.validate('obj.json#', { foo: 'abcde', bar: 'fghg' }); + result .should.equal(false); + ajv.errors .should.have.length(1); + }; + } +}); From 8720547e6e6c93129663c52e4182d97ae4e9c049 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 10 Feb 2019 12:55:07 +0000 Subject: [PATCH 215/333] skip browser tests --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7767cfca8..2abe4feef 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "build": "del-cli lib/dotjs/*.js '!lib/dotjs/index.js' && node scripts/compile-dots.js", "test-karma": "karma start", "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 8 npm run test-browser", + "test": "npm run jshint && npm run eslint && npm run test-ts && npm run build && npm run test-cov", "prepublish": "npm run build && npm run bundle", "watch": "watch 'npm run build' ./lib/dot" }, From 098df6d4adb7fbc3701dbb5f1b60d7829099444a Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 10 Feb 2019 16:00:37 +0000 Subject: [PATCH 216/333] test: enable browser tests in node 10 --- package.json | 2 +- scripts/prepare-tests | 5 ++++- ...8_code_clean-up not.spec.js => 388_code_clean-up.spec.js} | 0 3 files changed, 5 insertions(+), 2 deletions(-) rename spec/issues/{388_code_clean-up not.spec.js => 388_code_clean-up.spec.js} (100%) diff --git a/package.json b/package.json index 2abe4feef..3548a6692 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "build": "del-cli lib/dotjs/*.js '!lib/dotjs/index.js' && node scripts/compile-dots.js", "test-karma": "karma start", "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", + "test": "npm run jshint && npm run eslint && npm run test-ts && npm run build && npm run test-cov && if-node-version 10 npm run test-browser", "prepublish": "npm run build && npm run bundle", "watch": "watch 'npm run build' ./lib/dot" }, diff --git a/scripts/prepare-tests b/scripts/prepare-tests index 6f62634e2..684703318 100755 --- a/scripts/prepare-tests +++ b/scripts/prepare-tests @@ -4,6 +4,9 @@ set -e mkdir -p .browser +echo +echo Preparing browser tests: + find spec -type f -name '*.spec.js' | \ xargs -I {} sh -c \ -'export f="{}"; browserify $f -t require-globify -t brfs -x ajv -u buffer -o $(echo $f | sed -e "s/spec/.browser/");' +'export f="{}"; echo $f; browserify $f -t require-globify -t brfs -x ajv -u buffer -o $(echo $f | sed -e "s/spec/.browser/");' diff --git a/spec/issues/388_code_clean-up not.spec.js b/spec/issues/388_code_clean-up.spec.js similarity index 100% rename from spec/issues/388_code_clean-up not.spec.js rename to spec/issues/388_code_clean-up.spec.js From c52f2e1b87e1393dc130cf54199611b7f47fa0f1 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 10 Feb 2019 18:31:24 +0000 Subject: [PATCH 217/333] update package.json scripts --- package.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 3548a6692..a03ca2b58 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,10 @@ "scripts": { "eslint": "eslint lib/{compile/,}*.js spec/{**/,}*.js scripts --ignore-pattern spec/JSON-Schema-Test-Suite", "jshint": "jshint lib/{compile/,}*.js", + "lint": "npm run jshint && npm run eslint", "test-spec": "mocha spec/{**/,}*.spec.js -R spec", "test-fast": "AJV_FAST_TEST=true npm run test-spec", - "test-debug": "mocha spec/*.spec.js --debug-brk -R spec", + "test-debug": "npm run test-spec -- --inspect-brk", "test-cov": "nyc npm run test-spec", "test-ts": "tsc --target ES5 --noImplicitAny --noEmit spec/typescript/index.ts", "bundle": "del-cli dist && node ./scripts/bundle.js . Ajv pure_getters", @@ -24,7 +25,8 @@ "build": "del-cli lib/dotjs/*.js '!lib/dotjs/index.js' && node scripts/compile-dots.js", "test-karma": "karma start", "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 10 npm run test-browser", + "test-all": "npm run test-ts && npm run test-cov && if-node-version 10 npm run test-browser", + "test": "npm run lint && npm run build && npm run test-all", "prepublish": "npm run build && npm run bundle", "watch": "watch 'npm run build' ./lib/dot" }, From f6d25def2b63259378a028d802818cda9cd11fa6 Mon Sep 17 00:00:00 2001 From: tridium Date: Thu, 21 Feb 2019 12:35:44 -0500 Subject: [PATCH 218/333] Replace single quotes with double quotes to get build scripts running on Windows (#946) --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a03ca2b58..a2b9b15f5 100644 --- a/package.json +++ b/package.json @@ -22,13 +22,13 @@ "test-ts": "tsc --target ES5 --noImplicitAny --noEmit spec/typescript/index.ts", "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", + "build": "del-cli lib/dotjs/*.js \"!lib/dotjs/index.js\" && node scripts/compile-dots.js", "test-karma": "karma start", "test-browser": "del-cli .browser && npm run bundle && scripts/prepare-tests && npm run test-karma", "test-all": "npm run test-ts && npm run test-cov && if-node-version 10 npm run test-browser", "test": "npm run lint && npm run build && npm run test-all", "prepublish": "npm run build && npm run bundle", - "watch": "watch 'npm run build' ./lib/dot" + "watch": "watch \"npm run build\" ./lib/dot" }, "nyc": { "exclude": [ From 187e0212bd453e85587a4965f16772175c2a26ff Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 22 Feb 2019 10:52:52 +0000 Subject: [PATCH 219/333] fix: removeAdditional option breaking custom keywords, closes #955, closes epoberezkin/ajv-keywords#91 --- lib/keyword.js | 10 ++-- ...5_removeAdditional_custom_keywords.spec.js | 48 +++++++++++++++++++ 2 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 spec/issues/955_removeAdditional_custom_keywords.spec.js diff --git a/lib/keyword.js b/lib/keyword.js index 53d95c69d..5ea565c38 100644 --- a/lib/keyword.js +++ b/lib/keyword.js @@ -43,8 +43,6 @@ var definitionSchema = { } }; -var validateDefinition; - /** * Define custom keyword * @this Ajv @@ -56,7 +54,6 @@ function addKeyword(keyword, definition) { /* jshint validthis: true */ /* eslint no-shadow: 0 */ var RULES = this.RULES; - if (RULES.keywords[keyword]) throw new Error('Keyword ' + keyword + ' is already defined'); @@ -64,10 +61,11 @@ function addKeyword(keyword, definition) { throw new Error('Keyword ' + keyword + ' is not a valid identifier'); if (definition) { - validateDefinition = validateDefinition || this.compile(definitionSchema); + this.validateKeyword = this.validateKeyword + || this.compile(definitionSchema, true); - if (!validateDefinition(definition)) - throw new Error('custom keyword definition is invalid: ' + this.errorsText(validateDefinition.errors)); + if (!this.validateKeyword(definition)) + throw new Error('custom keyword definition is invalid: ' + this.errorsText(this.validateKeyword.errors)); var dataType = definition.type; if (Array.isArray(dataType)) { diff --git a/spec/issues/955_removeAdditional_custom_keywords.spec.js b/spec/issues/955_removeAdditional_custom_keywords.spec.js new file mode 100644 index 000000000..4ef949d60 --- /dev/null +++ b/spec/issues/955_removeAdditional_custom_keywords.spec.js @@ -0,0 +1,48 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +describe('issue #955: option removeAdditional breaks custom keywords', function() { + it('should support custom keywords with option removeAdditional', function() { + var ajv = new Ajv({removeAdditional: 'all'}); + + ajv.addKeyword('minTrimmedLength', { + type: 'string', + compile: function(schema) { + return function(str) { + return str.trim().length >= schema; + }; + }, + metaSchema: {type: 'integer'} + }); + + var schema = { + type: 'object', + properties: { + foo: { + type: 'string', + minTrimmedLength: 3 + } + }, + required: ['foo'] + }; + + var validate = ajv.compile(schema); + + var data = { + foo: ' bar ', + baz: '' + }; + validate(data) .should.equal(true); + data .should.not.have.property('baz'); + + data = { + foo: ' ba ', + baz: '' + }; + validate(data) .should.equal(false); + data .should.not.have.property('baz'); + }); +}); From 6831b68f640f8f36a6099cafc443ced85f518d8a Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 22 Feb 2019 11:16:30 +0000 Subject: [PATCH 220/333] feat: extract method to validate custom keyword definition --- lib/ajv.d.ts | 10 ++++++++++ lib/ajv.js | 1 + lib/keyword.js | 30 ++++++++++++++++++++++++------ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index 6593d0788..8b0d9ab6d 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -122,6 +122,14 @@ declare namespace ajv { */ removeKeyword(keyword: string): Ajv; /** + * Validate keyword + * @this Ajv + * @param {object} definition keyword definition object + * @param {boolean} throwError true to throw exception if definition is invalid + * @return {boolean} validation result + */ + validateKeyword(definition: KeywordDefinition, throwError: boolean): boolean; + /** * Convert array of error message objects to string * @param {Array} errors optional array of validation errors, if not passed errors from the instance are used. * @param {object} options optional options with properties `separator` and `dataVar`. @@ -219,6 +227,8 @@ declare namespace ajv { metaSchema?: object; // schema: false makes validate not to expect schema (ValidateFunction) schema?: boolean; + statements?: boolean; + dependencies?: Array; modifying?: boolean; valid?: boolean; // one and only one of the following properties should be present diff --git a/lib/ajv.js b/lib/ajv.js index 0a97367fe..105315adb 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -30,6 +30,7 @@ var customKeyword = require('./keyword'); Ajv.prototype.addKeyword = customKeyword.add; Ajv.prototype.getKeyword = customKeyword.get; Ajv.prototype.removeKeyword = customKeyword.remove; +Ajv.prototype.validateKeyword = customKeyword.validate; var errorClasses = require('./compile/error_classes'); Ajv.ValidationError = errorClasses.Validation; diff --git a/lib/keyword.js b/lib/keyword.js index 5ea565c38..cf4d699b3 100644 --- a/lib/keyword.js +++ b/lib/keyword.js @@ -7,7 +7,8 @@ var metaSchema = require('./refs/json-schema-draft-07.json'); module.exports = { add: addKeyword, get: getKeyword, - remove: removeKeyword + remove: removeKeyword, + validate: validateKeyword }; var definitionSchema = { @@ -61,11 +62,7 @@ function addKeyword(keyword, definition) { throw new Error('Keyword ' + keyword + ' is not a valid identifier'); if (definition) { - this.validateKeyword = this.validateKeyword - || this.compile(definitionSchema, true); - - if (!this.validateKeyword(definition)) - throw new Error('custom keyword definition is invalid: ' + this.errorsText(this.validateKeyword.errors)); + this.validateKeyword(definition, true); var dataType = definition.type; if (Array.isArray(dataType)) { @@ -158,3 +155,24 @@ function removeKeyword(keyword) { } return this; } + + +/** + * Validate keyword definition + * @this Ajv + * @param {Object} definition keyword definition object. + * @param {Boolean} throwError true to throw exception if definition is invalid + * @return {boolean} validation result + */ +function validateKeyword(definition, throwError) { + validateKeyword.errors = null; + var v = this._validateKeyword = this._validateKeyword + || this.compile(definitionSchema, true); + + if (v(definition)) return true; + validateKeyword.errors = v.errors; + if (throwError) + throw new Error('custom keyword definition is invalid: ' + this.errorsText(v.errors)); + else + return false; +} From dffe473e73d7aa6a4a17b2d10ec4ee74d6c38d99 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Fri, 22 Feb 2019 19:44:53 +0000 Subject: [PATCH 221/333] chore(package): update mocha to version 6.0.0 (#952) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a2b9b15f5..5bd12f981 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "karma-chrome-launcher": "^2.2.0", "karma-mocha": "^1.1.1", "karma-sauce-launcher": "^2.0.0", - "mocha": "^5.1.1", + "mocha": "^6.0.0", "nyc": "^13.2.0", "pre-commit": "^1.1.1", "require-globify": "^1.3.0", From 2aa49aebd4e19c4e4e120424a6ed77990c95e591 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 22 Feb 2019 19:52:06 +0000 Subject: [PATCH 222/333] 6.9.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5bd12f981..32026c5b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.9.1", + "version": "6.9.2", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From c081061a1e6a998b65bd11aba7a0dc25b42d207d Mon Sep 17 00:00:00 2001 From: Teddy Katz Date: Fri, 1 Mar 2019 02:19:12 -0500 Subject: [PATCH 223/333] feat: invalidDefaults option to warn when defaults are ignored, fixes #957 --- README.md | 8 ++- lib/ajv.d.ts | 1 + lib/ajv.js | 2 +- lib/dot/defaults.def | 30 +++++++---- lib/dot/validate.jst | 9 +++- spec/options/useDefaults.spec.js | 88 +++++++++++++++++++++++++++++++- 6 files changed, 124 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 5ddefa134..c26aa078d 100644 --- a/README.md +++ b/README.md @@ -798,13 +798,14 @@ console.log(validate(data)); // true console.log(data); // [ 1, "foo" ] ``` -`default` keywords in other cases are ignored: +`default` keywords in other cases are invalid: - not in `properties` or `items` subschemas - in schemas inside `anyOf`, `oneOf` and `not` (see [#42](https://github.com/epoberezkin/ajv/issues/42)) - in `if` subschema of `switch` keyword - in schemas generated by custom macro keywords +The [`invalidDefaults` option](#options) customizes Ajv's behavior for invalid defaults (`false` ignores invalid defaults, `true` raises an error, and `"log"` outputs a warning). ## Coercing data types @@ -1070,6 +1071,7 @@ Defaults: removeAdditional: false, useDefaults: false, coerceTypes: false, + invalidDefaults: false, // asynchronous validation options: transpile: undefined, // requires ajv-async package // advanced options: @@ -1151,6 +1153,10 @@ Defaults: - `false` (default) - no type coercion. - `true` - coerce scalar data types. - `"array"` - in addition to coercions between scalar types, coerce scalar data to an array with one element and vice versa (as required by the schema). +- _invalidDefaults_: specify behavior for invalid `default` keywords in schemas. Option values: + - `false` (default) - ignore invalid defaults + - `true` - if an invalid default is present, throw an error + - `"log"` - if an invalid default is present, log warning ##### Asynchronous validation options diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index 8b0d9ab6d..bbba7a593 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -180,6 +180,7 @@ declare namespace ajv { removeAdditional?: boolean | 'all' | 'failing'; useDefaults?: boolean | 'shared'; coerceTypes?: boolean | 'array'; + invalidDefaults?: boolean | 'log'; async?: boolean | string; transpile?: string | ((code: string) => string); meta?: boolean | object; diff --git a/lib/ajv.js b/lib/ajv.js index 105315adb..0b975a28f 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -39,7 +39,7 @@ Ajv.$dataMetaSchema = $dataMetaSchema; var META_SCHEMA_ID = 'http://json-schema.org/draft-07/schema'; -var META_IGNORE_OPTIONS = [ 'removeAdditional', 'useDefaults', 'coerceTypes' ]; +var META_IGNORE_OPTIONS = [ 'removeAdditional', 'useDefaults', 'coerceTypes', 'invalidDefaults' ]; var META_SUPPORT_DATA = ['/properties']; /** diff --git a/lib/dot/defaults.def b/lib/dot/defaults.def index f100cc4bf..3e4a8de00 100644 --- a/lib/dot/defaults.def +++ b/lib/dot/defaults.def @@ -1,15 +1,25 @@ {{## def.assignDefault: - if ({{=$passData}} === undefined - {{? it.opts.useDefaults == 'empty' }} - || {{=$passData}} === null - || {{=$passData}} === '' + {{? it.compositeRule }} + {{? it.opts.invalidDefaults }} + {{? it.opts.invalidDefaults === 'log' }} + {{ it.logger.warn('default is ignored for: ' + $passData); }} + {{??}} + {{ throw new Error('default is ignored for: ' + $passData); }} + {{?}} {{?}} - ) - {{=$passData}} = {{? it.opts.useDefaults == 'shared' }} - {{= it.useDefault($sch.default) }} - {{??}} - {{= JSON.stringify($sch.default) }} - {{?}}; + {{??}} + if ({{=$passData}} === undefined + {{? it.opts.useDefaults == 'empty' }} + || {{=$passData}} === null + || {{=$passData}} === '' + {{?}} + ) + {{=$passData}} = {{? it.opts.useDefaults == 'shared' }} + {{= it.useDefault($sch.default) }} + {{??}} + {{= JSON.stringify($sch.default) }} + {{?}}; + {{?}} #}} diff --git a/lib/dot/validate.jst b/lib/dot/validate.jst index 89a5b3b49..4e05ce890 100644 --- a/lib/dot/validate.jst +++ b/lib/dot/validate.jst @@ -72,6 +72,13 @@ it.dataPathArr = [undefined]; }} + {{? it.opts.invalidDefaults && it.schema.default !== undefined }} + {{? it.opts.invalidDefaults === 'log' }} + {{ it.logger.warn('default is ignored in the schema root'); }} + {{??}} + {{ throw new Error('default is ignored in the schema root'); }} + {{?}} + {{?}} var vErrors = null; {{ /* don't edit, used in replace */ }} var errors = 0; {{ /* don't edit, used in replace */ }} @@ -177,7 +184,7 @@ {{? $rulesGroup.type }} if ({{= it.util.checkDataType($rulesGroup.type, $data) }}) { {{?}} - {{? it.opts.useDefaults && !it.compositeRule }} + {{? it.opts.useDefaults }} {{? $rulesGroup.type == 'object' && it.schema.properties }} {{# def.defaultProperties }} {{?? $rulesGroup.type == 'array' && Array.isArray(it.schema.items) }} diff --git a/spec/options/useDefaults.spec.js b/spec/options/useDefaults.spec.js index 7a12e8423..4570a326c 100644 --- a/spec/options/useDefaults.spec.js +++ b/spec/options/useDefaults.spec.js @@ -2,7 +2,7 @@ var Ajv = require('../ajv'); var getAjvInstances = require('../ajv_instances'); -require('../chai').should(); +var should = require('../chai').should(); describe('useDefaults options', function() { @@ -220,4 +220,90 @@ describe('useDefaults options', function() { }); }); }); + + describe('invalidDefaults option', function() { + it('should throw an error given an invalid default in the schema root when invalidDefaults is true', function() { + var ajv = new Ajv({useDefaults: true, invalidDefaults: true}); + var schema = { + default: 5, + properties: {} + }; + should.throw(function() { ajv.compile(schema); }); + }); + + it('should throw an error given an invalid default in oneOf when invalidDefaults is true', function() { + var ajv = new Ajv({useDefaults: true, invalidDefaults: true}); + var schema = { + oneOf: [ + { enum: ['foo', 'bar'] }, + { + properties: { + foo: { + default: true + } + } + } + ] + }; + should.throw(function() { ajv.compile(schema); }); + }); + + it('should log a warning given an invalid default in the schema root when invalidDefaults is "log"', function() { + var warnArg = null; + var ajv = new Ajv({ + useDefaults: true, + invalidDefaults: 'log', + logger: { + log: function() { + throw new Error('should not be called'); + }, + warn: function(warning) { + warnArg = warning; + }, + error: function() { + throw new Error('should not be called'); + } + } + }); + var schema = { + default: 5, + properties: {} + }; + ajv.compile(schema); + should.equal(warnArg, 'default is ignored in the schema root'); + }); + + it('should log a warning given an invalid default in oneOf when invalidDefaults is "log"', function() { + var warnArg = null; + var ajv = new Ajv({ + useDefaults: true, + invalidDefaults: 'log', + logger: { + log: function() { + throw new Error('should not be called'); + }, + warn: function(warning) { + warnArg = warning; + }, + error: function() { + throw new Error('should not be called'); + } + } + }); + var schema = { + oneOf: [ + { enum: ['foo', 'bar'] }, + { + properties: { + foo: { + default: true + } + } + } + ] + }; + ajv.compile(schema); + should.equal(warnArg, 'default is ignored for: data.foo'); + }); + }); }); From 88199d569c2d6d2123678bec08ba9a8ffcf841f1 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 3 Mar 2019 09:16:00 +0000 Subject: [PATCH 224/333] rename option to strictDefaults --- README.md | 15 ++++++++------- lib/ajv.d.ts | 2 +- lib/ajv.js | 2 +- lib/dot/defaults.def | 9 +++++---- lib/dot/validate.jst | 9 +++++---- spec/options/useDefaults.spec.js | 18 +++++++++--------- 6 files changed, 29 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index c26aa078d..b61da8b13 100644 --- a/README.md +++ b/README.md @@ -798,14 +798,15 @@ console.log(validate(data)); // true console.log(data); // [ 1, "foo" ] ``` -`default` keywords in other cases are invalid: +`default` keywords in other cases are ignored: - not in `properties` or `items` subschemas - in schemas inside `anyOf`, `oneOf` and `not` (see [#42](https://github.com/epoberezkin/ajv/issues/42)) - in `if` subschema of `switch` keyword - in schemas generated by custom macro keywords -The [`invalidDefaults` option](#options) customizes Ajv's behavior for invalid defaults (`false` ignores invalid defaults, `true` raises an error, and `"log"` outputs a warning). +The [`strictDefaults` option](#options) customizes Ajv's behavior for the defaults that Ajv ignores (`true` raises an error, and `"log"` outputs a warning). + ## Coercing data types @@ -1071,7 +1072,7 @@ Defaults: removeAdditional: false, useDefaults: false, coerceTypes: false, - invalidDefaults: false, + strictDefaults: false, // asynchronous validation options: transpile: undefined, // requires ajv-async package // advanced options: @@ -1153,10 +1154,10 @@ Defaults: - `false` (default) - no type coercion. - `true` - coerce scalar data types. - `"array"` - in addition to coercions between scalar types, coerce scalar data to an array with one element and vice versa (as required by the schema). -- _invalidDefaults_: specify behavior for invalid `default` keywords in schemas. Option values: - - `false` (default) - ignore invalid defaults - - `true` - if an invalid default is present, throw an error - - `"log"` - if an invalid default is present, log warning +- _strictDefaults_: specify behavior for ignored `default` keywords in schemas. Option values: + - `false` (default) - ignored defaults are not reported + - `true` - if an ignored default is present, throw an error + - `"log"` - if an ignored default is present, log warning ##### Asynchronous validation options diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index bbba7a593..63f110a0d 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -180,7 +180,7 @@ declare namespace ajv { removeAdditional?: boolean | 'all' | 'failing'; useDefaults?: boolean | 'shared'; coerceTypes?: boolean | 'array'; - invalidDefaults?: boolean | 'log'; + strictDefaults?: boolean | 'log'; async?: boolean | string; transpile?: string | ((code: string) => string); meta?: boolean | object; diff --git a/lib/ajv.js b/lib/ajv.js index 0b975a28f..611b93835 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -39,7 +39,7 @@ Ajv.$dataMetaSchema = $dataMetaSchema; var META_SCHEMA_ID = 'http://json-schema.org/draft-07/schema'; -var META_IGNORE_OPTIONS = [ 'removeAdditional', 'useDefaults', 'coerceTypes', 'invalidDefaults' ]; +var META_IGNORE_OPTIONS = [ 'removeAdditional', 'useDefaults', 'coerceTypes', 'strictDefaults' ]; var META_SUPPORT_DATA = ['/properties']; /** diff --git a/lib/dot/defaults.def b/lib/dot/defaults.def index 3e4a8de00..ec6c70fe9 100644 --- a/lib/dot/defaults.def +++ b/lib/dot/defaults.def @@ -1,10 +1,11 @@ {{## def.assignDefault: {{? it.compositeRule }} - {{? it.opts.invalidDefaults }} - {{? it.opts.invalidDefaults === 'log' }} - {{ it.logger.warn('default is ignored for: ' + $passData); }} + {{? it.opts.strictDefaults }} + {{ var $defaultMsg = 'default is ignored for: ' + $passData; }} + {{? it.opts.strictDefaults === 'log' }} + {{ it.logger.warn($defaultMsg); }} {{??}} - {{ throw new Error('default is ignored for: ' + $passData); }} + {{ throw new Error($defaultMsg); }} {{?}} {{?}} {{??}} diff --git a/lib/dot/validate.jst b/lib/dot/validate.jst index 4e05ce890..12a2fe494 100644 --- a/lib/dot/validate.jst +++ b/lib/dot/validate.jst @@ -72,11 +72,12 @@ it.dataPathArr = [undefined]; }} - {{? it.opts.invalidDefaults && it.schema.default !== undefined }} - {{? it.opts.invalidDefaults === 'log' }} - {{ it.logger.warn('default is ignored in the schema root'); }} + {{? it.schema.default !== undefined && it.opts.useDefaults && it.opts.strictDefaults }} + {{ var $defaultMsg = 'default is ignored in the schema root'; }} + {{? it.opts.strictDefaults === 'log' }} + {{ it.logger.warn($defaultMsg); }} {{??}} - {{ throw new Error('default is ignored in the schema root'); }} + {{ throw new Error($defaultMsg); }} {{?}} {{?}} diff --git a/spec/options/useDefaults.spec.js b/spec/options/useDefaults.spec.js index 4570a326c..122d778d4 100644 --- a/spec/options/useDefaults.spec.js +++ b/spec/options/useDefaults.spec.js @@ -221,9 +221,9 @@ describe('useDefaults options', function() { }); }); - describe('invalidDefaults option', function() { - it('should throw an error given an invalid default in the schema root when invalidDefaults is true', function() { - var ajv = new Ajv({useDefaults: true, invalidDefaults: true}); + describe('strictDefaults option', function() { + it('should throw an error given an ignored default in the schema root when strictDefaults is true', function() { + var ajv = new Ajv({useDefaults: true, strictDefaults: true}); var schema = { default: 5, properties: {} @@ -231,8 +231,8 @@ describe('useDefaults options', function() { should.throw(function() { ajv.compile(schema); }); }); - it('should throw an error given an invalid default in oneOf when invalidDefaults is true', function() { - var ajv = new Ajv({useDefaults: true, invalidDefaults: true}); + it('should throw an error given an ignored default in oneOf when strictDefaults is true', function() { + var ajv = new Ajv({useDefaults: true, strictDefaults: true}); var schema = { oneOf: [ { enum: ['foo', 'bar'] }, @@ -248,11 +248,11 @@ describe('useDefaults options', function() { should.throw(function() { ajv.compile(schema); }); }); - it('should log a warning given an invalid default in the schema root when invalidDefaults is "log"', function() { + it('should log a warning given an ignored default in the schema root when strictDefaults is "log"', function() { var warnArg = null; var ajv = new Ajv({ useDefaults: true, - invalidDefaults: 'log', + strictDefaults: 'log', logger: { log: function() { throw new Error('should not be called'); @@ -273,11 +273,11 @@ describe('useDefaults options', function() { should.equal(warnArg, 'default is ignored in the schema root'); }); - it('should log a warning given an invalid default in oneOf when invalidDefaults is "log"', function() { + it('should log a warning given an ignored default in oneOf when strictDefaults is "log"', function() { var warnArg = null; var ajv = new Ajv({ useDefaults: true, - invalidDefaults: 'log', + strictDefaults: 'log', logger: { log: function() { throw new Error('should not be called'); From 18268c5f38be48f1d95781e264365b193100954b Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 3 Mar 2019 09:36:26 +0000 Subject: [PATCH 225/333] additional tests for strictDefault options --- spec/options/strictDefaults.spec.js | 166 ++++++++++++++++++++++++++++ spec/options/useDefaults.spec.js | 86 -------------- 2 files changed, 166 insertions(+), 86 deletions(-) create mode 100644 spec/options/strictDefaults.spec.js diff --git a/spec/options/strictDefaults.spec.js b/spec/options/strictDefaults.spec.js new file mode 100644 index 000000000..ef48dd0d7 --- /dev/null +++ b/spec/options/strictDefaults.spec.js @@ -0,0 +1,166 @@ +'use strict'; + +var Ajv = require('../ajv'); +var getAjvInstances = require('../ajv_instances'); +var should = require('../chai').should(); + + +describe('strictDefaults option', function() { + describe('useDefaults = true', function() { + describe('strictDefaults = false', function() { + it('should NOT throw an error or log a warning given an ignored default', function() { + var output = {}; + var ajv = new Ajv({ + useDefaults: true, + strictDefaults: false, + logger: getLogger(output) + }); + var schema = { + default: 5, + properties: {} + }; + + ajv.compile(schema); + should.not.exist(output.warning); + }); + + it('should NOT throw an error or log a warning given an ignored default', function() { + var output = {}; + var ajv = new Ajv({ + useDefaults: true, + strictDefaults: false, + logger: getLogger(output) + }); + var schema = { + oneOf: [ + { enum: ['foo', 'bar'] }, + { + properties: { + foo: { + default: true + } + } + } + ] + }; + + ajv.compile(schema); + should.not.exist(output.warning); + }); + }); + + describe('strictDefaults = true', function() { + it('should throw an error given an ignored default in the schema root when strictDefaults is true', function() { + var ajv = new Ajv({useDefaults: true, strictDefaults: true}); + var schema = { + default: 5, + properties: {} + }; + should.throw(function() { ajv.compile(schema); }); + }); + + it('should throw an error given an ignored default in oneOf when strictDefaults is true', function() { + var ajv = new Ajv({useDefaults: true, strictDefaults: true}); + var schema = { + oneOf: [ + { enum: ['foo', 'bar'] }, + { + properties: { + foo: { + default: true + } + } + } + ] + }; + should.throw(function() { ajv.compile(schema); }); + }); + }); + + describe('strictDefaults = "log"', function() { + it('should log a warning given an ignored default in the schema root when strictDefaults is "log"', function() { + var output = {}; + var ajv = new Ajv({ + useDefaults: true, + strictDefaults: 'log', + logger: getLogger(output) + }); + var schema = { + default: 5, + properties: {} + }; + ajv.compile(schema); + should.equal(output.warning, 'default is ignored in the schema root'); + }); + + it('should log a warning given an ignored default in oneOf when strictDefaults is "log"', function() { + var output = {}; + var ajv = new Ajv({ + useDefaults: true, + strictDefaults: 'log', + logger: getLogger(output) + }); + var schema = { + oneOf: [ + { enum: ['foo', 'bar'] }, + { + properties: { + foo: { + default: true + } + } + } + ] + }; + ajv.compile(schema); + should.equal(output.warning, 'default is ignored for: data.foo'); + }); + }); + }); + + + describe('useDefaults = false', function() { + describe('strictDefaults = true', function() { + it('should NOT throw an error given an ignored default in the schema root when useDefaults is false', function() { + var ajv = new Ajv({useDefaults: false, strictDefaults: true}); + var schema = { + default: 5, + properties: {} + }; + should.not.throw(function() { ajv.compile(schema); }); + }); + + it('should NOT throw an error given an ignored default in oneOf when useDefaults is false', function() { + var ajv = new Ajv({useDefaults: false, strictDefaults: true}); + var schema = { + oneOf: [ + { enum: ['foo', 'bar'] }, + { + properties: { + foo: { + default: true + } + } + } + ] + }; + should.not.throw(function() { ajv.compile(schema); }); + }); + }); + }); + + + function getLogger(output) { + return { + log: function() { + throw new Error('log should not be called'); + }, + warn: function(warning) { + output.warning = warning; + }, + error: function() { + throw new Error('error should not be called'); + } + } + } +}); diff --git a/spec/options/useDefaults.spec.js b/spec/options/useDefaults.spec.js index 122d778d4..7d8329e5e 100644 --- a/spec/options/useDefaults.spec.js +++ b/spec/options/useDefaults.spec.js @@ -220,90 +220,4 @@ describe('useDefaults options', function() { }); }); }); - - describe('strictDefaults option', function() { - it('should throw an error given an ignored default in the schema root when strictDefaults is true', function() { - var ajv = new Ajv({useDefaults: true, strictDefaults: true}); - var schema = { - default: 5, - properties: {} - }; - should.throw(function() { ajv.compile(schema); }); - }); - - it('should throw an error given an ignored default in oneOf when strictDefaults is true', function() { - var ajv = new Ajv({useDefaults: true, strictDefaults: true}); - var schema = { - oneOf: [ - { enum: ['foo', 'bar'] }, - { - properties: { - foo: { - default: true - } - } - } - ] - }; - should.throw(function() { ajv.compile(schema); }); - }); - - it('should log a warning given an ignored default in the schema root when strictDefaults is "log"', function() { - var warnArg = null; - var ajv = new Ajv({ - useDefaults: true, - strictDefaults: 'log', - logger: { - log: function() { - throw new Error('should not be called'); - }, - warn: function(warning) { - warnArg = warning; - }, - error: function() { - throw new Error('should not be called'); - } - } - }); - var schema = { - default: 5, - properties: {} - }; - ajv.compile(schema); - should.equal(warnArg, 'default is ignored in the schema root'); - }); - - it('should log a warning given an ignored default in oneOf when strictDefaults is "log"', function() { - var warnArg = null; - var ajv = new Ajv({ - useDefaults: true, - strictDefaults: 'log', - logger: { - log: function() { - throw new Error('should not be called'); - }, - warn: function(warning) { - warnArg = warning; - }, - error: function() { - throw new Error('should not be called'); - } - } - }); - var schema = { - oneOf: [ - { enum: ['foo', 'bar'] }, - { - properties: { - foo: { - default: true - } - } - } - ] - }; - ajv.compile(schema); - should.equal(warnArg, 'default is ignored for: data.foo'); - }); - }); }); From 9a286893407918367a08bba6375869adac5f4ef8 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 3 Mar 2019 09:41:23 +0000 Subject: [PATCH 226/333] style: fix --- spec/options/strictDefaults.spec.js | 5 ++--- spec/options/useDefaults.spec.js | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/spec/options/strictDefaults.spec.js b/spec/options/strictDefaults.spec.js index ef48dd0d7..1f1093a69 100644 --- a/spec/options/strictDefaults.spec.js +++ b/spec/options/strictDefaults.spec.js @@ -1,7 +1,6 @@ 'use strict'; var Ajv = require('../ajv'); -var getAjvInstances = require('../ajv_instances'); var should = require('../chai').should(); @@ -43,7 +42,7 @@ describe('strictDefaults option', function() { } ] }; - + ajv.compile(schema); should.not.exist(output.warning); }); @@ -161,6 +160,6 @@ describe('strictDefaults option', function() { error: function() { throw new Error('error should not be called'); } - } + }; } }); diff --git a/spec/options/useDefaults.spec.js b/spec/options/useDefaults.spec.js index 7d8329e5e..7a12e8423 100644 --- a/spec/options/useDefaults.spec.js +++ b/spec/options/useDefaults.spec.js @@ -2,7 +2,7 @@ var Ajv = require('../ajv'); var getAjvInstances = require('../ajv_instances'); -var should = require('../chai').should(); +require('../chai').should(); describe('useDefaults options', function() { From e993bd6b4e6ca28487cdfea7aede37d141dd13d5 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 3 Mar 2019 10:49:16 +0000 Subject: [PATCH 227/333] feat: strictKeywords option to report unknown keywords, closes #781 --- README.md | 12 +++++- lib/compile/rules.js | 2 +- lib/compile/util.js | 7 +++ lib/dot/validate.jst | 11 +++++ spec/custom.spec.js | 3 ++ spec/options/strictKeywords.spec.js | 66 +++++++++++++++++++++++++++++ 6 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 spec/options/strictKeywords.spec.js diff --git a/README.md b/README.md index b61da8b13..c858efd09 100644 --- a/README.md +++ b/README.md @@ -1072,7 +1072,9 @@ Defaults: removeAdditional: false, useDefaults: false, coerceTypes: false, + // strict mode options strictDefaults: false, + strictKeywords: false, // asynchronous validation options: transpile: undefined, // requires ajv-async package // advanced options: @@ -1154,10 +1156,18 @@ Defaults: - `false` (default) - no type coercion. - `true` - coerce scalar data types. - `"array"` - in addition to coercions between scalar types, coerce scalar data to an array with one element and vice versa (as required by the schema). -- _strictDefaults_: specify behavior for ignored `default` keywords in schemas. Option values: + + +##### Strict mode options + +- _strictDefaults_: report ignored `default` keywords in schemas. Option values: - `false` (default) - ignored defaults are not reported - `true` - if an ignored default is present, throw an error - `"log"` - if an ignored default is present, log warning +- _strictKeywords_: report unknown keywords in schemas. Option values: + - `false` (default) - unknown keywords are not reported + - `true` - if an unknown keyword is present, throw an error + - `"log"` - if an unknown keyword is present, log warning ##### Asynchronous validation options diff --git a/lib/compile/rules.js b/lib/compile/rules.js index 66f196a93..08b25aeb9 100644 --- a/lib/compile/rules.js +++ b/lib/compile/rules.js @@ -20,7 +20,7 @@ module.exports = function rules() { var ALL = [ 'type', '$comment' ]; var KEYWORDS = [ - '$schema', '$id', 'id', '$data', 'title', + '$schema', '$id', 'id', '$data', '$async', 'title', 'description', 'default', 'definitions', 'examples', 'readOnly', 'writeOnly', 'contentMediaType', 'contentEncoding', diff --git a/lib/compile/util.js b/lib/compile/util.js index 263891c33..0efa00111 100644 --- a/lib/compile/util.js +++ b/lib/compile/util.js @@ -17,6 +17,7 @@ module.exports = { finalCleanUpCode: finalCleanUpCode, schemaHasRules: schemaHasRules, schemaHasRulesExcept: schemaHasRulesExcept, + schemaUnknownRules: schemaUnknownRules, toQuotedString: toQuotedString, getPathExpr: getPathExpr, getPath: getPath, @@ -183,6 +184,12 @@ function schemaHasRulesExcept(schema, rules, exceptKeyword) { } +function schemaUnknownRules(schema, rules) { + if (typeof schema == 'boolean') return; + for (var key in schema) if (!rules[key]) return key; +} + + function toQuotedString(str) { return '\'' + escapeQuotes(str) + '\''; } diff --git a/lib/dot/validate.jst b/lib/dot/validate.jst index 12a2fe494..3c92ef441 100644 --- a/lib/dot/validate.jst +++ b/lib/dot/validate.jst @@ -20,6 +20,17 @@ , $id = it.self._getId(it.schema); }} +{{ + if (it.opts.strictKeywords) { + var $unknownKwd = it.util.schemaUnknownRules(it.schema, it.RULES.keywords); + if ($unknownKwd) { + var $keywordsMsg = 'unknown keyword: ' + $unknownKwd; + if (it.opts.strictKeywords === 'log') it.logger.warn($keywordsMsg); + else throw new Error($keywordsMsg); + } + } +}} + {{? it.isTop }} var validate = {{?$async}}{{it.async = true;}}async {{?}}function(data, dataPath, parentData, parentDataProperty, rootData) { 'use strict'; diff --git a/spec/custom.spec.js b/spec/custom.spec.js index 5b1b6f03a..2924fceea 100644 --- a/spec/custom.spec.js +++ b/spec/custom.spec.js @@ -415,6 +415,7 @@ describe('Custom keywords', function () { it('should correctly expand macros in macro expansions', function() { instances.forEach(function (_ajv) { _ajv.addKeyword('range', { type: 'number', macro: macroRange }); + _ajv.addKeyword('exclusiveRange', { metaSchema: {type: 'boolean'} }); _ajv.addKeyword('myContains', { type: 'array', macro: macroContains }); var schema = { @@ -811,6 +812,7 @@ describe('Custom keywords', function () { function testRangeKeyword(definition, customErrors, numErrors) { instances.forEach(function (_ajv) { _ajv.addKeyword('x-range', definition); + _ajv.addKeyword('exclusiveRange', {metaSchema: {type: 'boolean'}}); var schema = { "x-range": [2, 4] }; var validate = _ajv.compile(schema); @@ -849,6 +851,7 @@ describe('Custom keywords', function () { function testMultipleRangeKeyword(definition, numErrors) { instances.forEach(function (_ajv) { _ajv.addKeyword('x-range', definition); + _ajv.addKeyword('exclusiveRange', {metaSchema: {type: 'boolean'}}); var schema = { "properties": { diff --git a/spec/options/strictKeywords.spec.js b/spec/options/strictKeywords.spec.js new file mode 100644 index 000000000..b212d15c7 --- /dev/null +++ b/spec/options/strictKeywords.spec.js @@ -0,0 +1,66 @@ +'use strict'; + +var Ajv = require('../ajv'); +var should = require('../chai').should(); + + +describe('strictKeywords option', function() { + describe('strictKeywords = false', function() { + it('should NOT throw an error or log a warning given an unknown keyword', function() { + var output = {}; + var ajv = new Ajv({ + strictKeywords: false, + logger: getLogger(output) + }); + var schema = { + properties: {}, + unknownKeyword: 1 + }; + + ajv.compile(schema); + should.not.exist(output.warning); + }); + }); + + describe('strictKeywords = true', function() { + it('should throw an error given an unknown keyword in the schema root when strictKeywords is true', function() { + var ajv = new Ajv({strictKeywords: true}); + var schema = { + properties: {}, + unknownKeyword: 1 + }; + should.throw(function() { ajv.compile(schema); }); + }); + }); + + describe('strictKeywords = "log"', function() { + it('should log a warning given an unknown keyword in the schema root when strictKeywords is "log"', function() { + var output = {}; + var ajv = new Ajv({ + strictKeywords: 'log', + logger: getLogger(output) + }); + var schema = { + properties: {}, + unknownKeyword: 1 + }; + ajv.compile(schema); + should.equal(output.warning, 'unknown keyword: unknownKeyword'); + }); + }); + + + function getLogger(output) { + return { + log: function() { + throw new Error('log should not be called'); + }, + warn: function(warning) { + output.warning = warning; + }, + error: function() { + throw new Error('error should not be called'); + } + }; + } +}); From 38d1acddade54dc6193c47d431880bd102336621 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 3 Mar 2019 10:54:16 +0000 Subject: [PATCH 228/333] refactor: strictDefaults option --- lib/dot/defaults.def | 15 +++++++-------- lib/dot/validate.jst | 14 ++++++-------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/lib/dot/defaults.def b/lib/dot/defaults.def index ec6c70fe9..a844cf285 100644 --- a/lib/dot/defaults.def +++ b/lib/dot/defaults.def @@ -1,13 +1,12 @@ {{## def.assignDefault: {{? it.compositeRule }} - {{? it.opts.strictDefaults }} - {{ var $defaultMsg = 'default is ignored for: ' + $passData; }} - {{? it.opts.strictDefaults === 'log' }} - {{ it.logger.warn($defaultMsg); }} - {{??}} - {{ throw new Error($defaultMsg); }} - {{?}} - {{?}} + {{ + if (it.opts.strictDefaults) { + var $defaultMsg = 'default is ignored for: ' + $passData; + if (it.opts.strictDefaults === 'log') it.logger.warn($defaultMsg); + else throw new Error($defaultMsg); + } + }} {{??}} if ({{=$passData}} === undefined {{? it.opts.useDefaults == 'empty' }} diff --git a/lib/dot/validate.jst b/lib/dot/validate.jst index 3c92ef441..f8a1edfc0 100644 --- a/lib/dot/validate.jst +++ b/lib/dot/validate.jst @@ -82,15 +82,13 @@ delete it.isTop; it.dataPathArr = [undefined]; + + if (it.schema.default !== undefined && it.opts.useDefaults && it.opts.strictDefaults) { + var $defaultMsg = 'default is ignored in the schema root'; + if (it.opts.strictDefaults === 'log') it.logger.warn($defaultMsg); + else throw new Error($defaultMsg); + } }} - {{? it.schema.default !== undefined && it.opts.useDefaults && it.opts.strictDefaults }} - {{ var $defaultMsg = 'default is ignored in the schema root'; }} - {{? it.opts.strictDefaults === 'log' }} - {{ it.logger.warn($defaultMsg); }} - {{??}} - {{ throw new Error($defaultMsg); }} - {{?}} - {{?}} var vErrors = null; {{ /* don't edit, used in replace */ }} var errors = 0; {{ /* don't edit, used in replace */ }} From 6c20483b6690af2c7eb760826f00ed6b37488cbb Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 3 Mar 2019 11:09:23 +0000 Subject: [PATCH 229/333] 6.10.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 32026c5b6..fda68f3b9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.9.2", + "version": "6.10.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From d10720734d806fd25606dafd47b64e97ee7d1e7d Mon Sep 17 00:00:00 2001 From: Romain DARY Date: Sun, 3 Mar 2019 16:46:16 +0100 Subject: [PATCH 230/333] Fix wrong json schema reference (#961) `examples` is a new keyword introduce in `draft-06`. Source : [JSON Schema Draft 6 MetaSchema](http://json-schema.org/draft-06/schema) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c858efd09..575861e55 100644 --- a/README.md +++ b/README.md @@ -227,7 +227,7 @@ JSON Schema specification defines several annotation keywords that describe sche - `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. +- `examples` (NEW in draft-06): 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". From 78a940367cd7fd392d8b8d89d646dfdc2350652d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 6 Apr 2019 20:34:35 +0100 Subject: [PATCH 231/333] update JSON-Schema-Test-Suite --- spec/JSON-Schema-Test-Suite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/JSON-Schema-Test-Suite b/spec/JSON-Schema-Test-Suite index 86f965e53..15ba997f9 160000 --- a/spec/JSON-Schema-Test-Suite +++ b/spec/JSON-Schema-Test-Suite @@ -1 +1 @@ -Subproject commit 86f965e53dda0b6c57e70ddd726243e1e061cf84 +Subproject commit 15ba997f9b937150a0ab88244d1d0fbf58526c48 From 6be5ff65f94a6a5127a404ea03f7607c7f29ebaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C6=87=CA=98=C6=81=CC=86=C4=85=C6=87=CC=81?= Date: Mon, 8 Apr 2019 18:53:01 +0200 Subject: [PATCH 232/333] fix(types): add strictKeywords to Options interface (#975) --- lib/ajv.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index 63f110a0d..8b29c5bde 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -181,6 +181,7 @@ declare namespace ajv { useDefaults?: boolean | 'shared'; coerceTypes?: boolean | 'array'; strictDefaults?: boolean | 'log'; + strictKeywords?: boolean | 'log'; async?: boolean | string; transpile?: string | ((code: string) => string); meta?: boolean | object; From bc993deceada5cc152ba0fd3b2e300012b2330a0 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Mon, 8 Apr 2019 17:53:27 +0100 Subject: [PATCH 233/333] chore(package): update karma to version 4.0.1 (#959) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fda68f3b9..24257407a 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "js-beautify": "^1.7.3", "jshint": "2.9.7", "json-schema-test": "^2.0.0", - "karma": "^3.0.0", + "karma": "^4.0.1", "karma-chrome-launcher": "^2.2.0", "karma-mocha": "^1.1.1", "karma-sauce-launcher": "^2.0.0", From ab841b462ec4baff37d2a7319cef13820b53d963 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 27 Apr 2019 10:32:39 +0100 Subject: [PATCH 234/333] fix: addKeyword and schema without ID, closes #1001 --- lib/definition_schema.js | 37 +++++++++++++++++++ lib/keyword.js | 34 +---------------- spec/JSON-Schema-Test-Suite | 2 +- ...1_addKeyword_and_schema_without_id.spec.js | 20 ++++++++++ 4 files changed, 59 insertions(+), 34 deletions(-) create mode 100644 lib/definition_schema.js create mode 100644 spec/issues/1001_addKeyword_and_schema_without_id.spec.js diff --git a/lib/definition_schema.js b/lib/definition_schema.js new file mode 100644 index 000000000..e5f6b911c --- /dev/null +++ b/lib/definition_schema.js @@ -0,0 +1,37 @@ +'use strict'; + +var metaSchema = require('./refs/json-schema-draft-07.json'); + +module.exports = { + $id: 'https://github.com/epoberezkin/ajv/blob/master/lib/definition_schema.js', + definitions: { + simpleTypes: metaSchema.definitions.simpleTypes + }, + type: 'object', + dependencies: { + schema: ['validate'], + $data: ['validate'], + statements: ['inline'], + valid: {not: {required: ['macro']}} + }, + properties: { + type: metaSchema.properties.type, + schema: {type: 'boolean'}, + statements: {type: 'boolean'}, + dependencies: { + type: 'array', + items: {type: 'string'} + }, + metaSchema: {type: 'object'}, + modifying: {type: 'boolean'}, + valid: {type: 'boolean'}, + $data: {type: 'boolean'}, + async: {type: 'boolean'}, + errors: { + anyOf: [ + {type: 'boolean'}, + {const: 'full'} + ] + } + } +}; diff --git a/lib/keyword.js b/lib/keyword.js index cf4d699b3..5fec19a67 100644 --- a/lib/keyword.js +++ b/lib/keyword.js @@ -2,7 +2,7 @@ var IDENTIFIER = /^[a-z_$][a-z0-9_$-]*$/i; var customRuleCode = require('./dotjs/custom'); -var metaSchema = require('./refs/json-schema-draft-07.json'); +var definitionSchema = require('./definition_schema'); module.exports = { add: addKeyword, @@ -11,38 +11,6 @@ module.exports = { validate: validateKeyword }; -var definitionSchema = { - definitions: { - simpleTypes: metaSchema.definitions.simpleTypes - }, - type: 'object', - dependencies: { - schema: ['validate'], - $data: ['validate'], - statements: ['inline'], - valid: {not: {required: ['macro']}} - }, - properties: { - type: metaSchema.properties.type, - schema: {type: 'boolean'}, - statements: {type: 'boolean'}, - dependencies: { - type: 'array', - items: {type: 'string'} - }, - metaSchema: {type: 'object'}, - modifying: {type: 'boolean'}, - valid: {type: 'boolean'}, - $data: {type: 'boolean'}, - async: {type: 'boolean'}, - errors: { - anyOf: [ - {type: 'boolean'}, - {const: 'full'} - ] - } - } -}; /** * Define custom keyword diff --git a/spec/JSON-Schema-Test-Suite b/spec/JSON-Schema-Test-Suite index 15ba997f9..eadeacb04 160000 --- a/spec/JSON-Schema-Test-Suite +++ b/spec/JSON-Schema-Test-Suite @@ -1 +1 @@ -Subproject commit 15ba997f9b937150a0ab88244d1d0fbf58526c48 +Subproject commit eadeacb04209a18fc81f1a1959e83eef72dcc97a diff --git a/spec/issues/1001_addKeyword_and_schema_without_id.spec.js b/spec/issues/1001_addKeyword_and_schema_without_id.spec.js new file mode 100644 index 000000000..bc3d0d7d0 --- /dev/null +++ b/spec/issues/1001_addKeyword_and_schema_without_id.spec.js @@ -0,0 +1,20 @@ +'use strict'; + +var Ajv = require('../ajv'); +require('../chai').should(); + + +describe('issue #1001: addKeyword breaks schema without ID', function() { + it('should allow using schemas without ID with addKeyword', function() { + var schema = { + definitions: { + foo: {} + } + }; + + var ajv = new Ajv(); + ajv.addSchema(schema); + ajv.addKeyword('myKeyword', {}); + ajv.getSchema('#/definitions/foo') .should.be.a('function'); + }); +}); From c3093bbd6a587024d551dc43e996d22e0ecde04e Mon Sep 17 00:00:00 2001 From: vanessa <32312712+vlbee@users.noreply.github.com> Date: Fri, 5 Jul 2019 22:22:00 +0100 Subject: [PATCH 235/333] Add "empty" to useDefaults Option type definition (#1020) --- lib/ajv.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index 8b29c5bde..5e7cfa7da 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -178,7 +178,7 @@ declare namespace ajv { extendRefs?: true | 'ignore' | 'fail'; loadSchema?: (uri: string, cb?: (err: Error, schema: object) => void) => PromiseLike; removeAdditional?: boolean | 'all' | 'failing'; - useDefaults?: boolean | 'shared'; + useDefaults?: boolean | 'empty' | 'shared'; coerceTypes?: boolean | 'array'; strictDefaults?: boolean | 'log'; strictKeywords?: boolean | 'log'; From 120d746154f1200d2c2a8c6c643b35691d01ea64 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2019 22:23:00 +0100 Subject: [PATCH 236/333] chore(package): update nyc to version 14.0.0 (#994) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 24257407a..190993f2f 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "karma-mocha": "^1.1.1", "karma-sauce-launcher": "^2.0.0", "mocha": "^6.0.0", - "nyc": "^13.2.0", + "nyc": "^14.0.0", "pre-commit": "^1.1.1", "require-globify": "^1.3.0", "typescript": "^2.8.3", From 3ca7571330ae1d772074e07d00500deef2173061 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 6 Jul 2019 17:51:19 +0100 Subject: [PATCH 237/333] chore: update jshint --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 190993f2f..78a21581d 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "glob": "^7.0.0", "if-node-version": "^1.0.0", "js-beautify": "^1.7.3", - "jshint": "2.9.7", + "jshint": "^2.10.2", "json-schema-test": "^2.0.0", "karma": "^4.0.1", "karma-chrome-launcher": "^2.2.0", From c468632d9c5f98d514938f8c33c76cb7ebd9c0f8 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 6 Jul 2019 17:55:14 +0100 Subject: [PATCH 238/333] test: update node.js versions for travis test --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c0cfe4295..0f25c1418 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,10 @@ before_script: - git submodule update --init - npm install -g codeclimate-test-reporter node_js: - - "6" - "8" - - "9" - "10" + - "11" + - "12" after_script: - codeclimate-test-reporter < coverage/lcov.info - coveralls < coverage/lcov.info From d4765343af76483bd6b6acfdd4206a39b16dc8ba Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Sat, 6 Jul 2019 18:06:53 +0100 Subject: [PATCH 239/333] chore(package): update eslint to version 6.0.0 (#1030) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 78a21581d..9dadb79b7 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "coveralls": "^3.0.1", "del-cli": "^1.1.0", "dot": "^1.0.3", - "eslint": "^5.0.0", + "eslint": "^6.0.0", "gh-pages-generator": "^0.2.3", "glob": "^7.0.0", "if-node-version": "^1.0.0", From 66c2907470d8a84c22af4d4755fa2bd9fd4b8dab Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Sat, 6 Jul 2019 18:12:46 +0100 Subject: [PATCH 240/333] chore(package): update del-cli to version 2.0.0 (#1014) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9dadb79b7..670f8df7d 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "browserify": "^16.2.0", "chai": "^4.0.1", "coveralls": "^3.0.1", - "del-cli": "^1.1.0", + "del-cli": "^2.0.0", "dot": "^1.0.3", "eslint": "^6.0.0", "gh-pages-generator": "^0.2.3", From 8b59052aa517d51c763e5eb8fef51487c7042a91 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 6 Jul 2019 19:02:54 +0100 Subject: [PATCH 241/333] 6.10.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 670f8df7d..bafebbaf3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.10.0", + "version": "6.10.1", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From dd827d1c01fd684aa6efa3e52eebc315a6128335 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 6 Jul 2019 19:55:10 +0100 Subject: [PATCH 242/333] label comments (for labelcious) --- .github/ISSUE_TEMPLATE/bug-or-error-report.md | 2 +- .github/config.yml | 28 +++++++++++++++++++ CONTRIBUTING.md | 6 ++-- 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 .github/config.yml diff --git a/.github/ISSUE_TEMPLATE/bug-or-error-report.md b/.github/ISSUE_TEMPLATE/bug-or-error-report.md index 3da68b71e..42c351946 100644 --- a/.github/ISSUE_TEMPLATE/bug-or-error-report.md +++ b/.github/ISSUE_TEMPLATE/bug-or-error-report.md @@ -2,7 +2,7 @@ name: Bug or error report about: Please use for issues related to incorrect validation behaviour title: '' -labels: '' +labels: 'bug report' assignees: '' --- diff --git a/.github/config.yml b/.github/config.yml new file mode 100644 index 000000000..60b3fab95 --- /dev/null +++ b/.github/config.yml @@ -0,0 +1,28 @@ +# Please supply comments to be used for GitHub labels +githubLabels: + bug: > + Bug confirmed - to be fixed. PR is welcome! + +# duplicate: > +# enhancement: > +# good first issue: > +# help wanted: > +# invalid: > +# question: > +# wont fix: > + + bug report: > + Thank you for the report! If you didn't post a code sample to RunKit yet, + please clone this notebook https://runkit.com/esp/ajv-issue, + post the code sample that demonstrates the bug and post the link here. + It will speed up the investigation and fixing! + + json schema: > + This question is about the usage of JSON Schema specification - it is not specific to Ajv. + Please use JSON Schema reference materials or [submit the question to Stack Overflow](https://stackoverflow.com/questions/ask?tags=jsonschema,ajv). + + - [JSON Schema specification](http://json-schema.org/) + - [Tutorial by Space Telescope Science Institute](http://json-schema.org/understanding-json-schema/) + - [validation keywords](https://github.com/epoberezkin/ajv#validation-keywords) (in Ajv docs) + - [combining schemas](https://github.com/epoberezkin/ajv#ref) (in Ajv docs) + - [Tutorial by @epoberezkin](https://code.tutsplus.com/tutorials/validating-data-with-json-schema-part-1--cms-25343) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 897f63485..eade3b876 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,7 +39,9 @@ Please make sure to include the following information in the issue: 5. Validation result, data AFTER validation, error messages. 6. What results did you expect? -[Create bug report](https://github.com/epoberezkin/ajv/issues/new). +Please include the link to the working code sample at Runkit.com (please clone https://runkit.com/esp/ajv-issue) - it will speed up investigation and fixing. + +[Create bug report](https://github.com/epoberezkin/ajv/issues/new?template=bug-or-error-report.md). #### Change proposals @@ -99,7 +101,7 @@ If nothing helps, please submit: 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. +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](https://stackoverflow.com/questions/ask?tags=jsonschema,ajv) (my account is [esp](https://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. #### Ajv usage questions From 482d2c51df60a3d956bdfedff8f4329c3b91035c Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 6 Jul 2019 20:03:57 +0100 Subject: [PATCH 243/333] labels config --- .github/config.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/config.yml b/.github/config.yml index 60b3fab95..ce9efb997 100644 --- a/.github/config.yml +++ b/.github/config.yml @@ -22,7 +22,11 @@ githubLabels: Please use JSON Schema reference materials or [submit the question to Stack Overflow](https://stackoverflow.com/questions/ask?tags=jsonschema,ajv). - [JSON Schema specification](http://json-schema.org/) + - [Tutorial by Space Telescope Science Institute](http://json-schema.org/understanding-json-schema/) + - [validation keywords](https://github.com/epoberezkin/ajv#validation-keywords) (in Ajv docs) + - [combining schemas](https://github.com/epoberezkin/ajv#ref) (in Ajv docs) + - [Tutorial by @epoberezkin](https://code.tutsplus.com/tutorials/validating-data-with-json-schema-part-1--cms-25343) From d9d6fba7570763d579b29f3c650276e3120f04ed Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Tue, 9 Jul 2019 15:29:55 +0100 Subject: [PATCH 244/333] Create FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..bf2439069 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +tidelift: "npm/ajv" From 69802d2de34394e857b026daf1e47c7a22997239 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Wed, 10 Jul 2019 14:00:14 +0100 Subject: [PATCH 245/333] security contact --- .github/ISSUE_TEMPLATE.md | 1 + CONTRIBUTING.md | 10 ++++++++++ README.md | 13 ++++++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index bac24aca4..ef1c8f934 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -3,6 +3,7 @@ Frequently Asked Questions: https://github.com/epoberezkin/ajv/blob/master/FAQ.m Please provide all info and reduce your schema and data to the smallest possible size. This template is for bug or error reports. For other issues please use: +- security vulnerability: https://tidelift.com/security) - a new feature/improvement: http://epoberezkin.github.io/ajv/contribute.html#changes - browser/compatibility issues: http://epoberezkin.github.io/ajv/contribute.html#compatibility - JSON-Schema standard: http://epoberezkin.github.io/ajv/contribute.html#json-schema diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eade3b876..c509accca 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,6 +5,7 @@ Thank you for your help making Ajv better! Every contribution is appreciated. If - [Documentation](#documentation) - [Issues](#issues) - [Bug reports](#bug-reports) + - [Security vulnerabilities](#security-vulnerabilities) - [Change proposals](#changes) - [Browser and compatibility issues](#compatibility) - [Installation and dependency issues](#installation) @@ -44,6 +45,15 @@ Please include the link to the working code sample at Runkit.com (please clone h [Create bug report](https://github.com/epoberezkin/ajv/issues/new?template=bug-or-error-report.md). +#### Security vulnerabilities + +To report a security vulnerability, please use the +[Tidelift security contact](https://tidelift.com/security). +Tidelift will coordinate the fix and disclosure. + +Please do NOT report security vulnerability via GitHub issues. + + #### Change proposals [Create a proposal](https://github.com/epoberezkin/ajv/issues/new?template=change.md) for a new feature, option or some other improvement. diff --git a/README.md b/README.md index 575861e55..eedb2a9a8 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,11 @@ ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); - [Defining custom keywords](#defining-custom-keywords) - [Asynchronous schema compilation](#asynchronous-schema-compilation) - [Asynchronous validation](#asynchronous-validation) - - [Security considerations](#security-considerations) +- [Security considerations](#security-considerations) + - [Security contact](#security-contact) + - [Untrusted schemas](#untrusted-schemas) + - [Circular references in objects](#circular-references-in-javascript-objects) + - [Trusted schemas](#security-risks-of-trusted-schemas) - Modifying data during validation - [Filtering data](#filtering-data) - [Assigning defaults](#assigning-defaults) @@ -611,6 +615,13 @@ See [Options](#options). JSON Schema, if properly used, can replace data sanitisation. It doesn't replace other API security considerations. It also introduces additional security aspects to consider. +##### Security contact + +To report a security vulnerability, please use the +[Tidelift security contact](https://tidelift.com/security). +Tidelift will coordinate the fix and disclosure. Please do NOT report security vulnerability via GitHub issues. + + ##### Untrusted schemas Ajv treats JSON schemas as trusted as your application code. This security model is based on the most common use case, when the schemas are static and bundled together with the application. From d289c38567ed278f516f177d65c69bae7634b07c Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Wed, 10 Jul 2019 22:09:20 +0100 Subject: [PATCH 246/333] Tidelift subscription --- CONTRIBUTING.md | 2 +- README.md | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c509accca..26f4bf029 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,7 +51,7 @@ To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. -Please do NOT report security vulnerability via GitHub issues. +Please do NOT report security vulnerabilities via GitHub issues. #### Change proposals diff --git a/README.md b/README.md index eedb2a9a8..1fea1e4f6 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ The fastest JSON Schema validator for Node.js and browser. Supports draft-04/06/ [![Greenkeeper badge](https://badges.greenkeeper.io/epoberezkin/ajv.svg)](https://greenkeeper.io/) [![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv) -### _Ajv and [related repositories](#related-packages) will be transfered to [ajv-validator](https://github.com/ajv-validator) org_ ## Using version 6 @@ -69,7 +68,7 @@ ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); - [Plugins](#plugins) - [Related packages](#related-packages) - [Some packages using Ajv](#some-packages-using-ajv) -- [Tests, Contributing, History, License](#tests) +- [Tests, Contributing, History, Support, License](#tests) ## Performance @@ -619,7 +618,7 @@ JSON Schema, if properly used, can replace data sanitisation. It doesn't replace To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). -Tidelift will coordinate the fix and disclosure. Please do NOT report security vulnerability via GitHub issues. +Tidelift will coordinate the fix and disclosure. Please do NOT report security vulnerabilities via GitHub issues. ##### Untrusted schemas @@ -1350,6 +1349,11 @@ __Please note__: [Changes in version 6.0.0](https://github.com/epoberezkin/ajv/r [Version 2.0.0](https://github.com/epoberezkin/ajv/releases/tag/2.0.0). +## Open-source software support + +Ajv is a part of [Tidelift subscription]((https://tidelift.com/subscription/pkg/npm-ajv?utm_source=npm-ajv&utm_medium=referral&utm_campaign=readme)) - it provides a centralised support to open-source software users, in addition to the support provided by software maintainers. + + ## License [MIT](https://github.com/epoberezkin/ajv/blob/master/LICENSE) From 669bf96c79a1aa425b6cc42dcb10c8c62b8869c9 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Wed, 10 Jul 2019 22:10:22 +0100 Subject: [PATCH 247/333] readme: fix link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1fea1e4f6..cafbd71c9 100644 --- a/README.md +++ b/README.md @@ -1351,7 +1351,7 @@ __Please note__: [Changes in version 6.0.0](https://github.com/epoberezkin/ajv/r ## Open-source software support -Ajv is a part of [Tidelift subscription]((https://tidelift.com/subscription/pkg/npm-ajv?utm_source=npm-ajv&utm_medium=referral&utm_campaign=readme)) - it provides a centralised support to open-source software users, in addition to the support provided by software maintainers. +Ajv is a part of [Tidelift subscription](https://tidelift.com/subscription/pkg/npm-ajv?utm_source=npm-ajv&utm_medium=referral&utm_campaign=readme) - it provides a centralised support to open-source software users, in addition to the support provided by software maintainers. ## License From 53b5682cb7a28f686b05842fa1ea3dea71fea870 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 13 Jul 2019 21:38:44 +0100 Subject: [PATCH 248/333] fix: the unknown keyword in the schema without known keywords inside compound schema (e.g. anyOf) is ignored with strictKeywords option --- lib/dot/definitions.def | 4 +++- spec/options/strictKeywords.spec.js | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/dot/definitions.def b/lib/dot/definitions.def index cdbe140bb..b68e064e8 100644 --- a/lib/dot/definitions.def +++ b/lib/dot/definitions.def @@ -63,7 +63,9 @@ {{## def.nonEmptySchema:_schema: - it.util.schemaHasRules(_schema, it.RULES.all) + (it.opts.strictKeywords + ? typeof _schema == 'object' && Object.keys(_schema).length > 0 + : it.util.schemaHasRules(_schema, it.RULES.all)) #}} diff --git a/spec/options/strictKeywords.spec.js b/spec/options/strictKeywords.spec.js index b212d15c7..4895b78e4 100644 --- a/spec/options/strictKeywords.spec.js +++ b/spec/options/strictKeywords.spec.js @@ -49,6 +49,19 @@ describe('strictKeywords option', function() { }); }); + describe('unknown keyword inside schema that has no known keyword in compound keyword', function() { + it('should throw an error given an unknown keyword when strictKeywords is true', function() { + var ajv = new Ajv({strictKeywords: true}); + var schema = { + anyOf: [ + { + unknownKeyword: 1 + } + ] + }; + should.throw(function() { ajv.compile(schema); }); + }); + }); function getLogger(output) { return { From 6e4a3464b935053c0a5b65fa27db454367d23b2b Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 14 Jul 2019 14:38:33 +0100 Subject: [PATCH 249/333] 6.10.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bafebbaf3..ca9f7c93f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.10.1", + "version": "6.10.2", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 098e1d2a25094eb3dfa0481a6fb73f4330a6b5c0 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sun, 14 Jul 2019 18:32:12 +0100 Subject: [PATCH 250/333] Update FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index bf2439069..c77c8e485 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1,2 @@ tidelift: "npm/ajv" +open_collective: "ajv" From 1163e1a0891491dd63b213705bf3ac02e2c486b6 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Fri, 19 Jul 2019 06:35:08 +0100 Subject: [PATCH 251/333] docs: regexp considerations (#1047) --- README.md | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cafbd71c9..1dc099d73 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); - [Untrusted schemas](#untrusted-schemas) - [Circular references in objects](#circular-references-in-javascript-objects) - [Trusted schemas](#security-risks-of-trusted-schemas) + - [ReDoS attack](#redos-attack) - Modifying data during validation - [Filtering data](#filtering-data) - [Assigning defaults](#assigning-defaults) @@ -240,7 +241,11 @@ __Please note__: Ajv does not implement validation of the keywords `examples`, ## Formats -The following formats are supported for string validation with "format" keyword: +Ajv implements formats defined by JSON Schema specification and several other formats. It is recommended NOT to use "format" keyword implementations with untrusted data, as they use potentially unsafe regular expressions - see [ReDoS attack](#redos-attack). + +__Please note__: if you need to use "format" keyword to validate untrusted data, you MUST assess their suitability and safety for your validation scenarios. + +The following formats are implemented for string validation with "format" keyword: - _date_: full-date according to [RFC3339](http://tools.ietf.org/html/rfc3339#section-5.6). - _time_: time with optional time-zone. @@ -646,9 +651,9 @@ An attempt to compile such schemas or validate such data would cause stack overf Some keywords in JSON Schemas can lead to very slow validation for certain data. These keywords include (but may be not limited to): -- `pattern` and `format` for large strings - use `maxLength` to mitigate +- `pattern` and `format` for large strings - in some cases using `maxLength` can help mitigate it, but certain regular expressions can lead to exponential validation time even with relatively short strings (see [ReDoS attack](#redos-attack)). +- `patternProperties` for large property names - use `propertyNames` to mitigate, but some regular expressions can have exponential evaluation time as well. - `uniqueItems` for large non-scalar arrays - use `maxItems` to mitigate -- `patternProperties` for large property names - use `propertyNames` to mitigate __Please note__: The suggestions above to prevent slow validation would only work if you do NOT use `allErrors: true` in production code (using it would continue validation after validation errors). @@ -660,13 +665,29 @@ const isSchemaSecure = ajv.compile(require('ajv/lib/refs/json-schema-secure.json const schema1 = {format: 'email'}; isSchemaSecure(schema1); // false -const schema2 = {format: 'email', maxLength: 256}; +const schema2 = {format: 'email', maxLength: MAX_LENGTH}; isSchemaSecure(schema2); // true ``` __Please note__: following all these recommendation is not a guarantee that validation of untrusted data is safe - it can still lead to some undesirable results. +## ReDoS attack + +Certain regular expressions can lead to the exponential evaluation time even with relatively short strings. + +Please assess the regular expressions you use in the schemas on their vulnerability to this attack - see [safe-regex](https://github.com/substack/safe-regex), for example. + +__Please note__: some formats that Ajv implements use [regular expressions](https://github.com/epoberezkin/ajv/blob/master/lib/compile/formats.js) that can be vulnerable to ReDoS attack, so if you use Ajv to validate data from untrusted sources __it is strongly recommended__ to consider the following: + +- making assessment of "format" implementations in Ajv. +- using `format: 'fast'` option that simplifies some of the regular expressions (although it does not guarantee that they are safe). +- replacing format implementations provided by Ajv with your own implementations of "format" keyword that either uses different regular expressions or another approach to format validation. +- disabling format validation by ignoring "format" keyword with option `format: false` + +Whatever mitigation you choose, please assume all formats provided by Ajv as potentially unsafe and make your own assessment of their suitability for your validation scenarios. + + ## Filtering data With [option `removeAdditional`](#options) (added by [andyscott](https://github.com/andyscott)) you can filter data during the validation. From db94b118d41fbc81c3d7ab605b94c4d15dc89af8 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 19 Jul 2019 06:39:20 +0100 Subject: [PATCH 252/333] docs: regexp considerations addition --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1dc099d73..d645c3f70 100644 --- a/README.md +++ b/README.md @@ -682,7 +682,7 @@ __Please note__: some formats that Ajv implements use [regular expressions](http - making assessment of "format" implementations in Ajv. - using `format: 'fast'` option that simplifies some of the regular expressions (although it does not guarantee that they are safe). -- replacing format implementations provided by Ajv with your own implementations of "format" keyword that either uses different regular expressions or another approach to format validation. +- replacing format implementations provided by Ajv with your own implementations of "format" keyword that either uses different regular expressions or another approach to format validation. Please see [addFormat](#api-addformat) method. - disabling format validation by ignoring "format" keyword with option `format: false` Whatever mitigation you choose, please assume all formats provided by Ajv as potentially unsafe and make your own assessment of their suitability for your validation scenarios. From 82112b089a079f3d889b02eddf0144cc7d2330a3 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2019 21:12:20 +0100 Subject: [PATCH 253/333] chore(package): update karma-chrome-launcher to version 3.0.0 (#1043) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ca9f7c93f..5c3dfc746 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "jshint": "^2.10.2", "json-schema-test": "^2.0.0", "karma": "^4.0.1", - "karma-chrome-launcher": "^2.2.0", + "karma-chrome-launcher": "^3.0.0", "karma-mocha": "^1.1.1", "karma-sauce-launcher": "^2.0.0", "mocha": "^6.0.0", From 382c2b9ed151287e4ff0f233d689563f90cd5c20 Mon Sep 17 00:00:00 2001 From: Christian Pillsbury Date: Thu, 15 Aug 2019 11:30:07 -0500 Subject: [PATCH 254/333] Issue #1061 - Update time and date-time format definitions to support two digit and colon-less variants of timezone offset. Add tests. Update test eslint to include global after function. --- lib/compile/formats.js | 6 +- spec/.eslintrc.yml | 1 + .../issues/1061_alternative_time_offsets.json | 74 +++++++++++++++++++ 3 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 spec/tests/issues/1061_alternative_time_offsets.json diff --git a/lib/compile/formats.js b/lib/compile/formats.js index 5c0fb9ef5..d06792aa4 100644 --- a/lib/compile/formats.js +++ b/lib/compile/formats.js @@ -4,7 +4,7 @@ var util = require('./util'); 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 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; var URIREF = /^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i; @@ -33,8 +33,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|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, + 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#][^\s#]*)?(?:#[^\\\s]*)?$/i, diff --git a/spec/.eslintrc.yml b/spec/.eslintrc.yml index f9c66d538..543e6eec9 100644 --- a/spec/.eslintrc.yml +++ b/spec/.eslintrc.yml @@ -7,5 +7,6 @@ globals: describe: false it: false before: false + after: false beforeEach: false afterEach: false diff --git a/spec/tests/issues/1061_alternative_time_offsets.json b/spec/tests/issues/1061_alternative_time_offsets.json new file mode 100644 index 000000000..bf80f9581 --- /dev/null +++ b/spec/tests/issues/1061_alternative_time_offsets.json @@ -0,0 +1,74 @@ +[ + { + "description": "Support for alternative ISO-8601 timezone offset formats (#1061)", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "valid positiive two digit", + "data": "2016-01-31T02:31:17+01", + "valid": true + }, + { + "description": "valid negative two digit", + "data": "2016-01-31T02:31:17-01", + "valid": true + }, + { + "description": "valid positiive four digit no colon", + "data": "2016-01-31T02:31:17+0130", + "valid": true + }, + { + "description": "valid negative four digit no colon", + "data": "2016-01-31T02:31:17-0130", + "valid": true + }, + { + "description": "invalid positiive three digit no colon", + "data": "2016-01-31T02:31:17+013", + "valid": false + }, + { + "description": "invalid negative three digit no colon", + "data": "2016-01-31T02:31:17-013", + "valid": false + } + ] + }, + { + "description": "Support for alternative ISO-8601 timezone offset formats (#1061)", + "schema": {"format": "time"}, + "tests": [ + { + "description": "valid positiive two digit", + "data": "02:31:17+01", + "valid": true + }, + { + "description": "valid negative two digit", + "data": "02:31:17-01", + "valid": true + }, + { + "description": "valid positiive four digit no colon", + "data": "02:31:17+0130", + "valid": true + }, + { + "description": "valid negative four digit no colon", + "data": "02:31:17-0130", + "valid": true + }, + { + "description": "invalid positiive three digit no colon", + "data": "02:31:17+013", + "valid": false + }, + { + "description": "invalid negative three digit no colon", + "data": "02:31:17-013", + "valid": false + } + ] + } +] From 608545c0d24ff45477ebede06e0fba0e188396fe Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Wed, 11 Sep 2019 14:36:21 +0100 Subject: [PATCH 255/333] revert eslint change --- spec/.eslintrc.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/.eslintrc.yml b/spec/.eslintrc.yml index 543e6eec9..f9c66d538 100644 --- a/spec/.eslintrc.yml +++ b/spec/.eslintrc.yml @@ -7,6 +7,5 @@ globals: describe: false it: false before: false - after: false beforeEach: false afterEach: false From 7f6ae8c4a2a057c3dfb924d1d46ded3c7758e56b Mon Sep 17 00:00:00 2001 From: Jess Date: Tue, 17 Sep 2019 17:53:41 -0700 Subject: [PATCH 256/333] Added financial contributors to the README --- README.md | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d645c3f70..be013ebb0 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ The fastest JSON Schema validator for Node.js and browser. Supports draft-04/06/07. [![Build Status](https://travis-ci.org/epoberezkin/ajv.svg?branch=master)](https://travis-ci.org/epoberezkin/ajv) -[![npm](https://img.shields.io/npm/v/ajv.svg)](https://www.npmjs.com/package/ajv) +[![Financial Contributors on Open Collective](https://opencollective.com/ajv/all/badge.svg?label=financial+contributors)](https://opencollective.com/ajv) [![npm](https://img.shields.io/npm/v/ajv.svg)](https://www.npmjs.com/package/ajv) [![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) [![Greenkeeper badge](https://badges.greenkeeper.io/epoberezkin/ajv.svg)](https://greenkeeper.io/) @@ -1375,6 +1375,36 @@ __Please note__: [Changes in version 6.0.0](https://github.com/epoberezkin/ajv/r Ajv is a part of [Tidelift subscription](https://tidelift.com/subscription/pkg/npm-ajv?utm_source=npm-ajv&utm_medium=referral&utm_campaign=readme) - it provides a centralised support to open-source software users, in addition to the support provided by software maintainers. +## Contributors + +### Code Contributors + +This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. + + +### Financial Contributors + +Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/ajv/contribute)] + +#### Individuals + + + +#### Organizations + +Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/ajv/contribute)] + + + + + + + + + + + + ## License [MIT](https://github.com/epoberezkin/ajv/blob/master/LICENSE) From 68b00801222c2fe2d77709929a3558bac4562f60 Mon Sep 17 00:00:00 2001 From: Jess Date: Tue, 17 Sep 2019 17:53:41 -0700 Subject: [PATCH 257/333] Added call to donate after npm install (optional) --- package.json | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5c3dfc746..c2f5240d8 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,8 @@ "test-all": "npm run test-ts && npm run test-cov && if-node-version 10 npm run test-browser", "test": "npm run lint && npm run build && npm run test-all", "prepublish": "npm run build && npm run bundle", - "watch": "watch \"npm run build\" ./lib/dot" + "watch": "watch \"npm run build\" ./lib/dot", + "postinstall": "opencollective-postinstall || true" }, "nyc": { "exclude": [ @@ -65,6 +66,7 @@ "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", + "opencollective-postinstall": "^2.0.2", "uri-js": "^4.2.2" }, "devDependencies": { @@ -94,5 +96,9 @@ "typescript": "^2.8.3", "uglify-js": "^3.3.24", "watch": "^1.0.0" + }, + "collective": { + "type": "opencollective", + "url": "https://opencollective.com/ajv" } -} +} \ No newline at end of file From 7b574e764b4b9065308c6b98d948a7e3e16fa19a Mon Sep 17 00:00:00 2001 From: Dominik Broj <19861998+thetric@users.noreply.github.com> Date: Tue, 24 Sep 2019 10:12:16 +0200 Subject: [PATCH 258/333] docs(readme): add usage notes for TypeScript users --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index d645c3f70..845481207 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,11 @@ Try it in the Node.js REPL: https://tonicdev.com/npm/ajv The fastest validation call: ```javascript +// Node.js require: var Ajv = require('ajv'); +// or ESM/TypeScript import +import Ajv from 'ajv'; + var ajv = new Ajv(); // options can be passed, e.g. {allErrors: true} var validate = ajv.compile(schema); var valid = validate(data); @@ -165,6 +169,10 @@ The best performance is achieved when using compiled functions returned by `comp __Please note__: every time a validation function or `ajv.validate` are called `errors` property is overwritten. You need to copy `errors` array reference to another variable if you want to use it later (e.g., in the callback). See [Validation errors](#validation-errors) +__Note for TypeScript users__: `ajv` provides its own TypeScript declarations +out of the box, so you don't need to install the deprecated `@types/ajv` +module. + ## Using in browser From 4e240b78f2387577f860e6af3c2c9f0ae6e8ba31 Mon Sep 17 00:00:00 2001 From: cjancsar <51799534+cjancsar@users.noreply.github.com> Date: Tue, 29 Oct 2019 13:50:09 -0400 Subject: [PATCH 259/333] Update FAQ.md (#1115) - Fix type "sence" to "sense" --- FAQ.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FAQ.md b/FAQ.md index 472a5edb0..36ac967cd 100644 --- a/FAQ.md +++ b/FAQ.md @@ -83,7 +83,7 @@ There are several ways to implement the described logic that would allow two pro ##### Why the validation fails when I use option `removeAdditional` with the keyword `anyOf`/etc.? -This problem is related to the problem explained above - properties treated as additional in the sence of `additionalProperties` keyword, based on `properties`/`patternProperties` keyword in the same schema object. +This problem is related to the problem explained above - properties treated as additional in the sense of `additionalProperties` keyword, based on `properties`/`patternProperties` keyword in the same schema object. See the exemple in [Filtering Data](https://github.com/epoberezkin/ajv#filtering-data) section of readme. From 25266ef7c277754225333de33f2ee56556f3c377 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sun, 24 Nov 2019 10:57:22 +0000 Subject: [PATCH 260/333] docs: typescript issue template --- .github/ISSUE_TEMPLATE/typescript.md | 42 ++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/typescript.md diff --git a/.github/ISSUE_TEMPLATE/typescript.md b/.github/ISSUE_TEMPLATE/typescript.md new file mode 100644 index 000000000..7385349b0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/typescript.md @@ -0,0 +1,42 @@ +--- +name: Missing or incorrect type definition +about: Please use for issues related to typescript types +title: '' +labels: 'typescript' +assignees: '' + +--- + + + +**What version of Ajv are you using? Does the issue happen if you use the latest version?** + + +**Your typescript code** + + + +```typescript + + +``` + + +**Typescript compiler error messages** + +``` + + +``` + +**Describe the change that should be made to address the issue?** + + +**Are you going to resolve the issue?** From 4e56bf6268aea25c6632ba52ea602d3ff7ee641d Mon Sep 17 00:00:00 2001 From: Leonardo Villela Date: Sun, 24 Nov 2019 11:01:27 +0000 Subject: [PATCH 261/333] Add dataPathArr to CompilationContext interface type definition (#1114) --- lib/ajv.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index 5e7cfa7da..ba75f8d2d 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -243,6 +243,7 @@ declare namespace ajv { interface CompilationContext { level: number; dataLevel: number; + dataPathArr: string[]; schema: any; schemaPath: string; baseId: string; From 0e542bdcd6cc12c98513cf475928520fae32a00d Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Sun, 24 Nov 2019 11:09:54 +0000 Subject: [PATCH 262/333] chore(package): update del-cli to version 3.0.0 (#1084) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5c3dfc746..b517e6e8a 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "browserify": "^16.2.0", "chai": "^4.0.1", "coveralls": "^3.0.1", - "del-cli": "^2.0.0", + "del-cli": "^3.0.0", "dot": "^1.0.3", "eslint": "^6.0.0", "gh-pages-generator": "^0.2.3", From 8c4671473cf1b92c609bb01aad981d632b6d7066 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sun, 24 Nov 2019 11:40:48 +0000 Subject: [PATCH 263/333] chore(package): update uglify-js to version 3.6.9 (#1125) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b517e6e8a..54eec94c0 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "pre-commit": "^1.1.1", "require-globify": "^1.3.0", "typescript": "^2.8.3", - "uglify-js": "^3.3.24", + "uglify-js": "^3.6.9", "watch": "^1.0.0" } } From d705f8e1e55d719cdc6c70ef89fd574616ce0b84 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 14 Dec 2019 16:21:22 +0000 Subject: [PATCH 264/333] fix(package): update fast-deep-equal to version 3.1.1 (#1135) Closes #1129 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 54eec94c0..6f15e2dd0 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "homepage": "https://github.com/epoberezkin/ajv", "tonicExampleFilename": ".tonic_example.js", "dependencies": { - "fast-deep-equal": "^2.0.1", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" From f7beda09e7bf76b3d3e8f631cba8a9577fdc344a Mon Sep 17 00:00:00 2001 From: sambauers Date: Wed, 8 Jan 2020 10:59:21 +1100 Subject: [PATCH 265/333] Improved hostname validation. Allow trailing dot. Check for octet count rather than string length. --- lib/compile/formats.js | 13 ++++++++++-- spec/tests/rules/format.json | 40 ++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/lib/compile/formats.js b/lib/compile/formats.js index d06792aa4..a5001c7b6 100644 --- a/lib/compile/formats.js +++ b/lib/compile/formats.js @@ -5,7 +5,7 @@ var util = require('./util'); 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 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; var URIREF = /^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i; // uri-template: https://tools.ietf.org/html/rfc6570 @@ -126,7 +126,16 @@ function date_time(str) { function hostname(str) { // https://tools.ietf.org/html/rfc1034#section-3.5 // https://tools.ietf.org/html/rfc1123#section-2 - return str.length <= 255 && HOSTNAME.test(str); + + // Count octets, not string length + // https://devblogs.microsoft.com/oldnewthing/?p=7873 + var count = str.length; + // Do not count trailing dot if present + if (/\.$/.test(str)) count--; + // Add leading and trailing "length octets" to count + count += 2; + + return count <= 255 && HOSTNAME.test(str); } diff --git a/spec/tests/rules/format.json b/spec/tests/rules/format.json index 226608bd4..e4b492dc6 100644 --- a/spec/tests/rules/format.json +++ b/spec/tests/rules/format.json @@ -82,10 +82,50 @@ "data": "123.example.com", "valid": true }, + { + "description": "valid hostname - trailing dot", + "data": "123.example.com.", + "valid": true + }, + { + "description": "valid hostname - single label", + "data": "localhost", + "valid": true + }, + { + "description": "valid hostname - single label with trailing dot", + "data": "localhost.", + "valid": true + }, { "description": "valid hostname #312", "data": "lead-routing-qa.lvuucj.0001.use1.cache.amazonaws.com", "valid": true + }, + { + "description": "valid hostname - maximum length label (63 chars)", + "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.example.com", + "valid": true + }, + { + "description": "invalid hostname - label too long (64 chars)", + "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl.example.com", + "valid": false + }, + { + "description": "valid hostname - maximum length hostname (255 octets)", + "data": "abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxy.example.com", + "valid": true + }, + { + "description": "valid hostname - maximum length hostname (255 octets) with trailing dot", + "data": "abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxy.example.com.", + "valid": true + }, + { + "description": "invalid hostname - hostname too long (256 octets)", + "data": "abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.example.com.", + "valid": false } ] }, From c3bbc3e35b09ba9ef5a2b6a4501c7b9812636d49 Mon Sep 17 00:00:00 2001 From: sambauers Date: Thu, 9 Jan 2020 11:52:52 +1100 Subject: [PATCH 266/333] More efficient hostname function. --- lib/compile/formats.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/compile/formats.js b/lib/compile/formats.js index a5001c7b6..429a1f29e 100644 --- a/lib/compile/formats.js +++ b/lib/compile/formats.js @@ -126,16 +126,15 @@ function date_time(str) { function hostname(str) { // https://tools.ietf.org/html/rfc1034#section-3.5 // https://tools.ietf.org/html/rfc1123#section-2 + if (!HOSTNAME.test(str)) return false; // Count octets, not string length // https://devblogs.microsoft.com/oldnewthing/?p=7873 - var count = str.length; + var octets = str.length; // Do not count trailing dot if present - if (/\.$/.test(str)) count--; - // Add leading and trailing "length octets" to count - count += 2; - - return count <= 255 && HOSTNAME.test(str); + if (/\.$/.test(str)) octets--; + // With starting and ending length octets this would be 255, not 253 + return octets <= 253; } From 508f640e640b1a404af3e1fe3c98776c0e05b302 Mon Sep 17 00:00:00 2001 From: sambauers Date: Thu, 9 Jan 2020 12:11:23 +1100 Subject: [PATCH 267/333] More terse hostname function. --- lib/compile/formats.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/compile/formats.js b/lib/compile/formats.js index 429a1f29e..f51a3b76f 100644 --- a/lib/compile/formats.js +++ b/lib/compile/formats.js @@ -126,15 +126,11 @@ function date_time(str) { function hostname(str) { // https://tools.ietf.org/html/rfc1034#section-3.5 // https://tools.ietf.org/html/rfc1123#section-2 - if (!HOSTNAME.test(str)) return false; - // Count octets, not string length // https://devblogs.microsoft.com/oldnewthing/?p=7873 - var octets = str.length; - // Do not count trailing dot if present - if (/\.$/.test(str)) octets--; + // Count octets, not string length (remove any trailing dot) // With starting and ending length octets this would be 255, not 253 - return octets <= 253; + return str.replace(/\.$/, '').length <= 253 && HOSTNAME.test(str); } From cc97b003f59db096171263a9ca6cf58936b3bc07 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Sat, 18 Jan 2020 08:07:05 +0000 Subject: [PATCH 268/333] chore(package): update nyc to version 15.0.0 (#1139) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6f15e2dd0..109e47663 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "karma-mocha": "^1.1.1", "karma-sauce-launcher": "^2.0.0", "mocha": "^6.0.0", - "nyc": "^14.0.0", + "nyc": "^15.0.0", "pre-commit": "^1.1.1", "require-globify": "^1.3.0", "typescript": "^2.8.3", From f38f2e4136c790b409b12528dd6acd51283654ab Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 18 Jan 2020 08:53:51 +0000 Subject: [PATCH 269/333] test: remove failing typescript test --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 109e47663..43479a6e8 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "build": "del-cli lib/dotjs/*.js \"!lib/dotjs/index.js\" && node scripts/compile-dots.js", "test-karma": "karma start", "test-browser": "del-cli .browser && npm run bundle && scripts/prepare-tests && npm run test-karma", - "test-all": "npm run test-ts && npm run test-cov && if-node-version 10 npm run test-browser", + "test-all": "npm run test-cov && if-node-version 10 npm run test-browser", "test": "npm run lint && npm run build && npm run test-all", "prepublish": "npm run build && npm run bundle", "watch": "watch \"npm run build\" ./lib/dot" From 03198c2b6d52ec5eb7ffbf7623f05db5372689a1 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 18 Jan 2020 09:00:24 +0000 Subject: [PATCH 270/333] 6.11.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 43479a6e8..da9218a83 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.10.2", + "version": "6.11.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From d9661b442e02077bc355af554a9e7c61e6390763 Mon Sep 17 00:00:00 2001 From: sambauers Date: Wed, 22 Jan 2020 12:46:58 +1100 Subject: [PATCH 271/333] Do hostname character count in regexp. --- lib/compile/formats.js | 8 +++----- spec/tests/rules/format.json | 5 +++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/compile/formats.js b/lib/compile/formats.js index f51a3b76f..0e6e910fa 100644 --- a/lib/compile/formats.js +++ b/lib/compile/formats.js @@ -5,7 +5,7 @@ var util = require('./util'); 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 HOSTNAME = /^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\.?$/i; 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; var URIREF = /^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i; // uri-template: https://tools.ietf.org/html/rfc6570 @@ -126,11 +126,9 @@ function date_time(str) { function hostname(str) { // https://tools.ietf.org/html/rfc1034#section-3.5 // https://tools.ietf.org/html/rfc1123#section-2 - // https://devblogs.microsoft.com/oldnewthing/?p=7873 - // Count octets, not string length (remove any trailing dot) - // With starting and ending length octets this would be 255, not 253 - return str.replace(/\.$/, '').length <= 253 && HOSTNAME.test(str); + // https://tools.ietf.org/html/rfc1034#section-3.1 + return HOSTNAME.test(str); } diff --git a/spec/tests/rules/format.json b/spec/tests/rules/format.json index e4b492dc6..c316a6ee5 100644 --- a/spec/tests/rules/format.json +++ b/spec/tests/rules/format.json @@ -124,6 +124,11 @@ }, { "description": "invalid hostname - hostname too long (256 octets)", + "data": "abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.example.com", + "valid": false + }, + { + "description": "invalid hostname - hostname too long (256 octets) with trailing dot", "data": "abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.example.com.", "valid": false } From d3488073b16c54704bc10f7d89645b34c49b0204 Mon Sep 17 00:00:00 2001 From: RadiationSickness Date: Tue, 4 Feb 2020 10:09:47 -0500 Subject: [PATCH 272/333] Add custom logger example The current value description for the custom logger option is not easily understandable. Adding an example to this would be greatly beneficial. --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index d645c3f70..641a6a4de 100644 --- a/README.md +++ b/README.md @@ -1151,6 +1151,15 @@ Defaults: - _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. + ```javascript + var ajv = new AJV.default({ + logger: { + log: log => console.log(log), + warn: () => null, + error: error => console.error(error), + }, + }); + ``` - `false` - logging is disabled. From 23ebb55c1ae8ef69bc464fd6a8e142e9b6ad813c Mon Sep 17 00:00:00 2001 From: Vasil Rangelov Date: Tue, 11 Feb 2020 09:58:45 +0200 Subject: [PATCH 273/333] fix(types) added undefined to getSchema() --- lib/ajv.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index ba75f8d2d..c946096ad 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -80,9 +80,9 @@ declare namespace ajv { /** * Get compiled schema from the instance by `key` or `ref`. * @param {string} keyRef `key` that was passed to `addSchema` or full schema reference (`schema.id` or resolved id). - * @return {Function} schema validating function (with property `schema`). + * @return {Function} schema validating function (with property `schema`). Returns undefined if keyRef can't be resolved to an existing schema. */ - getSchema(keyRef: string): ValidateFunction; + getSchema(keyRef: string): ValidateFunction | undefined; /** * Remove cached schema(s). * If no parameter is passed all schemas but meta-schemas are removed. From 10f6cba4c561fdea1b762156b4823d0826db818e Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2020 08:27:54 +0000 Subject: [PATCH 274/333] chore(package): update mocha to version 7.0.1 (#1156) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index da9218a83..5cfec97d2 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "karma-chrome-launcher": "^3.0.0", "karma-mocha": "^1.1.1", "karma-sauce-launcher": "^2.0.0", - "mocha": "^6.0.0", + "mocha": "^7.0.1", "nyc": "^15.0.0", "pre-commit": "^1.1.1", "require-globify": "^1.3.0", From 5a92f70f5f967d80e4599775dfc2c0b8647c1b23 Mon Sep 17 00:00:00 2001 From: RadiationSickness Date: Tue, 11 Feb 2020 10:58:20 -0500 Subject: [PATCH 275/333] Adjusting Example - Converting example to ES5 - Updating example to conform to other examples - Updating example to have custom warning message --- README.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 641a6a4de..cfa23b667 100644 --- a/README.md +++ b/README.md @@ -1152,12 +1152,18 @@ Defaults: - _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. ```javascript - var ajv = new AJV.default({ + var ajv = new Ajv({ logger: { - log: log => console.log(log), - warn: () => null, - error: error => console.error(error), - }, + log: function log(_log) { + return console.log(_log); + }, + warn: function warn(_warn) { + return console.warn("Custom Warning: ".concat(_warn)); + }, + error: function error(_error) { + return console.error(_error); + } + } }); ``` - `false` - logging is disabled. From d601e4856421f87c08c68abf45bc1d8397c5c47b Mon Sep 17 00:00:00 2001 From: Matti Manninen Date: Wed, 12 Feb 2020 16:27:07 +0200 Subject: [PATCH 276/333] Fixed the TypeScript type definition for Options' "format" --- lib/ajv.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index c946096ad..29cdee59a 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -169,7 +169,7 @@ declare namespace ajv { jsonPointers?: boolean; uniqueItems?: boolean; unicode?: boolean; - format?: string; + format?: false | string; formats?: object; unknownFormats?: true | string[] | 'ignore'; schemas?: Array | object; From d127103db174ebb09a8a59143d335e54d343afb2 Mon Sep 17 00:00:00 2001 From: RadiationSickness Date: Wed, 12 Feb 2020 10:29:29 -0500 Subject: [PATCH 277/333] Move Logging Sample To Error Section Updated and moved logging sample to new Error Logging subsection --- README.md | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index cfa23b667..41d703dc8 100644 --- a/README.md +++ b/README.md @@ -1151,21 +1151,6 @@ Defaults: - _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. - ```javascript - var ajv = new Ajv({ - logger: { - log: function log(_log) { - return console.log(_log); - }, - warn: function warn(_warn) { - return console.warn("Custom Warning: ".concat(_warn)); - }, - error: function error(_error) { - return console.error(_error); - } - } - }); - ``` - `false` - logging is disabled. @@ -1302,6 +1287,30 @@ 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). +### Error Logging + +Using the `logger` option when initiallizing Ajv will allow you to define custom logging. Here you can build upon the exisiting logging. The use of other logging packages is supported as long as the package or its associated wrapper exposes the required methods. If any of the required methods are missing an exception will be thrown. +- **Required Methods**: `log`, `warn`, `error` + +```javascript +var otherLogger = new OtherLogger(); +var ajv = new Ajv({ + logger: { + log: function log(_log) { + return console.log(_log); + }, + warn: function warn(_warn) { + return otherLogger.logWarn(_warn); + }, + error: function error(_error) { + otherLogger.logError(_error); + return console.error(_error); + } + } +}); +``` + + ## 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: From 46171d295dda49b6f68365665bbe8dedd34b6a66 Mon Sep 17 00:00:00 2001 From: Francisco Morais <35690067+franciscomorais@users.noreply.github.com> Date: Thu, 13 Feb 2020 14:16:31 +0000 Subject: [PATCH 278/333] Add keywords to ajv options --- lib/ajv.d.ts | 4 ++++ lib/ajv.js | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index 29cdee59a..cb600e168 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -171,6 +171,7 @@ declare namespace ajv { unicode?: boolean; format?: false | string; formats?: object; + keywords?: object; unknownFormats?: true | string[] | 'ignore'; schemas?: Array | object; schemaId?: '$id' | 'id' | 'auto'; @@ -252,6 +253,9 @@ declare namespace ajv { formats: { [index: string]: FormatDefinition | undefined; }; + keywords: { + [index: string]: KeywordDefinition | undefined; + }; compositeRule: boolean; validate: (schema: object) => boolean; util: { diff --git a/lib/ajv.js b/lib/ajv.js index 611b93835..06a45b650 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -69,6 +69,7 @@ function Ajv(opts) { this._metaOpts = getMetaSchemaOptions(this); if (opts.formats) addInitialFormats(this); + if (opts.keywords) addInitialKeywords(this); addDefaultMetaSchema(this); if (typeof opts.meta == 'object') this.addMetaSchema(opts.meta); if (opts.nullable) this.addKeyword('nullable', {metaSchema: {type: 'boolean'}}); @@ -467,6 +468,14 @@ function addInitialFormats(self) { } +function addInitialKeywords(self) { + for (var name in self._opts.keywords) { + var keyword = self._opts.keywords[name]; + self.addKeyword(name, keyword); + } +} + + function checkUnique(self, id) { if (self._schemas[id] || self._refs[id]) throw new Error('schema with key or id "' + id + '" already exists'); From 4d9dd8c2a69ef97949720bef50ba85074f0c9909 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sun, 16 Feb 2020 22:24:10 +0000 Subject: [PATCH 279/333] docs: link to $data reference proposal, closes #51 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d645c3f70..5569828aa 100644 --- a/README.md +++ b/README.md @@ -328,7 +328,7 @@ __Please note__: ## $data reference -With `$data` option you can use values from the validated data as the values for the schema keywords. See [proposal](https://github.com/json-schema/json-schema/wiki/$data-(v5-proposal)) for more information about how it works. +With `$data` option you can use values from the validated data as the values for the schema keywords. See [proposal](https://github.com/json-schema-org/json-schema-spec/issues/51) for more information about how it works. `$data` reference is supported in the keywords: const, enum, format, maximum/minimum, exclusiveMaximum / exclusiveMinimum, maxLength / minLength, maxItems / minItems, maxProperties / minProperties, formatMaximum / formatMinimum, formatExclusiveMaximum / formatExclusiveMinimum, multipleOf, pattern, required, uniqueItems. From 120748cc6cfa2366167b2da93ad34d1db69088fc Mon Sep 17 00:00:00 2001 From: sambauers Date: Mon, 17 Feb 2020 10:23:34 +1100 Subject: [PATCH 280/333] Only use regex for hostname checks. --- lib/compile/formats.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/lib/compile/formats.js b/lib/compile/formats.js index 0e6e910fa..44895b0b4 100644 --- a/lib/compile/formats.js +++ b/lib/compile/formats.js @@ -70,7 +70,7 @@ formats.full = { 'uri-template': URITEMPLATE, url: URL, email: /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i, - hostname: hostname, + hostname: HOSTNAME, ipv4: /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/, ipv6: /^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i, regex: regex, @@ -123,15 +123,6 @@ function date_time(str) { } -function hostname(str) { - // https://tools.ietf.org/html/rfc1034#section-3.5 - // https://tools.ietf.org/html/rfc1123#section-2 - // https://devblogs.microsoft.com/oldnewthing/?p=7873 - // https://tools.ietf.org/html/rfc1034#section-3.1 - return HOSTNAME.test(str); -} - - var NOT_URI_FRAGMENT = /\/|:/; function uri(str) { // http://jmrware.com/articles/2009/uri_regexp/URI_regex.html + optional protocol + required "." From f94db48984da812306acd8c080778807af514ede Mon Sep 17 00:00:00 2001 From: Francisco Morais <35690067+franciscomorais@users.noreply.github.com> Date: Thu, 13 Feb 2020 14:16:56 +0000 Subject: [PATCH 281/333] Update options validation spec --- spec/options/options_validation.spec.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/spec/options/options_validation.spec.js b/spec/options/options_validation.spec.js index e362826bc..3f86b2fc7 100644 --- a/spec/options/options_validation.spec.js +++ b/spec/options/options_validation.spec.js @@ -26,12 +26,35 @@ describe('validation options', function() { }}); var validate = ajv.compile({ format: 'identifier' }); + validate('Abc1') .should.equal(true); validate('123') .should.equal(false); validate(123) .should.equal(true); }); }); + describe('keywords', function() { + it('should add keywords from options', function() { + var ajv = new Ajv({ keywords: { + string: { + validate: function (schema, data ) { + + console.log(">>", data); + return /^[a-z_$][a-z0-9_$]*$/i.test(data); + } + } + }}); + + var validate = ajv.compile({ string: true }); + + validate('Abc1') .should.equal(true); + validate('foo bar').should.equal(false); + validate('123').should.equal(false); + validate(123).should.equal(false); + validate(123).should.equal(false); + }); + }); + describe('uniqueItems', function() { it('should not validate uniqueItems with uniqueItems option == false', function() { From 38191c2693ccb06ab2d79b24c6ca9aa29a4d2c03 Mon Sep 17 00:00:00 2001 From: Francisco Morais <35690067+franciscomorais@users.noreply.github.com> Date: Thu, 13 Feb 2020 14:17:10 +0000 Subject: [PATCH 282/333] Update readme with keywords option --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d645c3f70..cc6992d4d 100644 --- a/README.md +++ b/README.md @@ -1144,6 +1144,7 @@ Defaults: - `"full"` - more restrictive and slow validation. E.g., 25:00:00 and 2015/14/33 will be invalid time and date in 'full' mode but it will be valid in 'fast' mode. - `false` - ignore all format keywords. - _formats_: an object with custom formats. Keys and values will be passed to `addFormat` method. +- _keywords_: an object with custom keywords. Keys and values will be passed to `addKeyword` method. - _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. From e5bed30f2a057227ec4c256588906e220c4c302b Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 22 Feb 2020 13:14:51 +0000 Subject: [PATCH 283/333] test: update option keywords test --- spec/options/options_validation.spec.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/spec/options/options_validation.spec.js b/spec/options/options_validation.spec.js index 3f86b2fc7..950a0c289 100644 --- a/spec/options/options_validation.spec.js +++ b/spec/options/options_validation.spec.js @@ -28,6 +28,7 @@ describe('validation options', function() { var validate = ajv.compile({ format: 'identifier' }); validate('Abc1') .should.equal(true); + validate('foo bar') .should.equal(false); validate('123') .should.equal(false); validate(123) .should.equal(true); }); @@ -36,22 +37,20 @@ describe('validation options', function() { describe('keywords', function() { it('should add keywords from options', function() { var ajv = new Ajv({ keywords: { - string: { + identifier: { + type: 'string', validate: function (schema, data ) { - - console.log(">>", data); return /^[a-z_$][a-z0-9_$]*$/i.test(data); } } }}); - var validate = ajv.compile({ string: true }); + var validate = ajv.compile({ identifier: true }); validate('Abc1') .should.equal(true); - validate('foo bar').should.equal(false); - validate('123').should.equal(false); - validate(123).should.equal(false); - validate(123).should.equal(false); + validate('foo bar') .should.equal(false); + validate('123') .should.equal(false); + validate(123) .should.equal(true); }); }); From 0163e5c233bc9d6d2795cd498719c568d4a8653a Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 22 Feb 2020 13:29:55 +0000 Subject: [PATCH 284/333] docs: error logging code sample --- README.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index bc245a313..9bd1f5718 100644 --- a/README.md +++ b/README.md @@ -1158,7 +1158,7 @@ Defaults: - `[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. - _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: +- _logger_: sets the logging method. Default is the global `console` object that should have methods `log`, `warn` and `error`. See [Error logging](#error-logging). Option values: - custom logger - it should have methods `log`, `warn` and `error`. If any of these methods is missing an exception will be thrown. - `false` - logging is disabled. @@ -1296,7 +1296,7 @@ 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). -### Error Logging +### Error logging Using the `logger` option when initiallizing Ajv will allow you to define custom logging. Here you can build upon the exisiting logging. The use of other logging packages is supported as long as the package or its associated wrapper exposes the required methods. If any of the required methods are missing an exception will be thrown. - **Required Methods**: `log`, `warn`, `error` @@ -1305,15 +1305,13 @@ Using the `logger` option when initiallizing Ajv will allow you to define custom var otherLogger = new OtherLogger(); var ajv = new Ajv({ logger: { - log: function log(_log) { - return console.log(_log); + log: console.log.bind(console), + warn: function warn() { + otherLogger.logWarn.apply(otherLogger, arguments); }, - warn: function warn(_warn) { - return otherLogger.logWarn(_warn); - }, - error: function error(_error) { - otherLogger.logError(_error); - return console.error(_error); + error: function error() { + otherLogger.logError.apply(otherLogger, arguments); + console.error.apply(console, arguments); } } }); From 03d0012f0cf35a834933de07d79522fe7ec9e90a Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 22 Feb 2020 13:40:26 +0000 Subject: [PATCH 285/333] 6.12.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5cfec97d2..419583ece 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.11.0", + "version": "6.12.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From b15a73cdc1c1b0bd1859f3f26193c22101f51fc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quin=CC=83o=CC=81nez=2C=20Carlo=20J?= Date: Thu, 23 Jan 2020 16:15:53 -0800 Subject: [PATCH 286/333] Add to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d645c3f70..544b25aa3 100644 --- a/README.md +++ b/README.md @@ -1309,7 +1309,7 @@ If you have published a useful plugin please submit a PR to add it to the next s - [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) - plugin with custom validation keywords (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 - +- [ajv-formats-draft2019](https://github.com/luzlab/ajv-formats-draft2019) - format validators for draft2019 that aren't already included in ajv (ie. `idn-hostname`, `idn-email`, `iri`, `iri-reference` and `duration`). ## Some packages using Ajv From 0775bc1a6c5c313138373a2047b1b8772128873f Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sun, 22 Mar 2020 11:38:03 +0000 Subject: [PATCH 287/333] Update FUNDING.yml --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index c77c8e485..5dcdc3e50 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1,2 @@ +github: epoberezkin tidelift: "npm/ajv" -open_collective: "ajv" From 9f5752c1ff4b3cb5f078c4bccbc464a97828397f Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 4 Apr 2020 22:54:06 +0100 Subject: [PATCH 288/333] docs: sponsor --- README.md | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e7450b4a..788298b22 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,49 @@ The fastest JSON Schema validator for Node.js and browser. Supports draft-04/06/ [![npm](https://img.shields.io/npm/v/ajv.svg)](https://www.npmjs.com/package/ajv) [![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) -[![Greenkeeper badge](https://badges.greenkeeper.io/epoberezkin/ajv.svg)](https://greenkeeper.io/) [![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv) +[![GitHub Sponsors](https://img.shields.io/badge/$-sponsors-brightgreen)](https://github.com/sponsors/epoberezkin) + +## Please sponsor Ajv development + +Dear Ajv users! ❤️ + +I ask you to support the development of Ajv with donations. 🙏 + +Since 2015 Ajv has become widely used, thanks to your help and contributions: + +- **90** contributors 🏗 +- **5,000** dependent npm packages ⚙️ +- **7,000** github stars, from GitHub users [all over the world](https://www.google.com/maps/d/u/0/viewer?mid=1MGRV8ciFUGIbO1l0EKFWNJGYE7iSkDxP&ll=-3.81666561775622e-14%2C4.821737100000007&z=2) ⭐️ +- **5,000,000** dependent repositories on GitHub 🚀 +- **120,000,000** npm downloads per month! 💯 + +Your donations will fund futher development - small and large improvements, support of the next versions of JSON Schema specification, and, possibly, the code should be migrated to TypeScript to make it more maintainable. + +I will greatly appreciate anything you can help with to make it happen: + +- a **personal** donation - from $2 ☕️ +- your **company** donation - from $10 🍔 +- a **sponsorship** to get promoted on Ajv or related packages - from $50 💰 +- an **introduction** to a sponsor who would benefit from the promotion on Ajv page 🤝 + +| ‼️ Please make donations via [my GitHub sponsors page](https://github.com/sponsors/epoberezkin) - **GitHub pledged to DOUBLE them** ‼️ | +|---| + +#### Open Collective sponsors + + + + + + + + + + + + + ## Using version 6 From 081aeb0cd1b07e9a0f55b3226f1855062ba78cd9 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 5 Apr 2020 13:39:40 +0100 Subject: [PATCH 289/333] docs: readme, open collective --- .github/FUNDING.yml | 1 + README.md | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 5dcdc3e50..88fc551fa 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,2 +1,3 @@ github: epoberezkin tidelift: "npm/ajv" +open_collective: "ajv" diff --git a/README.md b/README.md index 788298b22..844dcb6cd 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ The fastest JSON Schema validator for Node.js and browser. Supports draft-04/06/ [![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv) [![GitHub Sponsors](https://img.shields.io/badge/$-sponsors-brightgreen)](https://github.com/sponsors/epoberezkin) -## Please sponsor Ajv development +## Please [sponsor Ajv](https://github.com/sponsors/epoberezkin) Dear Ajv users! ❤️ @@ -34,7 +34,7 @@ I will greatly appreciate anything you can help with to make it happen: - a **sponsorship** to get promoted on Ajv or related packages - from $50 💰 - an **introduction** to a sponsor who would benefit from the promotion on Ajv page 🤝 -| ‼️ Please make donations via [my GitHub sponsors page](https://github.com/sponsors/epoberezkin) - **GitHub pledged to DOUBLE them** ‼️ | +| Please [make donations via my GitHub sponsors page](https://github.com/sponsors/epoberezkin)
‼️ **GitHub will DOUBLE them** ‼️ | |---| #### Open Collective sponsors From 4e7786fcda9ed7f92ef52964db322a866c58d636 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2020 19:48:31 +0000 Subject: [PATCH 290/333] chore(package): update karma to version 5.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 419583ece..8b7cded28 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "js-beautify": "^1.7.3", "jshint": "^2.10.2", "json-schema-test": "^2.0.0", - "karma": "^4.0.1", + "karma": "^5.0.0", "karma-chrome-launcher": "^3.0.0", "karma-mocha": "^1.1.1", "karma-sauce-launcher": "^2.0.0", From f5c284cf924209a163039d319ed72b3cff5c1b78 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2020 16:31:37 +0000 Subject: [PATCH 291/333] chore(package): update karma-sauce-launcher to version 4.1.3 Closes #1174 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 419583ece..02458b9ad 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "karma": "^4.0.1", "karma-chrome-launcher": "^3.0.0", "karma-mocha": "^1.1.1", - "karma-sauce-launcher": "^2.0.0", + "karma-sauce-launcher": "^4.1.3", "mocha": "^7.0.1", "nyc": "^15.0.0", "pre-commit": "^1.1.1", From 591b36e2378e9e4be81da1d59571888c2ea28f23 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2020 21:09:00 +0000 Subject: [PATCH 292/333] chore(package): update karma-mocha to version 2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 419583ece..04e49a1da 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "json-schema-test": "^2.0.0", "karma": "^4.0.1", "karma-chrome-launcher": "^3.0.0", - "karma-mocha": "^1.1.1", + "karma-mocha": "^2.0.0", "karma-sauce-launcher": "^2.0.0", "mocha": "^7.0.1", "nyc": "^15.0.0", From bfced3b76a7ef7d3be0e4dac148c7c8aebe2dfb7 Mon Sep 17 00:00:00 2001 From: Sam Bauers Date: Sat, 18 Apr 2020 19:31:25 +1000 Subject: [PATCH 293/333] "hostname" format does not have full method Since v6.12.0 via #1143 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 844dcb6cd..e13fdec29 100644 --- a/README.md +++ b/README.md @@ -314,7 +314,7 @@ The following formats are implemented for string validation with "format" keywor __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 to mention this plugin here. -There are two modes of format validation: `fast` and `full`. This mode affects formats `date`, `time`, `date-time`, `uri`, `uri-reference`, `email`, and `hostname`. See [Options](#options) for details. +There are two modes of format validation: `fast` and `full`. This mode affects formats `date`, `time`, `date-time`, `uri`, `uri-reference`, and `email`. See [Options](#options) for details. You can add additional formats and replace any of the formats above using [addFormat](#api-addformat) method. From 3e9f3759e3e0190de418e8253ee2ca7bf749a4a8 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 18 Apr 2020 19:58:22 +0100 Subject: [PATCH 294/333] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d091a7625..e5b4b8fe4 100644 --- a/package.json +++ b/package.json @@ -85,8 +85,8 @@ "json-schema-test": "^2.0.0", "karma": "^5.0.0", "karma-chrome-launcher": "^3.0.0", - "karma-sauce-launcher": "^4.1.3", "karma-mocha": "^2.0.0", + "karma-sauce-launcher": "^4.1.3", "mocha": "^7.0.1", "nyc": "^15.0.0", "pre-commit": "^1.1.1", From 891f081862c4354eb5b837755b318cf9e6359244 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 18 Apr 2020 20:22:38 +0100 Subject: [PATCH 295/333] update readme --- README.md | 32 +------------------------------- package.json | 2 +- 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 1d984dae7..e13fdec29 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ The fastest JSON Schema validator for Node.js and browser. Supports draft-04/06/07. [![Build Status](https://travis-ci.org/epoberezkin/ajv.svg?branch=master)](https://travis-ci.org/epoberezkin/ajv) -[![Financial Contributors on Open Collective](https://opencollective.com/ajv/all/badge.svg?label=financial+contributors)](https://opencollective.com/ajv) [![npm](https://img.shields.io/npm/v/ajv.svg)](https://www.npmjs.com/package/ajv) +[![npm](https://img.shields.io/npm/v/ajv.svg)](https://www.npmjs.com/package/ajv) [![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) [![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv) @@ -1447,36 +1447,6 @@ __Please note__: [Changes in version 6.0.0](https://github.com/epoberezkin/ajv/r Ajv is a part of [Tidelift subscription](https://tidelift.com/subscription/pkg/npm-ajv?utm_source=npm-ajv&utm_medium=referral&utm_campaign=readme) - it provides a centralised support to open-source software users, in addition to the support provided by software maintainers. -## Contributors - -### Code Contributors - -This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. - - -### Financial Contributors - -Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/ajv/contribute)] - -#### Individuals - - - -#### Organizations - -Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/ajv/contribute)] - - - - - - - - - - - - ## License [MIT](https://github.com/epoberezkin/ajv/blob/master/LICENSE) diff --git a/package.json b/package.json index 4fc7b222e..bca24d21b 100644 --- a/package.json +++ b/package.json @@ -101,4 +101,4 @@ "type": "opencollective", "url": "https://opencollective.com/ajv" } -} \ No newline at end of file +} From b511ae230f19519d5f16f55cd8959330327adb7a Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 18 Apr 2020 20:28:21 +0100 Subject: [PATCH 296/333] 6.12.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bca24d21b..b75b84b34 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.12.0", + "version": "6.12.1", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 14bdb4b7576984c61be0b96c377efe7021f1564f Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 19 Apr 2020 23:59:37 +0100 Subject: [PATCH 297/333] remove postinstall --- package.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/package.json b/package.json index b75b84b34..d2a3d2258 100644 --- a/package.json +++ b/package.json @@ -28,8 +28,7 @@ "test-all": "npm run test-cov && if-node-version 10 npm run test-browser", "test": "npm run lint && npm run build && npm run test-all", "prepublish": "npm run build && npm run bundle", - "watch": "watch \"npm run build\" ./lib/dot", - "postinstall": "opencollective-postinstall || true" + "watch": "watch \"npm run build\" ./lib/dot" }, "nyc": { "exclude": [ @@ -66,7 +65,6 @@ "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", - "opencollective-postinstall": "^2.0.2", "uri-js": "^4.2.2" }, "devDependencies": { From 6a671057ea6aae690b5967ee26a0ddf8452c6297 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 19 Apr 2020 23:59:48 +0100 Subject: [PATCH 298/333] 6.12.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d2a3d2258..024ddacd4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.12.1", + "version": "6.12.2", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From b3ce7f348e73b3b9493cd7f39f02ebb373ae762d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 22 Apr 2020 18:25:59 +0100 Subject: [PATCH 299/333] Create CODE_OF_CONDUCT.md --- CODE_OF_CONDUCT.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..410cda641 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ajv.validator@gmail.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq From 1188d19e7931ae4a3b7988e4a1be278776d90d1d Mon Sep 17 00:00:00 2001 From: Alex Layton Date: Sat, 25 Apr 2020 16:06:40 -0400 Subject: [PATCH 300/333] Pass schema object to processCode function --- README.md | 2 +- lib/ajv.d.ts | 2 +- lib/compile/index.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e13fdec29..434298610 100644 --- a/README.md +++ b/README.md @@ -1169,7 +1169,7 @@ Defaults: errorDataPath: 'object', // deprecated messages: true, sourceCode: false, - processCode: undefined, // function (str: string): string {} + processCode: undefined, // function (str: string, schema: object): string {} cache: new Cache, serialize: undefined } diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index cb600e168..d988c3a99 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -196,7 +196,7 @@ declare namespace ajv { errorDataPath?: string, messages?: boolean; sourceCode?: boolean; - processCode?: (code: string) => string; + processCode?: (code: string, schema: object) => string; cache?: object; logger?: CustomLogger | false; nullable?: boolean; diff --git a/lib/compile/index.js b/lib/compile/index.js index f4d3f0d58..97518c424 100644 --- a/lib/compile/index.js +++ b/lib/compile/index.js @@ -113,7 +113,7 @@ function compile(schema, root, localRefs, baseId) { + vars(defaults, defaultCode) + vars(customRules, customRuleCode) + sourceCode; - if (opts.processCode) sourceCode = opts.processCode(sourceCode); + if (opts.processCode) sourceCode = opts.processCode(sourceCode, _schema); // console.log('\n\n\n *** \n', JSON.stringify(sourceCode)); var validate; try { From 43b99974d9c02660d77d8961e712fe1fd69be790 Mon Sep 17 00:00:00 2001 From: Alex Layton Date: Mon, 27 Apr 2020 12:30:34 -0400 Subject: [PATCH 301/333] Update README about using js_beautify --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 434298610..c307eed04 100644 --- a/README.md +++ b/README.md @@ -1279,7 +1279,7 @@ Defaults: - _messages_: Include human-readable messages in errors. `true` by default. `false` can be passed when custom messages are used (e.g. with [ajv-i18n](https://github.com/epoberezkin/ajv-i18n)). - _sourceCode_: add `sourceCode` property to validating function (for debugging; this code can be different from the result of toString call). - _processCode_: an optional function to process generated code before it is passed to Function constructor. It can be used to either beautify (the validating function is generated without line-breaks) or to transpile code. Starting from version 5.0.0 this option replaced options: - - `beautify` that formatted the generated function using [js-beautify](https://github.com/beautify-web/js-beautify). If you want to beautify the generated code pass `require('js-beautify').js_beautify`. + - `beautify` that formatted the generated function using [js-beautify](https://github.com/beautify-web/js-beautify). If you want to beautify the generated code pass a fucntion calling `require('js-beautify').js_beautify` as `processCode: code => js_beautify(code)`. - `transpile` that transpiled asynchronous validation function. You can still use `transpile` option with [ajv-async](https://github.com/epoberezkin/ajv-async) package. See [Asynchronous validation](#asynchronous-validation) for more information. - _cache_: an optional instance of cache to store compiled schemas using stable-stringified schema as a key. For example, set-associative cache [sacjs](https://github.com/epoberezkin/sacjs) can be used. If not passed then a simple hash is used which is good enough for the common use case (a limited number of statically defined schemas). Cache should have methods `put(key, value)`, `get(key)`, `del(key)` and `clear()`. - _serialize_: an optional function to serialize schema to cache key. Pass `false` to use schema itself as a key (e.g., if WeakMap used as a cache). By default [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) is used. From 8bc2067c61a84736271df599e35f54b76c2577d6 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 27 Apr 2020 21:13:41 +0100 Subject: [PATCH 302/333] package.json funding --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index 024ddacd4..0c085c672 100644 --- a/package.json +++ b/package.json @@ -98,5 +98,9 @@ "collective": { "type": "opencollective", "url": "https://opencollective.com/ajv" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } } From b36e066055cc290f0525c43fcbe0935d571bf1b5 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 27 Apr 2020 21:16:14 +0100 Subject: [PATCH 303/333] readme typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c307eed04..ebde8c500 100644 --- a/README.md +++ b/README.md @@ -1279,7 +1279,7 @@ Defaults: - _messages_: Include human-readable messages in errors. `true` by default. `false` can be passed when custom messages are used (e.g. with [ajv-i18n](https://github.com/epoberezkin/ajv-i18n)). - _sourceCode_: add `sourceCode` property to validating function (for debugging; this code can be different from the result of toString call). - _processCode_: an optional function to process generated code before it is passed to Function constructor. It can be used to either beautify (the validating function is generated without line-breaks) or to transpile code. Starting from version 5.0.0 this option replaced options: - - `beautify` that formatted the generated function using [js-beautify](https://github.com/beautify-web/js-beautify). If you want to beautify the generated code pass a fucntion calling `require('js-beautify').js_beautify` as `processCode: code => js_beautify(code)`. + - `beautify` that formatted the generated function using [js-beautify](https://github.com/beautify-web/js-beautify). If you want to beautify the generated code pass a function calling `require('js-beautify').js_beautify` as `processCode: code => js_beautify(code)`. - `transpile` that transpiled asynchronous validation function. You can still use `transpile` option with [ajv-async](https://github.com/epoberezkin/ajv-async) package. See [Asynchronous validation](#asynchronous-validation) for more information. - _cache_: an optional instance of cache to store compiled schemas using stable-stringified schema as a key. For example, set-associative cache [sacjs](https://github.com/epoberezkin/sacjs) can be used. If not passed then a simple hash is used which is good enough for the common use case (a limited number of statically defined schemas). Cache should have methods `put(key, value)`, `get(key)`, `del(key)` and `clear()`. - _serialize_: an optional function to serialize schema to cache key. Pass `false` to use schema itself as a key (e.g., if WeakMap used as a cache). By default [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) is used. From 3a3b196cc8834e197f73a71cf4978b209179cccd Mon Sep 17 00:00:00 2001 From: Alexsey Date: Tue, 5 May 2020 11:56:24 +0300 Subject: [PATCH 304/333] docs: readme, update supported node versions https://github.com/epoberezkin/ajv/issues/1181 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ebde8c500..5862db752 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ Performance of different validators by [json-schema-benchmark](https://github.co - correct string lengths for strings with unicode pairs (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 +- supports [browsers](#using-in-browser) and Node.js 0.10+ - [asynchronous loading](#asynchronous-schema-compilation) of referenced schemas during compilation - "All errors" validation mode with [option allErrors](#options) - [error messages with parameters](#validation-errors) describing error reasons to allow creating custom error messages From eb4490cf473ac63fc166e4676e4d75200f5033ac Mon Sep 17 00:00:00 2001 From: Emily Marigold Klassen Date: Tue, 5 May 2020 22:17:18 -0700 Subject: [PATCH 305/333] Fix link to switch plugin in CUSTOM.md --- CUSTOM.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CUSTOM.md b/CUSTOM.md index 4d3065e37..f1a43d5f8 100644 --- a/CUSTOM.md +++ b/CUSTOM.md @@ -294,7 +294,7 @@ The first parameter passed to inline keyword compilation function (and the 3rd p - _opts_ - Ajv instance option. You should not be changing them. - _formats_ - all formats available in Ajv instance, including the custom ones. - _compositeRule_ - boolean indicating that the current schema is inside the compound keyword where failing some rule doesn't mean validation failure (`anyOf`, `oneOf`, `not`, `if` in `switch`). This flag is used to determine whether you can return validation result immediately after any error in case the option `allErrors` is not `true. You only need to do it if you have many steps in your keywords and potentially can define multiple errors. -- _validate_ - the function you need to use to compile subschemas in your keywords (see the [implementation](https://github.com/epoberezkin/ajv/blob/master/lib/dot/v5/switch.jst) of `switch` keyword for example). +- _validate_ - the function you need to use to compile subschemas in your keywords (see the [implementation](https://github.com/epoberezkin/ajv-keywords/blob/master/keywords/dot/switch.jst) of `switch` keyword for example). - _util_ - [Ajv utilities](#ajv-utilities) you can use in your inline compilation functions. - _self_ - Ajv instance. From c9f27463998824b65df69610df60759da443dc5a Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 6 May 2020 09:37:42 +0100 Subject: [PATCH 306/333] code of conduct link --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ebde8c500..6572f1a81 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,8 @@ ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); - [Plugins](#plugins) - [Related packages](#related-packages) - [Some packages using Ajv](#some-packages-using-ajv) -- [Tests, Contributing, History, Support, License](#tests) +- [Tests, Contributing, Changes history](#tests) +- [Support, Code of conduct, License](#open-source-software-support) ## Performance @@ -1442,6 +1443,13 @@ __Please note__: [Changes in version 6.0.0](https://github.com/epoberezkin/ajv/r [Version 2.0.0](https://github.com/epoberezkin/ajv/releases/tag/2.0.0). +## Code of conduct + +Please review and follow the [Code of conduct](https://github.com/epoberezkin/ajv/blob/master/CODE_OF_CONDUCT.md). + +Please report any unacceptable behaviour to ajv.validator@gmail.com - it will be reviewed by the project team. + + ## Open-source software support Ajv is a part of [Tidelift subscription](https://tidelift.com/subscription/pkg/npm-ajv?utm_source=npm-ajv&utm_medium=referral&utm_campaign=readme) - it provides a centralised support to open-source software users, in addition to the support provided by software maintainers. From e828d45fdef996a6ab4b609e8b823ac3881339ae Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 6 May 2020 11:28:34 +0100 Subject: [PATCH 307/333] update readme --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 6572f1a81..878ca35ef 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,18 @@ The fastest JSON Schema validator for Node.js and browser. Supports draft-04/06/ [![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv) [![GitHub Sponsors](https://img.shields.io/badge/$-sponsors-brightgreen)](https://github.com/sponsors/epoberezkin) -## Please [sponsor Ajv](https://github.com/sponsors/epoberezkin) -Dear Ajv users! ❤️ +## Please [sponsor Ajv development](https://github.com/sponsors/epoberezkin) -I ask you to support the development of Ajv with donations. 🙏 +I will get straight to the point - I need your support to ensure that the development of Ajv continues. + +I have developed Ajv for 5 years in my free time, but it is not sustainable. I'd appreciate if you consider supporting its further development with donations: +- [GitHub sponsors page](https://github.com/sponsors/epoberezkin) (GitHub will match it) +- [Ajv Open Collective️](https://opencollective.com/ajv) + +There are many small and large improvements that are long due, including the support of the next versions of JSON Schema specification, improving website and documentation, and making Ajv more modular and maintainable to address its limitations - what Ajv needs to evolve is much more than what I can contribute in my free time. + +I would also really appreciate any advice you could give on how to raise funds for Ajv development - whether some suitable open-source fund I could apply to or some sponsor I should approach. Since 2015 Ajv has become widely used, thanks to your help and contributions: @@ -25,17 +32,10 @@ Since 2015 Ajv has become widely used, thanks to your help and contributions: - **5,000,000** dependent repositories on GitHub 🚀 - **120,000,000** npm downloads per month! 💯 -Your donations will fund futher development - small and large improvements, support of the next versions of JSON Schema specification, and, possibly, the code should be migrated to TypeScript to make it more maintainable. - -I will greatly appreciate anything you can help with to make it happen: +I believe it would benefit all Ajv users to help put together the fund that will be used for its further development - it would allow to bring some additional maintainers to the project. -- a **personal** donation - from $2 ☕️ -- your **company** donation - from $10 🍔 -- a **sponsorship** to get promoted on Ajv or related packages - from $50 💰 -- an **introduction** to a sponsor who would benefit from the promotion on Ajv page 🤝 +Thank you -| Please [make donations via my GitHub sponsors page](https://github.com/sponsors/epoberezkin)
‼️ **GitHub will DOUBLE them** ‼️ | -|---| #### Open Collective sponsors From ce8209a75f967e8aa718ed7fa3b77c15d27ca762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20De=20Boey?= Date: Fri, 8 May 2020 22:07:43 +0200 Subject: [PATCH 308/333] Add tests for Node 14.x --- .travis.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0f25c1418..b053cda9c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,11 @@ before_script: - git submodule update --init - npm install -g codeclimate-test-reporter node_js: - - "8" - - "10" - - "11" - - "12" + - 8 + - 10 + - 11 + - 12 + - 14 after_script: - codeclimate-test-reporter < coverage/lcov.info - coveralls < coverage/lcov.info From 3ff84f37ee5af32093df7a654b76a3a9c331af40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20De=20Boey?= Date: Sat, 9 May 2020 17:33:18 +0200 Subject: [PATCH 309/333] Remove Node 11 from tests --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b053cda9c..11a0afa18 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ before_script: node_js: - 8 - 10 - - 11 - 12 - 14 after_script: From 66e8a356fbd5124d065caaaddea2e8122ebed5fd Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 9 May 2020 17:47:02 +0100 Subject: [PATCH 310/333] fix logo url --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 878ca35ef..12b271cb9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Ajv logo +Ajv logo # Ajv: Another JSON Schema Validator From b392b01764f44a42bcc8500281fab41221291f62 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 11 May 2020 17:26:23 +0100 Subject: [PATCH 311/333] change links to ajv-validator org --- .github/ISSUE_TEMPLATE.md | 12 +- .github/ISSUE_TEMPLATE/bug-or-error-report.md | 8 +- .github/ISSUE_TEMPLATE/change.md | 4 +- .github/ISSUE_TEMPLATE/compatibility.md | 4 +- .github/ISSUE_TEMPLATE/installation.md | 4 +- .github/ISSUE_TEMPLATE/typescript.md | 4 +- .github/PULL_REQUEST_TEMPLATE.md | 2 +- .github/config.yml | 4 +- COERCION.md | 2 +- CONTRIBUTING.md | 18 +-- CUSTOM.md | 14 +-- FAQ.md | 14 +-- KEYWORDS.md | 6 +- README.md | 116 +++++++++--------- bower.json | 2 +- lib/compile/equal.js | 2 +- lib/data.js | 2 +- lib/definition_schema.js | 2 +- lib/keyword.js | 2 +- lib/refs/data.json | 2 +- lib/refs/json-schema-secure.json | 2 +- package.json | 6 +- scripts/publish-built-version | 2 +- scripts/travis-gh-pages | 2 +- spec/security/array.json | 6 +- spec/security/object.json | 2 +- spec/security/string.json | 4 +- 27 files changed, 124 insertions(+), 124 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index ef1c8f934..558501036 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,12 +1,12 @@ @@ -16,7 +16,7 @@ This template is for bug or error reports. For other issues please use: **Ajv options object** - + ```javascript @@ -49,7 +49,7 @@ This template is for bug or error reports. For other issues please use: **What version of Ajv are you using? Does the issue happen if you use the latest version?** @@ -21,7 +21,7 @@ For other issues please see https://github.com/epoberezkin/ajv/blob/master/CONTR **Ajv options object** - + ```javascript @@ -54,7 +54,7 @@ For other issues please see https://github.com/epoberezkin/ajv/blob/master/CONTR **What version of Ajv you are you using?** diff --git a/.github/ISSUE_TEMPLATE/compatibility.md b/.github/ISSUE_TEMPLATE/compatibility.md index 02b649dc7..79aa63999 100644 --- a/.github/ISSUE_TEMPLATE/compatibility.md +++ b/.github/ISSUE_TEMPLATE/compatibility.md @@ -8,11 +8,11 @@ assignees: '' --- **The version of Ajv you are using** diff --git a/.github/ISSUE_TEMPLATE/installation.md b/.github/ISSUE_TEMPLATE/installation.md index 6ebbddd0e..1786e9f2f 100644 --- a/.github/ISSUE_TEMPLATE/installation.md +++ b/.github/ISSUE_TEMPLATE/installation.md @@ -8,11 +8,11 @@ assignees: '' --- **What version of Ajv are you using? Does the issue happen if you use the latest version?** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d7feecdbd..7abf655f1 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,7 +2,7 @@ Thank you for submitting a pull request to Ajv. Before continuing, please read the guidelines: -https://github.com/epoberezkin/ajv/blob/master/CONTRIBUTING.md#pull-requests +https://github.com/ajv-validator/ajv/blob/master/CONTRIBUTING.md#pull-requests If the pull request contains code please make sure there is an issue that we agreed to resolve (if it is a documentation improvement there is no need for an issue). diff --git a/.github/config.yml b/.github/config.yml index ce9efb997..1f6c1054f 100644 --- a/.github/config.yml +++ b/.github/config.yml @@ -25,8 +25,8 @@ githubLabels: - [Tutorial by Space Telescope Science Institute](http://json-schema.org/understanding-json-schema/) - - [validation keywords](https://github.com/epoberezkin/ajv#validation-keywords) (in Ajv docs) + - [validation keywords](https://github.com/ajv-validator/ajv#validation-keywords) (in Ajv docs) - - [combining schemas](https://github.com/epoberezkin/ajv#ref) (in Ajv docs) + - [combining schemas](https://github.com/ajv-validator/ajv#ref) (in Ajv docs) - [Tutorial by @epoberezkin](https://code.tutsplus.com/tutorials/validating-data-with-json-schema-part-1--cms-25343) diff --git a/COERCION.md b/COERCION.md index f310c2d67..6a0a41a68 100644 --- a/COERCION.md +++ b/COERCION.md @@ -1,6 +1,6 @@ # Ajv type coercion rules -To enable type coercion pass option `coerceTypes` to Ajv with `true` or `array` (it is `false` by default). See [example](https://github.com/epoberezkin/ajv#coercing-data-types). +To enable type coercion pass option `coerceTypes` to Ajv with `true` or `array` (it is `false` by default). See [example](https://github.com/ajv-validator/ajv#coercing-data-types). The coercion rules are different from JavaScript: - to validate user input as expected diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 26f4bf029..4f2f8aaed 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,7 +24,7 @@ Ajv has a lot of features and maintaining documentation takes time. I appreciate ## Issues -Before submitting the issue please search the existing issues and also review [Frequently Asked Questions](https://github.com/epoberezkin/ajv/blob/master/FAQ.md). +Before submitting the issue please search the existing issues and also review [Frequently Asked Questions](https://github.com/ajv-validator/ajv/blob/master/FAQ.md). I would really appreciate the time you spend providing all the information and reducing both your schema and data to the smallest possible size when they still have the issue. Simplifying the issue also makes it more valuable for other users (in cases it turns out to be an incorrect usage rather than a bug). @@ -34,7 +34,7 @@ I would really appreciate the time you spend providing all the information and r 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). +2. Ajv options object (see https://github.com/ajv-validator/ajv#options). 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. @@ -42,7 +42,7 @@ Please make sure to include the following information in the issue: Please include the link to the working code sample at Runkit.com (please clone https://runkit.com/esp/ajv-issue) - it will speed up investigation and fixing. -[Create bug report](https://github.com/epoberezkin/ajv/issues/new?template=bug-or-error-report.md). +[Create bug report](https://github.com/ajv-validator/ajv/issues/new?template=bug-or-error-report.md). #### Security vulnerabilities @@ -56,7 +56,7 @@ Please do NOT report security vulnerabilities via GitHub issues. #### Change proposals -[Create a proposal](https://github.com/epoberezkin/ajv/issues/new?template=change.md) for a new feature, option or some other improvement. +[Create a proposal](https://github.com/ajv-validator/ajv/issues/new?template=change.md) for a new feature, option or some other improvement. Please include this information: @@ -76,7 +76,7 @@ Please include as much details as possible. #### Browser and compatibility issues -[Create an issue](https://github.com/epoberezkin/ajv/issues/new?template=compatibility.md) to report a compatibility problem that only happens in a particular environment (when your code works correctly in node.js v8+ in linux systems but fails in some other environment). +[Create an issue](https://github.com/ajv-validator/ajv/issues/new?template=compatibility.md) to report a compatibility problem that only happens in a particular environment (when your code works correctly in node.js v8+ in linux systems but fails in some other environment). Please include this information: @@ -90,7 +90,7 @@ Please include this information: #### Installation and dependency issues -[Create an issue](https://github.com/epoberezkin/ajv/issues/new?template=installation.md) to report problems that happen during Ajv installation or when Ajv is missing some dependency. +[Create an issue](https://github.com/ajv-validator/ajv/issues/new?template=installation.md) to report problems that happen during Ajv installation or when Ajv is missing some dependency. Before submitting the issue, please try the following: - use the latest stable Node.js and `npm` @@ -143,9 +143,9 @@ npm run test-fast git commit -nm 'type: message' ``` -All validation functions are generated using doT templates in [dot](https://github.com/epoberezkin/ajv/tree/master/lib/dot) folder. Templates are precompiled so doT is not a run-time dependency. +All validation functions are generated using doT templates in [dot](https://github.com/ajv-validator/ajv/tree/master/lib/dot) folder. Templates are precompiled so doT is not a run-time dependency. -`npm run build` - compiles templates to [dotjs](https://github.com/epoberezkin/ajv/tree/master/lib/dotjs) folder. +`npm run build` - compiles templates to [dotjs](https://github.com/ajv-validator/ajv/tree/master/lib/dotjs) folder. `npm run watch` - automatically compiles templates when files in dot folder change @@ -154,7 +154,7 @@ All validation functions are generated using doT templates in [dot](https://gith To make accepting your changes faster please follow these steps: -1. Submit an [issue with the bug](https://github.com/epoberezkin/ajv/issues/new) or with the proposed change (unless the contribution is to fix the documentation typos and mistakes). +1. Submit an [issue with the bug](https://github.com/ajv-validator/ajv/issues/new) or with the proposed change (unless the contribution is to fix the documentation typos and mistakes). 2. Please describe the proposed api and implementation plan (unless the issue is a relatively simple bug and fixing it doesn't change any api). 3. Once agreed, please write as little code as possible to achieve the desired result. 4. Please avoid unnecessary changes, refactoring or changing coding styles as part of your change (unless the change was proposed as refactoring). diff --git a/CUSTOM.md b/CUSTOM.md index f1a43d5f8..68cac5ab3 100644 --- a/CUSTOM.md +++ b/CUSTOM.md @@ -34,7 +34,7 @@ This way to define keywords is useful for: - testing your keywords before converting them to compiled/inlined keywords - defining keywords that do not depend on the schema value (e.g., when the value is always `true`). In this case you can add option `schema: false` to the keyword definition and the schemas won't be passed to the validation function, it will only receive the same 4 parameters as compiled validation function (see the next section). - defining keywords where the schema is a value used in some expression. -- defining keywords that support [$data reference](https://github.com/epoberezkin/ajv#data-reference) - in this case validation function is required, either as the only option or in addition to compile, macro or inline function (see below). +- defining keywords that support [$data reference](https://github.com/ajv-validator/ajv#data-reference) - in this case validation function is required, either as the only option or in addition to compile, macro or inline function (see below). __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. @@ -197,7 +197,7 @@ console.log(validate([3,4,5])); // true, number 5 matches schema inside "contain `contains` keyword is already available in Ajv with option `v5: true`. -See the example of defining recursive macro keyword `deepProperties` in the [test](https://github.com/epoberezkin/ajv/blob/master/spec/custom.spec.js#L151). +See the example of defining recursive macro keyword `deepProperties` in the [test](https://github.com/ajv-validator/ajv/blob/master/spec/custom.spec.js#L151). ### Define keyword with "inline" compilation function @@ -294,7 +294,7 @@ The first parameter passed to inline keyword compilation function (and the 3rd p - _opts_ - Ajv instance option. You should not be changing them. - _formats_ - all formats available in Ajv instance, including the custom ones. - _compositeRule_ - boolean indicating that the current schema is inside the compound keyword where failing some rule doesn't mean validation failure (`anyOf`, `oneOf`, `not`, `if` in `switch`). This flag is used to determine whether you can return validation result immediately after any error in case the option `allErrors` is not `true. You only need to do it if you have many steps in your keywords and potentially can define multiple errors. -- _validate_ - the function you need to use to compile subschemas in your keywords (see the [implementation](https://github.com/epoberezkin/ajv-keywords/blob/master/keywords/dot/switch.jst) of `switch` keyword for example). +- _validate_ - the function you need to use to compile subschemas in your keywords (see the [implementation](https://github.com/ajv-validator/ajv-keywords/blob/master/keywords/dot/switch.jst) of `switch` keyword for example). - _util_ - [Ajv utilities](#ajv-utilities) you can use in your inline compilation functions. - _self_ - Ajv instance. @@ -311,8 +311,8 @@ There is a number of variables and expressions you can use in the generated (val - `'validate.schema' + it.schemaPath` - current level schema available at validation time (the same schema at compile time is `it.schema`). - `'validate.schema' + it.schemaPath + '.' + keyword` - the value of your custom keyword at validation-time. Keyword is passed as the second parameter to the inline compilation function to allow using the same function to compile multiple keywords. - `'valid' + it.level` - the variable that you have to declare and to assign the validation result to if your keyword returns statements rather than expression (`statements: true`). -- `'errors'` - the number of encountered errors. See [Reporting errors in custom keywords](https://github.com/epoberezkin/ajv/blob/master/CUSTOM.md#reporting-errors-in-custom-keywords). -- `'vErrors'` - the array with errors collected so far. See [Reporting errors in custom keywords](https://github.com/epoberezkin/ajv/blob/master/CUSTOM.md#reporting-errors-in-custom-keywords). +- `'errors'` - the number of encountered errors. See [Reporting errors in custom keywords](https://github.com/ajv-validator/ajv/blob/master/CUSTOM.md#reporting-errors-in-custom-keywords). +- `'vErrors'` - the array with errors collected so far. See [Reporting errors in custom keywords](https://github.com/ajv-validator/ajv/blob/master/CUSTOM.md#reporting-errors-in-custom-keywords). ## Ajv utilities @@ -410,7 +410,7 @@ All custom keywords but macro keywords can optionally create custom error messag Synchronous validating and compiled keywords should define errors by assigning them to `.errors` property of the validation function. Asynchronous keywords can return promise that rejects with `new Ajv.ValidationError(errors)`, where `errors` is an array of custom validation errors (if you don't want to define custom errors in asynchronous keyword, its validation function can return the promise that resolves with `false`). -Inline custom keyword should increase error counter `errors` and add error to `vErrors` array (it can be null). This can be done for both synchronous and asynchronous keywords. See [example range keyword](https://github.com/epoberezkin/ajv/blob/master/spec/custom_rules/range_with_errors.jst). +Inline custom keyword should increase error counter `errors` and add error to `vErrors` array (it can be null). This can be done for both synchronous and asynchronous keywords. See [example range keyword](https://github.com/ajv-validator/ajv/blob/master/spec/custom_rules/range_with_errors.jst). When inline keyword performs validation Ajv checks whether it created errors by comparing errors count before and after validation. To skip this check add option `errors` (can be `"full"`, `true` or `false`) to keyword definition: @@ -429,7 +429,7 @@ Each error object should at least have properties `keyword`, `message` and `para Inlined keywords can optionally define `dataPath` and `schemaPath` properties in error objects, that will be assigned by Ajv unless `errors` option of the keyword is `"full"`. -If custom keyword doesn't create errors, the default error will be created in case the keyword fails validation (see [Validation errors](https://github.com/epoberezkin/ajv#validation-errors)). +If custom keyword doesn't create errors, the default error will be created in case the keyword fails validation (see [Validation errors](https://github.com/ajv-validator/ajv#validation-errors)). ## Short-circuit validation diff --git a/FAQ.md b/FAQ.md index 36ac967cd..f010a51c8 100644 --- a/FAQ.md +++ b/FAQ.md @@ -9,7 +9,7 @@ The purpose of this document is to help find answers quicker. I am happy to cont Ajv implements JSON schema specification. Before submitting the issue about the behaviour of any validation keywords please review them in: - [JSON Schema specification](https://tools.ietf.org/html/draft-handrews-json-schema-validation-00) (draft-07) -- [Validation keywords](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md) in Ajv documentation +- [Validation keywords](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md) in Ajv documentation - [JSON Schema tutorial](https://spacetelescope.github.io/understanding-json-schema/) (for draft-04) @@ -20,13 +20,13 @@ Ajv implements JSON schema specification. Before submitting the issue about the ##### Why Ajv validates only the first item of the array? -"items" keyword support [two syntaxes](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#items) - 1) when the schema applies to all items; 2) when there is a different schema for each item in the beginning of the array. This problem means you are using the second syntax. +"items" keyword support [two syntaxes](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md#items) - 1) when the schema applies to all items; 2) when there is a different schema for each item in the beginning of the array. This problem means you are using the second syntax. ## Ajv API for returning validation errors -See [#65](https://github.com/epoberezkin/ajv/issues/65), [#212](https://github.com/epoberezkin/ajv/issues/212), [#236](https://github.com/epoberezkin/ajv/issues/236), [#242](https://github.com/epoberezkin/ajv/issues/242), [#256](https://github.com/epoberezkin/ajv/issues/256). +See [#65](https://github.com/ajv-validator/ajv/issues/65), [#212](https://github.com/ajv-validator/ajv/issues/212), [#236](https://github.com/ajv-validator/ajv/issues/236), [#242](https://github.com/ajv-validator/ajv/issues/242), [#256](https://github.com/ajv-validator/ajv/issues/256). ##### Why Ajv assigns errors as a property of validation function (or instance) instead of returning an object with validation results and errors? @@ -56,7 +56,7 @@ Since the property name is already in the params object, in an application you c ## Additional properties inside compound keywords anyOf, oneOf, etc. -See [#127](https://github.com/epoberezkin/ajv/issues/127), [#129](https://github.com/epoberezkin/ajv/issues/129), [#134](https://github.com/epoberezkin/ajv/issues/134), [#140](https://github.com/epoberezkin/ajv/issues/140), [#193](https://github.com/epoberezkin/ajv/issues/193), [#205](https://github.com/epoberezkin/ajv/issues/205), [#238](https://github.com/epoberezkin/ajv/issues/238), [#264](https://github.com/epoberezkin/ajv/issues/264). +See [#127](https://github.com/ajv-validator/ajv/issues/127), [#129](https://github.com/ajv-validator/ajv/issues/129), [#134](https://github.com/ajv-validator/ajv/issues/134), [#140](https://github.com/ajv-validator/ajv/issues/140), [#193](https://github.com/ajv-validator/ajv/issues/193), [#205](https://github.com/ajv-validator/ajv/issues/205), [#238](https://github.com/ajv-validator/ajv/issues/238), [#264](https://github.com/ajv-validator/ajv/issues/264). ##### Why the keyword `additionalProperties: false` fails validation when some properties are "declared" inside a subschema in `anyOf`/etc.? @@ -85,13 +85,13 @@ There are several ways to implement the described logic that would allow two pro This problem is related to the problem explained above - properties treated as additional in the sense of `additionalProperties` keyword, based on `properties`/`patternProperties` keyword in the same schema object. -See the exemple in [Filtering Data](https://github.com/epoberezkin/ajv#filtering-data) section of readme. +See the exemple in [Filtering Data](https://github.com/ajv-validator/ajv#filtering-data) section of readme. ## Generating schemas with resolved references ($ref) -See [#22](https://github.com/epoberezkin/ajv/issues/22), [#125](https://github.com/epoberezkin/ajv/issues/125), [#146](https://github.com/epoberezkin/ajv/issues/146), [#228](https://github.com/epoberezkin/ajv/issues/228), [#336](https://github.com/epoberezkin/ajv/issues/336), [#454](https://github.com/epoberezkin/ajv/issues/454). +See [#22](https://github.com/ajv-validator/ajv/issues/22), [#125](https://github.com/ajv-validator/ajv/issues/125), [#146](https://github.com/ajv-validator/ajv/issues/146), [#228](https://github.com/ajv-validator/ajv/issues/228), [#336](https://github.com/ajv-validator/ajv/issues/336), [#454](https://github.com/ajv-validator/ajv/issues/454). ##### Why Ajv does not replace references ($ref) with the actual referenced schemas as some validators do? @@ -108,4 +108,4 @@ There were many conversations about the meaning of `$ref` in [JSON Schema GitHub There are two possible approaches: 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. +2. Use a specially constructed JSON Schema with a [custom keyword](https://github.com/ajv-validator/ajv/blob/master/CUSTOM.md) to traverse and modify your schema. diff --git a/KEYWORDS.md b/KEYWORDS.md index 32740dc41..6601a9a1b 100644 --- a/KEYWORDS.md +++ b/KEYWORDS.md @@ -202,7 +202,7 @@ _invalid_: `"abc"` ### `formatMaximum` / `formatMinimum` and `formatExclusiveMaximum` / `formatExclusiveMinimum` (proposed) -Defined in [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) package. +Defined in [ajv-keywords](https://github.com/ajv-validator/ajv-keywords) package. The value of keyword `formatMaximum` (`formatMinimum`) should be a string. This value is the maximum (minimum) allowed value for the data to be valid as determined by `format` keyword. @@ -618,7 +618,7 @@ _invalid_: `{"foo": "any value"}` ### `patternRequired` (proposed) -Defined in [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) package. +Defined in [ajv-keywords](https://github.com/ajv-validator/ajv-keywords) package. The value of this keyword should be an array of strings, each string being a regular expression. For data object to be valid each regular expression in this array should match at least one property name in the data object. @@ -670,7 +670,7 @@ _valid_: `"foo"` _invalid_: any other value -The same can be achieved with `enum` keyword using the array with one item. But `const` keyword is more than just a syntax sugar for `enum`. In combination with the [$data reference](https://github.com/epoberezkin/ajv#data-reference) it allows to define equality relations between different parts of the data. This cannot be achieved with `enum` keyword even with `$data` reference because `$data` cannot be used in place of one item - it can only be used in place of the whole array in `enum` keyword. +The same can be achieved with `enum` keyword using the array with one item. But `const` keyword is more than just a syntax sugar for `enum`. In combination with the [$data reference](https://github.com/ajv-validator/ajv#data-reference) it allows to define equality relations between different parts of the data. This cannot be achieved with `enum` keyword even with `$data` reference because `$data` cannot be used in place of one item - it can only be used in place of the whole array in `enum` keyword. __Example__ diff --git a/README.md b/README.md index 12b271cb9..b0f7bda07 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Thank you [JSON Schema draft-07](http://json-schema.org/latest/json-schema-validation.html) is published. -[Ajv version 6.0.0](https://github.com/epoberezkin/ajv/releases/tag/v6.0.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](https://github.com/ajv-validator/ajv/releases/tag/v6.0.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 schemas you need to explicitly add the meta-schema to the validator instance: @@ -80,7 +80,7 @@ ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); - [Performance](#performance) - [Features](#features) - [Getting started](#getting-started) -- [Frequently Asked Questions](https://github.com/epoberezkin/ajv/blob/master/FAQ.md) +- [Frequently Asked Questions](https://github.com/ajv-validator/ajv/blob/master/FAQ.md) - [Using in browser](#using-in-browser) - [Command line interface](#command-line-interface) - Validation @@ -134,7 +134,7 @@ Performance of different validators by [json-schema-benchmark](https://github.co ## Features - 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)) + - all validation keywords (see [JSON Schema validation keywords](https://github.com/ajv-validator/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) @@ -144,14 +144,14 @@ Performance of different validators by [json-schema-benchmark](https://github.co - [asynchronous loading](#asynchronous-schema-compilation) of referenced schemas during compilation - "All errors" validation mode with [option allErrors](#options) - [error messages with parameters](#validation-errors) describing error reasons to allow creating custom error messages -- i18n error messages support with [ajv-i18n](https://github.com/epoberezkin/ajv-i18n) package +- i18n error messages support with [ajv-i18n](https://github.com/ajv-validator/ajv-i18n) package - [filtering data](#filtering-data) from additional properties - [assigning defaults](#assigning-defaults) to missing properties and items - [coercing data](#coercing-data-types) to the types specified in `type` keywords - [custom keywords](#defining-custom-keywords) - draft-06/07 keywords `const`, `contains`, `propertyNames` and `if/then/else` - draft-06 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/ajv-validator/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 @@ -235,15 +235,15 @@ Ajv is tested with these browsers: [![Sauce Test Status](https://saucelabs.com/browser-matrix/epoberezkin.svg)](https://saucelabs.com/u/epoberezkin) -__Please note__: some frameworks, e.g. Dojo, may redefine global require in such way that is not compatible with CommonJS module format. In such case Ajv bundle has to be loaded before the framework and then you can use global Ajv (see issue [#234](https://github.com/epoberezkin/ajv/issues/234)). +__Please note__: some frameworks, e.g. Dojo, may redefine global require in such way that is not compatible with CommonJS module format. In such case Ajv bundle has to be loaded before the framework and then you can use global Ajv (see issue [#234](https://github.com/ajv-validator/ajv/issues/234)). ## Command line interface -CLI is available as a separate npm package [ajv-cli](https://github.com/jessedc/ajv-cli). It supports: +CLI is available as a separate npm package [ajv-cli](https://github.com/ajv-validator/ajv-cli). It supports: - 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)) +- BETA: generating standalone module exporting a validation function to be used without Ajv (using [ajv-pack](https://github.com/ajv-validator/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 @@ -258,20 +258,20 @@ CLI is available as a separate npm package [ajv-cli](https://github.com/jessedc/ 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 -- [for strings](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#keywords-for-strings) - maxLength, minLength, pattern, format -- [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, [if/then/else](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#ifthenelse) +- [type](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md#type) +- [for numbers](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md#keywords-for-numbers) - maximum, minimum, exclusiveMaximum, exclusiveMinimum, multipleOf +- [for strings](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md#keywords-for-strings) - maxLength, minLength, pattern, format +- [for arrays](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md#keywords-for-arrays) - maxItems, minItems, uniqueItems, items, additionalItems, [contains](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md#contains) +- [for objects](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md#keywords-for-objects) - maxProperties, minProperties, required, properties, patternProperties, additionalProperties, dependencies, [propertyNames](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md#propertynames) +- [for all types](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md#keywords-for-all-types) - enum, [const](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md#const) +- [compound keywords](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md#compound-keywords) - not, oneOf, anyOf, allOf, [if/then/else](https://github.com/ajv-validator/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/ajv-validator/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. +- [patternRequired](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md#patternrequired-proposed) - like `required` but with patterns that some property should match. +- [formatMaximum, formatMinimum, formatExclusiveMaximum, formatExclusiveMinimum](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md#formatmaximum--formatminimum-and-exclusiveformatmaximum--exclusiveformatminimum-proposed) - setting limits for date, time, etc. -See [JSON Schema validation keywords](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md) for more details. +See [JSON Schema validation keywords](https://github.com/ajv-validator/ajv/blob/master/KEYWORDS.md) for more details. ## Annotation keywords @@ -321,7 +321,7 @@ You can add additional formats and replace any of the formats above using [addFo The option `unknownFormats` allows changing the default behaviour when an unknown format is encountered. In this case Ajv can either fail schema compilation (default) or ignore it (default in versions before 5.0.0). You also can whitelist specific format(s) to be ignored. See [Options](#options) for details. -You can find regular expressions used for format validation and the sources that were used in [formats.js](https://github.com/epoberezkin/ajv/blob/master/lib/compile/formats.js). +You can find regular expressions used for format validation and the sources that were used in [formats.js](https://github.com/ajv-validatorv/ajv/blob/master/lib/compile/formats.js). ## Combining schemas with $ref @@ -430,7 +430,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/ajv-validator/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: @@ -489,7 +489,7 @@ The schemas above are equivalent to this schema: The properties `source` and `with` in the keywords `$merge` and `$patch` can use absolute or relative `$ref` to point to other schemas previously added to the Ajv instance or to the fragments of the current schema. -See the package [ajv-merge-patch](https://github.com/epoberezkin/ajv-merge-patch) for more information. +See the package [ajv-merge-patch](https://github.com/ajv-validator/ajv-merge-patch) for more information. ## Defining custom keywords @@ -537,9 +537,9 @@ console.log(validate(2)); // false console.log(validate(4)); // false ``` -Several custom keywords (typeof, instanceof, range and propertyNames) are defined in [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) package - they can be used for your schemas and as a starting point for your own custom keywords. +Several custom keywords (typeof, instanceof, range and propertyNames) are defined in [ajv-keywords](https://github.com/ajv-validator/ajv-keywords) package - they can be used for your schemas and as a starting point for your own custom keywords. -See [Defining custom keywords](https://github.com/epoberezkin/ajv/blob/master/CUSTOM.md) for more details. +See [Defining custom keywords](https://github.com/ajv-validator/ajv/blob/master/CUSTOM.md) for more details. ## Asynchronous schema compilation @@ -638,7 +638,7 @@ validate({ userId: 1, postId: 19 }) ### Using transpilers with asynchronous validation functions. -[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). +[ajv-async](https://github.com/ajv-validator/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 @@ -682,7 +682,7 @@ Ajv treats JSON schemas as trusted as your application code. This security model If your schemas are received from untrusted sources (or generated from untrusted data) there are several scenarios you need to prevent: - compiling schemas can cause stack overflow (if they are too deep) -- compiling schemas can be slow (e.g. [#557](https://github.com/epoberezkin/ajv/issues/557)) +- compiling schemas can be slow (e.g. [#557](https://github.com/ajv-validator/ajv/issues/557)) - validating certain data can be slow It is difficult to predict all the scenarios, but at the very least it may help to limit the size of untrusted schemas (e.g. limit JSON string length) and also the maximum schema object depth (that can be high for relatively small JSON strings). You also may want to mitigate slow regular expressions in `pattern` and `patternProperties` keywords. @@ -692,7 +692,7 @@ Regardless the measures you take, using untrusted schemas increases security ris ##### Circular references in JavaScript objects -Ajv does not support schemas and validated data that have circular references in objects. See [issue #802](https://github.com/epoberezkin/ajv/issues/802). +Ajv does not support schemas and validated data that have circular references in objects. See [issue #802](https://github.com/ajv-validator/ajv/issues/802). An attempt to compile such schemas or validate such data would cause stack overflow (or will not complete in case of asynchronous validation). Depending on the parser you use, untrusted data can lead to circular references. @@ -707,7 +707,7 @@ Some keywords in JSON Schemas can lead to very slow validation for certain data. __Please note__: The suggestions above to prevent slow validation would only work if you do NOT use `allErrors: true` in production code (using it would continue validation after validation errors). -You can validate your JSON schemas against [this meta-schema](https://github.com/epoberezkin/ajv/blob/master/lib/refs/json-schema-secure.json) to check that these recommendations are followed: +You can validate your JSON schemas against [this meta-schema](https://github.com/ajv-validator/ajv/blob/master/lib/refs/json-schema-secure.json) to check that these recommendations are followed: ```javascript const isSchemaSecure = ajv.compile(require('ajv/lib/refs/json-schema-secure.json')); @@ -728,7 +728,7 @@ Certain regular expressions can lead to the exponential evaluation time even wit Please assess the regular expressions you use in the schemas on their vulnerability to this attack - see [safe-regex](https://github.com/substack/safe-regex), for example. -__Please note__: some formats that Ajv implements use [regular expressions](https://github.com/epoberezkin/ajv/blob/master/lib/compile/formats.js) that can be vulnerable to ReDoS attack, so if you use Ajv to validate data from untrusted sources __it is strongly recommended__ to consider the following: +__Please note__: some formats that Ajv implements use [regular expressions](https://github.com/ajv-validator/ajv/blob/master/lib/compile/formats.js) that can be vulnerable to ReDoS attack, so if you use Ajv to validate data from untrusted sources __it is strongly recommended__ to consider the following: - making assessment of "format" implementations in Ajv. - using `format: 'fast'` option that simplifies some of the regular expressions (although it does not guarantee that they are safe). @@ -808,7 +808,7 @@ The intention of the schema above is to allow objects with either the string pro With the option `removeAdditional: true` the validation will pass for the object `{ "foo": "abc"}` but will fail for the object `{"bar": 1}`. It happens because while the first subschema in `oneOf` is validated, the property `bar` is removed because it is an additional property according to the standard (because it is not included in `properties` keyword in the same schema). -While this behaviour is unexpected (issues [#129](https://github.com/epoberezkin/ajv/issues/129), [#134](https://github.com/epoberezkin/ajv/issues/134)), it is correct. To have the expected behaviour (both objects are allowed and additional properties are removed) the schema has to be refactored in this way: +While this behaviour is unexpected (issues [#129](https://github.com/ajv-validator/ajv/issues/129), [#134](https://github.com/ajv-validator/ajv/issues/134)), it is correct. To have the expected behaviour (both objects are allowed and additional properties are removed) the schema has to be refactored in this way: ```json { @@ -882,7 +882,7 @@ console.log(data); // [ 1, "foo" ] `default` keywords in other cases are ignored: - not in `properties` or `items` subschemas -- in schemas inside `anyOf`, `oneOf` and `not` (see [#42](https://github.com/epoberezkin/ajv/issues/42)) +- in schemas inside `anyOf`, `oneOf` and `not` (see [#42](https://github.com/ajv-validator/ajv/issues/42)) - in `if` subschema of `switch` keyword - in schemas generated by custom macro keywords @@ -940,7 +940,7 @@ console.log(data); // { "foo": [1], "bar": false } The coercion rules, as you can see from the example, are different from JavaScript both to validate user input as expected and to have the coercion reversible (to correctly validate cases where different types are defined in subschemas of "anyOf" and other compound keywords). -See [Coercion rules](https://github.com/epoberezkin/ajv/blob/master/COERCION.md) for details. +See [Coercion rules](https://github.com/ajv-validator/ajv/blob/master/COERCION.md) for details. ## API @@ -1058,9 +1058,9 @@ Function should return validation result as `true` or `false`. If object is passed it should have properties `validate`, `compare` and `async`: - _validate_: a string, RegExp or a function as described above. -- _compare_: an optional comparison function that accepts two strings and compares them according to the format meaning. This function is used with keywords `formatMaximum`/`formatMinimum` (defined in [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) package). It should return `1` if the first value is bigger than the second value, `-1` if it is smaller and `0` if it is equal. +- _compare_: an optional comparison function that accepts two strings and compares them according to the format meaning. This function is used with keywords `formatMaximum`/`formatMinimum` (defined in [ajv-keywords](https://github.com/ajv-validator/ajv-keywords) package). It should return `1` if the first value is bigger than the second value, `-1` if it is smaller and `0` if it is equal. - _async_: an optional `true` value if `validate` is an asynchronous function; in this case it should return a promise that resolves with a value `true` or `false`. -- _type_: an optional type of data that the format applies to. It can be `"string"` (default) or `"number"` (see https://github.com/epoberezkin/ajv/issues/291#issuecomment-259923858). If the type of data is different, the validation will pass. +- _type_: an optional type of data that the format applies to. It can be `"string"` (default) or `"number"` (see https://github.com/ajv-validator/ajv/issues/291#issuecomment-259923858). If the type of data is different, the validation will pass. Custom formats can be also added via `formats` option. @@ -1234,7 +1234,7 @@ Defaults: - `true` - insert defaults by value (object literal is used). - `"empty"` - in addition to missing or undefined, use defaults for properties and items that are equal to `null` or `""` (an empty string). - `"shared"` (deprecated) - insert defaults by reference. If the default is an object, it will be shared by all instances of validated data. If you modify the inserted default in the validated data, it will be modified in the schema as well. -- _coerceTypes_: change data type of data to match `type` keyword. See the example in [Coercing data types](#coercing-data-types) and [coercion rules](https://github.com/epoberezkin/ajv/blob/master/COERCION.md). Option values: +- _coerceTypes_: change data type of data to match `type` keyword. See the example in [Coercing data types](#coercing-data-types) and [coercion rules](https://github.com/ajv-validator/ajv/blob/master/COERCION.md). Option values: - `false` (default) - no type coercion. - `true` - coerce scalar data types. - `"array"` - in addition to coercions between scalar types, coerce scalar data to an array with one element and vice versa (as required by the schema). @@ -1254,7 +1254,7 @@ Defaults: ##### Asynchronous validation options -- _transpile_: Requires [ajv-async](https://github.com/epoberezkin/ajv-async) package. It determines whether Ajv transpiles compiled asynchronous validation function. Option values: +- _transpile_: Requires [ajv-async](https://github.com/ajv-validator/ajv-async) package. It determines whether Ajv transpiles compiled asynchronous validation function. Option values: - `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. @@ -1275,13 +1275,13 @@ Defaults: - _passContext_: pass validation context to custom keyword functions. If this option is `true` and you pass some context to the compiled validation function with `validate.call(context, data)`, the `context` will be available as `this` in your custom keywords. By default `this` is Ajv instance. - _loopRequired_: by default `required` keyword is compiled into a single expression (or a sequence of statements in `allErrors` mode). In case of a very large number of properties in this keyword it may result in a very big validation function. Pass integer to set the number of properties above which `required` keyword will be validated in a loop - smaller validation function size but also worse performance. - _ownProperties_: by default Ajv iterates over all enumerable object properties; when this option is `true` only own enumerable object properties (i.e. found directly on the object rather than on its prototype) are iterated. Contributed by @mbroadst. -- _multipleOfPrecision_: by default `multipleOf` keyword is validated by comparing the result of division with parseInt() of that result. It works for dividers that are bigger than 1. For small dividers such as 0.01 the result of the division is usually not integer (even when it should be integer, see issue [#84](https://github.com/epoberezkin/ajv/issues/84)). If you need to use fractional dividers set this option to some positive integer N to have `multipleOf` validated using this formula: `Math.abs(Math.round(division) - division) < 1e-N` (it is slower but allows for float arithmetics deviations). +- _multipleOfPrecision_: by default `multipleOf` keyword is validated by comparing the result of division with parseInt() of that result. It works for dividers that are bigger than 1. For small dividers such as 0.01 the result of the division is usually not integer (even when it should be integer, see issue [#84](https://github.com/ajv-validator/ajv/issues/84)). If you need to use fractional dividers set this option to some positive integer N to have `multipleOf` validated using this formula: `Math.abs(Math.round(division) - division) < 1e-N` (it is slower but allows for float arithmetics deviations). - _errorDataPath_ (deprecated): set `dataPath` to point to 'object' (default) or to 'property' when validating keywords `required`, `additionalProperties` and `dependencies`. -- _messages_: Include human-readable messages in errors. `true` by default. `false` can be passed when custom messages are used (e.g. with [ajv-i18n](https://github.com/epoberezkin/ajv-i18n)). +- _messages_: Include human-readable messages in errors. `true` by default. `false` can be passed when custom messages are used (e.g. with [ajv-i18n](https://github.com/ajv-validator/ajv-i18n)). - _sourceCode_: add `sourceCode` property to validating function (for debugging; this code can be different from the result of toString call). - _processCode_: an optional function to process generated code before it is passed to Function constructor. It can be used to either beautify (the validating function is generated without line-breaks) or to transpile code. Starting from version 5.0.0 this option replaced options: - `beautify` that formatted the generated function using [js-beautify](https://github.com/beautify-web/js-beautify). If you want to beautify the generated code pass a function calling `require('js-beautify').js_beautify` as `processCode: code => js_beautify(code)`. - - `transpile` that transpiled asynchronous validation function. You can still use `transpile` option with [ajv-async](https://github.com/epoberezkin/ajv-async) package. See [Asynchronous validation](#asynchronous-validation) for more information. + - `transpile` that transpiled asynchronous validation function. You can still use `transpile` option with [ajv-async](https://github.com/ajv-validator/ajv-async) package. See [Asynchronous validation](#asynchronous-validation) for more information. - _cache_: an optional instance of cache to store compiled schemas using stable-stringified schema as a key. For example, set-associative cache [sacjs](https://github.com/epoberezkin/sacjs) can be used. If not passed then a simple hash is used which is good enough for the common use case (a limited number of statically defined schemas). Cache should have methods `put(key, value)`, `get(key)`, `del(key)` and `clear()`. - _serialize_: an optional function to serialize schema to cache key. Pass `false` to use schema itself as a key (e.g., if WeakMap used as a cache). By default [fast-json-stable-stringify](https://github.com/epoberezkin/fast-json-stable-stringify) is used. @@ -1298,7 +1298,7 @@ Each error is an object with the following properties: - _keyword_: validation keyword. - _dataPath_: the path to the part of the data that was validated. By default `dataPath` uses JavaScript property access notation (e.g., `".prop[1].subProp"`). When the option `jsonPointers` is true (see [Options](#options)) `dataPath` will be set using JSON pointer standard (e.g., `"/prop/1/subProp"`). - _schemaPath_: the path (JSON-pointer as a URI fragment) to the schema of the keyword that failed validation. -- _params_: the object with the additional information about error that can be used to create custom error messages (e.g., using [ajv-i18n](https://github.com/epoberezkin/ajv-i18n) package). See below for parameters set by all keywords. +- _params_: the object with the additional information about error that can be used to create custom error messages (e.g., using [ajv-i18n](https://github.com/ajv-validator/ajv-i18n) package). See below for parameters set by all keywords. - _message_: the standard error message (can be excluded with option `messages` set to false). - _schema_: the schema of the keyword (added with `verbose` option). - _parentSchema_: the schema containing the keyword (added with `verbose` option) @@ -1373,15 +1373,15 @@ If you have published a useful plugin please submit a PR to add it to the next s ## Related packages -- [ajv-async](https://github.com/epoberezkin/ajv-async) - plugin to configure async validation mode +- [ajv-async](https://github.com/ajv-validator/ajv-async) - plugin to configure async validation mode - [ajv-bsontype](https://github.com/BoLaMN/ajv-bsontype) - plugin to validate mongodb's bsonType formats - [ajv-cli](https://github.com/jessedc/ajv-cli) - command line interface -- [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) - 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 (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 +- [ajv-errors](https://github.com/ajv-validator/ajv-errors) - plugin for custom error messages +- [ajv-i18n](https://github.com/ajv-validator/ajv-i18n) - internationalised error messages +- [ajv-istanbul](https://github.com/ajv-validator/ajv-istanbul) - plugin to instrument generated validation code to measure test coverage of your schemas +- [ajv-keywords](https://github.com/ajv-validator/ajv-keywords) - plugin with custom validation keywords (select, typeof, etc.) +- [ajv-merge-patch](https://github.com/ajv-validator/ajv-merge-patch) - plugin with keywords $merge and $patch +- [ajv-pack](https://github.com/ajv-validator/ajv-pack) - produces a compact module exporting validation functions - [ajv-formats-draft2019](https://github.com/luzlab/ajv-formats-draft2019) - format validators for draft2019 that aren't already included in ajv (ie. `idn-hostname`, `idn-email`, `iri`, `iri-reference` and `duration`). ## Some packages using Ajv @@ -1419,33 +1419,33 @@ npm test ## Contributing -All validation functions are generated using doT templates in [dot](https://github.com/epoberezkin/ajv/tree/master/lib/dot) folder. Templates are precompiled so doT is not a run-time dependency. +All validation functions are generated using doT templates in [dot](https://github.com/ajv-validator/ajv/tree/master/lib/dot) folder. Templates are precompiled so doT is not a run-time dependency. -`npm run build` - compiles templates to [dotjs](https://github.com/epoberezkin/ajv/tree/master/lib/dotjs) folder. +`npm run build` - compiles templates to [dotjs](https://github.com/ajv-validator/ajv/tree/master/lib/dotjs) folder. `npm run watch` - automatically compiles templates when files in dot folder change -Please see [Contributing guidelines](https://github.com/epoberezkin/ajv/blob/master/CONTRIBUTING.md) +Please see [Contributing guidelines](https://github.com/ajv-validator/ajv/blob/master/CONTRIBUTING.md) ## Changes history -See https://github.com/epoberezkin/ajv/releases +See https://github.com/ajv-validator/ajv/releases -__Please note__: [Changes in version 6.0.0](https://github.com/epoberezkin/ajv/releases/tag/v6.0.0). +__Please note__: [Changes in version 6.0.0](https://github.com/ajv-validator/ajv/releases/tag/v6.0.0). -[Version 5.0.0](https://github.com/epoberezkin/ajv/releases/tag/5.0.0). +[Version 5.0.0](https://github.com/ajv-validator/ajv/releases/tag/5.0.0). -[Version 4.0.0](https://github.com/epoberezkin/ajv/releases/tag/4.0.0). +[Version 4.0.0](https://github.com/ajv-validator/ajv/releases/tag/4.0.0). -[Version 3.0.0](https://github.com/epoberezkin/ajv/releases/tag/3.0.0). +[Version 3.0.0](https://github.com/ajv-validator/ajv/releases/tag/3.0.0). -[Version 2.0.0](https://github.com/epoberezkin/ajv/releases/tag/2.0.0). +[Version 2.0.0](https://github.com/ajv-validator/ajv/releases/tag/2.0.0). ## Code of conduct -Please review and follow the [Code of conduct](https://github.com/epoberezkin/ajv/blob/master/CODE_OF_CONDUCT.md). +Please review and follow the [Code of conduct](https://github.com/ajv-validator/ajv/blob/master/CODE_OF_CONDUCT.md). Please report any unacceptable behaviour to ajv.validator@gmail.com - it will be reviewed by the project team. @@ -1457,4 +1457,4 @@ Ajv is a part of [Tidelift subscription](https://tidelift.com/subscription/pkg/n ## License -[MIT](https://github.com/epoberezkin/ajv/blob/master/LICENSE) +[MIT](https://github.com/ajv-validator/ajv/blob/master/LICENSE) diff --git a/bower.json b/bower.json index 048c089b6..507989c62 100644 --- a/bower.json +++ b/bower.json @@ -11,7 +11,7 @@ "schema", "validator" ], - "homepage": "https://github.com/epoberezkin/ajv", + "homepage": "https://github.com/ajv-validator/ajv", "moduleType": [ "amd", "globals", diff --git a/lib/compile/equal.js b/lib/compile/equal.js index 4b271d543..d6c2acef8 100644 --- a/lib/compile/equal.js +++ b/lib/compile/equal.js @@ -1,5 +1,5 @@ 'use strict'; // do NOT remove this file - it would break pre-compiled schemas -// https://github.com/epoberezkin/ajv/issues/889 +// https://github.com/ajv-validator/ajv/issues/889 module.exports = require('fast-deep-equal'); diff --git a/lib/data.js b/lib/data.js index 5f1ad85ef..f11142bec 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/ajv-validator/ajv/master/lib/refs/data.json#' } ] }; } diff --git a/lib/definition_schema.js b/lib/definition_schema.js index e5f6b911c..ad86d4f0b 100644 --- a/lib/definition_schema.js +++ b/lib/definition_schema.js @@ -3,7 +3,7 @@ var metaSchema = require('./refs/json-schema-draft-07.json'); module.exports = { - $id: 'https://github.com/epoberezkin/ajv/blob/master/lib/definition_schema.js', + $id: 'https://github.com/ajv-validator/ajv/blob/master/lib/definition_schema.js', definitions: { simpleTypes: metaSchema.definitions.simpleTypes }, diff --git a/lib/keyword.js b/lib/keyword.js index 5fec19a67..06da9a2df 100644 --- a/lib/keyword.js +++ b/lib/keyword.js @@ -46,7 +46,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/ajv-validator/ajv/master/lib/refs/data.json#' } ] }; } diff --git a/lib/refs/data.json b/lib/refs/data.json index 87a2d1437..64aac5b61 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/ajv-validator/ajv/master/lib/refs/data.json#", "description": "Meta-schema for $data reference (JSON Schema extension proposal)", "type": "object", "required": [ "$data" ], diff --git a/lib/refs/json-schema-secure.json b/lib/refs/json-schema-secure.json index 66faf84f8..0f7aad479 100644 --- a/lib/refs/json-schema-secure.json +++ b/lib/refs/json-schema-secure.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-secure.json#", + "$id": "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/json-schema-secure.json#", "title": "Meta-schema for the security assessment of JSON Schemas", "description": "If a JSON Schema fails validation against this meta-schema, it may be unsafe to validate untrusted data", "definitions": { diff --git a/package.json b/package.json index 0c085c672..10717b435 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/epoberezkin/ajv.git" + "url": "https://github.com/ajv-validator/ajv.git" }, "keywords": [ "JSON", @@ -57,9 +57,9 @@ "author": "Evgeny Poberezkin", "license": "MIT", "bugs": { - "url": "https://github.com/epoberezkin/ajv/issues" + "url": "https://github.com/ajv-validator/ajv/issues" }, - "homepage": "https://github.com/epoberezkin/ajv", + "homepage": "https://github.com/ajv-validator/ajv", "tonicExampleFilename": ".tonic_example.js", "dependencies": { "fast-deep-equal": "^3.1.1", diff --git a/scripts/publish-built-version b/scripts/publish-built-version index 2796ed25f..1b5712372 100755 --- a/scripts/publish-built-version +++ b/scripts/publish-built-version @@ -8,7 +8,7 @@ if [[ -n $TRAVIS_TAG && $TRAVIS_JOB_NUMBER =~ ".3" ]]; then git config user.email "$GIT_USER_EMAIL" git config user.name "$GIT_USER_NAME" - git clone https://${GITHUB_TOKEN}@github.com/epoberezkin/ajv-dist.git ../ajv-dist + git clone https://${GITHUB_TOKEN}@github.com/ajv-validator/ajv-dist.git ../ajv-dist rm -rf ../ajv-dist/dist mkdir ../ajv-dist/dist diff --git a/scripts/travis-gh-pages b/scripts/travis-gh-pages index 46ded1611..b3d4f3d0f 100755 --- a/scripts/travis-gh-pages +++ b/scripts/travis-gh-pages @@ -5,7 +5,7 @@ set -e if [[ "$TRAVIS_BRANCH" == "master" && "$TRAVIS_PULL_REQUEST" == "false" && $TRAVIS_JOB_NUMBER =~ ".3" ]]; then git diff --name-only $TRAVIS_COMMIT_RANGE | grep -qE '\.md$|^LICENSE$|travis-gh-pages$' && { rm -rf ../gh-pages - git clone -b gh-pages --single-branch https://${GITHUB_TOKEN}@github.com/epoberezkin/ajv.git ../gh-pages + git clone -b gh-pages --single-branch https://${GITHUB_TOKEN}@github.com/ajv-validator/ajv.git ../gh-pages mkdir -p ../gh-pages/_source cp *.md ../gh-pages/_source cp LICENSE ../gh-pages/_source diff --git a/spec/security/array.json b/spec/security/array.json index b089a49bb..25b655f95 100644 --- a/spec/security/array.json +++ b/spec/security/array.json @@ -1,7 +1,7 @@ [ { "description": "uniqueItems without type keyword should be used together with maxItems", - "schema": {"$ref": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-secure.json#"}, + "schema": {"$ref": "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/json-schema-secure.json#"}, "tests": [ { "description": "uniqueItems keyword used without maxItems is unsafe", @@ -29,7 +29,7 @@ }, { "description": "uniqueItems with scalar type(s) is safe to use without maxItems", - "schema": {"$ref": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-secure.json#"}, + "schema": {"$ref": "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/json-schema-secure.json#"}, "tests": [ { "description": "uniqueItems keyword with a single scalar type is safe", @@ -55,7 +55,7 @@ }, { "description": "uniqueItems with compound type(s) should be used together with maxItems", - "schema": {"$ref": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-secure.json#"}, + "schema": {"$ref": "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/json-schema-secure.json#"}, "tests": [ { "description": "uniqueItems keyword with a single compound type and without maxItems is unsafe", diff --git a/spec/security/object.json b/spec/security/object.json index e8efedecb..9de069b8a 100644 --- a/spec/security/object.json +++ b/spec/security/object.json @@ -1,7 +1,7 @@ [ { "description": "patternProperties keyword should be used together with propertyNames", - "schema": { "$ref": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-secure.json#" }, + "schema": { "$ref": "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/json-schema-secure.json#" }, "tests": [ { "description": "patternProperties keyword used without propertyNames is unsafe", diff --git a/spec/security/string.json b/spec/security/string.json index 6aa4f861e..c6fb55292 100644 --- a/spec/security/string.json +++ b/spec/security/string.json @@ -1,7 +1,7 @@ [ { "description": "pattern keyword should be used together with maxLength", - "schema": { "$ref": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-secure.json#" }, + "schema": { "$ref": "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/json-schema-secure.json#" }, "tests": [ { "description": "pattern keyword used without maxLength is unsafe", @@ -22,7 +22,7 @@ }, { "description": "format keyword should be used together with maxLength", - "schema": { "$ref": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-secure.json#" }, + "schema": { "$ref": "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/json-schema-secure.json#" }, "tests": [ { "description": "format keyword used without maxLength is unsafe", From 12b683aa5b2a2090251284c1df31ffae09de1da5 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 11 May 2020 17:44:52 +0100 Subject: [PATCH 312/333] docs: travis and coveralls links --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b0f7bda07..9401cfd54 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ The fastest JSON Schema validator for Node.js and browser. Supports draft-04/06/07. -[![Build Status](https://travis-ci.org/epoberezkin/ajv.svg?branch=master)](https://travis-ci.org/epoberezkin/ajv) +[![Build Status](https://travis-ci.org/ajv-validator/ajv.svg?branch=master)](https://travis-ci.org/ajv-validator/ajv) [![npm](https://img.shields.io/npm/v/ajv.svg)](https://www.npmjs.com/package/ajv) [![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) +[![Coverage Status](https://coveralls.io/repos/ajv-validator/ajv/badge.svg?branch=master&service=github)](https://coveralls.io/github/ajv-validator/ajv?branch=master) [![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv) [![GitHub Sponsors](https://img.shields.io/badge/$-sponsors-brightgreen)](https://github.com/sponsors/epoberezkin) From 7ff7bd9f6ece256bd5020ea92382ffbe90f69994 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 11 May 2020 17:54:25 +0100 Subject: [PATCH 313/333] node version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5862db752..7904fd1eb 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ Performance of different validators by [json-schema-benchmark](https://github.co - correct string lengths for strings with unicode pairs (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+ +- supports [browsers](#using-in-browser) and Node.js 0.10-14.x - [asynchronous loading](#asynchronous-schema-compilation) of referenced schemas during compilation - "All errors" validation mode with [option allErrors](#options) - [error messages with parameters](#validation-errors) describing error reasons to allow creating custom error messages From 0b14f6093d0226405d5dc5fded9736133f34e976 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 11 May 2020 17:59:50 +0100 Subject: [PATCH 314/333] docs: fix coveralls badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a6f44afcd..a8757398e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ The fastest JSON Schema validator for Node.js and browser. Supports draft-04/06/ [![Build Status](https://travis-ci.org/ajv-validator/ajv.svg?branch=master)](https://travis-ci.org/ajv-validator/ajv) [![npm](https://img.shields.io/npm/v/ajv.svg)](https://www.npmjs.com/package/ajv) [![npm downloads](https://img.shields.io/npm/dm/ajv.svg)](https://www.npmjs.com/package/ajv) -[![Coverage Status](https://coveralls.io/repos/ajv-validator/ajv/badge.svg?branch=master&service=github)](https://coveralls.io/github/ajv-validator/ajv?branch=master) +[![Coverage Status](https://coveralls.io/repos/github/ajv-validator/ajv/badge.svg?branch=master)](https://coveralls.io/github/ajv-validator/ajv?branch=master) [![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv) [![GitHub Sponsors](https://img.shields.io/badge/$-sponsors-brightgreen)](https://github.com/sponsors/epoberezkin) From 48d1ca31ebd5d3bfa2dd51e1444b9247096c2086 Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Mon, 1 Jun 2020 19:54:50 +1200 Subject: [PATCH 315/333] docs: update list of file types supported by `ajvi-cli` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a8757398e..8424e171c 100644 --- a/README.md +++ b/README.md @@ -249,7 +249,7 @@ CLI is available as a separate npm package [ajv-cli](https://github.com/ajv-vali - testing expected validity of data against JSON Schema - referenced schemas - custom meta-schemas -- files in JSON and JavaScript format +- files in JSON, JSON5, YAML, and JavaScript format - all Ajv options - reporting changes in data after validation in [JSON-patch](https://tools.ietf.org/html/rfc6902) format From 2ce8002136cf3155aa0ff7fef1c35bc2fdec507f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2020 08:18:23 +0000 Subject: [PATCH 316/333] Bump typescript from 2.9.2 to 3.9.5 Bumps [typescript](https://github.com/Microsoft/TypeScript) from 2.9.2 to 3.9.5. - [Release notes](https://github.com/Microsoft/TypeScript/releases) - [Commits](https://github.com/Microsoft/TypeScript/compare/v2.9.2...v3.9.5) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 10717b435..4cd9f69d5 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "nyc": "^15.0.0", "pre-commit": "^1.1.1", "require-globify": "^1.3.0", - "typescript": "^2.8.3", + "typescript": "^3.9.5", "uglify-js": "^3.6.9", "watch": "^1.0.0" }, From edd8c92786edf342bdc51d6024e8e75fd0b0da69 Mon Sep 17 00:00:00 2001 From: Issac Gerges Date: Fri, 22 May 2020 11:47:25 -0500 Subject: [PATCH 317/333] Add option for strictNumbers. Resolves #1128. --- README.md | 5 ++- lib/ajv.d.ts | 1 + lib/compile/util.js | 13 ++++--- lib/dot/uniqueItems.jst | 2 +- lib/dot/validate.jst | 4 +-- spec/options/strictNumbers.spec.js | 55 ++++++++++++++++++++++++++++++ 6 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 spec/options/strictNumbers.spec.js diff --git a/README.md b/README.md index a8757398e..98f5991b2 100644 --- a/README.md +++ b/README.md @@ -1156,6 +1156,7 @@ Defaults: // strict mode options strictDefaults: false, strictKeywords: false, + strictNumbers: false, // asynchronous validation options: transpile: undefined, // requires ajv-async package // advanced options: @@ -1250,7 +1251,9 @@ Defaults: - `false` (default) - unknown keywords are not reported - `true` - if an unknown keyword is present, throw an error - `"log"` - if an unknown keyword is present, log warning - +- _strictNumbers_: validate numbers strictly, failing validation for NaN and Infinity. Option values: + - `false` (default) - NaN or Infinity will pass validation for numeric types + - `true` - NaN or Infinity will not pass validation for numeric types ##### Asynchronous validation options diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index d988c3a99..cc2881b33 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -183,6 +183,7 @@ declare namespace ajv { coerceTypes?: boolean | 'array'; strictDefaults?: boolean | 'log'; strictKeywords?: boolean | 'log'; + strictNumbers?: boolean; async?: boolean | string; transpile?: string | ((code: string) => string); meta?: boolean | object; diff --git a/lib/compile/util.js b/lib/compile/util.js index 0efa00111..702f6e19d 100644 --- a/lib/compile/util.js +++ b/lib/compile/util.js @@ -36,7 +36,7 @@ function copy(o, to) { } -function checkDataType(dataType, data, negate) { +function checkDataType(dataType, data, strictNumbers, negate) { var EQUAL = negate ? ' !== ' : ' === ' , AND = negate ? ' || ' : ' && ' , OK = negate ? '!' : '' @@ -49,15 +49,18 @@ function checkDataType(dataType, data, negate) { NOT + 'Array.isArray(' + data + '))'; case 'integer': return '(typeof ' + data + EQUAL + '"number"' + AND + NOT + '(' + data + ' % 1)' + - AND + data + EQUAL + data + ')'; + AND + data + EQUAL + data + + (strictNumbers ? (AND + OK + 'isFinite(' + data + ')') : '') + ')'; + case 'number': return '(typeof ' + data + EQUAL + '"' + dataType + '"' + + (strictNumbers ? (AND + OK + 'isFinite(' + data + ')') : '') + ')'; default: return 'typeof ' + data + EQUAL + '"' + dataType + '"'; } } -function checkDataTypes(dataTypes, data) { +function checkDataTypes(dataTypes, data, strictNumbers) { switch (dataTypes.length) { - case 1: return checkDataType(dataTypes[0], data, true); + case 1: return checkDataType(dataTypes[0], data, strictNumbers, true); default: var code = ''; var types = toHash(dataTypes); @@ -70,7 +73,7 @@ function checkDataTypes(dataTypes, data) { } if (types.number) delete types.integer; for (var t in types) - code += (code ? ' && ' : '' ) + checkDataType(t, data, true); + code += (code ? ' && ' : '' ) + checkDataType(t, data, strictNumbers, true); return code; } diff --git a/lib/dot/uniqueItems.jst b/lib/dot/uniqueItems.jst index 22f82f99d..e69b8308d 100644 --- a/lib/dot/uniqueItems.jst +++ b/lib/dot/uniqueItems.jst @@ -38,7 +38,7 @@ for (;i--;) { var item = {{=$data}}[i]; {{ var $method = 'checkDataType' + ($typeIsArray ? 's' : ''); }} - if ({{= it.util[$method]($itemType, 'item', true) }}) continue; + if ({{= it.util[$method]($itemType, 'item', it.opts.strictNumbers, true) }}) continue; {{? $typeIsArray}} if (typeof item == 'string') item = '"' + item; {{?}} diff --git a/lib/dot/validate.jst b/lib/dot/validate.jst index f8a1edfc0..bae653ff6 100644 --- a/lib/dot/validate.jst +++ b/lib/dot/validate.jst @@ -140,7 +140,7 @@ , $method = $typeIsArray ? 'checkDataTypes' : 'checkDataType'; }} - if ({{= it.util[$method]($typeSchema, $data, true) }}) { + if ({{= it.util[$method]($typeSchema, $data, it.opts.strictNumbers, true) }}) { #}} {{? it.schema.$ref && $refKeywords }} @@ -192,7 +192,7 @@ {{~ it.RULES:$rulesGroup }} {{? $shouldUseGroup($rulesGroup) }} {{? $rulesGroup.type }} - if ({{= it.util.checkDataType($rulesGroup.type, $data) }}) { + if ({{= it.util.checkDataType($rulesGroup.type, $data, it.opts.strictNumbers) }}) { {{?}} {{? it.opts.useDefaults }} {{? $rulesGroup.type == 'object' && it.schema.properties }} diff --git a/spec/options/strictNumbers.spec.js b/spec/options/strictNumbers.spec.js new file mode 100644 index 000000000..adf63b026 --- /dev/null +++ b/spec/options/strictNumbers.spec.js @@ -0,0 +1,55 @@ +'use strict'; + +var Ajv = require('../ajv'); + +describe('structNumbers option', function() { + var ajv; + describe('strictNumbers default', testWithoutStrictNumbers(new Ajv())); + describe('strictNumbers = false', testWithoutStrictNumbers(new Ajv({strictNumbers: false}))); + describe('strictNumbers = true', function() { + beforeEach(function () { + ajv = new Ajv({strictNumbers: true}); + }); + + it('should fail validation for NaN/Infinity as type number', function() { + var validate = ajv.compile({type: 'number'}); + validate("1.1").should.equal(false); + validate(1.1).should.equal(true); + validate(1).should.equal(true); + validate(NaN).should.equal(false); + validate(Infinity).should.equal(false); + }); + + it('should fail validation for NaN as type integer', function() { + var validate = ajv.compile({type: 'integer'}); + validate("1.1").should.equal(false); + validate(1.1).should.equal(false); + validate(1).should.equal(true); + validate(NaN).should.equal(false); + validate(Infinity).should.equal(false); + }); + }); +}); + + +function testWithoutStrictNumbers(_ajv) { + return function () { + it('should NOT fail validation for NaN/Infinity as type number', function() { + var validate = _ajv.compile({type: 'number'}); + validate("1.1").should.equal(false); + validate(1.1).should.equal(true); + validate(1).should.equal(true); + validate(NaN).should.equal(true); + validate(Infinity).should.equal(true); + }); + + it('should NOT fail validation for NaN/Infinity as type integer', function() { + var validate = _ajv.compile({type: 'integer'}); + validate("1.1").should.equal(false); + validate(1.1).should.equal(false); + validate(1).should.equal(true); + validate(NaN).should.equal(false); + validate(Infinity).should.equal(true); + }); + }; +} From 854dbefa5a4006f8070cca9f2af9674ee1eb9b06 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 11 Jun 2020 08:36:33 +0000 Subject: [PATCH 318/333] Bump mocha from 7.2.0 to 8.0.1 Bumps [mocha](https://github.com/mochajs/mocha) from 7.2.0 to 8.0.1. - [Release notes](https://github.com/mochajs/mocha/releases) - [Changelog](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md) - [Commits](https://github.com/mochajs/mocha/compare/v7.2.0...v8.0.1) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4cd9f69d5..d3215f120 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "karma-chrome-launcher": "^3.0.0", "karma-mocha": "^2.0.0", "karma-sauce-launcher": "^4.1.3", - "mocha": "^7.0.1", + "mocha": "^8.0.1", "nyc": "^15.0.0", "pre-commit": "^1.1.1", "require-globify": "^1.3.0", From 54c96b05e633b699b2eb07de580d0851024d162e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 23 Jun 2020 08:44:24 +0000 Subject: [PATCH 319/333] Bump eslint from 6.8.0 to 7.3.1 Bumps [eslint](https://github.com/eslint/eslint) from 6.8.0 to 7.3.1. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v6.8.0...v7.3.1) Signed-off-by: dependabot-preview[bot] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4cd9f69d5..84be93923 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "coveralls": "^3.0.1", "del-cli": "^3.0.0", "dot": "^1.0.3", - "eslint": "^6.0.0", + "eslint": "^7.3.1", "gh-pages-generator": "^0.2.3", "glob": "^7.0.0", "if-node-version": "^1.0.0", From e7f0c81c136a06b3e08c742b75828bf6071a2ddb Mon Sep 17 00:00:00 2001 From: Vadim Cebaniuc Date: Sat, 27 Jun 2020 19:18:52 +0300 Subject: [PATCH 320/333] Fix mistype in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 10487a386..c340f339c 100644 --- a/README.md +++ b/README.md @@ -321,7 +321,7 @@ You can add additional formats and replace any of the formats above using [addFo The option `unknownFormats` allows changing the default behaviour when an unknown format is encountered. In this case Ajv can either fail schema compilation (default) or ignore it (default in versions before 5.0.0). You also can whitelist specific format(s) to be ignored. See [Options](#options) for details. -You can find regular expressions used for format validation and the sources that were used in [formats.js](https://github.com/ajv-validatorv/ajv/blob/master/lib/compile/formats.js). +You can find regular expressions used for format validation and the sources that were used in [formats.js](https://github.com/ajv-validator/ajv/blob/master/lib/compile/formats.js). ## Combining schemas with $ref From 0006f34ce5eab2d233154aee4b8f5715f298c030 Mon Sep 17 00:00:00 2001 From: Graham Lea Date: Tue, 30 Jun 2020 17:33:03 +1000 Subject: [PATCH 321/333] Document pre-compiled schemas for CSP in README Pre-compiled schemas are a workaround for maintaining a secure Content Security Policy (CSP) Fixes #1228 --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index c340f339c..bae892d27 100644 --- a/README.md +++ b/README.md @@ -238,6 +238,15 @@ Ajv is tested with these browsers: __Please note__: some frameworks, e.g. Dojo, may redefine global require in such way that is not compatible with CommonJS module format. In such case Ajv bundle has to be loaded before the framework and then you can use global Ajv (see issue [#234](https://github.com/ajv-validator/ajv/issues/234)). +### Ajv & Content Security Policies (CSP) + +If you're using Ajv to compile a schema (the typical use) in a browser document that is loaded with a Content Security Policy (CSP), that policy will require a `script-src` directive that includes the value `'unsafe-eval'`. +:warning: NOTE, however, that `unsafe-eval` is NOT recommended in a secure CSP[[1]](https://developer.chrome.com/extensions/contentSecurityPolicy#relaxing-eval), as it has the potential to open the document to cross-site scripting (XSS) attacks. + +In order to make use of Ajv without easing your CSP, you can [pre-compile a schema using the CLI](https://github.com/ajv-validator/ajv-cli#compile-schemas). This will transpile the schema JSON into a JavaScript file that exports a `validate` function that works simlarly to a schema compiled at runtime. +Note that the pre-compiled schemas, which are created using [ajv-pack](https://github.com/ajv-validator/ajv-pack#limitations), are not functionally equivalent to Ajv and there are known limitations. + + ## Command line interface CLI is available as a separate npm package [ajv-cli](https://github.com/ajv-validator/ajv-cli). It supports: From c581ff3dc1cc6e0acb39e16e2b0f2bcce5dc8857 Mon Sep 17 00:00:00 2001 From: Graham Lea Date: Tue, 30 Jun 2020 21:24:29 +1000 Subject: [PATCH 322/333] Clarify limitations of ajv-pack in README Fixes #1228 --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bae892d27..7d2b1f0ea 100644 --- a/README.md +++ b/README.md @@ -238,13 +238,14 @@ Ajv is tested with these browsers: __Please note__: some frameworks, e.g. Dojo, may redefine global require in such way that is not compatible with CommonJS module format. In such case Ajv bundle has to be loaded before the framework and then you can use global Ajv (see issue [#234](https://github.com/ajv-validator/ajv/issues/234)). -### Ajv & Content Security Policies (CSP) +### Ajv and Content Security Policies (CSP) If you're using Ajv to compile a schema (the typical use) in a browser document that is loaded with a Content Security Policy (CSP), that policy will require a `script-src` directive that includes the value `'unsafe-eval'`. :warning: NOTE, however, that `unsafe-eval` is NOT recommended in a secure CSP[[1]](https://developer.chrome.com/extensions/contentSecurityPolicy#relaxing-eval), as it has the potential to open the document to cross-site scripting (XSS) attacks. In order to make use of Ajv without easing your CSP, you can [pre-compile a schema using the CLI](https://github.com/ajv-validator/ajv-cli#compile-schemas). This will transpile the schema JSON into a JavaScript file that exports a `validate` function that works simlarly to a schema compiled at runtime. -Note that the pre-compiled schemas, which are created using [ajv-pack](https://github.com/ajv-validator/ajv-pack#limitations), are not functionally equivalent to Ajv and there are known limitations. + +Note that pre-compilation of schemas is performed using [ajv-pack](https://github.com/ajv-validator/ajv-pack) and there are [some limitations to the schema features it can compile](https://github.com/ajv-validator/ajv-pack#limitations). A successfully pre-compiled schema is equivalent to the same schema compiled at runtime. ## Command line interface From 0e2c3463a28ac19b5ea8324511889540c41125fa Mon Sep 17 00:00:00 2001 From: Graham Lea Date: Tue, 30 Jun 2020 21:26:54 +1000 Subject: [PATCH 323/333] Add Contents link to CSP section Fixes #1228 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7d2b1f0ea..29beb7286 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); - [Getting started](#getting-started) - [Frequently Asked Questions](https://github.com/ajv-validator/ajv/blob/master/FAQ.md) - [Using in browser](#using-in-browser) + - [Ajv and Content Security Policies (CSP)](#ajv-and-content-security-policies-csp) - [Command line interface](#command-line-interface) - Validation - [Keywords](#validation-keywords) From fd64fb4c939c6f6b8d68aa4c6c57d8be8cc1994d Mon Sep 17 00:00:00 2001 From: Graham Lea Date: Tue, 30 Jun 2020 21:31:56 +1000 Subject: [PATCH 324/333] Add link to CSP section in Security section Fixes #1228 --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 29beb7286..5e502db93 100644 --- a/README.md +++ b/README.md @@ -733,6 +733,10 @@ isSchemaSecure(schema2); // true __Please note__: following all these recommendation is not a guarantee that validation of untrusted data is safe - it can still lead to some undesirable results. +##### Content Security Policies (CSP) +See [Ajv and Content Security Policies (CSP)](#ajv-and-content-security-policies-csp) + + ## ReDoS attack Certain regular expressions can lead to the exponential evaluation time even with relatively short strings. From 24d4f8fd8f812051bce521454c5152b87eb27c9c Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Tue, 30 Jun 2020 17:26:25 +0100 Subject: [PATCH 325/333] remove code post-processing --- lib/compile/util.js | 38 --------------------------- lib/dot/allOf.jst | 2 -- lib/dot/anyOf.jst | 2 -- lib/dot/contains.jst | 2 -- lib/dot/definitions.def | 6 ----- lib/dot/dependencies.jst | 2 -- lib/dot/if.jst | 2 -- lib/dot/items.jst | 2 -- lib/dot/properties.jst | 2 -- lib/dot/propertyNames.jst | 2 -- lib/dot/validate.jst | 6 ----- spec/issues/388_code_clean-up.spec.js | 28 -------------------- 12 files changed, 94 deletions(-) delete mode 100644 spec/issues/388_code_clean-up.spec.js diff --git a/lib/compile/util.js b/lib/compile/util.js index 702f6e19d..f232a6fb1 100644 --- a/lib/compile/util.js +++ b/lib/compile/util.js @@ -13,8 +13,6 @@ module.exports = { ucs2length: require('./ucs2length'), varOccurences: varOccurences, varReplace: varReplace, - cleanUpCode: cleanUpCode, - finalCleanUpCode: finalCleanUpCode, schemaHasRules: schemaHasRules, schemaHasRulesExcept: schemaHasRulesExcept, schemaUnknownRules: schemaUnknownRules, @@ -139,42 +137,6 @@ function varReplace(str, dataVar, expr) { } -var EMPTY_ELSE = /else\s*{\s*}/g - , EMPTY_IF_NO_ELSE = /if\s*\([^)]+\)\s*\{\s*\}(?!\s*else)/g - , EMPTY_IF_WITH_ELSE = /if\s*\(([^)]+)\)\s*\{\s*\}\s*else(?!\s*if)/g; -function cleanUpCode(out) { - return out.replace(EMPTY_ELSE, '') - .replace(EMPTY_IF_NO_ELSE, '') - .replace(EMPTY_IF_WITH_ELSE, 'if (!($1))'); -} - - -var ERRORS_REGEXP = /[^v.]errors/g - , REMOVE_ERRORS = /var errors = 0;|var vErrors = null;|validate.errors = vErrors;/g - , REMOVE_ERRORS_ASYNC = /var errors = 0;|var vErrors = null;/g - , RETURN_VALID = 'return errors === 0;' - , RETURN_TRUE = 'validate.errors = null; return true;' - , RETURN_ASYNC = /if \(errors === 0\) return data;\s*else throw new ValidationError\(vErrors\);/ - , RETURN_DATA_ASYNC = 'return data;' - , ROOTDATA_REGEXP = /[^A-Za-z_$]rootData[^A-Za-z0-9_$]/g - , REMOVE_ROOTDATA = /if \(rootData === undefined\) rootData = data;/; - -function finalCleanUpCode(out, async) { - var matches = out.match(ERRORS_REGEXP); - if (matches && matches.length == 2) { - out = async - ? out.replace(REMOVE_ERRORS_ASYNC, '') - .replace(RETURN_ASYNC, RETURN_DATA_ASYNC) - : out.replace(REMOVE_ERRORS, '') - .replace(RETURN_VALID, RETURN_TRUE); - } - - matches = out.match(ROOTDATA_REGEXP); - if (!matches || matches.length !== 3) return out; - return out.replace(REMOVE_ROOTDATA, ''); -} - - function schemaHasRules(schema, rules) { if (typeof schema == 'boolean') return !schema; for (var key in schema) if (rules[key]) return true; diff --git a/lib/dot/allOf.jst b/lib/dot/allOf.jst index 4c2836311..0e782fe98 100644 --- a/lib/dot/allOf.jst +++ b/lib/dot/allOf.jst @@ -30,5 +30,3 @@ {{= $closingBraces.slice(0,-1) }} {{?}} {{?}} - -{{# def.cleanUp }} diff --git a/lib/dot/anyOf.jst b/lib/dot/anyOf.jst index 086cf2b33..ea909ee62 100644 --- a/lib/dot/anyOf.jst +++ b/lib/dot/anyOf.jst @@ -39,8 +39,6 @@ } else { {{# def.resetErrors }} {{? it.opts.allErrors }} } {{?}} - - {{# def.cleanUp }} {{??}} {{? $breakOnError }} if (true) { diff --git a/lib/dot/contains.jst b/lib/dot/contains.jst index 925d2c84b..4dc996741 100644 --- a/lib/dot/contains.jst +++ b/lib/dot/contains.jst @@ -53,5 +53,3 @@ var {{=$valid}}; {{# def.resetErrors }} {{?}} {{? it.opts.allErrors }} } {{?}} - -{{# def.cleanUp }} diff --git a/lib/dot/definitions.def b/lib/dot/definitions.def index b68e064e8..162231a8e 100644 --- a/lib/dot/definitions.def +++ b/lib/dot/definitions.def @@ -112,12 +112,6 @@ #}} -{{## def.cleanUp: {{ out = it.util.cleanUpCode(out); }} #}} - - -{{## def.finalCleanUp: {{ out = it.util.finalCleanUpCode(out, $async); }} #}} - - {{## def.$data: {{ var $isData = it.opts.$data && $schema && $schema.$data diff --git a/lib/dot/dependencies.jst b/lib/dot/dependencies.jst index c41f33422..9ff68a78d 100644 --- a/lib/dot/dependencies.jst +++ b/lib/dot/dependencies.jst @@ -76,5 +76,3 @@ var missing{{=$lvl}}; {{= $closingBraces }} if ({{=$errs}} == errors) { {{?}} - -{{# def.cleanUp }} diff --git a/lib/dot/if.jst b/lib/dot/if.jst index 7ccc9b7f7..adb503612 100644 --- a/lib/dot/if.jst +++ b/lib/dot/if.jst @@ -65,8 +65,6 @@ {{# def.extraError:'if' }} } {{? $breakOnError }} else { {{?}} - - {{# def.cleanUp }} {{??}} {{? $breakOnError }} if (true) { diff --git a/lib/dot/items.jst b/lib/dot/items.jst index 8c0f5acb5..acc932a26 100644 --- a/lib/dot/items.jst +++ b/lib/dot/items.jst @@ -96,5 +96,3 @@ var {{=$valid}}; {{= $closingBraces }} if ({{=$errs}} == errors) { {{?}} - -{{# def.cleanUp }} diff --git a/lib/dot/properties.jst b/lib/dot/properties.jst index 862067e75..f3de23650 100644 --- a/lib/dot/properties.jst +++ b/lib/dot/properties.jst @@ -240,5 +240,3 @@ var {{=$nextValid}} = true; {{= $closingBraces }} if ({{=$errs}} == errors) { {{?}} - -{{# def.cleanUp }} diff --git a/lib/dot/propertyNames.jst b/lib/dot/propertyNames.jst index ee52b2151..d456ccafc 100644 --- a/lib/dot/propertyNames.jst +++ b/lib/dot/propertyNames.jst @@ -50,5 +50,3 @@ var {{=$errs}} = errors; {{= $closingBraces }} if ({{=$errs}} == errors) { {{?}} - -{{# def.cleanUp }} diff --git a/lib/dot/validate.jst b/lib/dot/validate.jst index bae653ff6..fd833a535 100644 --- a/lib/dot/validate.jst +++ b/lib/dot/validate.jst @@ -254,12 +254,6 @@ var {{=$valid}} = errors === errs_{{=$lvl}}; {{?}} -{{# def.cleanUp }} - -{{? $top }} - {{# def.finalCleanUp }} -{{?}} - {{ function $shouldUseGroup($rulesGroup) { var rules = $rulesGroup.rules; diff --git a/spec/issues/388_code_clean-up.spec.js b/spec/issues/388_code_clean-up.spec.js deleted file mode 100644 index 9a0288362..000000000 --- a/spec/issues/388_code_clean-up.spec.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -var Ajv = require('../ajv'); -var should = require('../chai').should(); - - -describe('issue #388, code clean-up not working', function() { - it('should remove assignement to rootData if it is not used', function() { - var ajv = new Ajv; - var validate = ajv.compile({ - type: 'object', - properties: { - foo: { type: 'string' } - } - }); - var code = validate.toString(); - code.match(/rootData/g).length .should.equal(1); - }); - - it('should remove assignement to errors if they are not used', function() { - var ajv = new Ajv; - var validate = ajv.compile({ - type: 'object' - }); - var code = validate.toString(); - should.equal(code.match(/[^.]errors|vErrors/g), null); - }); -}); From 65b2f7d76b190ac63a0d4e9154c712d7aa37049f Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Tue, 30 Jun 2020 19:30:48 +0100 Subject: [PATCH 326/333] validate numbers in schemas during schema compilation --- lib/dot/_limit.jst | 9 ++++++++ lib/dot/_limitItems.jst | 2 ++ lib/dot/_limitLength.jst | 2 ++ lib/dot/_limitProperties.jst | 2 ++ lib/dot/definitions.def | 7 ++++++ spec/ajv.spec.js | 44 ++++++++++++++++++++++++++++++++++++ 6 files changed, 66 insertions(+) diff --git a/lib/dot/_limit.jst b/lib/dot/_limit.jst index e10806fd3..f15218922 100644 --- a/lib/dot/_limit.jst +++ b/lib/dot/_limit.jst @@ -17,6 +17,15 @@ , $op = $isMax ? '<' : '>' , $notOp = $isMax ? '>' : '<' , $errorKeyword = undefined; + + if (!($isData || typeof $schema == 'number' || $schema === undefined)) { + throw new Error($keyword + ' must be number'); + } + if (!($isDataExcl || $schemaExcl === undefined + || typeof $schemaExcl == 'number' + || typeof $schemaExcl == 'boolean')) { + throw new Error($exclusiveKeyword + ' must be number or boolean'); + } }} {{? $isDataExcl }} diff --git a/lib/dot/_limitItems.jst b/lib/dot/_limitItems.jst index a3e078e51..741329e77 100644 --- a/lib/dot/_limitItems.jst +++ b/lib/dot/_limitItems.jst @@ -3,6 +3,8 @@ {{# def.setupKeyword }} {{# def.$data }} +{{# def.numberKeyword }} + {{ var $op = $keyword == 'maxItems' ? '>' : '<'; }} if ({{# def.$dataNotType:'number' }} {{=$data}}.length {{=$op}} {{=$schemaValue}}) { {{ var $errorKeyword = $keyword; }} diff --git a/lib/dot/_limitLength.jst b/lib/dot/_limitLength.jst index cfc8dbb01..285c66bd2 100644 --- a/lib/dot/_limitLength.jst +++ b/lib/dot/_limitLength.jst @@ -3,6 +3,8 @@ {{# def.setupKeyword }} {{# def.$data }} +{{# def.numberKeyword }} + {{ var $op = $keyword == 'maxLength' ? '>' : '<'; }} if ({{# def.$dataNotType:'number' }} {{# def.strLength }} {{=$op}} {{=$schemaValue}}) { {{ var $errorKeyword = $keyword; }} diff --git a/lib/dot/_limitProperties.jst b/lib/dot/_limitProperties.jst index da7ea776f..c4c21551a 100644 --- a/lib/dot/_limitProperties.jst +++ b/lib/dot/_limitProperties.jst @@ -3,6 +3,8 @@ {{# def.setupKeyword }} {{# def.$data }} +{{# def.numberKeyword }} + {{ var $op = $keyword == 'maxProperties' ? '>' : '<'; }} if ({{# def.$dataNotType:'number' }} Object.keys({{=$data}}).length {{=$op}} {{=$schemaValue}}) { {{ var $errorKeyword = $keyword; }} diff --git a/lib/dot/definitions.def b/lib/dot/definitions.def index 162231a8e..db4ea6f32 100644 --- a/lib/dot/definitions.def +++ b/lib/dot/definitions.def @@ -138,6 +138,13 @@ #}} +{{## def.numberKeyword: + {{? !($isData || typeof $schema == 'number') }} + {{ throw new Error($keyword + ' must be number'); }} + {{?}} +#}} + + {{## def.beginDefOut: {{ var $$outStack = $$outStack || []; diff --git a/spec/ajv.spec.js b/spec/ajv.spec.js index e3bf766b7..88cf13a74 100644 --- a/spec/ajv.spec.js +++ b/spec/ajv.spec.js @@ -512,5 +512,49 @@ describe('Ajv', function () { }); }); }); + + describe('sub-schema validation outside of definitions during compilation', function() { + it('maximum', function() { + passValidationThrowCompile({ + $ref: '#/foo', + foo: {maximum: 'bar'} + }); + }); + + it('exclusiveMaximum', function() { + passValidationThrowCompile({ + $ref: '#/foo', + foo: {exclusiveMaximum: 'bar'} + }); + }); + + it('maxItems', function() { + passValidationThrowCompile({ + $ref: '#/foo', + foo: {maxItems: 'bar'} + }); + }); + + it('maxLength', function() { + passValidationThrowCompile({ + $ref: '#/foo', + foo: {maxLength: 'bar'} + }); + }); + + it('maxProperties', function() { + passValidationThrowCompile({ + $ref: '#/foo', + foo: {maxProperties: 'bar'} + }); + }); + + function passValidationThrowCompile(schema) { + ajv.validateSchema(schema) .should.equal(true); + should.throw(function() { + ajv.compile(schema); + }); + } + }); }); }); From 1105fd5ad9afdb08656db33ba222191036870785 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Tue, 30 Jun 2020 19:56:19 +0100 Subject: [PATCH 327/333] ignore proto properties --- lib/dot/properties.jst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/dot/properties.jst b/lib/dot/properties.jst index f3de23650..5cebb9b12 100644 --- a/lib/dot/properties.jst +++ b/lib/dot/properties.jst @@ -28,9 +28,9 @@ , $nextData = 'data' + $dataNxt , $dataProperties = 'dataProperties' + $lvl; - var $schemaKeys = Object.keys($schema || {}) + var $schemaKeys = Object.keys($schema || {}).filter(notProto) , $pProperties = it.schema.patternProperties || {} - , $pPropertyKeys = Object.keys($pProperties) + , $pPropertyKeys = Object.keys($pProperties).filter(notProto) , $aProperties = it.schema.additionalProperties , $someProperties = $schemaKeys.length || $pPropertyKeys.length , $noAdditional = $aProperties === false @@ -42,8 +42,11 @@ , $currentBaseId = it.baseId; var $required = it.schema.required; - if ($required && !(it.opts.$data && $required.$data) && $required.length < it.opts.loopRequired) + if ($required && !(it.opts.$data && $required.$data) && $required.length < it.opts.loopRequired) { var $requiredHash = it.util.toHash($required); + } + + function notProto(p) { return p !== '__proto__'; } }} From 9c009a96ab9b2289211b3ed20a0b5fad4b8defe8 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 1 Jul 2020 10:00:58 +0100 Subject: [PATCH 328/333] validate numbers in multipleOf --- lib/dot/multipleOf.jst | 2 ++ spec/ajv.spec.js | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/lib/dot/multipleOf.jst b/lib/dot/multipleOf.jst index 5f8dd33b5..6d88a456f 100644 --- a/lib/dot/multipleOf.jst +++ b/lib/dot/multipleOf.jst @@ -3,6 +3,8 @@ {{# def.setupKeyword }} {{# def.$data }} +{{# def.numberKeyword }} + var division{{=$lvl}}; if ({{?$isData}} {{=$schemaValue}} !== undefined && ( diff --git a/spec/ajv.spec.js b/spec/ajv.spec.js index 88cf13a74..118a827ad 100644 --- a/spec/ajv.spec.js +++ b/spec/ajv.spec.js @@ -549,6 +549,13 @@ describe('Ajv', function () { }); }); + it('multipleOf', function() { + passValidationThrowCompile({ + $ref: '#/foo', + foo: {maxProperties: 'bar'} + }); + }); + function passValidationThrowCompile(schema) { ajv.validateSchema(schema) .should.equal(true); should.throw(function() { From 68d72c41d5eca933404cfcf909856b61ab3b6251 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 1 Jul 2020 10:42:57 +0100 Subject: [PATCH 329/333] update regex --- lib/compile/util.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/compile/util.js b/lib/compile/util.js index f232a6fb1..5d54e2efb 100644 --- a/lib/compile/util.js +++ b/lib/compile/util.js @@ -137,6 +137,8 @@ function varReplace(str, dataVar, expr) { } + + function schemaHasRules(schema, rules) { if (typeof schema == 'boolean') return !schema; for (var key in schema) if (rules[key]) return true; @@ -215,7 +217,7 @@ function getData($data, lvl, paths) { function joinPaths (a, b) { if (a == '""') return b; - return (a + ' + ' + b).replace(/' \+ '/g, ''); + return (a + ' + ' + b).replace(/([^\\])' \+ '/g, '$1'); } From f2b1e3d2c89288561ee68d7459a41b7222cc520d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 1 Jul 2020 11:25:30 +0100 Subject: [PATCH 330/333] whitespace --- lib/compile/util.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/compile/util.js b/lib/compile/util.js index 5d54e2efb..ef07b8c75 100644 --- a/lib/compile/util.js +++ b/lib/compile/util.js @@ -137,8 +137,6 @@ function varReplace(str, dataVar, expr) { } - - function schemaHasRules(schema, rules) { if (typeof schema == 'boolean') return !schema; for (var key in schema) if (rules[key]) return true; From 988982d3fde08e3ea074e8942442834e78c45587 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 1 Jul 2020 11:56:05 +0100 Subject: [PATCH 331/333] ignore proto properties --- lib/dot/dependencies.jst | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/dot/dependencies.jst b/lib/dot/dependencies.jst index 9ff68a78d..e4bdddec8 100644 --- a/lib/dot/dependencies.jst +++ b/lib/dot/dependencies.jst @@ -19,6 +19,7 @@ , $ownProperties = it.opts.ownProperties; for ($property in $schema) { + if ($property == '__proto__') continue; var $sch = $schema[$property]; var $deps = Array.isArray($sch) ? $propertyDeps : $schemaDeps; $deps[$property] = $sch; From d6aabb8e97029130cdb607dcd2e78a6d567e10d5 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 4 Jul 2020 14:01:07 +0100 Subject: [PATCH 332/333] test: remove node 8 from travis test --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 11a0afa18..80bb5bf49 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ before_script: - git submodule update --init - npm install -g codeclimate-test-reporter node_js: - - 8 - 10 - 12 - 14 From 521c3a53f15f5502fb4a734194932535d311267c Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 4 Jul 2020 16:44:23 +0100 Subject: [PATCH 333/333] 6.12.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index db0849ea8..c2a1c1f8e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.12.2", + "version": "6.12.3", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts",