-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
154 lines (118 loc) · 3.53 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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
const is = {
object(value) {
return value !== null && typeof value === 'object';
},
string(value) {
return typeof value === 'string';
}
};
const enforcement = {
validate(Class, value) {
if (value === undefined || value === null) {
return false;
}
return Object.getPrototypeOf(value) === Class.prototype;
},
normalise(Class, value) {
if ([ String, Number, Boolean, Symbol, BigInt ].includes(Class)) {
if (value === undefined) {
return Class();
}
return Class(value);
}
if (value === undefined) {
return new Class();
}
return new Class(value);
}
};
class TypeEnforcement {
constructor(rules) {
if (is.object(rules) === false) {
throw new TypeError(`Unexpected argument or 'undefined' or 'null'`);
}
Object.freeze(rules);
const schema = {};
Object.keys(rules).forEach((i) => {
const sample = rules[i];
if (is.object(sample) === false) {
throw new TypeError(
`Unexpected sample '${i}' or 'undefined' or 'null'`
);
}
const names = Object.keys(sample);
for (const i of names) {
if (sample[i] === undefined || sample[i] === null) {
throw new TypeError(
`The prototype object '${i}' must have a constructor function`
);
}
if (typeof sample[i].prototype.constructor !== 'function') {
throw new TypeError(`Order '${i}' not found`);
}
}
schema[i] = names;
});
this.rules = rules;
this.schema = schema;
}
validate(order, values, {skip = false} = {}) {
if (is.string(order) === false || is.object(values) === false) {
return new TypeError(`Unexpected argument or 'undefined' or 'null'`);
}
if (this.schema.hasOwnProperty(order) === false) {
return new TypeError(`Order '${order}' not found`);
}
const fields = Object.keys(values);
const rule = this.rules[order];
const schema = this.schema[order];
if (skip === false) {
const missing = schema.filter((i) => {
return fields.indexOf(i) === -1;
});
if (missing.length > 0) {
return new TypeError(`Missing fields '${missing}' in order '${order}'`);
}
}
const spare = fields.filter((i) => {
return schema.indexOf(i) === -1;
});
if (spare.length > 0) {
return new TypeError(`Redundant fields '${spare}' in order '${order}'`);
}
for (const i of fields) {
const Class = rule[i];
const value = values[i];
if (enforcement.validate(Class, value) === false) {
return new TypeError(
`Invalid value '${i}' in order '${order}'. Expected ${Class.name}`
);
}
}
return null;
}
normalise(order, values) {
if (is.string(order) === false || is.object(values) === false) {
throw new TypeError(`Unexpected argument or 'undefined' or 'null'`);
}
if (this.schema.hasOwnProperty(order) === false) {
throw new TypeError(`Order '${order}' not found`);
}
const fields = Object.keys(values);
const rule = this.rules[order];
const schema = this.schema[order];
const spare = fields.filter((i) => {
return schema.indexOf(i) === -1;
});
if (spare.length > 0) {
throw new TypeError(`Redundant fields '${spare}' in order '${order}'`);
}
for (const i of fields) {
const Class = rule[i];
const value = values[i];
values[i] = enforcement.normalise(Class, value);
}
return values;
}
}
module.exports = TypeEnforcement;