-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
128 lines (118 loc) · 2.93 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/**
* Validate object against schema.
*
* @param {Object} data
* @param {Object} schema
* @return {Promise}
* @api public
*/
module.exports = function (data, schema = {}) {
return new Promise(resolve => {
resolve(validator(data, schema))
})
}
/**
* Validate object against schema.
*
* @param {Object} data
* @param {Object} schema
* @return {Object}
* @api public
*/
function validator (data, schema) {
const result = Object.assign({}, data)
Object.keys(schema).map(key => {
let value = schema[key]
const type = typeof value
if (type !== 'object') {
value = {
transform: value
}
}
validate(value, result, key)
})
return result
}
/**
* Validate data key/value against passed schema.
*
* Required field is always check first in order to
* avoid unnecessary validations.
*
* @param {Object} schema
* @param {Object} data
* @param {String} key
* @api private
*/
function validate (schema, data, key) {
let value = data[key]
const bool = value == null || value === ''
const { required, type, validate, transform, elements } = schema
const def = schema['default']
// trigger error if undefined but required
if (required && bool) throw new Error(`field ${key} is missing`)
// coerce to type
if (type && !bool) {
let before = coerce(type, key, value)
data[key] = elements
? (type === 'object' ? validator(before, elements) : before.map(item => validator(item, elements)))
: before
}
// validate value
if (validate) {
const cb = check(validate, key)
if (type === 'array') value.map(cb)
else cb(value)
}
// apply default value
if (def && bool) data[key] = def
// apply transform
if (transform && def == null) {
data[key] = typeof transform === 'function'
? (type === 'array' ? value.map(transform) : transform(value))
: transform
}
}
/**
* Validate a value given a specific function.
*
* @param {Function} cb
* @param {String} key
* @return {Function}
* @api private
*/
function check (cb, key) {
return value => {
if (!cb(value)) throw new Error(`field ${key} can not be validated`)
}
}
/**
* Coerce value.
*
* Trigger
*
* @param {String} type
* @param {String} field name
* @param {Any} value
* @api private
*/
function coerce (type, field, value) {
let result
if (type === 'string') {
result = String(value).trim()
} else if (type === 'number') {
result = Number(value)
if (isNaN(result)) throw new Error(`field ${field} can not be converted to a number`)
} else if (type === 'array') {
result = [].concat(value)
} else if (type === 'date') {
result = Date.parse(value)
if (isNaN(result)) throw new Error(`field ${field} can not be converted into a date`)
} else if (type === 'boolean') {
result = Boolean(value)
} else if (type === 'object') {
if (typeof value !== 'object') throw new Error(`field ${field} is not an object`)
result = value
}
return result
}