isvalid 3.0.2 → 3.1.2
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/lib/formalize.js +33 -14
- package/lib/index.js +1 -0
- package/lib/plugins.js +54 -0
- package/lib/utils.js +1 -1
- package/lib/validate.js +18 -1
- package/package.json +2 -1
- package/test/formalize.js +6 -0
- package/test/index.js +18 -1
- package/test/validate.js +16 -0
package/lib/formalize.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
const merge = require('merge'),
|
|
4
4
|
SchemaError = require('./errors/schema.js'),
|
|
5
5
|
utils = require('./utils.js'),
|
|
6
|
-
ranges = require('./ranges.js')
|
|
6
|
+
ranges = require('./ranges.js'),
|
|
7
|
+
plugins = require('./plugins');
|
|
7
8
|
|
|
8
9
|
const finalize = (formalizedSchema, nonFormalizedSchema) => {
|
|
9
10
|
|
|
@@ -26,7 +27,9 @@ const formalizeObject = (formalizedSchema, nonFormalizedSchema, options) => {
|
|
|
26
27
|
|
|
27
28
|
formalizedSchema.schema = formalizedSchema.schema || {};
|
|
28
29
|
|
|
29
|
-
if (utils.instanceTypeName(formalizedSchema.schema) !== 'object')
|
|
30
|
+
if (utils.instanceTypeName(formalizedSchema.schema) !== 'object') {
|
|
31
|
+
throw new SchemaError(formalizedSchema.schema, 'Object schemas must be an object.');
|
|
32
|
+
}
|
|
30
33
|
|
|
31
34
|
// Build new formalized schema into this.
|
|
32
35
|
let formalizedSubSchema = {};
|
|
@@ -37,7 +40,7 @@ const formalizeObject = (formalizedSchema, nonFormalizedSchema, options) => {
|
|
|
37
40
|
formalizedSubSchema[key] = formalizeAny(formalizedSchema.schema[key], options);
|
|
38
41
|
});
|
|
39
42
|
|
|
40
|
-
formalizedSchema.schema = formalizedSubSchema;
|
|
43
|
+
formalizedSchema.schema = finalize(formalizedSubSchema, nonFormalizedSchema.schema);
|
|
41
44
|
|
|
42
45
|
if (typeof formalizedSchema.required === 'undefined') {
|
|
43
46
|
if (Object.keys(formalizedSubSchema).some((key) => formalizedSubSchema[key].required === true || formalizedSubSchema[key].required === 'implicit')) {
|
|
@@ -45,17 +48,17 @@ const formalizeObject = (formalizedSchema, nonFormalizedSchema, options) => {
|
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
|
|
48
|
-
return
|
|
51
|
+
return formalizedSchema;
|
|
49
52
|
|
|
50
53
|
};
|
|
51
54
|
|
|
52
|
-
const formalizeArray = (formalizedSchema,
|
|
55
|
+
const formalizeArray = (formalizedSchema, options) => {
|
|
53
56
|
|
|
54
57
|
// formalizedSchema has been pre-processed by formalizeAny, so
|
|
55
58
|
// we only need to formalize the sub-schema.
|
|
56
59
|
|
|
57
60
|
// If no sub-schema is provided we consider the schema final.
|
|
58
|
-
if (typeof formalizedSchema.schema === 'undefined') return
|
|
61
|
+
if (typeof formalizedSchema.schema === 'undefined') return formalizedSchema;
|
|
59
62
|
|
|
60
63
|
formalizedSchema.schema = formalizeAny(formalizedSchema.schema, options);
|
|
61
64
|
|
|
@@ -64,7 +67,7 @@ const formalizeArray = (formalizedSchema, nonFormalizedSchema, options) => {
|
|
|
64
67
|
if (formalizedSchema.schema.required === true) formalizedSchema.required = true;
|
|
65
68
|
}
|
|
66
69
|
|
|
67
|
-
return
|
|
70
|
+
return formalizedSchema;
|
|
68
71
|
|
|
69
72
|
};
|
|
70
73
|
|
|
@@ -75,14 +78,14 @@ const formalizeAny = (schema, options = {}) => {
|
|
|
75
78
|
|
|
76
79
|
if (!schema.type && !schema.post && !schema.pre && !schema.equal) {
|
|
77
80
|
if ('object' == utils.instanceTypeName(schema)) {
|
|
78
|
-
return
|
|
81
|
+
return formalizeAny({ type: Object, schema: schema }, options);
|
|
79
82
|
}
|
|
80
83
|
if ('array' == utils.instanceTypeName(schema)) {
|
|
81
|
-
if (schema.length === 0) return formalizeAny({ type: Array },
|
|
82
|
-
return
|
|
84
|
+
if (schema.length === 0) return formalizeAny({ type: Array }, options);
|
|
85
|
+
return formalizeAny({ type: Array, schema: schema[0] }, options);
|
|
83
86
|
}
|
|
84
87
|
if ((typeof schema === 'string' && schema.length) || (typeof schema === 'function' && utils.typeName(schema) !== undefined)) {
|
|
85
|
-
return formalizeAny({ type: schema },
|
|
88
|
+
return formalizeAny({ type: schema }, options);
|
|
86
89
|
}
|
|
87
90
|
throw new SchemaError(schema, 'Schemas must have at least on validator of `type`, `post`/`pre` and/or `equal`.');
|
|
88
91
|
}
|
|
@@ -131,6 +134,10 @@ const formalizeAny = (schema, options = {}) => {
|
|
|
131
134
|
});
|
|
132
135
|
}
|
|
133
136
|
|
|
137
|
+
const pluginValidators = plugins.validatorsForType(type);
|
|
138
|
+
|
|
139
|
+
merge(validators, pluginValidators);
|
|
140
|
+
|
|
134
141
|
// If post validator is provided allow for options.
|
|
135
142
|
if (formalizedSchema.pre !== undefined || formalizedSchema.post !== undefined) {
|
|
136
143
|
merge(validators, { 'options': 'any' });
|
|
@@ -150,6 +157,7 @@ const formalizeAny = (schema, options = {}) => {
|
|
|
150
157
|
|
|
151
158
|
// Test for - and transform - errors in validator.
|
|
152
159
|
if (Array.isArray(test) &&
|
|
160
|
+
|
|
153
161
|
test.length === 2 &&
|
|
154
162
|
validator.includes(utils.instanceTypeName(test[0]))) {
|
|
155
163
|
|
|
@@ -260,10 +268,21 @@ const formalizeAny = (schema, options = {}) => {
|
|
|
260
268
|
// Finalize objects and arrays if necessary.
|
|
261
269
|
if (formalizedSchema.type) {
|
|
262
270
|
if (utils.isSameType('object', utils.typeName(formalizedSchema.type))) {
|
|
263
|
-
|
|
271
|
+
formalizedSchema = formalizeObject(formalizedSchema, schema, options);
|
|
264
272
|
}
|
|
265
|
-
if (utils.isSameType('array', utils.typeName(formalizedSchema.type))) {
|
|
266
|
-
|
|
273
|
+
else if (utils.isSameType('array', utils.typeName(formalizedSchema.type))) {
|
|
274
|
+
formalizedSchema = formalizeArray(formalizedSchema, options);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
for (const validator of Object.keys(pluginValidators)) {
|
|
279
|
+
try {
|
|
280
|
+
const result = plugins.formalizeValidator(type, validator, formalizedSchema[validator]) || formalizedSchema[validator];
|
|
281
|
+
if (typeof result !== 'undefined') formalizedSchema[validator] = result;
|
|
282
|
+
} catch (error) {
|
|
283
|
+
throw new SchemaError(
|
|
284
|
+
schema,
|
|
285
|
+
error.message);
|
|
267
286
|
}
|
|
268
287
|
}
|
|
269
288
|
|
package/lib/index.js
CHANGED
package/lib/plugins.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const
|
|
4
|
+
merge = require('merge'),
|
|
5
|
+
utils = require('./utils');
|
|
6
|
+
|
|
7
|
+
exports = module.exports = {
|
|
8
|
+
_plugins: []
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
exports.use = function(plugin) {
|
|
12
|
+
this._plugins.push(merge(plugin(utils), {
|
|
13
|
+
_validatorsForType: function(type) {
|
|
14
|
+
return Object.fromEntries(
|
|
15
|
+
Object.entries(
|
|
16
|
+
this.validatorsForType(type))
|
|
17
|
+
.map(([key, value]) => {
|
|
18
|
+
if (typeof value !== 'string') value = utils.typeName(value);
|
|
19
|
+
return [key, !Array.isArray(value) ? [value] : value];
|
|
20
|
+
})
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
}));
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
exports.validatorsForType = function(type) {
|
|
27
|
+
return Object.fromEntries(Object.entries(merge(...this._plugins
|
|
28
|
+
.filter((plugin) => plugin.supportsType(type))
|
|
29
|
+
.map((plugin) => plugin._validatorsForType(type)))));
|
|
30
|
+
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
exports.formalizeValidator = function(type, identifier, config) {
|
|
34
|
+
return this._plugins
|
|
35
|
+
.filter((plugin) => plugin.supportsType(type))
|
|
36
|
+
.filter((plugin) => Object.keys(plugin._validatorsForType(type)).includes(identifier))
|
|
37
|
+
.reduce((config, plugin) => {
|
|
38
|
+
if (typeof plugin.formalizeValidator !== 'function') return config;
|
|
39
|
+
return plugin.formalizeValidator(type, identifier, config);
|
|
40
|
+
}, config);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
exports.validate = async function(type, identifier, config, data) {
|
|
44
|
+
|
|
45
|
+
const plugins = this._plugins
|
|
46
|
+
.filter((plugin) => plugin.supportsType(type));
|
|
47
|
+
|
|
48
|
+
for (const plugin of plugins) {
|
|
49
|
+
data = await plugin.validate(type, identifier, config, data);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return data;
|
|
53
|
+
|
|
54
|
+
};
|
package/lib/utils.js
CHANGED
|
@@ -13,5 +13,5 @@ exports.isSameType = function(obj1, obj2) {
|
|
|
13
13
|
if (typeof obj1 === 'undefined' || typeof obj2 === 'undefined') {
|
|
14
14
|
return typeof obj1 === typeof obj2;
|
|
15
15
|
}
|
|
16
|
-
return obj1.toLowerCase() == obj2.toLowerCase();
|
|
16
|
+
return exports.typeName(obj1).toLowerCase() == exports.typeName(obj2).toLowerCase();
|
|
17
17
|
};
|
package/lib/validate.js
CHANGED
|
@@ -5,7 +5,8 @@ const ValidationError = require('./errors/validation.js'),
|
|
|
5
5
|
unique = require('./unique.js'),
|
|
6
6
|
formalize = require('./formalize.js'),
|
|
7
7
|
utils = require('./utils.js'),
|
|
8
|
-
equals = require('./equals.js')
|
|
8
|
+
equals = require('./equals.js'),
|
|
9
|
+
plugins = require('./plugins.js');
|
|
9
10
|
|
|
10
11
|
const checkBoolValue = (name, schema, defaults) => {
|
|
11
12
|
if (schema[name] === undefined) return defaults[name] === true;
|
|
@@ -403,6 +404,22 @@ const validateAny = async (data, schema, options, keyPath, validatedData) => {
|
|
|
403
404
|
}
|
|
404
405
|
}
|
|
405
406
|
|
|
407
|
+
const pluginValidators = Object.keys(plugins.validatorsForType(schema.type))
|
|
408
|
+
.filter((validator) => typeof schema[validator] !== 'undefined');
|
|
409
|
+
|
|
410
|
+
for (const pluginValidator of pluginValidators) {
|
|
411
|
+
try {
|
|
412
|
+
data = await plugins.validate(schema.type, pluginValidator, schema[pluginValidator], data);
|
|
413
|
+
} catch (error) {
|
|
414
|
+
throw new ValidationError(
|
|
415
|
+
keyPath,
|
|
416
|
+
schema._nonFormalizedSchema,
|
|
417
|
+
pluginValidator,
|
|
418
|
+
(schema.errors || {})[pluginValidator] || customErrorMessage((options.errorMessages || {})[pluginValidator] || error.message)
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
406
423
|
return await validatePost(data, schema, options, keyPath, validatedData);
|
|
407
424
|
|
|
408
425
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "isvalid",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.2",
|
|
4
4
|
"description": "Async JSON validation library for node.js.",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"merge": "^2.1.1"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
+
"@trenskow/caseit": "^1.2.0",
|
|
21
22
|
"body-parser": "^1.20.0",
|
|
22
23
|
"chai": "^4.3.6",
|
|
23
24
|
"chai-as-promised": "^7.1.1",
|
package/test/formalize.js
CHANGED
|
@@ -179,5 +179,11 @@ describe('schema', function() {
|
|
|
179
179
|
it ('should throw error if array len is negative.', () => {
|
|
180
180
|
expect(f({ type: Array, len: '-2-' })).to.throw(SchemaError);
|
|
181
181
|
});
|
|
182
|
+
it ('should throw error if validator is not supported', () => {
|
|
183
|
+
expect(f({ type: String, nonExistingValidator: 'myValue' })).to.throw(SchemaError);
|
|
184
|
+
});
|
|
185
|
+
it ('should throw error if plugin validator fails formalizing.', () => {
|
|
186
|
+
expect(f({ type: String, casing: 'not-supported' })).to.throw(SchemaError);
|
|
187
|
+
});
|
|
182
188
|
});
|
|
183
189
|
});
|
package/test/index.js
CHANGED
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const chai = require('chai'),
|
|
4
|
-
chaiAsPromised = require('chai-as-promised')
|
|
4
|
+
chaiAsPromised = require('chai-as-promised'),
|
|
5
|
+
caseit = require('@trenskow/caseit'),
|
|
6
|
+
isvalid = require('../');
|
|
5
7
|
|
|
6
8
|
chai.use(chaiAsPromised);
|
|
7
9
|
|
|
10
|
+
isvalid.plugins.use(function (utils) {
|
|
11
|
+
return {
|
|
12
|
+
supportsType: (type) => utils.isSameType(type, String),
|
|
13
|
+
validatorsForType: () => { return { ensureCase: String }; },
|
|
14
|
+
formalizeValidator: (_, __, config) => {
|
|
15
|
+
if (!config) return;
|
|
16
|
+
if (!caseit.supported.includes(config)) throw new Error(`Only case types: ${caseit.supported.map((casing) => `\`${casing}\``).join(', ')} are supported.`);
|
|
17
|
+
},
|
|
18
|
+
validate: (_, __, config, data) => {
|
|
19
|
+
if (caseit(data, config) !== data) throw new Error(`Is not ${config} case.`);
|
|
20
|
+
return data;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
|
|
8
25
|
require('./ranges.js');
|
|
9
26
|
require('./equals.js');
|
|
10
27
|
require('./unique.js');
|
package/test/validate.js
CHANGED
|
@@ -756,4 +756,20 @@ describe('validate', function() {
|
|
|
756
756
|
describe('other validator', function() {
|
|
757
757
|
commonTests.all(Test, new Test(), 123);
|
|
758
758
|
});
|
|
759
|
+
describe('plugin validators', function() {
|
|
760
|
+
it ('should throw error if casing does not match.', function() {
|
|
761
|
+
return expect(isvalid('my-string', { type: String, ensureCase: 'camel' }))
|
|
762
|
+
.to.eventually.rejectedWith(ValidationError)
|
|
763
|
+
.and.to.have.property('message', 'Is not camel case.');
|
|
764
|
+
});
|
|
765
|
+
it ('should throw error with custom message if casing does not match.', function() {
|
|
766
|
+
return expect(isvalid('my-string', { type: String, ensureCase: ['camel', 'Something is not right!'] }))
|
|
767
|
+
.to.eventually.rejectedWith(ValidationError)
|
|
768
|
+
.and.to.have.property('message', 'Something is not right!');
|
|
769
|
+
});
|
|
770
|
+
it ('should come back with correct value.', function() {
|
|
771
|
+
return expect(isvalid('myString', { type: String, ensureCase: 'camel' }))
|
|
772
|
+
.to.eventually.equal('myString');
|
|
773
|
+
});
|
|
774
|
+
});
|
|
759
775
|
});
|