@@ -62,18 +62,29 @@ var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*O
62
62
}
63
63
var errors = [ ] ;
64
64
// validate a value against a property definition
65
- function checkProp ( value , schema , path , i ) {
65
+ function checkProp ( value , schema , path , i ) {
66
66
67
67
var l ;
68
- path += path ? typeof i == 'number' ? '[' + i + ']' : typeof i == 'undefined' ? '' : '.' + i : i ;
69
- function addError ( message ) {
70
- errors . push ( { property :path , message :message } ) ;
68
+ if ( typeof i !== 'undefined' ) {
69
+ if ( typeof i === 'number' ) {
70
+ path += '[' + i + ']' ;
71
+ } else {
72
+ var escaped = i . replace ( / \. / g, '\\.' ) ;
73
+ path += path ? ( '.' + escaped ) : escaped ;
74
+ }
75
+ }
76
+ function addError ( message , value ) {
77
+ var error = { property :path , message :message , schema :schema } ;
78
+ if ( typeof value !== 'undefined' ) {
79
+ error . value = value ;
80
+ }
81
+ errors . push ( error ) ;
71
82
}
72
83
73
84
if ( ( typeof schema != 'object' || schema instanceof Array ) && ( path || typeof schema != 'function' ) && ! ( schema && getType ( schema ) ) ) {
74
85
if ( typeof schema == 'function' ) {
75
86
if ( ! ( value instanceof schema ) ) {
76
- addError ( "is not an instance of the class/constructor " + schema . name ) ;
87
+ addError ( "is not an instance of the class/constructor " + schema . name , value ) ;
77
88
}
78
89
} else if ( schema ) {
79
90
addError ( "Invalid schema/property definition " + schema ) ;
@@ -94,7 +105,7 @@ var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*O
94
105
! ( value instanceof Array && type == 'array' ) &&
95
106
! ( value instanceof Date && type == 'date' ) &&
96
107
! ( type == 'integer' && value % 1 === 0 ) ) {
97
- return [ { property :path , message :( typeof value ) + " value found, but a " + type + " is required" } ] ;
108
+ return [ { property :path , message :( typeof value ) + " value found, but a " + type + " is required" , schema : schema , value : value } ] ;
98
109
}
99
110
if ( type instanceof Array ) {
100
111
var unionErrors = [ ] ;
@@ -124,14 +135,14 @@ var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*O
124
135
} else {
125
136
errors = errors . concat ( checkType ( getType ( schema ) , value ) ) ;
126
137
if ( schema . disallow && ! checkType ( schema . disallow , value ) . length ) {
127
- addError ( " disallowed value was matched" ) ;
138
+ addError ( " disallowed value was matched" , value ) ;
128
139
}
129
140
if ( value !== null ) {
130
141
if ( value instanceof Array ) {
131
142
if ( schema . items ) {
132
143
var itemsIsArray = schema . items instanceof Array ;
133
144
var propDef = schema . items ;
134
- for ( i = 0 , l = value . length ; i < l ; i += 1 ) {
145
+ for ( var i = 0 , l = value . length ; i < l ; i += 1 ) {
135
146
if ( itemsIsArray )
136
147
propDef = schema . items [ i ] ;
137
148
if ( options . coerce )
@@ -140,30 +151,40 @@ var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*O
140
151
}
141
152
}
142
153
if ( schema . minItems && value . length < schema . minItems ) {
143
- addError ( "There must be a minimum of " + schema . minItems + " in the array" ) ;
154
+ addError ( "There must be a minimum of " + schema . minItems + " in the array" , value ) ;
144
155
}
145
156
if ( schema . maxItems && value . length > schema . maxItems ) {
146
- addError ( "There must be a maximum of " + schema . maxItems + " in the array" ) ;
157
+ addError ( "There must be a maximum of " + schema . maxItems + " in the array" , value ) ;
158
+ }
159
+ if ( schema . uniqueItems ) {
160
+ for ( var i = 0 , l = value . length ; i < l ; i += 1 ) {
161
+ for ( var j = i + 1 ; j < l ; j += 1 ) {
162
+ if ( compareItems ( value [ i ] , value [ j ] ) ) {
163
+ addError ( "The items in the array must be unique." , value ) ;
164
+ break ;
165
+ }
166
+ }
167
+ }
147
168
}
148
169
} else if ( schema . properties || schema . additionalProperties ) {
149
170
errors . concat ( checkObj ( value , schema . properties , path , schema . additionalProperties ) ) ;
150
171
}
151
172
if ( schema . pattern && typeof value == 'string' && ! value . match ( schema . pattern ) ) {
152
- addError ( "does not match the regex pattern " + schema . pattern ) ;
173
+ addError ( "does not match the regex pattern " + schema . pattern , value ) ;
153
174
}
154
175
if ( schema . maxLength && typeof value == 'string' && value . length > schema . maxLength ) {
155
- addError ( "may only be " + schema . maxLength + " characters long" ) ;
176
+ addError ( "may only be " + schema . maxLength + " characters long" , value ) ;
156
177
}
157
178
if ( schema . minLength && typeof value == 'string' && value . length < schema . minLength ) {
158
- addError ( "must be at least " + schema . minLength + " characters long" ) ;
179
+ addError ( "must be at least " + schema . minLength + " characters long" , value ) ;
159
180
}
160
181
if ( typeof schema . minimum !== undefined && typeof value == typeof schema . minimum &&
161
182
schema . minimum > value ) {
162
- addError ( "must have a minimum value of " + schema . minimum ) ;
183
+ addError ( "must have a minimum value of " + schema . minimum , value ) ;
163
184
}
164
185
if ( typeof schema . maximum !== undefined && typeof value == typeof schema . maximum &&
165
186
schema . maximum < value ) {
166
- addError ( "must have a maximum value of " + schema . maximum ) ;
187
+ addError ( "must have a maximum value of " + schema . maximum , value ) ;
167
188
}
168
189
if ( schema [ 'enum' ] ) {
169
190
var enumer = schema [ 'enum' ] ;
@@ -176,12 +197,12 @@ var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*O
176
197
}
177
198
}
178
199
if ( ! found ) {
179
- addError ( "does not have a value in the enumeration " + enumer . join ( ", " ) ) ;
200
+ addError ( "does not have a value in the enumeration " + enumer . join ( ", " ) , value ) ;
180
201
}
181
202
}
182
203
if ( typeof schema . maxDecimal == 'number' &&
183
204
( value . toString ( ) . match ( new RegExp ( "\\.[0-9]{" + ( schema . maxDecimal + 1 ) + ",}" ) ) ) ) {
184
- addError ( "may only have " + schema . maxDecimal + " digits of decimal places" ) ;
205
+ addError ( "may only have " + schema . maxDecimal + " digits of decimal places" , value ) ;
185
206
}
186
207
}
187
208
}
@@ -190,12 +211,20 @@ var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*O
190
211
// validate an object against a schema
191
212
function checkObj ( instance , objTypeDef , path , additionalProp ) {
192
213
214
+ function addError ( message , value ) {
215
+ var error = { property :path , message :message , schema :objTypeDef } ;
216
+ if ( typeof value !== 'undefined' ) {
217
+ error . value = value ;
218
+ }
219
+ errors . push ( error ) ;
220
+ }
221
+
193
222
if ( typeof objTypeDef == 'object' ) {
194
223
if ( typeof instance != 'object' || instance instanceof Array ) {
195
- errors . push ( { property : path , message : "an object is required" } ) ;
224
+ addError ( "an object is required" , instance ) ;
196
225
}
197
-
198
- for ( var i in objTypeDef ) {
226
+
227
+ for ( var i in objTypeDef ) {
199
228
if ( objTypeDef . hasOwnProperty ( i ) ) {
200
229
var value = instance [ i ] ;
201
230
// skip _not_ specified properties
@@ -212,19 +241,19 @@ var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*O
212
241
}
213
242
}
214
243
}
215
- for ( i in instance ) {
244
+ for ( var i in instance ) {
216
245
if ( instance . hasOwnProperty ( i ) && ! ( i . charAt ( 0 ) == '_' && i . charAt ( 1 ) == '_' ) && objTypeDef && ! objTypeDef [ i ] && additionalProp === false ) {
217
246
if ( options . filter ) {
218
247
delete instance [ i ] ;
219
248
continue ;
220
249
} else {
221
- errors . push ( { property : path , message : ( typeof value ) + "The property " + i +
222
- " is not defined in the schema and the schema does not allow additional properties" } ) ;
250
+ addError ( "The property ' " + i +
251
+ "' is not defined in the schema and the schema does not allow additional properties" , instance ) ;
223
252
}
224
253
}
225
254
var requires = objTypeDef && objTypeDef [ i ] && objTypeDef [ i ] . requires ;
226
255
if ( requires && ! ( requires in instance ) ) {
227
- errors . push ( { property : path , message : "the presence of the property " + i + " requires that " + requires + " also be present" } ) ;
256
+ addError ( "the presence of the property " + i + " requires that " + requires + " also be present" , instance ) ;
228
257
}
229
258
value = instance [ i ] ;
230
259
if ( additionalProp && ( ! ( objTypeDef && typeof objTypeDef == 'object' ) || ! ( i in objTypeDef ) ) ) {
@@ -239,6 +268,39 @@ var validate = exports._validate = function(/*Any*/instance,/*Object*/schema,/*O
239
268
}
240
269
return errors ;
241
270
}
271
+ // compare a value against another for equality (for uniqueItems)
272
+ function compareItems ( item1 , item2 ) {
273
+ if ( typeof item1 !== typeof item2 ) {
274
+ return false ;
275
+ }
276
+ if ( Array . isArray ( item1 ) ) {
277
+ if ( item1 . length !== item2 . length ) {
278
+ return false ;
279
+ }
280
+ for ( var i = 0 , l = item1 . length ; i < l ; i += 1 ) {
281
+ if ( ! compareItems ( item1 [ i ] , item2 [ i ] ) ) {
282
+ return false ;
283
+ }
284
+ }
285
+ return true ;
286
+ }
287
+ if ( item1 instanceof Object ) {
288
+ var item1Keys = Object . keys ( item1 ) ;
289
+ var item2Keys = Object . keys ( item2 ) ;
290
+ if ( item1Keys . length !== item2Keys . length ) {
291
+ return false ;
292
+ }
293
+ for ( var i = 0 , l = item1Keys . length ; i < l ; i += 1 ) {
294
+ var key = item1Keys [ i ] ;
295
+ if ( ! item2 . hasOwnProperty ( key ) ||
296
+ ! compareItems ( item1 [ key ] , item2 [ key ] ) ) {
297
+ return false ;
298
+ }
299
+ }
300
+ return true ;
301
+ }
302
+ return item1 === item2 ;
303
+ }
242
304
if ( schema ) {
243
305
checkProp ( instance , schema , '' , _changing || '' ) ;
244
306
}
0 commit comments