kequapp
Version:
DEPRECATED: renamed to @kequtech/arbor
131 lines (130 loc) • 3.74 kB
JavaScript
import { Ex } from "../ex.js";
export function normalizeBody(body, options) {
const result = { ...body };
const errors = {};
if (options.skipNormalize === true)
return result;
const { required = [], arrays = [], numbers = [], booleans = [], trim = false, validate, throws = true, } = options;
// trim strings
if (trim) {
for (const key of Object.keys(result)) {
if (Array.isArray(result[key])) {
result[key] = result[key].map(trimValue).filter((v) => v !== undefined);
}
else {
const value = trimValue(result[key]);
if (value === undefined)
delete result[key];
else
result[key] = value;
}
}
}
// arrays
for (const key of arrays) {
if (!Array.isArray(result[key])) {
result[key] = result[key] === undefined ? [] : [result[key]];
}
}
// not arrays!
for (const key of Object.keys(result)) {
if (arrays.includes(key))
continue;
if (Array.isArray(result[key])) {
result[key] = result[key][0];
}
}
// required
for (const key of required) {
if (Array.isArray(result[key])) {
const values = result[key].filter((value) => !isEmpty(value));
result[key] = values;
if (values.length > 0)
continue;
}
else {
if (!isEmpty(result[key]))
continue;
}
errors[key] = 'is required';
}
// numbers
for (const key of numbers) {
if (!(key in result) || key in errors)
continue;
if (Array.isArray(result[key])) {
const values = result[key].map(toNumber);
result[key] = values;
if (!values.some((value) => Number.isNaN(value)))
continue;
}
else {
const value = toNumber(result[key]);
result[key] = value;
if (!Number.isNaN(value))
continue;
}
errors[key] = 'must be a number';
}
// booleans
for (const key of booleans) {
if (!(key in result) || key in errors)
continue;
if (Array.isArray(result[key])) {
const values = result[key].map(toBoolean);
result[key] = values;
}
else {
result[key] = toBoolean(result[key]);
}
}
// validate
if (validate) {
for (const [key, validator] of Object.entries(validate)) {
if (!(key in result) || key in errors)
continue;
const error = validator?.(result[key]);
if (error)
errors[key] = error;
}
}
const key = Object.keys(errors)[0];
if (throws) {
// old school
if (!key)
return result;
throw Ex.UnprocessableEntity(`Value ${key} ${errors[key]}`, {
errors,
body,
});
}
else {
if (!key)
return { ...result, ok: true };
return { errors, ok: false };
}
}
function trimValue(value) {
if (typeof value === 'string') {
const trimmed = value.trim();
return trimmed === '' ? undefined : trimmed;
}
return value;
}
function isEmpty(value) {
return value === null || value === undefined;
}
function toNumber(value) {
return parseFloat(value);
}
function toBoolean(value) {
if (typeof value === 'string' && value.toLowerCase() === 'false') {
return false;
}
else if (value === '0') {
return false;
}
else {
return Boolean(value);
}
}