'use strict'; var helpers = require('./helpers'); /** @type ValidatorResult */ var ValidatorResult = helpers.ValidatorResult; /** @type SchemaError */ var SchemaError = helpers.SchemaError; var attribute = {}; attribute.ignoreProperties = { // informative properties 'id': true, 'default': true, 'description': true, 'title': true, // arguments to other properties 'additionalItems': true, 'then': true, 'else': true, // special-handled properties '$schema': true, '$ref': true, 'extends': true, }; /** * @name validators */ var validators = attribute.validators = {}; /** * Validates whether the instance if of a certain type * @param instance * @param schema * @param options * @param ctx * @return {ValidatorResult|null} */ validators.type = function validateType (instance, schema, options, ctx) { // Ignore undefined instances if (instance === undefined) { return null; } var result = new ValidatorResult(instance, schema, options, ctx); var types = Array.isArray(schema.type) ? schema.type : [schema.type]; if (!types.some(this.testType.bind(this, instance, schema, options, ctx))) { var list = types.map(function (v) { if(!v) return; var id = v.$id || v.id; return id ? ('<' + id + '>') : (v+''); }); result.addError({ name: 'type', argument: list, message: "is not of a type(s) " + list, }); } return result; }; function testSchemaNoThrow(instance, options, ctx, callback, schema){ var throwError = options.throwError; var throwAll = options.throwAll; options.throwError = false; options.throwAll = false; var res = this.validateSchema(instance, schema, options, ctx); options.throwError = throwError; options.throwAll = throwAll; if (!res.valid && callback instanceof Function) { callback(res); } return res.valid; } /** * Validates whether the instance matches some of the given schemas * @param instance * @param schema * @param options * @param ctx * @return {ValidatorResult|null} */ validators.anyOf = function validateAnyOf (instance, schema, options, ctx) { // Ignore undefined instances if (instance === undefined) { return null; } var result = new ValidatorResult(instance, schema, options, ctx); var inner = new ValidatorResult(instance, schema, options, ctx); if (!Array.isArray(schema.anyOf)){ throw new SchemaError("anyOf must be an array"); } if (!schema.anyOf.some( testSchemaNoThrow.bind( this, instance, options, ctx, function(res){inner.importErrors(res);} ))) { var list = schema.anyOf.map(function (v, i) { var id = v.$id || v.id; if(id) return '<' + id + '>'; return(v.title && JSON.stringify(v.title)) || (v['$ref'] && ('<' + v['$ref'] + '>')) || '[subschema '+i+']'; }); if (options.nestedErrors) { result.importErrors(inner); } result.addError({ name: 'anyOf', argument: list, message: "is not any of " + list.join(','), }); } return result; }; /** * Validates whether the instance matches every given schema * @param instance * @param schema * @param options * @param ctx * @return {String|null} */ validators.allOf = function validateAllOf (instance, schema, options, ctx) { // Ignore undefined instances if (instance === undefined) { return null; } if (!Array.isArray(schema.allOf)){ throw new SchemaError("allOf must be an array"); } var result = new ValidatorResult(instance, schema, options, ctx); var self = this; schema.allOf.forEach(function(v, i){ var valid = self.validateSchema(instance, v, options, ctx); if(!valid.valid){ var id = v.$id || v.id; var msg = id || (v.title && JSON.stringify(v.title)) || (v['$ref'] && ('<' + v['$ref'] + '>')) || '[subschema '+i+']'; result.addError({ name: 'allOf', argument: { id: msg, length: valid.errors.length, valid: valid }, message: 'does not match allOf schema ' + msg + ' with ' + valid.errors.length + ' error[s]:', }); result.importErrors(valid); } }); return result; }; /** * Validates whether the instance matches exactly one of the given schemas * @param instance * @param schema * @param options * @param ctx * @return {String|null} */ validators.oneOf = function validateOneOf (instance, schema, options, ctx) { // Ignore undefined instances if (instance === undefined) { return null; } if (!Array.isArray(schema.oneOf)){ throw new SchemaError("oneOf must be an array"); } var result = new ValidatorResult(instance, schema, options, ctx); var inner = new ValidatorResult(instance, schema, options, ctx); var count = schema.oneOf.filter( testSchemaNoThrow.bind( this, instance, options, ctx, function(res) {inner.importErrors(res);} ) ).length; var list = schema.oneOf.map(function (v, i) { var id = v.$id || v.id; return id || (v.title && JSON.stringify(v.title)) || (v['$ref'] && ('<' + v['$ref'] + '>')) || '[subschema '+i+']'; }); if (count!==1) { if (options.nestedErrors) { result.importErrors(inner); } result.addError({ name: 'oneOf', argument: list, message: "is not exactly one from " + list.join(','), }); } return result; }; /** * Validates "then" or "else" depending on the result of validating "if" * @param instance * @param schema * @param options * @param ctx * @return {String|null} */ validators.if = function validateIf (instance, schema, options, ctx) { // Ignore undefined instances if (instance === undefined) return null; if (!helpers.isSchema(schema.if)) throw new Error('Expected "if" keyword to be a schema'); var ifValid = testSchemaNoThrow.call(this, instance, options, ctx, null, schema.if); var result = new ValidatorResult(instance, schema, options, ctx); var res; if(ifValid){ if (schema.then === undefined) return; if (!helpers.isSchema(schema.then)) throw new Error('Expected "then" keyword to be a schema'); res = this.validateSchema(instance, schema.then, options, ctx.makeChild(schema.then)); result.importErrors(res); }else{ if (schema.else === undefined) return; if (!helpers.isSchema(schema.else)) throw new Error('Expected "else" keyword to be a schema'); res = this.validateSchema(instance, schema.else, options, ctx.makeChild(schema.else)); result.importErrors(res); } return result; }; function getEnumerableProperty(object, key){ // Determine if `key` shows up in `for(var key in object)` // First test Object.hasOwnProperty.call as an optimization: that guarantees it does if(Object.hasOwnProperty.call(object, key)) return object[key]; // Test `key in object` as an optimization; false means it won't if(!(key in object)) return; while( (object = Object.getPrototypeOf(object)) ){ if(Object.propertyIsEnumerable.call(object, key)) return object[key]; } } /** * Validates propertyNames * @param instance * @param schema * @param options * @param ctx * @return {String|null|ValidatorResult} */ validators.propertyNames = function validatePropertyNames (instance, schema, options, ctx) { if(!this.types.object(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); var subschema = schema.propertyNames!==undefined ? schema.propertyNames : {}; if(!helpers.isSchema(subschema)) throw new SchemaError('Expected "propertyNames" to be a schema (object or boolean)'); for (var property in instance) { if(getEnumerableProperty(instance, property) !== undefined){ var res = this.validateSchema(property, subschema, options, ctx.makeChild(subschema)); result.importErrors(res); } } return result; }; /** * Validates properties * @param instance * @param schema * @param options * @param ctx * @return {String|null|ValidatorResult} */ validators.properties = function validateProperties (instance, schema, options, ctx) { if(!this.types.object(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); var properties = schema.properties || {}; for (var property in properties) { var subschema = properties[property]; if(subschema===undefined){ continue; }else if(subschema===null){ throw new SchemaError('Unexpected null, expected schema in "properties"'); } if (typeof options.preValidateProperty == 'function') { options.preValidateProperty(instance, property, subschema, options, ctx); } var prop = getEnumerableProperty(instance, property); var res = this.validateSchema(prop, subschema, options, ctx.makeChild(subschema, property)); if(res.instance !== result.instance[property]) result.instance[property] = res.instance; result.importErrors(res); } return result; }; /** * Test a specific property within in instance against the additionalProperties schema attribute * This ignores properties with definitions in the properties schema attribute, but no other attributes. * If too many more types of property-existence tests pop up they may need their own class of tests (like `type` has) * @private * @return {boolean} */ function testAdditionalProperty (instance, schema, options, ctx, property, result) { if(!this.types.object(instance)) return; if (schema.properties && schema.properties[property] !== undefined) { return; } if (schema.additionalProperties === false) { result.addError({ name: 'additionalProperties', argument: property, message: "is not allowed to have the additional property " + JSON.stringify(property), }); } else { var additionalProperties = schema.additionalProperties || {}; if (typeof options.preValidateProperty == 'function') { options.preValidateProperty(instance, property, additionalProperties, options, ctx); } var res = this.validateSchema(instance[property], additionalProperties, options, ctx.makeChild(additionalProperties, property)); if(res.instance !== result.instance[property]) result.instance[property] = res.instance; result.importErrors(res); } } /** * Validates patternProperties * @param instance * @param schema * @param options * @param ctx * @return {String|null|ValidatorResult} */ validators.patternProperties = function validatePatternProperties (instance, schema, options, ctx) { if(!this.types.object(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); var patternProperties = schema.patternProperties || {}; for (var property in instance) { var test = true; for (var pattern in patternProperties) { var subschema = patternProperties[pattern]; if(subschema===undefined){ continue; }else if(subschema===null){ throw new SchemaError('Unexpected null, expected schema in "patternProperties"'); } try { var regexp = new RegExp(pattern, 'u'); } catch(_e) { // In the event the stricter handling causes an error, fall back on the forgiving handling // DEPRECATED regexp = new RegExp(pattern); } if (!regexp.test(property)) { continue; } test = false; if (typeof options.preValidateProperty == 'function') { options.preValidateProperty(instance, property, subschema, options, ctx); } var res = this.validateSchema(instance[property], subschema, options, ctx.makeChild(subschema, property)); if(res.instance !== result.instance[property]) result.instance[property] = res.instance; result.importErrors(res); } if (test) { testAdditionalProperty.call(this, instance, schema, options, ctx, property, result); } } return result; }; /** * Validates additionalProperties * @param instance * @param schema * @param options * @param ctx * @return {String|null|ValidatorResult} */ validators.additionalProperties = function validateAdditionalProperties (instance, schema, options, ctx) { if(!this.types.object(instance)) return; // if patternProperties is defined then we'll test when that one is called instead if (schema.patternProperties) { return null; } var result = new ValidatorResult(instance, schema, options, ctx); for (var property in instance) { testAdditionalProperty.call(this, instance, schema, options, ctx, property, result); } return result; }; /** * Validates whether the instance value is at least of a certain length, when the instance value is a string. * @param instance * @param schema * @return {String|null} */ validators.minProperties = function validateMinProperties (instance, schema, options, ctx) { if (!this.types.object(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); var keys = Object.keys(instance); if (!(keys.length >= schema.minProperties)) { result.addError({ name: 'minProperties', argument: schema.minProperties, message: "does not meet minimum property length of " + schema.minProperties, }); } return result; }; /** * Validates whether the instance value is at most of a certain length, when the instance value is a string. * @param instance * @param schema * @return {String|null} */ validators.maxProperties = function validateMaxProperties (instance, schema, options, ctx) { if (!this.types.object(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); var keys = Object.keys(instance); if (!(keys.length <= schema.maxProperties)) { result.addError({ name: 'maxProperties', argument: schema.maxProperties, message: "does not meet maximum property length of " + schema.maxProperties, }); } return result; }; /** * Validates items when instance is an array * @param instance * @param schema * @param options * @param ctx * @return {String|null|ValidatorResult} */ validators.items = function validateItems (instance, schema, options, ctx) { var self = this; if (!this.types.array(instance)) return; if (schema.items===undefined) return; var result = new ValidatorResult(instance, schema, options, ctx); instance.every(function (value, i) { if(Array.isArray(schema.items)){ var items = schema.items[i]===undefined ? schema.additionalItems : schema.items[i]; }else{ var items = schema.items; } if (items === undefined) { return true; } if (items === false) { result.addError({ name: 'items', message: "additionalItems not permitted", }); return false; } var res = self.validateSchema(value, items, options, ctx.makeChild(items, i)); if(res.instance !== result.instance[i]) result.instance[i] = res.instance; result.importErrors(res); return true; }); return result; }; /** * Validates the "contains" keyword * @param instance * @param schema * @param options * @param ctx * @return {String|null|ValidatorResult} */ validators.contains = function validateContains (instance, schema, options, ctx) { var self = this; if (!this.types.array(instance)) return; if (schema.contains===undefined) return; if (!helpers.isSchema(schema.contains)) throw new Error('Expected "contains" keyword to be a schema'); var result = new ValidatorResult(instance, schema, options, ctx); var count = instance.some(function (value, i) { var res = self.validateSchema(value, schema.contains, options, ctx.makeChild(schema.contains, i)); return res.errors.length===0; }); if(count===false){ result.addError({ name: 'contains', argument: schema.contains, message: "must contain an item matching given schema", }); } return result; }; /** * Validates minimum and exclusiveMinimum when the type of the instance value is a number. * @param instance * @param schema * @return {String|null} */ validators.minimum = function validateMinimum (instance, schema, options, ctx) { if (!this.types.number(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); if (schema.exclusiveMinimum && schema.exclusiveMinimum === true) { if(!(instance > schema.minimum)){ result.addError({ name: 'minimum', argument: schema.minimum, message: "must be greater than " + schema.minimum, }); } } else { if(!(instance >= schema.minimum)){ result.addError({ name: 'minimum', argument: schema.minimum, message: "must be greater than or equal to " + schema.minimum, }); } } return result; }; /** * Validates maximum and exclusiveMaximum when the type of the instance value is a number. * @param instance * @param schema * @return {String|null} */ validators.maximum = function validateMaximum (instance, schema, options, ctx) { if (!this.types.number(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); if (schema.exclusiveMaximum && schema.exclusiveMaximum === true) { if(!(instance < schema.maximum)){ result.addError({ name: 'maximum', argument: schema.maximum, message: "must be less than " + schema.maximum, }); } } else { if(!(instance <= schema.maximum)){ result.addError({ name: 'maximum', argument: schema.maximum, message: "must be less than or equal to " + schema.maximum, }); } } return result; }; /** * Validates the number form of exclusiveMinimum when the type of the instance value is a number. * @param instance * @param schema * @return {String|null} */ validators.exclusiveMinimum = function validateExclusiveMinimum (instance, schema, options, ctx) { // Support the boolean form of exclusiveMinimum, which is handled by the "minimum" keyword. if(typeof schema.exclusiveMinimum === 'boolean') return; if (!this.types.number(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); var valid = instance > schema.exclusiveMinimum; if (!valid) { result.addError({ name: 'exclusiveMinimum', argument: schema.exclusiveMinimum, message: "must be strictly greater than " + schema.exclusiveMinimum, }); } return result; }; /** * Validates the number form of exclusiveMaximum when the type of the instance value is a number. * @param instance * @param schema * @return {String|null} */ validators.exclusiveMaximum = function validateExclusiveMaximum (instance, schema, options, ctx) { // Support the boolean form of exclusiveMaximum, which is handled by the "maximum" keyword. if(typeof schema.exclusiveMaximum === 'boolean') return; if (!this.types.number(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); var valid = instance < schema.exclusiveMaximum; if (!valid) { result.addError({ name: 'exclusiveMaximum', argument: schema.exclusiveMaximum, message: "must be strictly less than " + schema.exclusiveMaximum, }); } return result; }; /** * Perform validation for multipleOf and divisibleBy, which are essentially the same. * @param instance * @param schema * @param validationType * @param errorMessage * @returns {String|null} */ var validateMultipleOfOrDivisbleBy = function validateMultipleOfOrDivisbleBy (instance, schema, options, ctx, validationType, errorMessage) { if (!this.types.number(instance)) return; var validationArgument = schema[validationType]; if (validationArgument == 0) { throw new SchemaError(validationType + " cannot be zero"); } var result = new ValidatorResult(instance, schema, options, ctx); var instanceDecimals = helpers.getDecimalPlaces(instance); var divisorDecimals = helpers.getDecimalPlaces(validationArgument); var maxDecimals = Math.max(instanceDecimals , divisorDecimals); var multiplier = Math.pow(10, maxDecimals); if (Math.round(instance * multiplier) % Math.round(validationArgument * multiplier) !== 0) { result.addError({ name: validationType, argument: validationArgument, message: errorMessage + JSON.stringify(validationArgument), }); } return result; }; /** * Validates divisibleBy when the type of the instance value is a number. * @param instance * @param schema * @return {String|null} */ validators.multipleOf = function validateMultipleOf (instance, schema, options, ctx) { return validateMultipleOfOrDivisbleBy.call(this, instance, schema, options, ctx, "multipleOf", "is not a multiple of (divisible by) "); }; /** * Validates multipleOf when the type of the instance value is a number. * @param instance * @param schema * @return {String|null} */ validators.divisibleBy = function validateDivisibleBy (instance, schema, options, ctx) { return validateMultipleOfOrDivisbleBy.call(this, instance, schema, options, ctx, "divisibleBy", "is not divisible by (multiple of) "); }; /** * Validates whether the instance value is present. * @param instance * @param schema * @return {String|null} */ validators.required = function validateRequired (instance, schema, options, ctx) { var result = new ValidatorResult(instance, schema, options, ctx); if (instance === undefined && schema.required === true) { // A boolean form is implemented for reverse-compatibility with schemas written against older drafts result.addError({ name: 'required', message: "is required", }); } else if (this.types.object(instance) && Array.isArray(schema.required)) { schema.required.forEach(function(n){ if(getEnumerableProperty(instance, n)===undefined){ result.addError({ name: 'required', argument: n, message: "requires property " + JSON.stringify(n), }); } }); } return result; }; /** * Validates whether the instance value matches the regular expression, when the instance value is a string. * @param instance * @param schema * @return {String|null} */ validators.pattern = function validatePattern (instance, schema, options, ctx) { if (!this.types.string(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); var pattern = schema.pattern; try { var regexp = new RegExp(pattern, 'u'); } catch(_e) { // In the event the stricter handling causes an error, fall back on the forgiving handling // DEPRECATED regexp = new RegExp(pattern); } if (!instance.match(regexp)) { result.addError({ name: 'pattern', argument: schema.pattern, message: "does not match pattern " + JSON.stringify(schema.pattern.toString()), }); } return result; }; /** * Validates whether the instance value is of a certain defined format or a custom * format. * The following formats are supported for string types: * - date-time * - date * - time * - ip-address * - ipv6 * - uri * - color * - host-name * - alpha * - alpha-numeric * - utc-millisec * @param instance * @param schema * @param [options] * @param [ctx] * @return {String|null} */ validators.format = function validateFormat (instance, schema, options, ctx) { if (instance===undefined) return; var result = new ValidatorResult(instance, schema, options, ctx); if (!result.disableFormat && !helpers.isFormat(instance, schema.format, this)) { result.addError({ name: 'format', argument: schema.format, message: "does not conform to the " + JSON.stringify(schema.format) + " format", }); } return result; }; /** * Validates whether the instance value is at least of a certain length, when the instance value is a string. * @param instance * @param schema * @return {String|null} */ validators.minLength = function validateMinLength (instance, schema, options, ctx) { if (!this.types.string(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); var hsp = instance.match(/[\uDC00-\uDFFF]/g); var length = instance.length - (hsp ? hsp.length : 0); if (!(length >= schema.minLength)) { result.addError({ name: 'minLength', argument: schema.minLength, message: "does not meet minimum length of " + schema.minLength, }); } return result; }; /** * Validates whether the instance value is at most of a certain length, when the instance value is a string. * @param instance * @param schema * @return {String|null} */ validators.maxLength = function validateMaxLength (instance, schema, options, ctx) { if (!this.types.string(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); // TODO if this was already computed in "minLength", use that value instead of re-computing var hsp = instance.match(/[\uDC00-\uDFFF]/g); var length = instance.length - (hsp ? hsp.length : 0); if (!(length <= schema.maxLength)) { result.addError({ name: 'maxLength', argument: schema.maxLength, message: "does not meet maximum length of " + schema.maxLength, }); } return result; }; /** * Validates whether instance contains at least a minimum number of items, when the instance is an Array. * @param instance * @param schema * @return {String|null} */ validators.minItems = function validateMinItems (instance, schema, options, ctx) { if (!this.types.array(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); if (!(instance.length >= schema.minItems)) { result.addError({ name: 'minItems', argument: schema.minItems, message: "does not meet minimum length of " + schema.minItems, }); } return result; }; /** * Validates whether instance contains no more than a maximum number of items, when the instance is an Array. * @param instance * @param schema * @return {String|null} */ validators.maxItems = function validateMaxItems (instance, schema, options, ctx) { if (!this.types.array(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); if (!(instance.length <= schema.maxItems)) { result.addError({ name: 'maxItems', argument: schema.maxItems, message: "does not meet maximum length of " + schema.maxItems, }); } return result; }; /** * Deep compares arrays for duplicates * @param v * @param i * @param a * @private * @return {boolean} */ function testArrays (v, i, a) { var j, len = a.length; for (j = i + 1, len; j < len; j++) { if (helpers.deepCompareStrict(v, a[j])) { return false; } } return true; } /** * Validates whether there are no duplicates, when the instance is an Array. * @param instance * @return {String|null} */ validators.uniqueItems = function validateUniqueItems (instance, schema, options, ctx) { if (schema.uniqueItems!==true) return; if (!this.types.array(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); if (!instance.every(testArrays)) { result.addError({ name: 'uniqueItems', message: "contains duplicate item", }); } return result; }; /** * Validate for the presence of dependency properties, if the instance is an object. * @param instance * @param schema * @param options * @param ctx * @return {null|ValidatorResult} */ validators.dependencies = function validateDependencies (instance, schema, options, ctx) { if (!this.types.object(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); for (var property in schema.dependencies) { if (instance[property] === undefined) { continue; } var dep = schema.dependencies[property]; var childContext = ctx.makeChild(dep, property); if (typeof dep == 'string') { dep = [dep]; } if (Array.isArray(dep)) { dep.forEach(function (prop) { if (instance[prop] === undefined) { result.addError({ // FIXME there's two different "dependencies" errors here with slightly different outputs // Can we make these the same? Or should we create different error types? name: 'dependencies', argument: childContext.propertyPath, message: "property " + prop + " not found, required by " + childContext.propertyPath, }); } }); } else { var res = this.validateSchema(instance, dep, options, childContext); if(result.instance !== res.instance) result.instance = res.instance; if (res && res.errors.length) { result.addError({ name: 'dependencies', argument: childContext.propertyPath, message: "does not meet dependency required by " + childContext.propertyPath, }); result.importErrors(res); } } } return result; }; /** * Validates whether the instance value is one of the enumerated values. * * @param instance * @param schema * @return {ValidatorResult|null} */ validators['enum'] = function validateEnum (instance, schema, options, ctx) { if (instance === undefined) { return null; } if (!Array.isArray(schema['enum'])) { throw new SchemaError("enum expects an array", schema); } var result = new ValidatorResult(instance, schema, options, ctx); if (!schema['enum'].some(helpers.deepCompareStrict.bind(null, instance))) { result.addError({ name: 'enum', argument: schema['enum'], message: "is not one of enum values: " + schema['enum'].map(String).join(','), }); } return result; }; /** * Validates whether the instance exactly matches a given value * * @param instance * @param schema * @return {ValidatorResult|null} */ validators['const'] = function validateEnum (instance, schema, options, ctx) { if (instance === undefined) { return null; } var result = new ValidatorResult(instance, schema, options, ctx); if (!helpers.deepCompareStrict(schema['const'], instance)) { result.addError({ name: 'const', argument: schema['const'], message: "does not exactly match expected constant: " + schema['const'], }); } return result; }; /** * Validates whether the instance if of a prohibited type. * @param instance * @param schema * @param options * @param ctx * @return {null|ValidatorResult} */ validators.not = validators.disallow = function validateNot (instance, schema, options, ctx) { var self = this; if(instance===undefined) return null; var result = new ValidatorResult(instance, schema, options, ctx); var notTypes = schema.not || schema.disallow; if(!notTypes) return null; if(!Array.isArray(notTypes)) notTypes=[notTypes]; notTypes.forEach(function (type) { if (self.testType(instance, schema, options, ctx, type)) { var id = type && (type.$id || type.id); var schemaId = id || type; result.addError({ name: 'not', argument: schemaId, message: "is of prohibited type " + schemaId, }); } }); return result; }; module.exports = attribute;