-
Notifications
You must be signed in to change notification settings - Fork 5
/
index.js
115 lines (95 loc) · 3.26 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
'use strict'
const { isNil, isFunction, isBoolean, merge, reduce, castArray } = require('lodash')
const { format } = require('util')
const chaste = require('chaste')
const is = require('kind-of')
const DEFAULT = {
BLUEPRINT: {
casting: true,
transform: []
}
}
function createSchemaRule (rule, globalRules) {
const schema = isFunction(rule) ? { type: rule } : rule
const fields = merge({}, globalRules, schema)
return Object.assign({ ...DEFAULT.BLUEPRINT, ...fields })
}
function addRule (globalRules, schema, blueprint, name) {
schema[name] = createSchemaRule(blueprint, globalRules)
}
function typeError (message, key, value) {
const error = new TypeError(message)
error.key = key
error.value = value
return error
}
function throwTypeError (key, value, type, message) {
if (!message || isBoolean(message)) {
message = `Expected \`${type}\` for \`${key}\`, got \`${value}\``
}
throw typeError(message, key, value)
}
function throwValidationError (key, value, description) {
let message
if (description) {
message = isFunction(description) ? description(value) : format(description, value)
} else message = `Fail '${value}' validation for '${key}'.`
throw typeError(message, key, value)
}
function getValidator (rule) {
const validate = rule.validate
return isFunction(validate) ? validate : validate.validator
}
function Osom (schemaBlueprint, globalRules) {
if (!(this instanceof Osom)) return new Osom(schemaBlueprint, globalRules)
globalRules = globalRules || {}
const schemaTypes = {}
const schema = reduce(
schemaBlueprint,
function (schema, blueprint, name) {
addRule(globalRules, schema, blueprint, name)
const type = schema[name].type
schemaTypes[name] = type.name.toLowerCase()
schema[name].type = chaste(type)
return schema
},
{}
)
function osom (obj) {
obj = obj || {}
return reduce(
schema,
function applyRule (objSchema, rule, name) {
const value = obj[name]
const hasValue = !isNil(value)
const validate = rule.validate
const isRequired = rule.required || !isNil(validate)
const expectedValue = schemaTypes[name]
const isMissing = !hasValue && !validate && isRequired
if (isMissing) throwTypeError(name, value, expectedValue, isRequired)
const isCasting = rule.casting
const isCastingDisabled = hasValue && !isCasting
const isInvalidType = isCastingDisabled && is(value) !== expectedValue
if (isInvalidType) {
throwTypeError(name, value, expectedValue, isRequired)
}
let TypedValue
const defaultValue = isFunction(rule.default) ? rule.default : () => rule.default
if (hasValue) TypedValue = isCasting ? rule.type(value) : value
else if (defaultValue) TypedValue = defaultValue()
TypedValue = reduce(castArray(rule.transform), (acc, fn) => fn(acc, obj), TypedValue)
if (isRequired && validate) {
const validator = getValidator(rule)
if (!validator(TypedValue)) {
throwValidationError(name, value, validate.message)
}
}
if (!isNil(TypedValue)) objSchema[name] = TypedValue
return objSchema
},
{}
)
}
return osom
}
module.exports = Osom