2015-05-20 03:55:53 +03:00
'use strict' ;
var compileSchema = require ( './compile' )
2015-06-07 14:46:53 +03:00
, resolve = require ( './compile/resolve' )
2015-05-20 04:02:47 +03:00
, stableStringify = require ( 'json-stable-stringify' )
2015-06-13 15:42:58 +03:00
, formats = require ( './compile/formats' )
, util = require ( './compile/util' ) ;
2015-05-20 03:55:53 +03:00
2015-05-30 01:32:47 +03:00
module . exports = Ajv ;
2015-05-20 03:55:53 +03:00
2015-06-07 14:46:53 +03:00
var META _SCHEMA _ID = 'http://json-schema.org/draft-04/schema' ;
2015-06-18 00:06:07 +03:00
var SCHEMA _URI _FORMAT = /^(?:(?:[a-z][a-z0-9+-.]*:)?\/\/)?[^\s]*$/i ;
function SCHEMA _URI _FORMAT _FUNC ( str ) {
return SCHEMA _URI _FORMAT . test ( str ) ;
}
2015-06-07 14:46:53 +03:00
2015-05-20 03:55:53 +03:00
/ * *
* Creates validator instance .
* Usage : ` jv(opts) `
* @ param { Object } opts optional options
* @ return { Object } jv instance
* /
2015-05-30 01:32:47 +03:00
function Ajv ( opts ) {
if ( ! ( this instanceof Ajv ) ) return new Ajv ( opts ) ;
2015-05-20 03:55:53 +03:00
var self = this ;
2015-05-26 04:11:36 +03:00
this . opts = opts || { } ;
this . _schemas = { } ;
2015-06-07 14:46:53 +03:00
this . _refs = { } ;
2015-05-26 04:11:36 +03:00
this . _byJson = { } ;
2015-06-13 15:42:58 +03:00
this . _formats = formats ( this . opts . format ) ;
2015-05-20 03:55:53 +03:00
2015-05-26 04:11:36 +03:00
// this is done on purpose, so that methods are bound to the instance
// (without using bind) so that they can be used without the instance
this . validate = validate ;
this . compile = compile ;
this . addSchema = addSchema ;
2015-06-18 00:06:07 +03:00
this . validateSchema = validateSchema ;
2015-05-26 04:11:36 +03:00
this . getSchema = getSchema ;
2015-06-13 15:42:58 +03:00
this . addFormat = addFormat ;
2015-06-17 04:05:52 +03:00
this . errorsText = errorsText ;
2015-06-13 15:42:58 +03:00
addInitialSchemas ( ) ;
addInitialFormats ( ) ;
2015-05-26 04:11:36 +03:00
/ * *
* 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.
2015-06-07 14:46:53 +03:00
* @ param { String | Object } schemaKeyRef key , ref or schema object
2015-05-26 04:11:36 +03:00
* @ param { Any } data to be validated
* @ return { Boolean } validation result . Errors from the last validation will be available in ` jv.errors ` ( and also in compiled schema : ` schema.errors ` ) .
* /
2015-06-07 14:46:53 +03:00
function validate ( schemaKeyRef , data ) {
if ( typeof schemaKeyRef == 'string' ) {
var v = getSchema ( schemaKeyRef ) ;
if ( ! v ) {
v = getRef ( schemaKeyRef ) ;
if ( ! v ) throw new Error ( 'no schema with key or ref "' + schemaKeyRef + '"' ) ;
}
} else v = _addSchema ( schemaKeyRef ) ;
2015-06-08 10:00:35 +03:00
var valid = v ( data ) ;
self . errors = v . errors ;
return valid ;
2015-05-26 04:11:36 +03:00
}
/ * *
* Create validator for passed schema .
* @ param { String | Object } schema
* @ return { Object } validation result { valid : true / false , errors : [ ... ] }
* /
function compile ( schema ) {
2015-05-30 21:11:06 +03:00
return _addSchema ( schema ) ;
2015-05-26 04:11:36 +03:00
}
/ * *
* Adds schema to the instance .
2015-06-07 14:46:53 +03:00
* @ param { Object | Array } schema schema or array of schemas . If array is passed , ` key ` 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 { Function } compiled schema with method ` validate ` that accepts ` data ` .
2015-05-26 04:11:36 +03:00
* /
2015-06-07 14:46:53 +03:00
function addSchema ( schema , key , _skipValidation ) {
if ( Array . isArray ( schema ) )
return schema . map ( function ( sch ) { return addSchema ( sch ) ; } ) ;
// can key/id have # inside?
var key = resolve . normalizeId ( key || schema . id ) ;
checkUnique ( key ) ;
var validate = self . _schemas [ key ] = _addSchema ( schema , _skipValidation ) ;
2015-05-30 21:11:06 +03:00
return validate ;
2015-05-26 04:11:36 +03:00
}
2015-06-18 00:06:07 +03:00
/ * *
* Validate schema
* @ param { Object } schema schema to validate
* @ return { Boolean }
* /
2015-06-07 14:46:53 +03:00
function validateSchema ( schema ) {
var $schema = schema . $schema || META _SCHEMA _ID ;
2015-06-18 00:06:07 +03:00
var currentUriFormat = self . _formats . uri ;
self . _formats . uri = typeof currentUriFormat == 'function'
? SCHEMA _URI _FORMAT _FUNC
: SCHEMA _URI _FORMAT ;
var valid = validate ( $schema , schema ) ;
self . _formats . uri = currentUriFormat ;
return valid ;
2015-06-07 14:46:53 +03:00
}
/ * *
* Get compiled schema from the instance by ` key ` .
* @ param { String } key ` key ` that was passed to ` addSchema ` ( or ` schema.id ` ) .
* @ return { Function } schema validating function ( with property ` schema ` ) .
* /
function getSchema ( key ) {
key = resolve . normalizeId ( key ) ;
return self . _schemas [ key ] ;
}
2015-05-26 04:11:36 +03:00
/ * *
2015-06-07 14:46:53 +03:00
* Get compiled schema from the instance by ` id ` .
* @ param { String } id ` schema.id ` or any reference in any of previously passed schemas .
* @ return { Function } schema validating function ( with property ` schema ` ) .
2015-05-26 04:11:36 +03:00
* /
2015-06-07 14:46:53 +03:00
function getRef ( ref ) {
ref = resolve . normalizeId ( ref ) ;
// TODO
return self . _refs [ ref ] ;
2015-05-26 04:11:36 +03:00
}
2015-06-07 14:46:53 +03:00
function _addSchema ( schema , skipValidation ) {
if ( typeof schema != 'object' ) throw new Error ( 'schema should be object' ) ;
2015-05-30 11:53:04 +03:00
var str = stableStringify ( schema ) ;
2015-06-07 14:46:53 +03:00
if ( self . _byJson [ str ] ) return self . _byJson [ str ] ;
var id = resolve . normalizeId ( schema . id ) ;
if ( id ) checkUnique ( id ) ;
2015-06-17 04:05:52 +03:00
var ok = skipValidation || self . opts . validateSchema === false
|| validateSchema ( schema ) ;
2015-06-18 00:06:07 +03:00
if ( ! ok ) {
var message = 'schema is invalid:' + errorsText ( ) ;
if ( self . opts . validateSchema == 'log' ) console . error ( message ) ;
else throw new Error ( message ) ;
}
2015-06-07 14:46:53 +03:00
resolve . ids . call ( self , schema ) ;
var validate = self . _refs [ id ] = self . _byJson [ str ] = compileSchema . call ( self , schema ) ;
return validate ;
}
2015-06-17 04:05:52 +03:00
function errorsText ( errors , opts ) {
errors = errors || self . errors ;
if ( ! errors ) return 'No errors' ;
opts = opts || { } ;
var separator = opts . separator || ', ' ;
var dataVar = opts . dataVar || 'data' ;
var text = errors . reduce ( function ( txt , e ) {
return e ? txt + e . keyword + ' ' + dataVar + e . dataPath + ': ' + e . message + separator : txt ;
} , '' ) ;
return text . slice ( 0 , - separator . length ) ;
}
2015-06-13 15:42:58 +03:00
function addFormat ( name , format ) {
if ( typeof format == 'string' ) format = new RegExp ( format ) ;
self . _formats [ name ] = format ;
}
function addInitialSchemas ( ) {
if ( self . opts . meta !== false )
addSchema ( require ( './refs/json-schema-draft-04.json' ) , META _SCHEMA _ID , true ) ;
var optsSchemas = self . opts . schemas ;
if ( ! optsSchemas ) return ;
if ( Array . isArray ( optsSchemas ) ) addSchema ( optsSchemas ) ;
else for ( var key in optsSchemas ) addSchema ( optsSchemas [ key ] , key ) ;
}
function addInitialFormats ( ) {
var optsFormats = self . opts . formats ;
if ( ! optsFormats ) return ;
for ( var name in optsFormats ) {
var format = optsFormats [ name ] ;
addFormat ( name , format ) ;
}
}
2015-06-07 14:46:53 +03:00
function checkUnique ( id ) {
2015-06-17 00:19:00 +03:00
if ( self . _schemas [ id ] || self . _refs [ id ] )
2015-06-07 14:46:53 +03:00
throw new Error ( 'schema with key or id "' + id + '" already exists' ) ;
2015-05-26 04:11:36 +03:00
}
2015-05-20 03:55:53 +03:00
}