| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 | 'use strict';var URI = require('uri-js')  , equal = require('fast-deep-equal')  , util = require('./util')  , SchemaObject = require('./schema_obj')  , traverse = require('json-schema-traverse');module.exports = resolve;resolve.normalizeId = normalizeId;resolve.fullPath = getFullPath;resolve.url = resolveUrl;resolve.ids = resolveIds;resolve.inlineRef = inlineRef;resolve.schema = resolveSchema;/** * [resolve and compile the references ($ref)] * @this   Ajv * @param  {Function} compile reference to schema compilation funciton (localCompile) * @param  {Object} root object with information about the root schema for the current schema * @param  {String} ref reference to resolve * @return {Object|Function} schema object (if the schema can be inlined) or validation function */function resolve(compile, root, ref) {  /* jshint validthis: true */  var refVal = this._refs[ref];  if (typeof refVal == 'string') {    if (this._refs[refVal]) refVal = this._refs[refVal];    else return resolve.call(this, compile, root, refVal);  }  refVal = refVal || this._schemas[ref];  if (refVal instanceof SchemaObject) {    return inlineRef(refVal.schema, this._opts.inlineRefs)            ? refVal.schema            : refVal.validate || this._compile(refVal);  }  var res = resolveSchema.call(this, root, ref);  var schema, v, baseId;  if (res) {    schema = res.schema;    root = res.root;    baseId = res.baseId;  }  if (schema instanceof SchemaObject) {    v = schema.validate || compile.call(this, schema.schema, root, undefined, baseId);  } else if (schema !== undefined) {    v = inlineRef(schema, this._opts.inlineRefs)        ? schema        : compile.call(this, schema, root, undefined, baseId);  }  return v;}/** * Resolve schema, its root and baseId * @this Ajv * @param  {Object} root root object with properties schema, refVal, refs * @param  {String} ref  reference to resolve * @return {Object} object with properties schema, root, baseId */function resolveSchema(root, ref) {  /* jshint validthis: true */  var p = URI.parse(ref)    , refPath = _getFullPath(p)    , baseId = getFullPath(this._getId(root.schema));  if (Object.keys(root.schema).length === 0 || refPath !== baseId) {    var id = normalizeId(refPath);    var refVal = this._refs[id];    if (typeof refVal == 'string') {      return resolveRecursive.call(this, root, refVal, p);    } else if (refVal instanceof SchemaObject) {      if (!refVal.validate) this._compile(refVal);      root = refVal;    } else {      refVal = this._schemas[id];      if (refVal instanceof SchemaObject) {        if (!refVal.validate) this._compile(refVal);        if (id == normalizeId(ref))          return { schema: refVal, root: root, baseId: baseId };        root = refVal;      } else {        return;      }    }    if (!root.schema) return;    baseId = getFullPath(this._getId(root.schema));  }  return getJsonPointer.call(this, p, baseId, root.schema, root);}/* @this Ajv */function resolveRecursive(root, ref, parsedRef) {  /* jshint validthis: true */  var res = resolveSchema.call(this, root, ref);  if (res) {    var schema = res.schema;    var baseId = res.baseId;    root = res.root;    var id = this._getId(schema);    if (id) baseId = resolveUrl(baseId, id);    return getJsonPointer.call(this, parsedRef, baseId, schema, root);  }}var PREVENT_SCOPE_CHANGE = util.toHash(['properties', 'patternProperties', 'enum', 'dependencies', 'definitions']);/* @this Ajv */function getJsonPointer(parsedRef, baseId, schema, root) {  /* jshint validthis: true */  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];    if (part) {      part = util.unescapeFragment(part);      schema = schema[part];      if (schema === undefined) break;      var id;      if (!PREVENT_SCOPE_CHANGE[part]) {        id = this._getId(schema);        if (id) baseId = resolveUrl(baseId, id);        if (schema.$ref) {          var $ref = resolveUrl(baseId, schema.$ref);          var res = resolveSchema.call(this, root, $ref);          if (res) {            schema = res.schema;            root = res.root;            baseId = res.baseId;          }        }      }    }  }  if (schema !== undefined && schema !== root.schema)    return { schema: schema, root: root, baseId: baseId };}var SIMPLE_INLINED = util.toHash([  'type', 'format', 'pattern',  'maxLength', 'minLength',  'maxProperties', 'minProperties',  'maxItems', 'minItems',  'maximum', 'minimum',  'uniqueItems', 'multipleOf',  'required', 'enum']);function inlineRef(schema, limit) {  if (limit === false) return false;  if (limit === undefined || limit === true) return checkNoRef(schema);  else if (limit) return countKeys(schema) <= limit;}function checkNoRef(schema) {  var item;  if (Array.isArray(schema)) {    for (var i=0; i<schema.length; i++) {      item = schema[i];      if (typeof item == 'object' && !checkNoRef(item)) return false;    }  } else {    for (var key in schema) {      if (key == '$ref') return false;      item = schema[key];      if (typeof item == 'object' && !checkNoRef(item)) return false;    }  }  return true;}function countKeys(schema) {  var count = 0, item;  if (Array.isArray(schema)) {    for (var i=0; i<schema.length; i++) {      item = schema[i];      if (typeof item == 'object') count += countKeys(item);      if (count == Infinity) return Infinity;    }  } else {    for (var key in schema) {      if (key == '$ref') return Infinity;      if (SIMPLE_INLINED[key]) {        count++;      } else {        item = schema[key];        if (typeof item == 'object') count += countKeys(item) + 1;        if (count == Infinity) return Infinity;      }    }  }  return count;}function getFullPath(id, normalize) {  if (normalize !== false) id = normalizeId(id);  var p = URI.parse(id);  return _getFullPath(p);}function _getFullPath(p) {  return URI.serialize(p).split('#')[0] + '#';}var TRAILING_SLASH_HASH = /#\/?$/;function normalizeId(id) {  return id ? id.replace(TRAILING_SLASH_HASH, '') : '';}function resolveUrl(baseId, id) {  id = normalizeId(id);  return URI.resolve(baseId, id);}/* @this Ajv */function resolveIds(schema) {  var schemaId = normalizeId(this._getId(schema));  var baseIds = {'': schemaId};  var fullPaths = {'': getFullPath(schemaId, false)};  var localRefs = {};  var self = this;  traverse(schema, {allKeys: true}, function(sch, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex) {    if (jsonPtr === '') return;    var id = self._getId(sch);    var baseId = baseIds[parentJsonPtr];    var fullPath = fullPaths[parentJsonPtr] + '/' + parentKeyword;    if (keyIndex !== undefined)      fullPath += '/' + (typeof keyIndex == 'number' ? keyIndex : util.escapeFragment(keyIndex));    if (typeof id == 'string') {      id = baseId = normalizeId(baseId ? URI.resolve(baseId, id) : id);      var refVal = self._refs[id];      if (typeof refVal == 'string') refVal = self._refs[refVal];      if (refVal && refVal.schema) {        if (!equal(sch, refVal.schema))          throw new Error('id "' + id + '" resolves to more than one schema');      } else if (id != normalizeId(fullPath)) {        if (id[0] == '#') {          if (localRefs[id] && !equal(sch, localRefs[id]))            throw new Error('id "' + id + '" resolves to more than one schema');          localRefs[id] = sch;        } else {          self._refs[id] = fullPath;        }      }    }    baseIds[jsonPtr] = baseId;    fullPaths[jsonPtr] = fullPath;  });  return localRefs;}
 |