@allan1361/iota-big3-sdk-middleware
Version:
🏆 A+ Grade Certified Enterprise Middleware Framework - Phase 3 Certified (90/100) with advanced resilience patterns, comprehensive type safety, and production-ready observability
294 lines • 10.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ValidationMiddleware = void 0;
exports.createValidationMiddleware = createValidationMiddleware;
const events_1 = require("events");
class ValidationMiddleware extends events_1.EventEmitter {
constructor(config = {}) {
super();
this.validators = new Map();
this.config = {
abortEarly: config.abortEarly ?? true,
stripUnknown: config.stripUnknown ?? true,
coerceTypes: config.coerceTypes ?? true,
customErrorHandler: config.customErrorHandler
};
}
registerValidator(name, validator) {
this.validators.set(name, validator);
}
validate(schema) {
return async (req, res, next) => {
const errors = [];
if (schema.body) {
const bodyErrors = await this.validateDataAsync(req.body, schema.body, 'body');
errors.push(...bodyErrors);
}
if (schema.params) {
const paramsErrors = await this.validateDataAsync(req.params, schema.params, 'params');
errors.push(...paramsErrors);
}
if (schema.query) {
const queryErrors = await this.validateDataAsync(req.query, schema.query, 'query');
errors.push(...queryErrors);
}
if (schema.headers) {
const headersErrors = await this.validateDataAsync(req.headers, schema.headers, 'headers');
errors.push(...headersErrors);
}
if (errors.length > 0) {
this.emit('validation:failed', {
path: req.path,
method: req.method,
errors
});
if (this.config.customErrorHandler) {
this.config.customErrorHandler(errors, req, res);
}
else {
res.status(400).json({
error: 'Validation failed',
errors: errors.map(e => ({
field: e.field,
message: e.message
}))
});
}
return;
}
this.emit('validation:passed', {
path: req.path,
method: req.method
});
next();
};
}
async validateDataAsync(data, schema, prefix) {
const errors = [];
if (typeof schema === 'string' && this.validators.has(schema)) {
const validator = this.validators.get(schema);
const validationErrors = validator(data);
if (validationErrors) {
return validationErrors.map(e => ({
...e,
field: `${prefix}.${e.field}`
}));
}
return [];
}
if (typeof schema === 'function') {
try {
const result = await schema(data);
if (result !== true) {
errors.push({
field: prefix,
message: typeof result === 'string' ? result : 'Validation failed'
});
}
}
catch (error) {
errors.push({
field: prefix,
message: error.message || 'Validation error'
});
}
return errors;
}
if (typeof schema === 'object' && schema !== null) {
if (schema.required && Array.isArray(schema.required)) {
for (const field of schema.required) {
if (!(field in data)) {
errors.push({
field: `${prefix}.${field}`,
message: `${field} is required`,
type: 'required'
});
if (this.config.abortEarly)
return errors;
}
}
}
if (schema.properties) {
for (const [field, fieldSchema] of Object.entries(schema.properties)) {
if (field in data) {
const fieldErrors = await this.validateFieldAsync(data[field], fieldSchema, `${prefix}.${field}`);
errors.push(...fieldErrors);
if (this.config.abortEarly && errors.length > 0)
return errors;
}
}
}
if (this.config.stripUnknown && schema.properties) {
for (const field of Object.keys(data)) {
if (!(field in schema.properties)) {
delete data[field];
}
}
}
}
return errors;
}
async validateFieldAsync(value, schema, field) {
const errors = [];
if (schema.type) {
const actualType = Array.isArray(value) ? 'array' : typeof value;
const expectedTypes = Array.isArray(schema.type) ? schema.type : [schema.type];
if (!expectedTypes.includes(actualType)) {
if (this.config.coerceTypes) {
const coerced = this.coerceType(value, schema.type);
if (coerced !== undefined) {
value = coerced;
}
else {
errors.push({
field,
message: `Expected ${schema.type} but got ${actualType}`,
value,
type: 'type'
});
}
}
else {
errors.push({
field,
message: `Expected ${schema.type} but got ${actualType}`,
value,
type: 'type'
});
}
}
}
if (typeof value === 'string') {
if (schema.minLength && value.length < schema.minLength) {
errors.push({
field,
message: `String must be at least ${schema.minLength} characters`,
value,
type: 'minLength'
});
}
if (schema.maxLength && value.length > schema.maxLength) {
errors.push({
field,
message: `String must be at most ${schema.maxLength} characters`,
value,
type: 'maxLength'
});
}
if (schema.pattern && !new RegExp(schema.pattern).test(value)) {
errors.push({
field,
message: `String does not match pattern ${schema.pattern}`,
value,
type: 'pattern'
});
}
}
if (typeof value === 'number') {
if (schema.minimum !== undefined && value < schema.minimum) {
errors.push({
field,
message: `Number must be at least ${schema.minimum}`,
value,
type: 'minimum'
});
}
if (schema.maximum !== undefined && value > schema.maximum) {
errors.push({
field,
message: `Number must be at most ${schema.maximum}`,
value,
type: 'maximum'
});
}
}
if (Array.isArray(value)) {
if (schema.minItems && value.length < schema.minItems) {
errors.push({
field,
message: `Array must have at least ${schema.minItems} items`,
value,
type: 'minItems'
});
}
if (schema.maxItems && value.length > schema.maxItems) {
errors.push({
field,
message: `Array must have at most ${schema.maxItems} items`,
value,
type: 'maxItems'
});
}
}
if (schema.enum && !schema.enum.includes(value)) {
errors.push({
field,
message: `Value must be one of: ${schema.enum.join(', ')}`,
value,
type: 'enum'
});
}
if (schema.validate) {
try {
const result = await schema.validate(value);
if (result !== true) {
errors.push({
field,
message: typeof result === 'string' ? result : 'Custom validation failed',
value,
type: 'custom'
});
}
}
catch (error) {
errors.push({
field,
message: error.message || 'Validation error',
value,
type: 'custom'
});
}
}
return errors;
}
coerceType(value, targetType) {
if (targetType === 'string') {
return String(value);
}
if (targetType === 'number') {
const num = Number(value);
return isNaN(num) ? undefined : num;
}
if (targetType === 'boolean') {
if (value === 'true' || value === '1' || value === 1)
return true;
if (value === 'false' || value === '0' || value === 0)
return false;
return undefined;
}
if (targetType === 'array' && typeof value === 'string') {
try {
const parsed = JSON.parse(value);
return Array.isArray(parsed) ? parsed : undefined;
}
catch {
return undefined;
}
}
if (targetType === 'object' && typeof value === 'string') {
try {
const parsed = JSON.parse(value);
return typeof parsed === 'object' ? parsed : undefined;
}
catch {
return undefined;
}
}
return undefined;
}
}
exports.ValidationMiddleware = ValidationMiddleware;
function createValidationMiddleware(config) {
return new ValidationMiddleware(config);
}
//# sourceMappingURL=validation-middleware.js.map