fets
Version:
TypeScript HTTP Framework focusing on e2e type-safety, easy setup, performance & great developer experience
147 lines (146 loc) • 6.54 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.useTypeBox = useTypeBox;
const typebox_1 = require("@sinclair/typebox");
const compiler_1 = require("@sinclair/typebox/compiler");
const value_1 = require("@sinclair/typebox/value");
const server_1 = require("@whatwg-node/server");
const utils_js_1 = require("./utils.js");
function createValidateFn(schema) {
try {
const validator = compiler_1.TypeCompiler.Compile(schema);
return function getErrors(data) {
return validator.Errors(data);
};
}
catch (e) {
return function getErrors(data) {
return value_1.Value.Errors(schema, data);
};
}
}
function sanitizeError({ schema, type, ...error }, name) {
return {
...error,
name,
};
}
function useTypeBox({ components = {}, } = {}) {
const validateFnBySchema = new WeakMap();
function getValidateFn(schema) {
let validateFn = validateFnBySchema.get(schema);
if (!validateFn) {
validateFn = createValidateFn({
...schema,
components,
});
validateFnBySchema.set(schema, validateFn);
}
return validateFn;
}
return {
onRouteHandle({ route: { schemas }, request }) {
if (schemas?.request?.headers && typebox_1.TypeGuard.IsSchema(schemas.request.headers)) {
const validateFn = getValidateFn(schemas.request.headers);
const headersObj = (0, utils_js_1.getHeadersObj)(request.headers);
const errors = [...validateFn(headersObj)].map(error => sanitizeError(error, 'headers'));
if (errors.length) {
throw new server_1.HTTPError(400, 'Bad Request', {
'x-error-type': 'validation',
}, {
errors,
});
}
}
if (schemas?.request?.params && typebox_1.TypeGuard.IsSchema(schemas.request.params)) {
const validateFn = getValidateFn(schemas.request.params);
const errors = [...validateFn(request.params)].map(error => sanitizeError(error, 'params'));
if (errors.length) {
throw new server_1.HTTPError(400, 'Bad Request', {
'x-error-type': 'validation',
}, {
errors,
});
}
}
if (schemas?.request?.query && typebox_1.TypeGuard.IsSchema(schemas.request.query)) {
const validateFn = getValidateFn(schemas.request.query);
const errors = [...validateFn(request.query)].map(error => sanitizeError(error, 'query'));
if (errors.length) {
throw new server_1.HTTPError(400, 'Bad Request', {
'x-error-type': 'validation',
}, {
errors,
});
}
}
if (schemas?.request?.json && typebox_1.TypeGuard.IsSchema(schemas.request.json)) {
const validateFn = getValidateFn(schemas.request.json);
const origReqJsonMethod = request.json.bind(request);
Object.defineProperty(request, 'json', {
value: () => origReqJsonMethod().then(jsonObj => {
const errors = [...validateFn(jsonObj)].map(({ schema, type, ...error }) => ({
name: 'json',
...error,
}));
if (errors.length) {
throw new server_1.HTTPError(400, 'Bad Request', {
'x-error-type': 'validation',
}, {
errors,
});
}
return jsonObj;
}),
configurable: true,
});
}
if (schemas?.request?.formData && typebox_1.TypeGuard.IsSchema(schemas.request.formData)) {
const validateFn = getValidateFn(schemas.request.formData);
const origMethod = request.formData.bind(request);
Object.defineProperty(request, 'formData', {
configurable: true,
value: () => origMethod().then(formData => {
const formDataObj = {};
const jobs = [];
formData.forEach((value, key) => {
if (value != null) {
if (typeof value === 'string') {
formDataObj[key] = value;
}
else {
jobs.push(value.arrayBuffer().then(buffer => {
const typedArray = isByteArray(buffer) ? buffer : new Uint8Array(buffer);
const binaryStrParts = [];
typedArray.forEach((byte, index) => {
binaryStrParts[index] = String.fromCharCode(byte);
});
formDataObj[key] = binaryStrParts.join('');
}));
}
}
});
function handleErrors() {
const errors = [...validateFn(formDataObj)].map(error => sanitizeError(error, 'formData'));
if (errors.length) {
throw new server_1.HTTPError(400, 'Bad Request', {
'x-error-type': 'validation',
}, {
errors,
});
}
return formDataObj;
}
if (jobs.length) {
return Promise.all(jobs).then(handleErrors);
}
return handleErrors();
}),
});
}
},
};
}
function isByteArray(value) {
return value?.forEach != null;
}