Skip to content
This repository was archived by the owner on Nov 2, 2023. It is now read-only.

Commit 830520d

Browse files
committed
added commonjs impl
1 parent a14190b commit 830520d

File tree

3 files changed

+302
-0
lines changed

3 files changed

+302
-0
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
JSON Schema is a repository for the JSON Schema specification, reference schemas and a CommonJS implementation of JSON Schema.
2+
3+
Code is licensed under the AFL or BSD license as part of the Persevere
4+
project which is administered under the Dojo foundation,
5+
and all contributions require a Dojo CLA.

lib/json-schema.js

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
/**
2+
* JSONSchema Validator - Validates JavaScript objects using JSON Schemas
3+
* (http://www.json.com/json-schema-proposal/)
4+
*
5+
* Copyright (c) 2007 Kris Zyp SitePen (www.sitepen.com)
6+
* Licensed under the MIT (MIT-LICENSE.txt) license.
7+
To use the validator call JSONSchema.validate with an instance object and an optional schema object.
8+
If a schema is provided, it will be used to validate. If the instance object refers to a schema (self-validating),
9+
that schema will be used to validate and the schema parameter is not necessary (if both exist,
10+
both validations will occur).
11+
The validate method will return an array of validation errors. If there are no errors, then an
12+
empty list will be returned. A validation error will have two properties:
13+
"property" which indicates which property had the error
14+
"message" which indicates what the error was
15+
*/
16+
17+
// setup primitive classes to be JSON Schema types
18+
String.type = "string";
19+
Boolean.type = "boolean";
20+
Number.type = "number";
21+
exports.Integer = {type:"integer"};
22+
Object.type = "object";
23+
Array.type = "array";
24+
Date.type = "date";
25+
26+
exports.validate = function(/*Any*/instance,/*Object*/schema) {
27+
// Summary:
28+
// To use the validator call JSONSchema.validate with an instance object and an optional schema object.
29+
// If a schema is provided, it will be used to validate. If the instance object refers to a schema (self-validating),
30+
// that schema will be used to validate and the schema parameter is not necessary (if both exist,
31+
// both validations will occur).
32+
// The validate method will return an object with two properties:
33+
// valid: A boolean indicating if the instance is valid by the schema
34+
// errors: An array of validation errors. If there are no errors, then an
35+
// empty list will be returned. A validation error will have two properties:
36+
// property: which indicates which property had the error
37+
// message: which indicates what the error was
38+
//
39+
return validate(instance,schema,false);
40+
};
41+
exports.checkPropertyChange = function(/*Any*/value,/*Object*/schema, /*String*/ property) {
42+
// Summary:
43+
// The checkPropertyChange method will check to see if an value can legally be in property with the given schema
44+
// This is slightly different than the validate method in that it will fail if the schema is readonly and it will
45+
// not check for self-validation, it is assumed that the passed in value is already internally valid.
46+
// The checkPropertyChange method will return the same object type as validate, see JSONSchema.validate for
47+
// information.
48+
//
49+
return validate(value,schema, property || "property");
50+
};
51+
var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*Boolean*/ _changing) {
52+
53+
var errors = [];
54+
// validate a value against a property definition
55+
function checkProp(value, schema, path,i){
56+
var l;
57+
path += path ? typeof i == 'number' ? '[' + i + ']' : typeof i == 'undefined' ? '' : '.' + i : i;
58+
function addError(message){
59+
errors.push({property:path,message:message});
60+
}
61+
62+
if((typeof schema != 'object' || schema instanceof Array) && (path || typeof schema != 'function') && !(schema && schema.type)){
63+
if(typeof schema == 'function'){
64+
if(!(value instanceof schema)){
65+
addError("is not an instance of the class/constructor " + schema.name);
66+
}
67+
}else if(schema){
68+
addError("Invalid schema/property definition " + schema);
69+
}
70+
return null;
71+
}
72+
if(_changing && schema.readonly){
73+
addError("is a readonly field, it can not be changed");
74+
}
75+
if(schema['extends']){ // if it extends another schema, it must pass that schema as well
76+
checkProp(value,schema['extends'],path,i);
77+
}
78+
// validate a value against a type definition
79+
function checkType(type,value){
80+
if(type){
81+
if(typeof type == 'string' && type != 'any' &&
82+
(type == 'null' ? value !== null : typeof value != type) &&
83+
!(value instanceof Array && type == 'array') &&
84+
!(value instanceof Date && type == 'date') &&
85+
!(type == 'integer' && value%1===0)){
86+
return [{property:path,message:(typeof value) + " value found, but a " + type + " is required"}];
87+
}
88+
if(type instanceof Array){
89+
var unionErrors=[];
90+
for(var j = 0; j < type.length; j++){ // a union type
91+
if(!(unionErrors=checkType(type[j],value)).length){
92+
break;
93+
}
94+
}
95+
if(unionErrors.length){
96+
return unionErrors;
97+
}
98+
}else if(typeof type == 'object'){
99+
var priorErrors = errors;
100+
errors = [];
101+
checkProp(value,type,path);
102+
var theseErrors = errors;
103+
errors = priorErrors;
104+
return theseErrors;
105+
}
106+
}
107+
return [];
108+
}
109+
if(value === undefined){
110+
if(!schema.optional && !schema.get){
111+
addError("is missing and it is not optional");
112+
}
113+
}else{
114+
errors = errors.concat(checkType(schema.type,value));
115+
if(schema.disallow && !checkType(schema.disallow,value).length){
116+
addError(" disallowed value was matched");
117+
}
118+
if(value !== null){
119+
if(value instanceof Array){
120+
if(schema.items){
121+
if(schema.items instanceof Array){
122+
for(i=0,l=value.length; i<l; i++){
123+
errors.concat(checkProp(value[i],schema.items[i],path,i));
124+
}
125+
}else{
126+
for(i=0,l=value.length; i<l; i++){
127+
errors.concat(checkProp(value[i],schema.items,path,i));
128+
}
129+
}
130+
}
131+
if(schema.minItems && value.length < schema.minItems){
132+
addError("There must be a minimum of " + schema.minItems + " in the array");
133+
}
134+
if(schema.maxItems && value.length > schema.maxItems){
135+
addError("There must be a maximum of " + schema.maxItems + " in the array");
136+
}
137+
}else if(schema.properties || schema.additionalProperties){
138+
errors.concat(checkObj(value, schema.properties, path, schema.additionalProperties));
139+
}
140+
if(schema.pattern && typeof value == 'string' && !value.match(schema.pattern)){
141+
addError("does not match the regex pattern " + schema.pattern);
142+
}
143+
if(schema.maxLength && typeof value == 'string' && value.length > schema.maxLength){
144+
addError("may only be " + schema.maxLength + " characters long");
145+
}
146+
if(schema.minLength && typeof value == 'string' && value.length < schema.minLength){
147+
addError("must be at least " + schema.minLength + " characters long");
148+
}
149+
if(typeof schema.minimum !== undefined && typeof value == typeof schema.minimum &&
150+
schema.minimum > value){
151+
addError("must have a minimum value of " + schema.minimum);
152+
}
153+
if(typeof schema.maximum !== undefined && typeof value == typeof schema.maximum &&
154+
schema.maximum < value){
155+
addError("must have a maximum value of " + schema.maximum);
156+
}
157+
if(schema['enum']){
158+
var enumer = schema['enum'];
159+
l = enumer.length;
160+
var found;
161+
for(var j = 0; j < l; j++){
162+
if(enumer[j]===value){
163+
found=1;
164+
break;
165+
}
166+
}
167+
if(!found){
168+
addError("does not have a value in the enumeration " + enumer.join(", "));
169+
}
170+
}
171+
if(typeof schema.maxDecimal == 'number' &&
172+
(value.toString().match(new RegExp("\\.[0-9]{" + (schema.maxDecimal + 1) + ",}")))){
173+
addError("may only have " + schema.maxDecimal + " digits of decimal places");
174+
}
175+
}
176+
}
177+
return null;
178+
}
179+
// validate an object against a schema
180+
function checkObj(instance,objTypeDef,path,additionalProp){
181+
182+
if(typeof objTypeDef =='object'){
183+
if(typeof instance != 'object' || instance instanceof Array){
184+
errors.push({property:path,message:"an object is required"});
185+
}
186+
187+
for(var i in objTypeDef){
188+
if(objTypeDef.hasOwnProperty(i) && !(i.charAt(0) == '_' && i.charAt(1) == '_')){
189+
var value = instance[i];
190+
var propDef = objTypeDef[i];
191+
// set default
192+
if(value === undefined && propDef["default"]){
193+
value = instance[i] = propDef["default"];
194+
}
195+
if(exports.coerce){
196+
value = instance[i] = exports.coerce(value, propDef);
197+
}
198+
checkProp(value,propDef,path,i);
199+
}
200+
}
201+
}
202+
for(i in instance){
203+
if(instance.hasOwnProperty(i) && !(i.charAt(0) == '_' && i.charAt(1) == '_') && objTypeDef && !objTypeDef[i] && additionalProp===false){
204+
errors.push({property:path,message:(typeof value) + "The property " + i +
205+
" is not defined in the schema and the schema does not allow additional properties"});
206+
}
207+
var requires = objTypeDef && objTypeDef[i] && objTypeDef[i].requires;
208+
if(requires && !(requires in instance)){
209+
errors.push({property:path,message:"the presence of the property " + i + " requires that " + requires + " also be present"});
210+
}
211+
value = instance[i];
212+
if(additionalProp && (!(objTypeDef && typeof objTypeDef == 'object') || !(i in objTypeDef))){
213+
if(exports.coerce){
214+
value = instance[i] = exports.coerce(value, additionalProp);
215+
}
216+
checkProp(value,additionalProp,path,i);
217+
}
218+
if(!_changing && value && value.$schema){
219+
errors = errors.concat(checkProp(value,value.$schema,path,i));
220+
}
221+
}
222+
return errors;
223+
}
224+
if(schema){
225+
checkProp(instance,schema,'',_changing || '');
226+
}
227+
if(!_changing && instance && instance.$schema){
228+
checkProp(instance,instance.$schema,'','');
229+
}
230+
return {valid:!errors.length,errors:errors};
231+
};
232+
exports.mustBeValid = function(result){
233+
// summary:
234+
// This checks to ensure that the result is valid and will throw an appropriate error message if it is not
235+
// result: the result returned from checkPropertyChange or validate
236+
if(!result.valid){
237+
throw new TypeError(result.errors.map(function(error){return "for property " + error.property + ': ' + error.message;}).join(", \n"));
238+
}
239+
}
240+
/* will add this later
241+
newFromSchema : function() {
242+
}
243+
*/
244+
245+
exports.cacheLinks = true;
246+
exports.getLink = function(relation, instance, schema){
247+
// gets the URI of the link for the given relation based on the instance and schema
248+
// for example:
249+
// getLink(
250+
// "brother",
251+
// {"brother_id":33},
252+
// {links:[{rel:"brother", href:"Brother/{brother_id}"}]}) ->
253+
// "Brother/33"
254+
var links = schema.__linkTemplates;
255+
if(!links){
256+
links = {};
257+
var schemaLinks = schema.links;
258+
if(schemaLinks && schemaLinks instanceof Array){
259+
schemaLinks.forEach(function(link){
260+
/* // TODO: allow for multiple same-name relations
261+
if(links[link.rel]){
262+
if(!(links[link.rel] instanceof Array)){
263+
links[link.rel] = [links[link.rel]];
264+
}
265+
}*/
266+
links[link.rel] = link.href;
267+
});
268+
}
269+
if(exports.cacheLinks){
270+
schema.__linkTemplates = links;
271+
}
272+
}
273+
var linkTemplate = links[relation];
274+
return linkTemplate && exports.substitute(linkTemplate, instance);
275+
};
276+
277+
exports.substitute = function(linkTemplate, instance){
278+
return linkTemplate.replace(/\{([^\}]*)\}/g, function(t, property){
279+
var value = instance[decodeURIComponent(property)];
280+
if(value instanceof Array){
281+
// the value is an array, it should produce a URI like /Table/(4,5,8) and store.get() should handle that as an array of values
282+
return '(' + value.join(',') + ')';
283+
}
284+
return value;
285+
});
286+
};

package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "json-schema",
3+
"author": "Kris Zyp",
4+
"dependencies": [],
5+
"contributors": [],
6+
"keywords": [
7+
"json",
8+
"schema"
9+
],
10+
"engines": {"node":">=0.1.30", "rhino": true}
11+
}

0 commit comments

Comments
 (0)