Files
vendoo/app/kernel/input-schema.mjs
T

44 lines
2.2 KiB
JavaScript

import { PlatformError } from './errors.mjs';
function fail(message, field) {
throw new PlatformError(message, { code: 'INPUT_VALIDATION_FAILED', status: 400, expose: true, details: { field } });
}
export function objectSchema(fields, { allowUnknown = false } = {}) {
const contract = Object.freeze({ ...fields });
return input => {
const source = input && typeof input === 'object' && !Array.isArray(input) ? input : {};
const output = {};
if (!allowUnknown) for (const key of Object.keys(source)) if (!contract[key]) fail(`Unbekanntes Eingabefeld: ${key}`, key);
for (const [name, rule] of Object.entries(contract)) {
let value = source[name];
if (value === undefined || value === null) {
if (rule.required) fail(`${name} ist erforderlich.`, name);
continue;
}
if (rule.type === 'string') {
value = String(value);
if (rule.trim !== false) value = value.trim();
if (rule.minLength && value.length < rule.minLength) fail(`${name} ist zu kurz.`, name);
if (rule.maxLength && value.length > rule.maxLength) fail(`${name} ist zu lang.`, name);
if (rule.pattern && !rule.pattern.test(value)) fail(`${name} besitzt ein ungültiges Format.`, name);
} else if (rule.type === 'boolean') {
if (typeof value !== 'boolean') fail(`${name} muss true oder false sein.`, name);
} else if (rule.type === 'number' || rule.type === 'integer') {
value = Number(value);
if (!Number.isFinite(value)) fail(`${name} muss eine Zahl sein.`, name);
if (rule.type === 'integer' && !Number.isInteger(value)) fail(`${name} muss eine ganze Zahl sein.`, name);
if (rule.min !== undefined && value < rule.min) fail(`${name} ist zu klein.`, name);
if (rule.max !== undefined && value > rule.max) fail(`${name} ist zu groß.`, name);
}
if (rule.enum && !rule.enum.includes(value)) fail(`${name} enthält einen nicht erlaubten Wert.`, name);
if (typeof rule.validate === 'function') {
const result = rule.validate(value);
if (result !== true) fail(typeof result === 'string' ? result : `${name} ist ungültig.`, name);
}
output[name] = value;
}
return Object.freeze(output);
};
}