From 2cfc4e537495d91e11c1c45216be67b78fec4776 Mon Sep 17 00:00:00 2001 From: piebev Date: Thu, 3 Jul 2014 22:44:34 +0200 Subject: [PATCH] - fixed some validation - fixed build script - added debug build --- build.sh | 2 +- js-schema.debug.js | 1011 ++++++++++++++++++++++++++++++++++++++ js-schema.min.js | 2 +- lib/extensions/Number.js | 2 +- lib/patterns/class.js | 2 +- lib/patterns/object.js | 11 +- lib/patterns/or.js | 17 +- lib/patterns/regexp.js | 2 +- 8 files changed, 1037 insertions(+), 12 deletions(-) create mode 100644 js-schema.debug.js diff --git a/build.sh b/build.sh index 31304ac..774c310 100755 --- a/build.sh +++ b/build.sh @@ -3,6 +3,6 @@ DEBUG="js-schema.debug.js" MIN="js-schema.min.js" -browserify index.js | sed 's|require("/index.js")|window.schema = require("/index.js")|g' >$DEBUG +browserify index.js -o $DEBUG -s schema uglifyjs $DEBUG >$MIN diff --git a/js-schema.debug.js b/js-schema.debug.js new file mode 100644 index 0000000..60129f5 --- /dev/null +++ b/js-schema.debug.js @@ -0,0 +1,1011 @@ +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.schema=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0 && instance.length < this.min) throw new Error('array does not contain enought entries (' + instance.length + ' instead of ' + this.min) + if (this.max < Infinity && instance.length > this.max) throw new Error('array does contain too many entries (' + instance.length + ' instead of ' + this.min) + } + + // Checking conformance to the given item schema + for (var i = 0; i < instance.length; i++) { + try { + this.itemSchema.validate(instance[i]) + } catch(e) { + throw new Error('array contains invalid entry at ' + i + ' --> ' + e) + } + } + + return true + }, + + toJSON : Schema.session(function() { + var json = Schema.prototype.toJSON.call(this, true) + + if (json['$ref'] != null) return json + + json.type = 'array' + + if (this.min > 0) json.minItems = this.min + if (this.max < Infinity) json.maxItems = this.max + if (this.itemSchema !== anything) json.items = this.itemSchema.toJSON() + + return json + }) +}) + +Schema.fromJSON.def(function(sch) { + if (!sch || sch.type !== 'array') return + + // Tuple typing is not yet supported + if (sch.items instanceof Array) return + + return new ArraySchema(Schema.fromJSON(sch.items), sch.maxItems, sch.minItems) +}) + +Array.of = function() { + // Possible signatures : (schema) + // (length, schema) + // (minLength, maxLength, schema) + var args = Array.prototype.slice.call(arguments).reverse() + if (args.length === 2) args[2] = args[1] + return new ArraySchema(Schema.fromJS(args[0]), args[1], args[2]).wrap() +} + +Array.like = function(other) { + return new EqualitySchema(other).wrap() +} + +Array.schema = new ArraySchema().wrap() + +},{"../BaseSchema":2,"../patterns/anything":10,"../patterns/equality":12}],4:[function(_dereq_,module,exports){ +var Schema = _dereq_('../BaseSchema') + +var BooleanSchema = module.exports = Schema.extensions.BooleanSchema = new Schema.extend({ + validate : function(instance) { + if (!Object(instance) instanceof Boolean) + throw new Error('not a Boolean') + + return true + }, + + toJSON : function() { + return { type : 'boolean' } + } +}) + +var booleanSchema = module.exports = new BooleanSchema().wrap() + +Schema.fromJSON.def(function(sch) { + if (!sch || sch.type !== 'boolean') return + + return booleanSchema +}) + +Boolean.schema = booleanSchema + +},{"../BaseSchema":2}],5:[function(_dereq_,module,exports){ +var ReferenceSchema = _dereq_('../patterns/reference') + +Function.reference = function(f) { + return new ReferenceSchema(f).wrap() +} + +},{"../patterns/reference":16}],6:[function(_dereq_,module,exports){ +var Schema = _dereq_('../BaseSchema') + +var NumberSchema = module.exports = Schema.extensions.NumberSchema = Schema.extend({ + initialize : function(minimum, exclusiveMinimum, maximum, exclusiveMaximum, divisibleBy) { + this.minimum = minimum != null ? minimum : -Infinity + this.exclusiveMinimum = exclusiveMinimum + this.maximum = minimum != null ? maximum : Infinity + this.exclusiveMaximum = exclusiveMaximum + this.divisibleBy = divisibleBy || 0 + }, + + min : function(minimum) { + return new NumberSchema( minimum, false + , this.maximum, this.exclusiveMaximum + , this.divisibleBy + ).wrap() + }, + + above : function(minimum) { + return new NumberSchema( minimum, true + , this.maximum, this.exclusiveMaximum + , this.divisibleBy + ).wrap() + }, + + max : function(maximum) { + return new NumberSchema( this.minimum, this.exclusiveMinimum + , maximum, false + , this.divisibleBy + ).wrap() + }, + + below : function(maximum) { + return new NumberSchema( this.minimum, this.exclusiveMinimum + , maximum, true + , this.divisibleBy + ).wrap() + }, + + step : function(divisibleBy) { + return new NumberSchema( this.minimum, this.exclusiveMinimum + , this.maximum, this.exclusiveMaximum + , divisibleBy + ).wrap() + }, + + publicFunctions : [ 'min', 'above', 'max', 'below', 'step' ], + + validate : function(instance) { + if (! Object(instance) instanceof Number) + throw new Error('not a Number') + if (!(this.exclusiveMinimum ? instance > this.minimum + : instance >= this.minimum)) + throw new Error(instance + ' does not exceed minimum ' + this.minimum) + if (! (this.exclusiveMaximum ? instance < this.maximum + : instance <= this.maximum)) + throw new Error(instance + ' exceeds maximum ' + this.maximum) + + if (!(this.divisibleBy === 0 || instance % this.divisibleBy === 0)) + throw new Error(' is not divisible by ' + this.divisibleBy ) + + return true + }, + + toJSON : function() { + var json = Schema.prototype.toJSON.call(this) + + json.type = ( this.divisibleBy !== 0 && this.divisibleBy % 1 === 0 ) ? 'integer' : 'number' + + if (this.divisibleBy !== 0 && this.divisibleBy !== 1) json.divisibleBy = this.divisibleBy + + if (this.minimum !== -Infinity) { + json.minimum = this.minimum + if (this.exclusiveMinimum === true) json.exclusiveMinimum = true + } + + if (this.maximum !== Infinity) { + json.maximum = this.maximum + if (this.exclusiveMaximum === true) json.exclusiveMaximum = true + } + + return json + } +}) + +Schema.fromJSON.def(function(sch) { + if (!sch || (sch.type !== 'number' && sch.type !== 'integer')) return + + return new NumberSchema( sch.minimum, sch.exclusiveMinimum + , sch.maximum, sch.exclusiveMaximum + , sch.divisibleBy || (sch.type === 'integer' ? 1 : 0) + ) +}) + +Number.schema = new NumberSchema().wrap() +Number.min = Number.schema.min +Number.above = Number.schema.above +Number.max = Number.schema.max +Number.below = Number.schema.below +Number.step = Number.schema.step + +Number.Integer = Number.step(1) + +},{"../BaseSchema":2}],7:[function(_dereq_,module,exports){ +var ReferenceSchema = _dereq_('../patterns/reference') + , EqualitySchema = _dereq_('../patterns/equality') + , ObjectSchema = _dereq_('../patterns/object') + +Object.like = function(other) { + return new EqualitySchema(other).wrap() +} + +Object.reference = function(o) { + return new ReferenceSchema(o).wrap() +} + +Object.schema = new ObjectSchema().wrap() + +},{"../patterns/equality":12,"../patterns/object":14,"../patterns/reference":16}],8:[function(_dereq_,module,exports){ +var Schema = _dereq_('../BaseSchema') + , schema = _dereq_('../schema') + +var SchemaReference = module.exports = Schema.extensions.SchemaReference = Schema.extend({ + validate : function() { + throw new Error('Trying to validate unresolved schema reference.') + }, + + resolve : function(schemaDescriptor) { + var schemaObject = Schema.fromJS(schemaDescriptor) + + for (var key in schemaObject) { + if (schemaObject[key] instanceof Function) { + this[key] = schemaObject[key].bind(schemaObject) + } else { + this[key] = schemaObject[key] + } + } + + delete this.resolve + }, + + publicFunctions : [ 'resolve' ] +}) + +schema.reference = function(schemaDescriptor) { + return new SchemaReference() +} + +function renewing(ref) { + ref.resolve = function() { + Schema.self = schema.self = renewing(new SchemaReference()) + return SchemaReference.prototype.resolve.apply(this, arguments) + } + return ref +} + +Schema.self = schema.self = renewing(new SchemaReference()) + +Schema.fromJSON.def(function(sch) { + if (sch.id == null && sch['$ref'] == null) return + + var id, session = Schema.session + + if (!session.deserialized) session.deserialized = { references: {}, subscribers: {} } + + if (sch.id != null) { + // This schema can be referenced in the future with the given ID + id = sch.id + + // Deserializing: + delete sch.id + var schemaObject = Schema.fromJSON(sch) + sch.id = id + + // Storing the schema object and notifying subscribers + session.deserialized.references[id] = schemaObject + ;(session.deserialized.subscribers[id] || []).forEach(function(callback) { + callback(schemaObject) + }) + + return schemaObject + + } else { + // Referencing a schema given somewhere else with the given ID + id = sch['$ref'] + + // If the referenced schema is already known, we are ready + if (session.deserialized.references[id]) return session.deserialized.references[id] + + // If not, returning a reference, and when the schema gets known, resolving the reference + if (!session.deserialized.subscribers[id]) session.deserialized.subscribers[id] = [] + var reference = new SchemaReference() + session.deserialized.subscribers[id].push(reference.resolve.bind(reference)) + + return reference + } +}) + +},{"../BaseSchema":2,"../schema":19}],9:[function(_dereq_,module,exports){ +var RegexpSchema = _dereq_('../patterns/regexp') + +String.of = function() { + // Possible signatures : (charset) + // (length, charset) + // (minLength, maxLength, charset) + var args = Array.prototype.slice.call(arguments).reverse() + , charset = args[0] ? ('[' + args[0] + ']') : '[a-zA-Z0-9]' + , max = args[1] + , min = (args.length > 2) ? args[2] : args[1] + , regexp = '^' + charset + '{' + (min || 0) + ',' + (max || '') + '}$' + + return new RegexpSchema(RegExp(regexp)).wrap() +} + +String.schema = new RegexpSchema().wrap() + +},{"../patterns/regexp":17}],10:[function(_dereq_,module,exports){ +var Schema = _dereq_('../BaseSchema') + +var AnythingSchema = module.exports = Schema.patterns.AnythingSchema = Schema.extend({ + validate : function(instance) { + if (instance == null) + throw new Error('may not be null') + return true + }, + + toJSON : function() { + return { type : 'any' } + } +}) + +var anything = AnythingSchema.instance = new AnythingSchema() + +Schema.fromJS.def(function(sch) { + if (sch === undefined) return anything +}) + +Schema.fromJSON.def(function(sch) { + if (sch.type === 'any') return anything +}) + +},{"../BaseSchema":2}],11:[function(_dereq_,module,exports){ +var Schema = _dereq_('../BaseSchema') + +var ClassSchema = module.exports = Schema.patterns.ClassSchema = Schema.extend({ + initialize : function(constructor) { + this.constructor = constructor + }, + + validate : function(instance) { + if (! (instance instanceof this.constructor)) + throw new Error('not a class constructor') + return true; + } +}) + + +Schema.fromJS.def(function(constructor) { + if (!(constructor instanceof Function)) return + + if (constructor.schema instanceof Function) { + return constructor.schema.unwrap() + } else { + return new ClassSchema(constructor) + } +}) + +},{"../BaseSchema":2}],12:[function(_dereq_,module,exports){ +var Schema = _dereq_('../BaseSchema') + +// Object deep equality +var equal = function(a, b) { + // if a or b is primitive, simple comparison + if (Object(a) !== a || Object(b) !== b) return a === b + + // both a and b must be Array, or none of them + if ((a instanceof Array) !== (b instanceof Array)) return false + + // they must have the same number of properties + if (Object.keys(a).length !== Object.keys(b).length) return false + + // and every property should be equal + for (var key in a) { + if (!equal(a[key], b[key])) return false + } + + // if every check succeeded, they are deep equal + return true +} + +var EqualitySchema = module.exports = Schema.patterns.EqualitySchema = Schema.extend({ + initialize : function(object) { + this.object = object + }, + + validate : function(instance) { + if (! equal(instance, this.object)) + throw new Error(instance + ' not equal to ' + this.object.toString() ) + return true; + }, + + toJSON : function() { + var json = Schema.prototype.toJSON.call(this) + + json['enum'] = [this.object] + + return json + } +}) + + +Schema.fromJS.def(function(sch) { + if (sch instanceof Array && sch.length === 1) return new EqualitySchema(sch[0]) +}) + +},{"../BaseSchema":2}],13:[function(_dereq_,module,exports){ +var Schema = _dereq_('../BaseSchema') + +var NothingSchema = module.exports = Schema.patterns.NothingSchema = Schema.extend({ + validate : function(instance) { + if (instance != null) + throw new Error('must be null') + return true + }, + + toJSON : function() { + return { type : 'null' } + } +}) + +var nothing = NothingSchema.instance = new NothingSchema() + +Schema.fromJS.def(function(sch) { + if (sch === null) return nothing +}) + +Schema.fromJSON.def(function(sch) { + if (sch.type === 'null') return nothing +}) + +},{"../BaseSchema":2}],14:[function(_dereq_,module,exports){ +var Schema = _dereq_('../BaseSchema') + , anything = _dereq_('./anything').instance + , nothing = _dereq_('./nothing').instance + +var ObjectSchema = module.exports = Schema.patterns.ObjectSchema = Schema.extend({ + initialize : function(properties, other) { + var self = this + + this.other = other || anything + this.properties = properties || [] + + // Sorting properties into two groups + this.stringProps = {}, this.regexpProps = [] + this.properties.forEach(function(property) { + if (typeof property.key === 'string') { + self.stringProps[property.key] = property + } else { + self.regexpProps.push(property) + } + }) + }, + + validate : function(instance) { + var self = this + + if (instance == null) throw new Error('No data') + + // Simple string properties + Object.keys(this.stringProps).every(function(key) { + if (self.stringProps[key].min === 0 && !(key in instance)) { + return true; + } + + try { + self.stringProps[key].value.validate(instance[key]) + } catch(e) { + throw new Error('Parse error for key "' + key + '" --> ' + e); + } + }) + + // If there are no RegExp and other validator, that's all + if (!this.regexpProps.length && this.other === anything) return true + + // Regexp and other properties + var checked + for (var key in instance) { + + // Checking the key against every key regexps + checked = false + Object.keys(this.regexpProps).every(function(key) { + if (!self.regexpProps[key].key.test(key)) return true + checked = true + try { + self.regexpProps[key].value.validate(instance[key]) + } catch(e) { + throw new Error('lala'); + // throw new Error('Parse error for regexp in key "' + key + '" --> ' + e); + } + }) + + // If the key is not matched by regexps and by simple string checks + // then check it against this.other + if (!checked && !(key in this.stringProps)) { + try { + this.other.validate(instance[key]) + } catch(e) { + throw new Error('Parse error for key "' + key + '" --> ' + e); + } + } + } + + // If all checks passed, the instance conforms to the schema + return true + }, + + toJSON : Schema.session(function() { + var i, property, regexp, json = Schema.prototype.toJSON.call(this, true) + + if (json['$ref'] != null) return json + + json.type = 'object' + + for (i in this.stringProps) { + property = this.stringProps[i] + json.properties = json.properties || {} + json.properties[property.key] = property.value.toJSON() + if (property.min === 1) json.properties[property.key].required = true + if (property.title) json.properties[property.key].title = property.title + } + + for (i = 0; i < this.regexpProps.length; i++) { + property = this.regexpProps[i] + json.patternProperties = json.patternProperties || {} + regexp = property.key.toString() + regexp = regexp.substr(2, regexp.length - 4) + json.patternProperties[regexp] = property.value.toJSON() + if (property.title) json.patternProperties[regexp].title = property.title + } + + if (this.other !== anything) { + json.additionalProperties = (this.other === nothing) ? false : this.other.toJSON() + } + + return json + }) +}) + +// Testing if a given string is a real regexp or just a single string escaped +// If it is just a string escaped, return the string. Otherwise return the regexp +var regexpString = (function() { + // Special characters that should be escaped when describing a regular string in regexp + var shouldBeEscaped = '[](){}^$?*+.'.split('').map(function(element) { + return RegExp('(\\\\)*\\' + element, 'g') + }) + // Special characters that shouldn't be escaped when describing a regular string in regexp + var shouldntBeEscaped = 'bBwWdDsS'.split('').map(function(element) { + return RegExp('(\\\\)*' + element, 'g') + }) + + return function(string) { + var i, j, match + + for (i = 0; i < shouldBeEscaped.length; i++) { + match = string.match(shouldBeEscaped[i]) + if (!match) continue + for (j = 0; j < match.length; j++) { + // If it is not escaped, it must be a regexp (e.g. [, \\[, \\\\[, etc.) + if (match[j].length % 2 === 1) return RegExp('^' + string + '$') + } + } + for (i = 0; i < shouldntBeEscaped.length; i++) { + match = string.match(shouldntBeEscaped[i]) + if (!match) continue + for (j = 0; j < match.length; j++) { + // If it is escaped, it must be a regexp (e.g. \b, \\\b, \\\\\b, etc.) + if (match[j].length % 2 === 0) return RegExp('^' + string + '$') + } + } + + // It is not a real regexp. Removing the escaping. + for (i = 0; i < shouldBeEscaped.length; i++) { + string = string.replace(shouldBeEscaped[i], function(match) { + return match.substr(1) + }) + } + + return string + } +})() + +Schema.fromJS.def(function(object) { + if (!(object instanceof Object)) return + + var other, property, properties = [] + for (var key in object) { + property = { value : Schema.fromJS(object[key]) } + + // '*' as property name means 'every other property should match this schema' + if (key === '*') { + other = property.value + continue + } + + // Handling special chars at the beginning of the property name + property.min = (key[0] === '*' || key[0] === '?') ? 0 : 1 + property.max = (key[0] === '*' || key[0] === '+') ? Infinity : 1 + key = key.replace(/^[*?+]/, '') + + // Handling property title that looks like: { 'a : an important property' : Number } + key = key.replace(/\s*:[^:]+$/, function(match) { + property.title = match.replace(/^\s*:\s*/, '') + return '' + }) + + // Testing if it is regexp-like or not. If it is, then converting to a regexp object + property.key = regexpString(key) + + properties.push(property) + } + + return new ObjectSchema(properties, other) +}) + +Schema.fromJSON.def(function(json) { + if (!json || json.type !== 'object') return + + var key, properties = [] + for (key in json.properties) { + properties.push({ min : json.properties[key].required ? 1 : 0 + , max : 1 + , key : key + , value : Schema.fromJSON(json.properties[key]) + , title : json.properties[key].title + }) + } + for (key in json.patternProperties) { + properties.push({ min : 0 + , max : Infinity + , key : RegExp('^' + key + '$') + , value : Schema.fromJSON(json.patternProperties[key]) + , title : json.patternProperties[key].title + }) + } + + var other + if (json.additionalProperties !== undefined) { + other = json.additionalProperties === false ? nothing : Schema.fromJSON(json.additionalProperties) + } + + return new ObjectSchema(properties, other) +}) + +},{"../BaseSchema":2,"./anything":10,"./nothing":13}],15:[function(_dereq_,module,exports){ +var Schema = _dereq_('../BaseSchema') + , EqualitySchema = _dereq_('../patterns/equality') + +var OrSchema = module.exports = Schema.patterns.OrSchema = Schema.extend({ + initialize : function(schemas) { + this.schemas = schemas + }, + + validate : function(instance) { + var error = ""; + if (!this.schemas.some(function(sch) { + try { + sch.validate(instance) + } catch (e) { + error += e + return false + } + return true; + })) { + throw new Error('All following conditions are false: ' + error); + } + + return true;; + }, + + toJSON : Schema.session(function() { + var json = Schema.prototype.toJSON.call(this, true) + , subjsons = this.schemas.map(function(sch) { return sch.toJSON() }) + , onlyEquality = subjsons.every(function(json) { + return json['enum'] instanceof Array && json['enum'].length === 1 + }) + + if (json['$ref'] != null) return json + + if (onlyEquality) { + json['enum'] = subjsons.map(function(json) { + return json['enum'][0] + }) + + } else { + json['type'] = subjsons.map(function(json) { + var simpleType = typeof json.type === 'string' && Object.keys(json).length === 1 + return simpleType ? json.type : json + }) + } + + return json + }) +}) + + +Schema.fromJS.def(function(schemas) { + if (schemas instanceof Array) return new OrSchema(schemas.map(function(sch) { + return sch === undefined ? Schema.self : Schema.fromJS(sch) + })) +}) + +Schema.fromJSON.def(function(sch) { + if (!sch) return + + if (sch['enum'] instanceof Array) { + return new OrSchema(sch['enum'].map(function(object) { + return new EqualitySchema(object) + })) + } + + if (sch['type'] instanceof Array) { + return new OrSchema(sch['type'].map(function(type) { + return Schema.fromJSON(typeof type === 'string' ? { type : type } : type) + })) + } +}) + +},{"../BaseSchema":2,"../patterns/equality":12}],16:[function(_dereq_,module,exports){ +var Schema = _dereq_('../BaseSchema') + +var ReferenceSchema = module.exports = Schema.patterns.ReferenceSchema = Schema.extend({ + initialize : function(value) { + this.value = value + }, + + validate : function(instance) { + if (instance !== this.value) + throw new Error(instance + ' is not the same as ' + this.value) + return true; + }, + + toJSON : function() { + var json = Schema.prototype.toJSON.call(this) + + json['enum'] = [this.value] + + return json + } +}) + + +Schema.fromJS.def(function(value) { + return new ReferenceSchema(value) +}) + +},{"../BaseSchema":2}],17:[function(_dereq_,module,exports){ +var Schema = _dereq_('../BaseSchema') + +var RegexpSchema = module.exports = Schema.patterns.RegexpSchema = Schema.extend({ + initialize : function(regexp) { + this.regexp = regexp + }, + + validate : function(instance) { + if (! Object(instance) instanceof String) { + throw new Error('not a String') + } + if (this.regexp && !this.regexp.test(instance)) + throw new Error('"' + instance + '" does not match pattern "' + this.regexp +'"') + + return true; + }, + + toJSON : function() { + var json = Schema.prototype.toJSON.call(this) + + json.type = 'string' + + if (this.regexp) { + json.pattern = this.regexp.toString() + json.pattern = json.pattern.substr(1, json.pattern.length - 2) + } + + return json + } +}) + +Schema.fromJSON.def(function(sch) { + if (!sch || sch.type !== 'string') return + + if ('pattern' in sch) { + return new RegexpSchema(RegExp('^' + sch.pattern + '$')) + } else if ('minLength' in sch || 'maxLength' in sch) { + return new RegexpSchema(RegExp('^.{' + [ sch.minLength || 0, sch.maxLength ].join(',') + '}$')) + } else { + return new RegexpSchema() + } +}) + +Schema.fromJS.def(function(regexp) { + if (regexp instanceof RegExp) return new RegexpSchema(regexp) +}) + +},{"../BaseSchema":2}],18:[function(_dereq_,module,exports){ +var Schema = _dereq_('../BaseSchema') + +Schema.fromJS.def(function(sch) { + if (sch instanceof Schema) return sch +}) + +},{"../BaseSchema":2}],19:[function(_dereq_,module,exports){ +var Schema = _dereq_('./BaseSchema') + +var schema = module.exports = function(schemaDescription) { + var doc, schemaObject + + if (arguments.length === 2) { + doc = schemaDescription + schemaDescription = arguments[1] + } + + if (this instanceof schema) { + // When called with new, create a schema object and then return the schema function + var constructor = Schema.extend(schemaDescription) + schemaObject = new constructor() + if (doc) schemaObject.doc = doc + return schemaObject.wrap() + + } else { + // When called as simple function, forward everything to fromJS + // and then resolve schema.self to the resulting schema object + schemaObject = Schema.fromJS(schemaDescription) + schema.self.resolve(schemaObject) + if (doc) schemaObject.doc = doc + return schemaObject.wrap() + } +} + +schema.Schema = Schema + +schema.toJSON = function(sch) { + return Schema.fromJS(sch).toJSON() +} + +schema.fromJS = function(sch) { + return Schema.fromJS(sch).wrap() +} + +schema.fromJSON = function(sch) { + return Schema.fromJSON(sch).wrap() +} + + +},{"./BaseSchema":2}]},{},[1]) +(1) +}); \ No newline at end of file diff --git a/js-schema.min.js b/js-schema.min.js index b4d1697..2f36c9d 100644 --- a/js-schema.min.js +++ b/js-schema.min.js @@ -1 +1 @@ -(function(){var require=function(file,cwd){var resolved=require.resolve(file,cwd||"/");var mod=require.modules[resolved];if(!mod)throw new Error("Failed to resolve module "+file+", tried "+resolved);var cached=require.cache[resolved];var res=cached?cached.exports:mod();return res};require.paths=[];require.modules={};require.cache={};require.extensions=[".js",".coffee",".json"];require._core={assert:true,events:true,fs:true,path:true,vm:true};require.resolve=function(){return function(x,cwd){if(!cwd)cwd="/";if(require._core[x])return x;var path=require.modules.path();cwd=path.resolve("/",cwd);var y=cwd||"/";if(x.match(/^(?:\.\.?\/|\/)/)){var m=loadAsFileSync(path.resolve(y,x))||loadAsDirectorySync(path.resolve(y,x));if(m)return m}var n=loadNodeModulesSync(x,y);if(n)return n;throw new Error("Cannot find module '"+x+"'");function loadAsFileSync(x){x=path.normalize(x);if(require.modules[x]){return x}for(var i=0;i=0;i--){if(parts[i]==="node_modules")continue;var dir=parts.slice(0,i+1).join("/")+"/node_modules";dirs.push(dir)}return dirs}}}();require.alias=function(from,to){var path=require.modules.path();var res=null;try{res=require.resolve(from+"/package.json","/")}catch(err){res=require.resolve(from,"/")}var basedir=path.dirname(res);var keys=(Object.keys||function(obj){var res=[];for(var key in obj)res.push(key);return res})(require.modules);for(var i=0;i=0;i--){var last=parts[i];if(last=="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up--;up){parts.unshift("..")}}return parts}var splitPathRe=/^(.+\/(?!$)|\/)?((?:.+?)?(\.[^.]*)?)$/;exports.resolve=function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:process.cwd();if(typeof path!=="string"||!path){continue}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=normalizeArray(filter(resolvedPath.split("/"),function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."};exports.normalize=function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.slice(-1)==="/";path=normalizeArray(filter(path.split("/"),function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path};exports.join=function(){var paths=Array.prototype.slice.call(arguments,0);return exports.normalize(filter(paths,function(p,index){return p&&typeof p==="string"}).join("/"))};exports.dirname=function(path){var dir=splitPathRe.exec(path)[1]||"";var isWindows=false;if(!dir){return"."}else if(dir.length===1||isWindows&&dir.length<=3&&dir.charAt(1)===":"){return dir}else{return dir.substring(0,dir.length-1)}};exports.basename=function(path,ext){var f=splitPathRe.exec(path)[2]||"";if(ext&&f.substr(-1*ext.length)===ext){f=f.substr(0,f.length-ext.length)}return f};exports.extname=function(path){return splitPathRe.exec(path)[3]||""};exports.relative=function(from,to){from=exports.resolve(from).substr(1);to=exports.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i0){var fn=queue.shift();fn()}}},true);return function nextTick(fn){queue.push(fn);window.postMessage("browserify-tick","*")}}return function nextTick(fn){setTimeout(fn,0)}}();process.title="browser";process.browser=true;process.env={};process.argv=[];process.binding=function(name){if(name==="evals")return require("vm");else throw new Error("No such module. (Possibly not yet loaded)")};(function(){var cwd="/";var path;process.cwd=function(){return cwd};process.chdir=function(dir){if(!path)path=require("path");cwd=path.resolve(dir,cwd)}})()});require.define("/lib/schema.js",function(require,module,exports,__dirname,__filename,process,global){var Schema=require("./BaseSchema");var schema=module.exports=function(schemaDescription){var doc,schemaObject;if(arguments.length===2){doc=schemaDescription;schemaDescription=arguments[1]}if(this instanceof schema){var constructor=Schema.extend(schemaDescription);schemaObject=new constructor;if(doc)schemaObject.doc=doc;return schemaObject.wrap()}else{schemaObject=Schema.fromJS(schemaDescription);schema.self.resolve(schemaObject);if(doc)schemaObject.doc=doc;return schemaObject.wrap()}};schema.Schema=Schema;schema.toJSON=function(sch){return Schema.fromJS(sch).toJSON()};schema.fromJS=function(sch){return Schema.fromJS(sch).wrap()};schema.fromJSON=function(sch){return Schema.fromJSON(sch).wrap()}});require.define("/lib/BaseSchema.js",function(require,module,exports,__dirname,__filename,process,global){var Schema=module.exports=function(){};Schema.prototype={wrap:function(){if(this.wrapped)return this.validate;this.wrapped=true;var publicFunctions=["toJSON","unwrap"];publicFunctions=publicFunctions.concat(this.publicFunctions||[]);for(var i=0;ithis.minimum:instance>=this.minimum)&&(this.exclusiveMaximum?instance2?args[2]:args[1],regexp="^"+charset+"{"+(min||0)+","+(max||"")+"}$";return new RegexpSchema(RegExp(regexp)).wrap()};String.schema=(new RegexpSchema).wrap()});require.define("/lib/extensions/Object.js",function(require,module,exports,__dirname,__filename,process,global){var ReferenceSchema=require("../patterns/reference"),EqualitySchema=require("../patterns/equality"),ObjectSchema=require("../patterns/object");Object.like=function(other){return new EqualitySchema(other).wrap()};Object.reference=function(o){return new ReferenceSchema(o).wrap()};Object.schema=(new ObjectSchema).wrap()});require.define("/lib/extensions/Array.js",function(require,module,exports,__dirname,__filename,process,global){var Schema=require("../BaseSchema"),EqualitySchema=require("../patterns/equality"),anything=require("../patterns/anything").instance;var ArraySchema=module.exports=Schema.extensions.ArraySchema=Schema.extend({initialize:function(itemSchema,max,min){this.itemSchema=itemSchema||anything;this.min=min||0;this.max=max||Infinity},validate:function(instance){if(!(instance instanceof Array))return false;if(this.min===this.max){if(instance.length!==this.min)return false}else{if(this.min>0&&instance.lengththis.max)return false}for(var i=0;i0)json.minItems=this.min;if(this.max0&&instance.lengththis.max)throw new Error("array does contain too many entries ("+instance.length+" instead of "+this.min)}for(var i=0;i "+e)}}return true},toJSON:Schema.session(function(){var json=Schema.prototype.toJSON.call(this,true);if(json["$ref"]!=null)return json;json.type="array";if(this.min>0)json.minItems=this.min;if(this.maxthis.minimum:instance>=this.minimum))throw new Error(instance+" does not exceed minimum "+this.minimum);if(!(this.exclusiveMaximum?instance2?args[2]:args[1],regexp="^"+charset+"{"+(min||0)+","+(max||"")+"}$";return new RegexpSchema(RegExp(regexp)).wrap()};String.schema=(new RegexpSchema).wrap()},{"../patterns/regexp":17}],10:[function(_dereq_,module,exports){var Schema=_dereq_("../BaseSchema");var AnythingSchema=module.exports=Schema.patterns.AnythingSchema=Schema.extend({validate:function(instance){if(instance==null)throw new Error("may not be null");return true},toJSON:function(){return{type:"any"}}});var anything=AnythingSchema.instance=new AnythingSchema;Schema.fromJS.def(function(sch){if(sch===undefined)return anything});Schema.fromJSON.def(function(sch){if(sch.type==="any")return anything})},{"../BaseSchema":2}],11:[function(_dereq_,module,exports){var Schema=_dereq_("../BaseSchema");var ClassSchema=module.exports=Schema.patterns.ClassSchema=Schema.extend({initialize:function(constructor){this.constructor=constructor},validate:function(instance){if(!(instance instanceof this.constructor))throw new Error("not a class constructor");return true}});Schema.fromJS.def(function(constructor){if(!(constructor instanceof Function))return;if(constructor.schema instanceof Function){return constructor.schema.unwrap()}else{return new ClassSchema(constructor)}})},{"../BaseSchema":2}],12:[function(_dereq_,module,exports){var Schema=_dereq_("../BaseSchema");var equal=function(a,b){if(Object(a)!==a||Object(b)!==b)return a===b;if(a instanceof Array!==b instanceof Array)return false;if(Object.keys(a).length!==Object.keys(b).length)return false;for(var key in a){if(!equal(a[key],b[key]))return false}return true};var EqualitySchema=module.exports=Schema.patterns.EqualitySchema=Schema.extend({initialize:function(object){this.object=object},validate:function(instance){if(!equal(instance,this.object))throw new Error(instance+" not equal to "+this.object.toString());return true},toJSON:function(){var json=Schema.prototype.toJSON.call(this);json["enum"]=[this.object];return json}});Schema.fromJS.def(function(sch){if(sch instanceof Array&&sch.length===1)return new EqualitySchema(sch[0])})},{"../BaseSchema":2}],13:[function(_dereq_,module,exports){var Schema=_dereq_("../BaseSchema");var NothingSchema=module.exports=Schema.patterns.NothingSchema=Schema.extend({validate:function(instance){if(instance!=null)throw new Error("must be null");return true},toJSON:function(){return{type:"null"}}});var nothing=NothingSchema.instance=new NothingSchema;Schema.fromJS.def(function(sch){if(sch===null)return nothing});Schema.fromJSON.def(function(sch){if(sch.type==="null")return nothing})},{"../BaseSchema":2}],14:[function(_dereq_,module,exports){var Schema=_dereq_("../BaseSchema"),anything=_dereq_("./anything").instance,nothing=_dereq_("./nothing").instance;var ObjectSchema=module.exports=Schema.patterns.ObjectSchema=Schema.extend({initialize:function(properties,other){var self=this;this.other=other||anything;this.properties=properties||[];this.stringProps={},this.regexpProps=[];this.properties.forEach(function(property){if(typeof property.key==="string"){self.stringProps[property.key]=property}else{self.regexpProps.push(property)}})},validate:function(instance){var self=this;if(instance==null)throw new Error("No data");Object.keys(this.stringProps).every(function(key){if(self.stringProps[key].min===0&&!(key in instance)){return true}try{self.stringProps[key].value.validate(instance[key])}catch(e){throw new Error('Parse error for key "'+key+'" --> '+e)}});if(!this.regexpProps.length&&this.other===anything)return true;var checked;for(var key in instance){checked=false;Object.keys(this.regexpProps).every(function(key){if(!self.regexpProps[key].key.test(key))return true;checked=true;try{self.regexpProps[key].value.validate(instance[key])}catch(e){throw new Error("lala")}});if(!checked&&!(key in this.stringProps)){try{this.other.validate(instance[key])}catch(e){throw new Error('Parse error for key "'+key+'" --> '+e)}}}return true},toJSON:Schema.session(function(){var i,property,regexp,json=Schema.prototype.toJSON.call(this,true);if(json["$ref"]!=null)return json;json.type="object";for(i in this.stringProps){property=this.stringProps[i];json.properties=json.properties||{};json.properties[property.key]=property.value.toJSON();if(property.min===1)json.properties[property.key].required=true;if(property.title)json.properties[property.key].title=property.title}for(i=0;i ' + e); + throw new Error('lala'); + // throw new Error('Parse error for regexp in key "' + key + '" --> ' + e); } }) diff --git a/lib/patterns/or.js b/lib/patterns/or.js index eb0c080..5fdf76f 100644 --- a/lib/patterns/or.js +++ b/lib/patterns/or.js @@ -7,9 +7,20 @@ var OrSchema = module.exports = Schema.patterns.OrSchema = Schema.extend({ }, validate : function(instance) { - return this.schemas.some(function(sch) { - return sch.validate(instance) //TODO: add a name of the schema to the thrown error? - }) + var error = ""; + if (!this.schemas.some(function(sch) { + try { + sch.validate(instance) + } catch (e) { + error += e + return false + } + return true; + })) { + throw new Error('All following conditions are false: ' + error); + } + + return true;; }, toJSON : Schema.session(function() { diff --git a/lib/patterns/regexp.js b/lib/patterns/regexp.js index 606f497..840f887 100644 --- a/lib/patterns/regexp.js +++ b/lib/patterns/regexp.js @@ -10,7 +10,7 @@ var RegexpSchema = module.exports = Schema.patterns.RegexpSchema = Schema.extend throw new Error('not a String') } if (this.regexp && !this.regexp.test(instance)) - throw new Error('"' + instance + '" does not match pattern "' + this.regexp +'"') + throw new Error('"' + instance + '" does not match pattern "' + this.regexp +'"') return true; },