From caf7557bc0784f34a693a4518c6546d4aa4b4628 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 18 Jul 2016 22:14:02 +0100 Subject: [PATCH 1/5] refactor: convert custom.def partial into custom.jst function (for better encapsulation) --- lib/dot/{custom.def => custom.jst} | 11 ++++++----- lib/dot/validate.jst | 6 +----- lib/keyword.js | 8 +++++++- 3 files changed, 14 insertions(+), 11 deletions(-) rename lib/dot/{custom.def => custom.jst} (94%) diff --git a/lib/dot/custom.def b/lib/dot/custom.jst similarity index 94% rename from lib/dot/custom.def rename to lib/dot/custom.jst index e84e509..68e7d0f 100644 --- a/lib/dot/custom.def +++ b/lib/dot/custom.jst @@ -1,10 +1,11 @@ +{{# def.definitions }} +{{# def.errors }} +{{# def.setupKeyword }} + {{ - var $schema = it.schema[$rule.keyword] + var $rule = this , $ruleValidate = it.useCustomRule($rule, $schema, it.schema, it) , $ruleErrs = $ruleValidate.code + '.errors' - , $schemaPath = it.schemaPath + '.' + $rule.keyword - , $errSchemaPath = it.errSchemaPath + '/' + $rule.keyword - , $errs = 'errs' + $lvl , $i = 'i' + $lvl , $ruleErr = 'ruleErr' + $lvl , $rDef = $rule.definition @@ -16,6 +17,7 @@ throw new Error('async keyword in sync schema'); }} + {{? !($inline || $macro) }}{{=$ruleErrs}} = null;{{?}} var {{=$errs}} = errors; var valid{{=$lvl}}; @@ -146,6 +148,5 @@ if (!{{# def.ruleValidationResult }}) { } {{?}} {{?}} - {{ $errorKeyword = undefined; }} } {{? $breakOnError }} else { {{?}} diff --git a/lib/dot/validate.jst b/lib/dot/validate.jst index f42da13..9e033ca 100644 --- a/lib/dot/validate.jst +++ b/lib/dot/validate.jst @@ -106,11 +106,7 @@ {{?}} {{~ $rulesGroup.rules:$rule }} {{? $shouldUseRule($rule) }} - {{? $rule.custom }} - {{# def.custom }} - {{??}} - {{= $rule.code(it, $rule.keyword) }} - {{?}} + {{= $rule.code(it, $rule.keyword) }} {{? $breakOnError }} {{ $closingBraces1 += '}'; }} {{?}} diff --git a/lib/keyword.js b/lib/keyword.js index a40d5d3..38209f7 100644 --- a/lib/keyword.js +++ b/lib/keyword.js @@ -1,6 +1,7 @@ 'use strict'; var IDENTIFIER = /^[a-z_$][a-z0-9_$]*$/i; +var customRuleCode = require('./dotjs/custom'); /** * Define custom keyword @@ -51,7 +52,12 @@ module.exports = function addKeyword(keyword, definition) { self.RULES.push(ruleGroup); } - var rule = { keyword: keyword, definition: definition, custom: true }; + var rule = { + keyword: keyword, + definition: definition, + custom: true, + code: customRuleCode + }; ruleGroup.rules.push(rule); } From ab4f65800bfc2ae9a47e2a939a86dfe661ededfe Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 18 Jul 2016 22:27:17 +0100 Subject: [PATCH 2/5] refactor: remove parallel ifs from def.$data partial --- lib/dot/definitions.def | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/dot/definitions.def b/lib/dot/definitions.def index 8e40fa6..7695e05 100644 --- a/lib/dot/definitions.def +++ b/lib/dot/definitions.def @@ -117,14 +117,14 @@ {{## def.$data: {{ - var $isData = it.opts.v5 && $schema.$data; - var $schemaValue = $isData - ? it.util.getData($schema.$data, $dataLvl, it.dataPathArr) - : $schema; + var $isData = it.opts.v5 && $schema.$data + , $schemaValue; }} {{? $isData }} - var schema{{=$lvl}} = {{=$schemaValue}}; + var schema{{=$lvl}} = {{= it.util.getData($schema.$data, $dataLvl, it.dataPathArr) }}; {{ $schemaValue = 'schema' + $lvl; }} + {{??}} + {{ $schemaValue = $schema; }} {{?}} #}} From 7f5fe70cdb57fd09a3ad2bbbd6df4ba3e3d5b5b5 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Tue, 19 Jul 2016 22:50:10 +0100 Subject: [PATCH 3/5] feat: $data support in custom keywords, #146 --- lib/compile/rules.js | 1 + lib/dot/custom.jst | 47 ++++++++++++++++------- lib/keyword.js | 25 +++++++++++- spec/custom.spec.js | 90 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+), 15 deletions(-) diff --git a/lib/compile/rules.js b/lib/compile/rules.js index 4ab2544..922c21a 100644 --- a/lib/compile/rules.js +++ b/lib/compile/rules.js @@ -33,6 +33,7 @@ module.exports = function rules() { RULES.keywords = util.toHash(RULES.all.concat(RULES.keywords)); RULES.all = util.toHash(RULES.all); RULES.types = util.toHash(RULES.types); + RULES.custom = {}; return RULES; }; diff --git a/lib/dot/custom.jst b/lib/dot/custom.jst index 68e7d0f..e92985a 100644 --- a/lib/dot/custom.jst +++ b/lib/dot/custom.jst @@ -1,17 +1,38 @@ {{# def.definitions }} {{# def.errors }} {{# def.setupKeyword }} +{{# def.$data }} {{ var $rule = this - , $ruleValidate = it.useCustomRule($rule, $schema, it.schema, it) - , $ruleErrs = $ruleValidate.code + '.errors' + , $rDef = $rule.definition + , $validate = $rDef.validate + , $compile = $rDef.compile + , $inline + , $macro + , $ruleValidate + , $validateCode; +}} + +{{? $isData && $rDef.$data }} + {{ $validateCode = 'keywordValidate' + $lvl; }} + var {{=$validateCode}} = RULES.custom['{{=$keyword}}'].definition + {{? $validate }}.validate{{??}}.compile({{=$schemaValue}}, validate.schema{{=it.schemaPath}}){{?}}; +{{??}} + {{ + $ruleValidate = it.useCustomRule($rule, $schema, it.schema, it); + $schemaValue = 'validate.schema' + $schemaPath; + $validateCode = $ruleValidate.code; + $inline = $rDef.inline; + $macro = $rDef.macro; + }} +{{?}} + +{{ + var $ruleErrs = $validateCode + '.errors' , $i = 'i' + $lvl , $ruleErr = 'ruleErr' + $lvl - , $rDef = $rule.definition - , $asyncKeyword = $rDef.async - , $inline = $rDef.inline - , $macro = $rDef.macro; + , $asyncKeyword = $rDef.async; if ($asyncKeyword && !it.async) throw new Error('async keyword in sync schema'); @@ -23,12 +44,12 @@ var {{=$errs}} = errors; var valid{{=$lvl}}; {{## def.callRuleValidate: - {{=$ruleValidate.code}}.call( + {{=$validateCode}}.call( {{? it.opts.passContext }}this{{??}}self{{?}} - {{? $rDef.compile || $rDef.schema === false }} + {{? $compile || $rDef.schema === false }} , {{=$data}} {{??}} - , validate.schema{{=$schemaPath}} + , {{=$schemaValue}} , {{=$data}} , validate.schema{{=it.schemaPath}} {{?}} @@ -69,7 +90,7 @@ var valid{{=$lvl}}; {{=$ruleErr}}.schemaPath = "{{=$errSchemaPath}}"; {{# _inline ? '}' : '' }} {{? it.opts.verbose }} - {{=$ruleErr}}.schema = validate.schema{{=$schemaPath}}; + {{=$ruleErr}}.schema = {{=$schemaValue}}; {{=$ruleErr}}.data = {{=$data}}; {{?}} } @@ -84,10 +105,10 @@ var valid{{=$lvl}}; $it.schemaPath = ''; }} {{# def.setCompositeRule }} - {{ var $code = it.validate($it).replace(/validate\.schema/g, $ruleValidate.code); }} + {{ var $code = it.validate($it).replace(/validate\.schema/g, $validateCode); }} {{# def.resetCompositeRule }} {{= $code }} -{{?? $rDef.compile || $rDef.validate }} +{{?? $compile || $validate }} {{# def.beginDefOut}} {{# def.callRuleValidate }} {{# def.storeDefOut:def_callRuleValidate }} @@ -104,7 +125,7 @@ var valid{{=$lvl}}; else throw e; } {{??}} - {{=$ruleValidate.code}}.errors = null; + {{=$validateCode}}.errors = null; {{?}} {{?}} {{?}} diff --git a/lib/keyword.js b/lib/keyword.js index 38209f7..d55a50b 100644 --- a/lib/keyword.js +++ b/lib/keyword.js @@ -29,8 +29,28 @@ module.exports = function addKeyword(keyword, definition) { _addRule(keyword, dataType, definition); } - if (definition.metaSchema) - definition.validateSchema = self.compile(definition.metaSchema, true); + var $data = definition.$data === true && this._opts.v5; + if ($data) { + if (!definition.validate) { + if (definition.compile) + console.warn('$data support: it is recommended to define "validate" function'); + else + throw new Error('$data support: neither "validate" nor "compile" functions are defined'); + } + } + + var metaSchema = definition.metaSchema; + if (metaSchema) { + if ($data) { + metaSchema = { + anyOf: [ + metaSchema, + { '$ref': 'https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-v5.json#/definitions/$data' } + ] + }; + } + definition.validateSchema = self.compile(metaSchema, true); + } } this.RULES.keywords[keyword] = true; @@ -59,6 +79,7 @@ module.exports = function addKeyword(keyword, definition) { code: customRuleCode }; ruleGroup.rules.push(rule); + self.RULES.custom[keyword] = rule; } diff --git a/spec/custom.spec.js b/spec/custom.spec.js index 2c97daa..7dcb77b 100644 --- a/spec/custom.spec.js +++ b/spec/custom.spec.js @@ -507,6 +507,72 @@ describe('Custom keywords', function () { }); + describe('$data reference support with custom keywords (v5 only)', function() { + beforeEach(function() { + instances = getAjvInstances({ + allErrors: true, + verbose: true, + inlineRefs: false + }, { v5: true }); + ajv = instances[0]; + }); + + it('should validate "interpreted" rule', function() { + testEvenKeyword$data({ + type: 'number', + $data: true, + validate: validateEven + }); + + function validateEven(schema, data) { + if (typeof schema != 'boolean') return false; + return data % 2 ? !schema : schema; + } + }); + + it('should validate "compiled" rule', function() { + testEvenKeyword$data({ + type: 'number', + $data: true, + compile: compileEven + }); + shouldBeInvalidSchema({ "even": "false" }); + + function compileEven(schema) { + if (typeof schema != 'boolean') throw new Error('The value of "even" keyword must be boolean'); + return schema ? isEven : isOdd; + } + + function isEven(data) { return data % 2 === 0; } + function isOdd(data) { return data % 2 !== 0; } + }); + + it('should validate "interpreted" rule with meta-schema', function() { + testEvenKeyword$data({ + type: 'number', + $data: true, + validate: validateEven, + metaSchema: { "type": "boolean" } + }); + shouldBeInvalidSchema({ "even": "false" }); + + function validateEven(schema, data) { + return data % 2 ? !schema : schema; + } + }); + + it('should fail if keyword definition has "$data" but no "validate" or "compile"', function() { + should.throw(function() { + ajv.addKeyword('even', { + type: 'number', + $data: true, + macro: function() { return {}; } + }); + }); + }); + }); + + function testEvenKeyword(definition, numErrors) { instances.forEach(function (ajv) { ajv.addKeyword('even', definition); @@ -520,6 +586,30 @@ describe('Custom keywords', function () { }); } + function testEvenKeyword$data(definition, numErrors) { + instances.forEach(function (ajv) { + var schema = { + "properties": { + "data": { "even": { "$data": "1/evenValue" } }, + "evenValue": {} + } + }; + ajv.addKeyword('even', definition); + var validate = ajv.compile(schema); + + shouldBeValid(validate, { data: 2, evenValue: true }); + shouldBeInvalid(validate, { data: 2, evenValue: false }); + shouldBeValid(validate, { data: 'abc', evenValue: true }); + shouldBeValid(validate, { data: 'abc', evenValue: false }); + shouldBeInvalid(validate, { data: 2.5, evenValue: true }, numErrors); + shouldBeValid(validate, { data: 2.5, evenValue: false }); + shouldBeInvalid(validate, { data: 3, evenValue: true }, numErrors); + shouldBeValid(validate, { data: 3, evenValue: false }); + + // shouldBeInvalid(validate, { data: 2, evenValue: "true" }); + }); + } + function testConstantKeyword(definition, numErrors) { instances.forEach(function (ajv) { ajv.addKeyword('constant', definition); From 5a80c7cff62a1086e5f5aa48aa82f9a7344b1707 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 22 Jul 2016 14:25:41 +0100 Subject: [PATCH 4/5] feat: support meta-schema validation in custom keywords with $data support, #146 --- lib/compile/index.js | 2 - lib/dot/custom.jst | 22 ++++--- lib/keyword.js | 10 +-- spec/custom.spec.js | 141 +++++++++++++++++++++++++++++++++++++++---- 4 files changed, 147 insertions(+), 28 deletions(-) diff --git a/lib/compile/index.js b/lib/compile/index.js index 217cf57..122dc7b 100644 --- a/lib/compile/index.js +++ b/lib/compile/index.js @@ -341,9 +341,7 @@ function vars(arr, statement) { */ var co = require('co'); - var ucs2length = util.ucs2length; - var equal = require('./equal'); // this error is thrown by async schemas to return validation errors via exception diff --git a/lib/dot/custom.jst b/lib/dot/custom.jst index e92985a..cf3ff1a 100644 --- a/lib/dot/custom.jst +++ b/lib/dot/custom.jst @@ -5,9 +5,10 @@ {{ var $rule = this + , $definition = 'definition' + $lvl , $rDef = $rule.definition , $validate = $rDef.validate - , $compile = $rDef.compile + , $compile , $inline , $macro , $ruleValidate @@ -15,14 +16,18 @@ }} {{? $isData && $rDef.$data }} - {{ $validateCode = 'keywordValidate' + $lvl; }} - var {{=$validateCode}} = RULES.custom['{{=$keyword}}'].definition - {{? $validate }}.validate{{??}}.compile({{=$schemaValue}}, validate.schema{{=it.schemaPath}}){{?}}; + {{ + $validateCode = 'keywordValidate' + $lvl; + var $validateSchema = $rDef.validateSchema; + }} + var {{=$definition}} = RULES.custom['{{=$keyword}}'].definition; + var {{=$validateCode}} = {{=$definition}}.validate; {{??}} {{ $ruleValidate = it.useCustomRule($rule, $schema, it.schema, it); $schemaValue = 'validate.schema' + $schemaPath; $validateCode = $ruleValidate.code; + $compile = $rDef.compile; $inline = $rDef.inline; $macro = $rDef.macro; }} @@ -108,7 +113,7 @@ var valid{{=$lvl}}; {{ var $code = it.validate($it).replace(/validate\.schema/g, $validateCode); }} {{# def.resetCompositeRule }} {{= $code }} -{{?? $compile || $validate }} +{{?? !$inline }} {{# def.beginDefOut}} {{# def.callRuleValidate }} {{# def.storeDefOut:def_callRuleValidate }} @@ -131,7 +136,10 @@ var valid{{=$lvl}}; {{?}} -if (!{{# def.ruleValidationResult }}) { +if ({{? $validateSchema }} + !{{=$definition}}.validateSchema({{=$schemaValue}}) || + {{?}} + !{{# def.ruleValidationResult }}) { {{ $errorKeyword = $rule.keyword; }} {{# def.beginDefOut}} {{# def.error:'custom' }} @@ -153,7 +161,7 @@ if (!{{# def.ruleValidationResult }}) { } {{?}} {{?}} - {{?? $macro}} + {{?? $macro }} {{# def.extraError:'custom' }} {{??}} {{? $rDef.errors === false}} diff --git a/lib/keyword.js b/lib/keyword.js index d55a50b..3cda765 100644 --- a/lib/keyword.js +++ b/lib/keyword.js @@ -30,14 +30,8 @@ module.exports = function addKeyword(keyword, definition) { } var $data = definition.$data === true && this._opts.v5; - if ($data) { - if (!definition.validate) { - if (definition.compile) - console.warn('$data support: it is recommended to define "validate" function'); - else - throw new Error('$data support: neither "validate" nor "compile" functions are defined'); - } - } + if ($data && !definition.validate) + throw new Error('$data support: neither "validate" nor "compile" functions are defined'); var metaSchema = definition.metaSchema; if (metaSchema) { diff --git a/spec/custom.spec.js b/spec/custom.spec.js index 7dcb77b..40e72bf 100644 --- a/spec/custom.spec.js +++ b/spec/custom.spec.js @@ -530,15 +530,23 @@ describe('Custom keywords', function () { } }); - it('should validate "compiled" rule', function() { + it('should validate rule with "compile" and "validate" funcs', function() { + var compileCalled; testEvenKeyword$data({ type: 'number', $data: true, - compile: compileEven + compile: compileEven, + validate: validateEven }); - shouldBeInvalidSchema({ "even": "false" }); + compileCalled .should.equal(true); + + function validateEven(schema, data) { + if (typeof schema != 'boolean') return false; + return data % 2 ? !schema : schema; + } function compileEven(schema) { + compileCalled = true; if (typeof schema != 'boolean') throw new Error('The value of "even" keyword must be boolean'); return schema ? isEven : isOdd; } @@ -547,21 +555,123 @@ describe('Custom keywords', function () { function isOdd(data) { return data % 2 !== 0; } }); - it('should validate "interpreted" rule with meta-schema', function() { + it('should validate with "compile" and "validate" funcs with meta-schema', function() { + var compileCalled; testEvenKeyword$data({ type: 'number', $data: true, + compile: compileEven, validate: validateEven, metaSchema: { "type": "boolean" } }); + compileCalled .should.equal(true); shouldBeInvalidSchema({ "even": "false" }); function validateEven(schema, data) { return data % 2 ? !schema : schema; } + + function compileEven(schema) { + compileCalled = true; + return schema ? isEven : isOdd; + } + + function isEven(data) { return data % 2 === 0; } + function isOdd(data) { return data % 2 !== 0; } }); - it('should fail if keyword definition has "$data" but no "validate" or "compile"', function() { + it('should validate rule with "macro" and "validate" funcs', function() { + var macroCalled; + testEvenKeyword$data({ + type: 'number', + $data: true, + macro: macroEven, + validate: validateEven + }, 2); + macroCalled .should.equal(true); + + function validateEven(schema, data) { + if (typeof schema != 'boolean') return false; + return data % 2 ? !schema : schema; + } + + function macroEven(schema) { + macroCalled = true; + if (schema === true) return { "multipleOf": 2 }; + if (schema === false) return { "not": { "multipleOf": 2 } }; + throw new Error('Schema for "even" keyword should be boolean'); + } + }); + + it('should validate with "macro" and "validate" funcs with meta-schema', function() { + var macroCalled; + testEvenKeyword$data({ + type: 'number', + $data: true, + macro: macroEven, + validate: validateEven, + metaSchema: { "type": "boolean" } + }, 2); + macroCalled .should.equal(true); + shouldBeInvalidSchema({ "even": "false" }); + + function validateEven(schema, data) { + return data % 2 ? !schema : schema; + } + + function macroEven(schema) { + macroCalled = true; + if (schema === true) return { "multipleOf": 2 }; + if (schema === false) return { "not": { "multipleOf": 2 } }; + } + }); + + it('should validate rule with "inline" and "validate" funcs', function() { + var inlineCalled; + testEvenKeyword$data({ + type: 'number', + $data: true, + inline: inlineEven, + validate: validateEven + }); + inlineCalled .should.equal(true); + + function validateEven(schema, data) { + if (typeof schema != 'boolean') return false; + return data % 2 ? !schema : schema; + } + + function inlineEven(it, keyword, schema) { + inlineCalled = true; + var op = schema ? '===' : '!=='; + return 'data' + (it.dataLevel || '') + ' % 2 ' + op + ' 0'; + } + }); + + it('should validate with "inline" and "validate" funcs with meta-schema', function() { + var inlineCalled; + testEvenKeyword$data({ + type: 'number', + $data: true, + inline: inlineEven, + validate: validateEven, + metaSchema: { "type": "boolean" } + }); + inlineCalled .should.equal(true); + shouldBeInvalidSchema({ "even": "false" }); + + function validateEven(schema, data) { + return data % 2 ? !schema : schema; + } + + function inlineEven(it, keyword, schema) { + inlineCalled = true; + var op = schema ? '===' : '!=='; + return 'data' + (it.dataLevel || '') + ' % 2 ' + op + ' 0'; + } + }); + + it('should fail if keyword definition has "$data" but no "validate"', function() { should.throw(function() { ajv.addKeyword('even', { type: 'number', @@ -588,25 +698,34 @@ describe('Custom keywords', function () { function testEvenKeyword$data(definition, numErrors) { instances.forEach(function (ajv) { - var schema = { + ajv.addKeyword('even', definition); + + var schema = { "even": true }; + var validate = ajv.compile(schema); + + shouldBeValid(validate, 2); + shouldBeValid(validate, 'abc'); + shouldBeInvalid(validate, 2.5, numErrors); + shouldBeInvalid(validate, 3, numErrors); + + schema = { "properties": { "data": { "even": { "$data": "1/evenValue" } }, "evenValue": {} } }; - ajv.addKeyword('even', definition); - var validate = ajv.compile(schema); + validate = ajv.compile(schema); shouldBeValid(validate, { data: 2, evenValue: true }); shouldBeInvalid(validate, { data: 2, evenValue: false }); shouldBeValid(validate, { data: 'abc', evenValue: true }); shouldBeValid(validate, { data: 'abc', evenValue: false }); - shouldBeInvalid(validate, { data: 2.5, evenValue: true }, numErrors); + shouldBeInvalid(validate, { data: 2.5, evenValue: true }); shouldBeValid(validate, { data: 2.5, evenValue: false }); - shouldBeInvalid(validate, { data: 3, evenValue: true }, numErrors); + shouldBeInvalid(validate, { data: 3, evenValue: true }); shouldBeValid(validate, { data: 3, evenValue: false }); - // shouldBeInvalid(validate, { data: 2, evenValue: "true" }); + shouldBeInvalid(validate, { data: 2, evenValue: "true" }); }); } From 13e98fbee193a290090af5b0b1c71468663673e6 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 22 Jul 2016 14:48:44 +0100 Subject: [PATCH 5/5] docs: support $data reference in custom keywords, closes #146 --- CUSTOM.md | 1 + README.md | 3 ++- package.json | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CUSTOM.md b/CUSTOM.md index 66f7e10..ead5d45 100644 --- a/CUSTOM.md +++ b/CUSTOM.md @@ -23,6 +23,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). __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. diff --git a/README.md b/README.md index 818678c..34b6969 100644 --- a/README.md +++ b/README.md @@ -815,10 +815,11 @@ 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 +- _$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. -_validate_, _compile_, _macro_ and _inline_ are mutually exclusive, only one should be used at a time. +_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. __Please note__: If the keyword is validating data type that is different from the type(s) in its definition, the validation function will not be called (and expanded macro will not be used), so there is no need to check for data type inside validation function or inside schema returned by macro function (unless you want to enforce a specific type and for some reason do not want to use a separate `type` keyword for that). In the same way as standard keywords work, if the keyword does not apply to the data type being validated, the validation of this keyword will succeed. diff --git a/package.json b/package.json index 0db8ab0..dc759a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "4.1.7", + "version": "4.2.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts",