Skip to content

Commit b8ad103

Browse files
committed
add support for binary and text bodies, added new library class validator
1 parent 385b41a commit b8ad103

File tree

4 files changed

+165
-1
lines changed

4 files changed

+165
-1
lines changed

lib/http/express.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export const init = async () => {
3636
app.use(multer({ storage: multer.memoryStorage() }).any());
3737
app.use(Express.json({ limit: '16mb' }));
3838
app.use(Express.urlencoded({ extended: true }));
39+
app.use(Express.text());
40+
app.use(Express.raw());
3941

4042
let static_base_url = config.public?.base_url || '/public';
4143
app.use(static_base_url, Express.static(`${config.paths.root}/public`));

lib/util/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import hash from '#exa/util/hash.js';
22
import json from '#exa/util/json.js';
33
import string from '#exa/util/string.js';
4+
import validator from '#exa/util/validator.js';
45

56
export {
67
hash,
78
json,
89
string,
10+
validator,
911
};

lib/util/validator.js

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import { DateTime } from 'luxon';
2+
3+
export default new class {
4+
5+
field_transforms = {}
6+
7+
validate(values, ruleset) {
8+
let errors = [];
9+
10+
for (const [field, rules] of Object.entries(ruleset)) {
11+
if (!rules) {
12+
continue;
13+
}
14+
15+
for (const rule of rules.split('|')) {
16+
const formatted_field = this.capitalize(field);
17+
const params = rule.split(':');
18+
const type = params[0];
19+
20+
let required = ['req', 'req_or'].includes(type);
21+
22+
if (!required && values[field] == null) {
23+
continue;
24+
}
25+
26+
switch (type) {
27+
case 'req':
28+
if (!values[field] && values[field] !== false) {
29+
errors.push({
30+
field,
31+
error: `${formatted_field} is required but not supplied`
32+
});
33+
}
34+
break;
35+
case 'req_or':
36+
if (values[params[1]]) {
37+
continue;
38+
}
39+
if (!values[field]) {
40+
errors.push({
41+
field,
42+
error: `${formatted_field} (or ${this.capitalize(params[1])}) is required but not supplied`
43+
});
44+
}
45+
break;
46+
case 'min': {
47+
let min_length = +params[1];
48+
49+
if (values[field]?.length < min_length) {
50+
errors.push({
51+
field,
52+
error: `${formatted_field} must be at least ${min_length} characters`
53+
});
54+
}
55+
break;
56+
}
57+
case 'max': {
58+
let max_length = +params[1];
59+
60+
if (values[field]?.length > max_length) {
61+
errors.push({
62+
field,
63+
error: `${formatted_field} must be less than ${max_length} characters`
64+
});
65+
}
66+
break;
67+
}
68+
case 'email':
69+
if (typeof values[field] !== 'string' || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(values[field])) {
70+
errors.push({
71+
field,
72+
error: `${formatted_field} must be a valid email address`
73+
});
74+
}
75+
break;
76+
case 'str':
77+
if (typeof values[field] !== 'string') {
78+
errors.push({
79+
field,
80+
error: `${formatted_field} must be a string`
81+
});
82+
}
83+
break;
84+
case 'bool':
85+
if (typeof values[field] !== 'boolean') {
86+
errors.push({
87+
field,
88+
error: `${formatted_field} must be true/false`
89+
});
90+
}
91+
break;
92+
case 'int': {
93+
let nan = isNaN(parseInt(values[field]));
94+
95+
if (nan) {
96+
errors.push({
97+
field,
98+
error: `${formatted_field} must be an integer`
99+
});
100+
}
101+
break;
102+
}
103+
case 'float': {
104+
let nan = isNaN(parseFloat(values[field]));
105+
106+
if (nan) {
107+
errors.push({
108+
field,
109+
error: `${formatted_field} must be a decimal`
110+
});
111+
}
112+
break;
113+
}
114+
case 'array':
115+
if (!Array.isArray(values[field])) {
116+
errors.push({
117+
field,
118+
error: `${formatted_field} must be an array`
119+
});
120+
}
121+
break;
122+
case 'isodate': {
123+
let date = DateTime.fromISO(values[field]);
124+
125+
if (!date.isValid) {
126+
errors.push({
127+
field,
128+
error: `${formatted_field} must be a valid ISO date`
129+
});
130+
}
131+
break;
132+
}
133+
}
134+
}
135+
}
136+
137+
return errors;
138+
}
139+
140+
format(errors) {
141+
return { errors };
142+
}
143+
144+
to_string(errors) {
145+
return errors.join('\n');
146+
}
147+
148+
capitalize(text) {
149+
if (this.field_transforms[text]) {
150+
return this.field_transforms[text];
151+
}
152+
153+
return text.toLowerCase()
154+
.replace(/_/gi, ' ')
155+
.split(' ')
156+
.map(s => s.charAt(0).toUpperCase() + s.substring(1))
157+
.join(' ');
158+
}
159+
160+
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@exajs/core",
33
"type": "module",
44
"author": "Brian Seymour <@realtux>",
5-
"version": "0.0.27",
5+
"version": "0.0.28",
66
"description": "modern opinionated node.js framework",
77
"license": "MIT",
88
"homepage": "https://github.com/realtux/exa",

0 commit comments

Comments
 (0)