isvalid 2.10.5 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +1 -1
- package/README.md +37 -2
- package/lib/formalize.js +29 -2
- package/lib/ranges.js +45 -31
- package/lib/validate.js +24 -0
- package/package.json +7 -7
- package/test/formalize.js +10 -1
- package/test/ranges.js +17 -6
- package/test/validate.js +15 -0
package/.eslintrc.js
CHANGED
package/README.md
CHANGED
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
* [`enum`](#enum)
|
|
43
43
|
- [`Number` Validators](#number-validators)
|
|
44
44
|
* [`range`](#range)
|
|
45
|
+
* [`float`](#float)
|
|
45
46
|
- [Custom Types](#custom-types)
|
|
46
47
|
* [`post`](#post)
|
|
47
48
|
+ [Example](#example-1)
|
|
@@ -448,6 +449,26 @@ Examples:
|
|
|
448
449
|
}
|
|
449
450
|
````
|
|
450
451
|
|
|
452
|
+
> Negative values can be wrapped in parentheses.
|
|
453
|
+
|
|
454
|
+
````javascript
|
|
455
|
+
{
|
|
456
|
+
type: Array,
|
|
457
|
+
len: '(-2)-2',
|
|
458
|
+
schema: { … }
|
|
459
|
+
}
|
|
460
|
+
````
|
|
461
|
+
|
|
462
|
+
> It also supports non-integer values.
|
|
463
|
+
|
|
464
|
+
````javascript
|
|
465
|
+
{
|
|
466
|
+
type: Array,
|
|
467
|
+
len: '(-2.2)-2.2',
|
|
468
|
+
schema: { … }
|
|
469
|
+
}
|
|
470
|
+
````
|
|
471
|
+
|
|
451
472
|
> An array that should have at least 2 items, exactly 5 items or 8 or more items.
|
|
452
473
|
|
|
453
474
|
##### `unique`
|
|
@@ -498,7 +519,7 @@ This does not do any actual validation. Instead it trims the input in both ends
|
|
|
498
519
|
##### `len`
|
|
499
520
|
Type: `String` or `Number`
|
|
500
521
|
|
|
501
|
-
This ensures that the string's length is within a specified range. You can use the same formatting as [`Array`'s `len`](#len) validator.
|
|
522
|
+
This ensures that the string's length is within a specified range. You can use the same formatting as [`Array`'s `len`](#len) validator described above (except it does not support ranges with negative values or non-integers).
|
|
502
523
|
|
|
503
524
|
##### `match`
|
|
504
525
|
Type: `RegExp`
|
|
@@ -535,7 +556,21 @@ Type: `Number`or `String`
|
|
|
535
556
|
|
|
536
557
|
This ensures that the number is within a certain range. If not the validator throws an error.
|
|
537
558
|
|
|
538
|
-
The `range` validator uses the same formatting as the [`Array`'s `len`](#len) validator described above.
|
|
559
|
+
The `range` validator uses the same formatting as the [`Array`'s `len`](#len) validator described above (except it does not support ranges with negative values or non-integers).
|
|
560
|
+
|
|
561
|
+
##### `float`
|
|
562
|
+
Type: `String` of value: `'allow'`, `'deny'`, `'round'`, `'floor'`, `'ceil'`
|
|
563
|
+
|
|
564
|
+
This tells the validator how to handle non-integers.
|
|
565
|
+
|
|
566
|
+
The validator has five options:
|
|
567
|
+
* `'allow'` Allow non-integer values.
|
|
568
|
+
* `'deny'` Throw a `ValidationError` if the value is a non-integer.
|
|
569
|
+
* `'round'` Round value to nearest integer.
|
|
570
|
+
* `'floor'` Round to integer less than or equal to value.
|
|
571
|
+
* `'ceil'` Round to integer bigger than or equal to value.
|
|
572
|
+
|
|
573
|
+
> Default is `'allow'`.
|
|
539
574
|
|
|
540
575
|
#### Custom Types
|
|
541
576
|
|
package/lib/formalize.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const merge = require('merge'),
|
|
4
4
|
SchemaError = require('./errors/schema.js'),
|
|
5
|
-
utils = require('./utils.js')
|
|
5
|
+
utils = require('./utils.js'),
|
|
6
|
+
ranges = require('./ranges.js');
|
|
6
7
|
|
|
7
8
|
const finalize = (formalizedSchema, nonFormalizedSchema) => {
|
|
8
9
|
|
|
@@ -125,7 +126,8 @@ const formalizeAny = (schema, options = {}) => {
|
|
|
125
126
|
'enum': [ 'array' ]
|
|
126
127
|
});
|
|
127
128
|
if (utils.isSameType('number', utils.typeName(type))) merge(validators, {
|
|
128
|
-
'range': [ 'string', 'number' ]
|
|
129
|
+
'range': [ 'string', 'number' ],
|
|
130
|
+
'float': [ 'string' ]
|
|
129
131
|
});
|
|
130
132
|
}
|
|
131
133
|
|
|
@@ -209,6 +211,31 @@ const formalizeAny = (schema, options = {}) => {
|
|
|
209
211
|
);
|
|
210
212
|
}
|
|
211
213
|
|
|
214
|
+
// Check number float
|
|
215
|
+
if (typeof formalizedSchema.float === 'string' &&
|
|
216
|
+
['allow', 'deny', 'round', 'floor', 'ceil'].indexOf(formalizedSchema.float) == -1) {
|
|
217
|
+
throw new SchemaError(
|
|
218
|
+
schema,
|
|
219
|
+
'Validator `float` must have value `\'allow\'`, `\'deny\'`, `\'round\'`, `\'floor\'` or `\'ceil\'`.'
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Check len
|
|
224
|
+
if (typeof formalizedSchema.len !== 'undefined') {
|
|
225
|
+
formalizedSchema.len = ranges.formalize(formalizedSchema.len, {
|
|
226
|
+
allowNegative: false,
|
|
227
|
+
allowNonIntegers: false
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Check range
|
|
232
|
+
if (typeof formalizedSchema.range !== 'undefined') {
|
|
233
|
+
formalizedSchema.range = ranges.formalize(formalizedSchema.range, {
|
|
234
|
+
allowNegative: true,
|
|
235
|
+
allowNonIntegers: true
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
212
239
|
// Check string enums
|
|
213
240
|
if (typeof formalizedSchema.enum !== 'undefined') {
|
|
214
241
|
if (formalizedSchema.enum.length < 1) {
|
package/lib/ranges.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const
|
|
4
|
+
SchemaError = require('./errors/schema.js');
|
|
5
|
+
|
|
6
|
+
module.exports.testIndex = function(ranges, value) {
|
|
7
|
+
return ranges.some(({ lower, upper }) => {
|
|
8
|
+
return lower <= value && upper >= value;
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
module.exports.formalize = function(ranges, options) {
|
|
4
13
|
|
|
5
14
|
// Convert to string if ranges is a Number.
|
|
6
15
|
if (ranges !== undefined && typeof ranges === 'number') {
|
|
@@ -12,45 +21,50 @@ module.exports.testIndex = function(ranges, index) {
|
|
|
12
21
|
throw new Error('Ranges must be a number or a string expressed as: ex. \'-2,4-6,8,10-\'.');
|
|
13
22
|
}
|
|
14
23
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
24
|
+
return ranges
|
|
25
|
+
.split(',')
|
|
26
|
+
.map((range) => {
|
|
19
27
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
for (let idx in r) {
|
|
28
|
+
// Get the boundaries of the range.
|
|
29
|
+
let boundaries = range.split(/(?<!\()-/);
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
// Low and high boundaries are the same where only one number is specified.
|
|
32
|
+
if (boundaries.length == 1) boundaries = [ boundaries[0], boundaries[0] ];
|
|
33
|
+
// Throw an error if there is not exactly to boundaries.
|
|
34
|
+
if (boundaries.length != 2) throw new SchemaError('Malformed range \'' + range + '\'.');
|
|
26
35
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
// Test for malformed boundaries
|
|
37
|
+
for (let bIdx = 0 ; bIdx < 2 ; bIdx++) {
|
|
38
|
+
if (!/^\(?[-0-9.]*\)?$/.test(boundaries[bIdx])) throw new SchemaError('Malformed boundary \'' + boundaries[bIdx] + '\'.');
|
|
39
|
+
}
|
|
31
40
|
|
|
32
|
-
|
|
33
|
-
for (let bIdx = 0 ; bIdx < 2 ; bIdx++) {
|
|
34
|
-
if (!/^[0-9]*$/.test(boundaries[bIdx])) throw new Error('Malformed boundary \'' + boundaries[bIdx] + '\'.');
|
|
35
|
-
}
|
|
41
|
+
boundaries = boundaries.map((boundary) => boundary.match(/^\(?([-0-9.]*)\)?$/)[1] || '');
|
|
36
42
|
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
let lower = boundaries[0];
|
|
44
|
+
let upper = boundaries[1];
|
|
39
45
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
if ((options || {}).allowNegative === false && (lower < 0 || upper < 0)) {
|
|
47
|
+
throw new SchemaError('Boundary cannot be a negative value.');
|
|
48
|
+
}
|
|
43
49
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
if ((options || {}).allowNonInteger === false && (!Number.isInteger(lower) || !Number.isInteger(upper))) {
|
|
51
|
+
throw new SchemaError('Boundary cannot be a non-integer value.');
|
|
52
|
+
}
|
|
47
53
|
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
// If no lower boundary is specified we use -Infinity
|
|
55
|
+
if (lower.length === 0) lower = -Infinity;
|
|
56
|
+
else lower = parseFloat(lower);
|
|
50
57
|
|
|
51
|
-
|
|
58
|
+
// If no higher boundary is specified we use Infinity;
|
|
59
|
+
if (upper.length === 0) upper = Infinity;
|
|
60
|
+
else upper = parseFloat(upper);
|
|
61
|
+
|
|
62
|
+
if (lower > upper) {
|
|
63
|
+
throw new SchemaError('Malformed range \'' + range +'\'');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return { lower, upper };
|
|
52
67
|
|
|
53
|
-
|
|
54
|
-
return false;
|
|
68
|
+
});
|
|
55
69
|
|
|
56
70
|
};
|
package/lib/validate.js
CHANGED
|
@@ -206,6 +206,30 @@ const validateNumber = async (data, schema, options, keyPath) => {
|
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
+
if (schema.float || options.defaults.float) {
|
|
210
|
+
if (!Number.isInteger(data)) {
|
|
211
|
+
switch (schema.float) {
|
|
212
|
+
case 'deny':
|
|
213
|
+
throw new ValidationError(
|
|
214
|
+
keyPath,
|
|
215
|
+
schema._nonFormalizedSchema,
|
|
216
|
+
'unknownKeys',
|
|
217
|
+
(schema.errors || {}).float || customErrorMessage(((options.errorMessages || {}).number || {}).float || 'Number must be an integer.'));
|
|
218
|
+
case 'round':
|
|
219
|
+
data = Math.round(data);
|
|
220
|
+
break;
|
|
221
|
+
case 'floor':
|
|
222
|
+
data = Math.floor(data);
|
|
223
|
+
break;
|
|
224
|
+
case 'ceil':
|
|
225
|
+
data = Math.ceil(data);
|
|
226
|
+
break;
|
|
227
|
+
default:
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
209
233
|
return data;
|
|
210
234
|
|
|
211
235
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "isvalid",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Async JSON validation library for node.js.",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -18,13 +18,13 @@
|
|
|
18
18
|
"merge": "^2.1.1"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"body-parser": "^1.
|
|
22
|
-
"chai": "^4.
|
|
21
|
+
"body-parser": "^1.20.0",
|
|
22
|
+
"chai": "^4.3.6",
|
|
23
23
|
"chai-as-promised": "^7.1.1",
|
|
24
|
-
"eslint": "^8.
|
|
25
|
-
"express": "^4.17.
|
|
26
|
-
"mocha": "^9.
|
|
27
|
-
"supertest": "^6.
|
|
24
|
+
"eslint": "^8.12.0",
|
|
25
|
+
"express": "^4.17.3",
|
|
26
|
+
"mocha": "^9.2.2",
|
|
27
|
+
"supertest": "^6.2.2"
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
30
|
"test": "./node_modules/mocha/bin/mocha ./test/index.js"
|
package/test/formalize.js
CHANGED
|
@@ -165,10 +165,19 @@ describe('schema', function() {
|
|
|
165
165
|
equal: '123'
|
|
166
166
|
})).to.have.property('equal').equal('123');
|
|
167
167
|
});
|
|
168
|
-
it('should come back with a priority if non is provided', () => {
|
|
168
|
+
it('should come back with a priority if non is provided.', () => {
|
|
169
169
|
expect(formalize({
|
|
170
170
|
type: Number
|
|
171
171
|
})).to.have.property('priority').equal(10);
|
|
172
172
|
});
|
|
173
|
+
it ('should throw error if number range is not valid.', () => {
|
|
174
|
+
expect(f({ type: Number, range: 'abc-abc' })).to.throw(SchemaError);
|
|
175
|
+
});
|
|
176
|
+
it ('should throw error if string len is negative.', () => {
|
|
177
|
+
expect(f({ type: String, len: '-2-' })).to.throw(SchemaError);
|
|
178
|
+
});
|
|
179
|
+
it ('should throw error if array len is negative.', () => {
|
|
180
|
+
expect(f({ type: Array, len: '-2-' })).to.throw(SchemaError);
|
|
181
|
+
});
|
|
173
182
|
});
|
|
174
183
|
});
|
package/test/ranges.js
CHANGED
|
@@ -3,31 +3,42 @@
|
|
|
3
3
|
const expect = require('chai').expect,
|
|
4
4
|
ranges = require('../lib/ranges.js');
|
|
5
5
|
|
|
6
|
+
function r(range, value) {
|
|
7
|
+
const formalized = ranges.formalize(range);
|
|
8
|
+
return ranges.testIndex(formalized, value);
|
|
9
|
+
}
|
|
10
|
+
|
|
6
11
|
describe('ranges', function() {
|
|
7
12
|
it ('should throw an error if ranges is not a string.', function() {
|
|
8
13
|
expect(function() {
|
|
9
|
-
|
|
14
|
+
r([123], 1);
|
|
10
15
|
}).to.throw(Error);
|
|
11
16
|
});
|
|
12
17
|
it ('should throw no error if ranges is a number.', function() {
|
|
13
18
|
expect(function() {
|
|
14
|
-
|
|
19
|
+
r(1, 1);
|
|
15
20
|
}).not.to.throw(Error);
|
|
16
21
|
});
|
|
17
22
|
it ('should throw error if ranges is string but format is invalid.', function() {
|
|
18
23
|
expect(function() {
|
|
19
|
-
|
|
24
|
+
r('abc', 1);
|
|
20
25
|
}).to.throw(Error);
|
|
21
26
|
});
|
|
22
27
|
it ('should throw error if index is not set.', function() {
|
|
23
28
|
expect(function() {
|
|
24
|
-
|
|
29
|
+
r(1);
|
|
25
30
|
});
|
|
26
31
|
});
|
|
27
32
|
it ('should return true if index is within range.', function() {
|
|
28
|
-
expect(
|
|
33
|
+
expect(r('-2,4-6,8,10-', 2)).to.equal(true);
|
|
29
34
|
});
|
|
30
35
|
it ('should return false if index is not within range.', function() {
|
|
31
|
-
expect(
|
|
36
|
+
expect(r('-2,4-6,8,10-', 3)).to.equal(false);
|
|
37
|
+
});
|
|
38
|
+
it ('should allow negative values wrapped in parentheses.', function() {
|
|
39
|
+
expect(r('(-2)-', -1)).to.equal(true);
|
|
40
|
+
});
|
|
41
|
+
it ('should allow for decimal values.', function() {
|
|
42
|
+
expect(r('(-2.2)-2.2', 0.1)).to.equal(true);
|
|
32
43
|
});
|
|
33
44
|
});
|
package/test/validate.js
CHANGED
|
@@ -712,6 +712,21 @@ describe('validate', function() {
|
|
|
712
712
|
return expect(isvalid(123, Number))
|
|
713
713
|
.to.eventually.equal(123);
|
|
714
714
|
});
|
|
715
|
+
it('should throw error if non-integers are not allowed.', () => {
|
|
716
|
+
expect(isvalid(2.2, { type: Number, float: 'deny' })).to.eventually.throw(ValidationError);
|
|
717
|
+
});
|
|
718
|
+
it ('should come back with non-integer values if they are allowed.', () => {
|
|
719
|
+
expect(isvalid(2.2, { type: Number })).to.eventually.equal(2.2);
|
|
720
|
+
});
|
|
721
|
+
it ('should come back with number rounded if `float` is set to `round`.', () => {
|
|
722
|
+
expect(isvalid(2.5, { type: Number, float: 'round' })).to.eventually.equal(3);
|
|
723
|
+
});
|
|
724
|
+
it ('should come back with number ceiled if `float` is set to `ceil`.', () => {
|
|
725
|
+
expect(isvalid(2.2, { type: Number, float: 'ceil' })).to.eventually.equal(3);
|
|
726
|
+
});
|
|
727
|
+
it ('should come back with number floored if `float` is set to `floor`.', () => {
|
|
728
|
+
expect(isvalid(2.8, { type: Number, float: 'ceil' })).to.eventually.equal(2);
|
|
729
|
+
});
|
|
715
730
|
describe('range', function() {
|
|
716
731
|
it('should come back with error if input is not within range.', () => {
|
|
717
732
|
return expect(isvalid(1, { type: Number, range: '2-4' }))
|