UNPKG

@gp_jcisneros/errors

Version:

Error handling utilities for GreenPay microservices and validation middleware

974 lines (753 loc) 27.7 kB
# @gp_jcisneros/errors Biblioteca de manejo de errores estandarizada para microservicios GreenPay con campos requeridos para transformación empresarial y **middleware de validación Zod**. ## 📦 Instalación **Requisitos:** Node.js 22.x o superior ```bash npm install @gp_jcisneros/errors ``` ## 🚀 Uso Rápido ```javascript const { CustomError, HttpError, ValidationError, DatabaseError, AWSError, IntegrationError, ZodError, ResponseHandler, // Zod Error Handlers convertZodErrorToValidationError, createDynamicSchemaValidation, createBaseSchemaValidation, } = require('@gp_jcisneros/errors'); // Crear un error personalizado con el nuevo constructor const error = new CustomError('Algo salió mal', { errorCode: 'GP_VALIDATION_ERROR', description: 'Error de validación en los datos de entrada', integration: 'transaction-service', statusCode: 400 }); // Usar métodos estáticos para crear errores const staticError = CustomError.create( 'Error de ejemplo', 'GP_STATIC_ERROR', 'Descripción del error estático', 'static-service', 400 ); const minimalError = CustomError.createMinimal( 'Error mínimo', 'GP_MINIMAL_ERROR', 'minimal-service' ); // Verificar campos requeridos console.log('Tiene campos requeridos:', error.hasRequiredFields()); console.log('Campos requeridos:', error.getRequiredFields()); // Convertir a JSON const jsonFormat = error.toJSON(); ``` ## 🏗️ Arquitectura de Errores Este NPM está diseñado para estandarizar el manejo de errores en todos los módulos de GreenPay con **campos requeridos** para facilitar la transformación empresarial. ### **Campos Requeridos Estandarizados:** Todos los errores incluyen tres campos requeridos para estandarización: - **`errorCode`**: Código alfanumérico que identifica el error - **`description`**: Descripción detallada del error - **`integration`**: Nombre del módulo de integración donde se está usando el error ### **Nuevo Constructor Flexible:** ```javascript // Nuevo constructor con objeto de opciones const error = new CustomError('Error de validación', { errorCode: 'GP_VALIDATION_ERROR', description: 'Error de validación en los datos de entrada', integration: 'transaction-service', statusCode: 400, // Campos adicionales personalizados customField: 'valor personalizado', metadata: { key: 'value' } }); // Métodos estáticos para crear errores const staticError = CustomError.create( 'Error de ejemplo', 'GP_STATIC_ERROR', 'Descripción del error estático', 'static-service', 400 ); const minimalError = CustomError.createMinimal( 'Error mínimo', 'GP_MINIMAL_ERROR', 'minimal-service' ); ``` ## 📚 Clases de Error ### CustomError Clase base con **campos requeridos estandarizados** y constructor flexible. #### Constructor Nuevo ```javascript const { CustomError } = require('@gp_jcisneros/errors'); // Constructor básico const error = new CustomError('Mensaje de error'); // Constructor con opciones const errorWithOptions = new CustomError('Error de validación', { errorCode: 'GP_VALIDATION_ERROR', description: 'Error de validación en los datos de entrada', integration: 'transaction-service', statusCode: 400 }); // Métodos estáticos const staticError = CustomError.create( 'Error de ejemplo', 'GP_STATIC_ERROR', 'Descripción del error estático', 'static-service', 400 ); const minimalError = CustomError.createMinimal( 'Error mínimo', 'GP_MINIMAL_ERROR', 'minimal-service' ); // Usando métodos setter const errorWithSetters = new CustomError('Error de base de datos'); errorWithSetters .setErrorCode('GP_DB_CONNECTION_ERROR') .setDescription('Error de conexión a la base de datos') .setIntegration('database-service'); // Usando setRequiredFields para establecer todos a la vez const errorWithSetRequired = new CustomError('Error de autenticación'); errorWithSetRequired.setRequiredFields( 'GP_AUTH_ERROR', 'Error de autenticación del usuario', 'auth-service' ); // Validar campos requeridos console.log('Tiene campos requeridos:', errorWithOptions.hasRequiredFields()); console.log('Campos requeridos:', errorWithOptions.getRequiredFields()); // Convertir a JSON const jsonFormat = errorWithOptions.toJSON(); // { // name: 'CustomError', // message: 'Error de validación', // statusCode: 400, // timestamp: '2024-12-19T10:30:00.000Z', // stack: '...', // errorCode: 'GP_VALIDATION_ERROR', // description: 'Error de validación en los datos de entrada', // integration: 'transaction-service' // } ``` ### HttpError Errores HTTP predefinidos con campos requeridos automáticos. ```javascript const { HttpError } = require('@gp_jcisneros/errors'); // Errores comunes con campos requeridos automáticos const badRequest = HttpError.badRequest('Datos inválidos'); // errorCode: 'HTTP_400', integration: 'http-service', statusCode: 400 const unauthorized = HttpError.unauthorized('Token inválido'); // errorCode: 'HTTP_401', integration: 'http-service', statusCode: 401 const notFound = HttpError.notFound('Usuario no encontrado'); // errorCode: 'HTTP_404', integration: 'http-service', statusCode: 404 const conflict = HttpError.conflict('Usuario ya existe'); // errorCode: 'HTTP_409', integration: 'http-service', statusCode: 409 const internalError = HttpError.internalServerError('Error interno'); // errorCode: 'HTTP_500', integration: 'http-service', statusCode: 500 // Constructor personalizado const customHttpError = new HttpError('Error personalizado', 418); // errorCode: 'HTTP_418', integration: 'http-service', statusCode: 418 // Verificar campos requeridos console.log('Tiene campos requeridos:', badRequest.hasRequiredFields()); ``` ### ValidationError Errores específicos para validación de datos con campos requeridos automáticos. ```javascript const { ValidationError } = require('@gp_jcisneros/errors'); // Errores de validación con campos requeridos automáticos const requiredError = ValidationError.required('email'); // errorCode: 'VALIDATION_EMAIL', integration: 'validation-service', statusCode: 400 const emailError = ValidationError.invalidEmail('email', 'invalid@'); // errorCode: 'VALIDATION_EMAIL', integration: 'validation-service', statusCode: 400 const lengthError = ValidationError.minLength('password', 8, '123'); // errorCode: 'VALIDATION_PASSWORD', integration: 'validation-service', statusCode: 400 const formatError = ValidationError.invalidFormat('phone', 'XXX-XXX-XXXX', '123'); // errorCode: 'VALIDATION_PHONE', integration: 'validation-service', statusCode: 400 // Error personalizado para un campo const customError = ValidationError.forField('age', 'Debe ser mayor de 18', 15); // errorCode: 'VALIDATION_AGE', integration: 'validation-service', statusCode: 400 // Constructor directo const directError = new ValidationError('Error personalizado', 'field', 'value'); // errorCode: 'VALIDATION_FIELD', integration: 'validation-service', statusCode: 400 // Obtener detalles de validación const details = requiredError.getValidationDetails(); ``` ### DatabaseError Errores específicos para operaciones de base de datos con campos requeridos automáticos. ```javascript const { DatabaseError } = require('@gp_jcisneros/errors'); // Errores de base de datos con campos requeridos automáticos const connectionError = DatabaseError.connection('Conexión fallida'); // errorCode: 'DB_CONNECT', integration: 'database-service', statusCode: 500 const notFoundError = DatabaseError.notFound('users'); // errorCode: 'DB_GET', integration: 'database-service', statusCode: 500 const duplicateError = DatabaseError.duplicateKey('users', 'email'); // errorCode: 'DB_INSERT', integration: 'database-service', statusCode: 500 const insertError = DatabaseError.insert('Error al insertar', 'users'); // errorCode: 'DB_INSERT', integration: 'database-service', statusCode: 500 const updateError = DatabaseError.update('Error al actualizar', 'users'); // errorCode: 'DB_UPDATE', integration: 'database-service', statusCode: 500 const deleteError = DatabaseError.delete('Error al eliminar', 'users'); // errorCode: 'DB_DELETE', integration: 'database-service', statusCode: 500 const constraintError = DatabaseError.constraintViolation('Constraint failed', 'users'); // errorCode: 'DB_CONSTRAINT', integration: 'database-service', statusCode: 500 // Constructor directo const directDbError = new DatabaseError('Error personalizado', 'QUERY', 'table'); // errorCode: 'DB_QUERY', integration: 'database-service', statusCode: 500 // Obtener detalles de base de datos const details = connectionError.getDatabaseDetails(); ``` ### AWSError Errores específicos para servicios AWS con campos requeridos automáticos. ```javascript const { AWSError } = require('@gp_jcisneros/errors'); // Errores de AWS con campos requeridos automáticos const dynamoError = AWSError.dynamoDB('ConditionalCheckFailedException', 'Condition check failed'); // errorCode: 'AWS_DYNAMODB', integration: 'aws-service', statusCode: 500 const s3Error = AWSError.s3('NoSuchKey', 'Object not found'); // errorCode: 'AWS_S3', integration: 'aws-service', statusCode: 500 const lambdaError = AWSError.lambda('ResourceNotFoundException', 'Function not found'); // errorCode: 'AWS_LAMBDA', integration: 'aws-service', statusCode: 500 const sqsError = AWSError.sqs('QueueDoesNotExist', 'Queue not found'); // errorCode: 'AWS_SQS', integration: 'aws-service', statusCode: 500 // Error para servicio personalizado const ec2Error = AWSError.forService('EC2', 'InvalidInstanceID.NotFound', 'Instance not found'); // errorCode: 'AWS_EC2', integration: 'aws-service', statusCode: 500 // Constructor directo const directAwsError = new AWSError('Error personalizado', 'SERVICE', 'ERROR_CODE'); // errorCode: 'AWS_SERVICE', integration: 'aws-service', statusCode: 500 // Obtener detalles de AWS const details = dynamoError.getAWSDetails(); ``` ### IntegrationError Errores específicos para integraciones externas con campos requeridos automáticos. ```javascript const { IntegrationError } = require('@gp_jcisneros/errors'); // Errores de integraciones específicas con campos requeridos automáticos const cybersourceError = IntegrationError.cybersource('Card stolen', 'CYBERSOURCE_05'); // errorCode: 'CYBERSOURCE_05', integration: 'cybersource', statusCode: 502 const stripeError = IntegrationError.stripe('Card declined', 'STRIPE_card_declined'); // errorCode: 'STRIPE_card_declined', integration: 'stripe', statusCode: 502 const adyenError = IntegrationError.adyen('Payment failed', 'ADYEN_123'); // errorCode: 'ADYEN_123', integration: 'adyen', statusCode: 502 // Error personalizado para integración const customIntegrationError = IntegrationError.custom('Custom error', 'CUSTOM_001', 'custom-service'); // errorCode: 'CUSTOM_001', integration: 'custom-service', statusCode: 502 // Constructor directo con contexto const directIntegrationError = new IntegrationError('Error personalizado', 'CODE', 'service', { context: 'data' }); // errorCode: 'CODE', integration: 'service', statusCode: 502, context: { context: 'data' } // Obtener detalles de integración const details = cybersourceError.getIntegrationDetails(); ``` ### ZodError Errores específicos para validación de Zod con campos requeridos automáticos y conversión estandarizada. ```javascript const { ZodError } = require('@gp_jcisneros/errors'); const { z } = require('zod'); // Crear error desde un ZodError de validación const schema = z.object({ email: z.string().email('Email inválido'), name: z.string().min(2, 'Nombre muy corto') }); try { schema.parse({ email: 'invalid', name: 'A' }); } catch (zodError) { const error = ZodError.fromZodError(zodError, 'user-service'); // errorCode: 'ZOD-001', integration: 'user-service', statusCode: 400 } // Errores específicos de Zod const requiredError = ZodError.required('email', 'auth-service'); // errorCode: 'ZOD-002', integration: 'auth-service', statusCode: 400 const providerError = ZodError.unsupportedProvider('paypal', 'credit-card', 'payment-service'); // errorCode: 'ZOD-003', integration: 'payment-service', statusCode: 400 const genericError = ZodError.generic(new Error('Internal error'), 'validation-service'); // errorCode: 'ZOD-004', integration: 'validation-service', statusCode: 500 const schemaError = ZodError.schemaValidation('Schema failed', 'user', { invalid: 'data' }, 'user-service'); // errorCode: 'ZOD-005', integration: 'user-service', statusCode: 400 const dynamicError = ZodError.dynamicSchema('Dynamic schema failed', 'stripe', 'payment-service'); // errorCode: 'ZOD-006', integration: 'payment-service', statusCode: 400 // Constructor directo const directError = new ZodError('Error personalizado', 'field', 'value', null); // errorCode: 'ZOD_FIELD', integration: 'zod-validation', statusCode: 400 // Obtener detalles de Zod const details = requiredError.getZodDetails(); // Obtener errores de validación const validationErrors = error.getValidationErrors(); // Verificar si tiene detalles de Zod const hasDetails = error.hasZodDetails(); ``` ### ResponseHandler Clase para manejar respuestas estandarizadas de API con formato consistente para éxito y error. ```javascript const { ResponseHandler } = require('@gp_jcisneros/errors'); // Handler básico const basicHandler = ResponseHandler.createHandler(); // Handler para transacciones const transactionHandler = ResponseHandler.createTransactionHandler(); // Handler para CRUD const crudHandler = ResponseHandler.createCRUDHandler(); // Handler personalizado const customHandler = ResponseHandler.createCustomHandler( 'Operación exitosa', 'Error en la operación' ); // Uso en Express app.get('/users/:id', async (req, res) => { try { const user = await getUser(req.params.id); return basicHandler(res, user); } catch (error) { return basicHandler(res, null, error); } }); // Uso directo de la clase const handler = new ResponseHandler({ defaultSuccessMessage: 'Operación exitosa', defaultErrorMessage: 'Error interno', includeStack: false }); handler.success(res, { id: 123, name: 'John' }); handler.error(res, new Error('User not found')); ``` ### Middleware de Validación Dinámica ``` ## 🔧 Zod Error Handler Middleware Middleware y utilidades para manejar errores de validación de Zod de manera estandarizada. ### Características - ✅ Conversión automática de errores de Zod a `ValidationError` - ✅ Middleware reutilizable para validación dinámica - ✅ Middleware para validación con schema base - ✅ Formato de respuesta estandarizado - ✅ Soporte para validaciones personalizadas - ✅ Integración con diferentes frameworks (Express, Fastify, etc.) ### Middleware de Validación Dinámica ```javascript const { createDynamicSchemaValidation } = require('@gp_jcisneros/errors'); const { z } = require('zod'); // Función para crear schema dinámico const createDynamicSchema = (provider, transactionType, extensions = {}) => { const baseSchema = z.object({ body: z.object({ amount: z.number().positive(), currency: z.string().length(3), paymentMethod: z.object({ bankProvider: z.string(), accountNumber: z.string().min(10) }) }) }); if (provider === 'bncr') { return baseSchema.extend({ body: z.object({ bncrSpecific: z.string().optional() }) }); } return null; // Proveedor no soportado }; // Crear middleware const validation = createDynamicSchemaValidation( createDynamicSchema, // Función para crear schema dinámico 'payment', // Tipo de transacción {}, // Extensiones personalizadas 'bncr-integration', // Nombre de la integración getCustomValidation, // Función para validación personalizada (opcional) null // Función de respuesta de error (opcional) ); // Usar en Express app.post('/transactions', validation, (req, res) => { res.json({ success: true }); }); ``` ### Middleware de Validación Base ```javascript const { createBaseSchemaValidation } = require('@gp_jcisneros/errors'); const { z } = require('zod'); const userSchema = z.object({ body: z.object({ email: z.string().email('Email debe ser válido'), name: z.string().min(2, 'Nombre debe tener al menos 2 caracteres'), age: z.number().min(18, 'Debe ser mayor de edad') }) }); // Crear middleware const validation = createBaseSchemaValidation( userSchema, // Schema base de Zod {}, // Extensiones personalizadas 'user-service', // Nombre de la integración getCustomValidation, // Función para validación personalizada (opcional) null // Función de respuesta de error (opcional) ); // Usar en Express app.post('/users', validation, (req, res) => { res.json({ success: true }); }); ``` ### Conversión Manual de Errores de Zod ```javascript const { convertZodErrorToValidationError } = require('@gp_jcisneros/errors'); const { z } = require('zod'); const schema = z.object({ email: z.string().email(), name: z.string().min(2) }); app.post('/custom-validation', async (req, res) => { try { await schema.parseAsync(req.body); res.json({ success: true }); } catch (error) { if (error instanceof z.ZodError) { const validationError = convertZodErrorToValidationError( error, 'custom-service' ); return res.status(400).json({ code: 400, status: "failed", message: validationError.message, error: { code: validationError.errorCode, message: validationError.description }, additionalData: validationError.additionalData }); } res.status(500).json({ error: 'Internal server error' }); } }); ``` ### Formato de Respuesta Estandarizado Todas las respuestas de error siguen este formato: ```json { "code": 400, "status": "failed", "message": "Event object failed validation: email must be a valid email address", "error": { "code": "GP-1204", "message": "Schema validation failed" }, "additionalData": { "integration": "bncr-integration", "field": "email", "value": "invalid-email", "type": "VALIDATION_ERROR", "validation": { "errors": [ { "field": "email", "message": "email must be a valid email address", "received": "invalid-email", "expected": "valid email format" } ], "issues": [...], "customValidation": {...} } } } ``` ### Códigos de Error | Código | Descripción | |--------|-------------| | GP-1204 | Error de validación de schema | | GP-1205 | Campo requerido faltante | | GP-1206 | Proveedor no soportado | | GP-500 | Error interno de validación | ### Integración con Diferentes Frameworks #### Express ```javascript const express = require('express'); const { createDynamicSchemaValidation } = require('@gp_jcisneros/errors'); const app = express(); // Middleware personalizado de respuesta de error const customErrorResponse = (res, error) => { return res.status(error.statusCode).json({ code: error.statusCode, status: "failed", message: error.message, error: { code: error.errorCode, message: error.description }, additionalData: error.additionalData }); }; const validation = createDynamicSchemaValidation( createDynamicSchema, 'payment', {}, 'my-service', getCustomValidation, customErrorResponse ); app.post('/api/transaction', validation, (req, res) => { res.json({ success: true }); }); ``` #### Fastify ```javascript const fastify = require('fastify'); const { createBaseSchemaValidation } = require('@gp_jcisneros/errors'); const app = fastify(); // Adaptador para Fastify const fastifyErrorResponse = (reply, error) => { return reply.status(error.statusCode).send({ code: error.statusCode, status: "failed", message: error.message, error: { code: error.errorCode, message: error.description }, additionalData: error.additionalData }); }; const validation = createBaseSchemaValidation( baseSchema, {}, 'fastify-service', getCustomValidation, fastifyErrorResponse ); app.addHook('preHandler', validation); ``` ## 📋 Ejemplos de Uso ### Validación con Errores Estandarizados ```javascript const { ValidationError } = require('@gp_jcisneros/errors'); function validateUserData(userData) { const errors = []; if (!userData.email) { errors.push(ValidationError.required('email')); } else if (!isValidEmail(userData.email)) { errors.push(ValidationError.invalidEmail('email', userData.email)); } if (!userData.password) { errors.push(ValidationError.required('password')); } else if (userData.password.length < 8) { errors.push(ValidationError.minLength('password', 8, userData.password)); } // Verificar que todos los errores tienen campos requeridos const validErrors = errors.filter(error => error.hasRequiredFields()); return validErrors; } ``` ### Error de Base de Datos ```javascript const { DatabaseError } = require('@gp_jcisneros/errors'); async function getUserById(userId) { try { const user = await db.users.findById(userId); if (!user) { throw DatabaseError.notFound('users'); } return user; } catch (error) { // El error ya tiene campos requeridos automáticos console.log('Campos requeridos:', error.getRequiredFields()); throw error; } } ``` ### Error de AWS ```javascript const { AWSError } = require('@gp_jcisneros/errors'); async function getS3Object(bucket, key) { try { const result = await s3.getObject({ Bucket: bucket, Key: key }).promise(); return result.Body; } catch (error) { if (error.code === 'NoSuchKey') { throw AWSError.s3('NoSuchKey', 'Object not found'); } throw error; } } ``` ### Error de Integración ```javascript const { IntegrationError } = require('@gp_jcisneros/errors'); async function processPayment(paymentData) { try { const response = await cybersourceAPI.processPayment(paymentData); if (response.error) { throw IntegrationError.cybersource( response.error.message, response.error.code, { transactionId: paymentData.transactionId } ); } return response; } catch (error) { // El error ya tiene campos requeridos automáticos console.log('Código de integración:', error.integrationCode); throw error; } } ``` ### Error HTTP Personalizado ```javascript const { HttpError } = require('@gp_jcisneros/errors'); function validateUserAccess(user, resource) { if (!user) { throw HttpError.unauthorized('Usuario no autenticado'); } if (!user.isActive) { throw HttpError.forbidden('Usuario inactivo'); } if (!user.hasPermission(resource)) { throw HttpError.forbidden('Sin permisos para acceder al recurso'); } return true; } ``` ## 🔮 Preparación para Transformación Empresarial ### **Campos requeridos disponibles para mapeo:** ```javascript const error = new CustomError('Error de validación', { errorCode: 'GP_VALIDATION_ERROR', description: 'Error de validación en los datos de entrada', integration: 'transaction-service', statusCode: 400 }); // Los campos requeridos están disponibles para transformación console.log(error.getRequiredFields()); // { // errorCode: 'GP_VALIDATION_ERROR', // description: 'Error de validación en los datos de entrada', // integration: 'transaction-service' // } // Verificar si tiene todos los campos requeridos if (error.hasRequiredFields()) { // Listo para transformación empresarial const businessCode = mapToBusinessCode(error.errorCode); console.log('Código de negocio:', businessCode); } ``` ### **Formato JSON:** ```javascript const jsonFormat = error.toJSON(); // { // name: 'CustomError', // message: 'Error de validación', // statusCode: 400, // timestamp: '2024-12-19T10:30:00.000Z', // stack: '...', // errorCode: 'GP_VALIDATION_ERROR', // description: 'Error de validación en los datos de entrada', // integration: 'transaction-service' // } ``` ## 🧪 Tests ```bash # Ejecutar tests npm test # Ejecutar tests con coverage npm test -- --coverage # Ejecutar tests en modo watch npm test -- --watch ``` ### **Cobertura de Tests:** - ✅ **59 tests pasando** (100% de éxito) - ✅ **6 suites de tests** completas - ✅ **Todas las clases** con tests completos - ✅ **Campos requeridos** validados - ✅ **Métodos de conversión** testeados - ✅ **Nuevo constructor** completamente testeado - ✅ **Métodos estáticos** validados ## 🚀 Despliegue Local ### Configuración del Token NPM Para publicar el paquete localmente, primero configura tu token de npm: ```bash # Configurar token de npm (reemplaza TU_TOKEN_AQUI con tu token real) npm config set //registry.npmjs.org/:_authToken=TU_TOKEN_AQUI # Verificar que estás autenticado npm whoami ``` ### Pasos para Publicar ```bash # 1. Instalar dependencias npm install # 2. Ejecutar tests npm test # 3. Ejecutar linting npm run lint # 4. Verificar qué se va a publicar npm pack # 5. Publicar el paquete npm publish --access public ``` ### Verificar la Publicación ```bash # Verificar que el paquete se publicó correctamente npm info @gp_jcisneros/errors # Instalar el paquete para probar npm install @gp_jcisneros/errors ``` ### Actualizar Versión ```bash # Incrementar versión patch (1.0.0 -> 1.0.1) npm version patch # Incrementar versión minor (1.0.0 -> 1.1.0) npm version minor # Incrementar versión major (1.0.0 -> 2.0.0) npm version major # Publicar nueva versión npm publish ``` ## 📦 Publicación ```bash # Lint y tests npm run lint npm test # Publicar npm publish ``` ## 🤝 Contribución 1. Fork el repositorio 2. Crea una rama para tu feature (`git checkout -b feature/nueva-funcionalidad`) 3. Commit tus cambios (`git commit -am 'Agregar nueva funcionalidad'`) 4. Push a la rama (`git push origin feature/nueva-funcionalidad`) 5. Crea un Pull Request ## 📄 Licencia MIT License - ver [LICENSE](LICENSE) para detalles. ## 🆘 Soporte Para soporte técnico, contacta al equipo de GreenPay o crea un issue en el repositorio. --- ## 🎯 **Estado del Proyecto** ### **✅ Completado:** - ✅ **Constructor flexible** con objeto de opciones - ✅ **Métodos estáticos** para crear errores fácilmente - ✅ **Campos requeridos estandarizados** en todas las clases - ✅ **59 tests pasando** (100% de éxito) - ✅ **Linter sin errores** - ✅ **Ejemplos funcionando** - ✅ **Documentación actualizada** - ✅ **Preparado para producción** ### **🚀 Listo para:** - ✅ **Publicación en NPM** - ✅ **Integración en otros módulos** - ✅ **Transformación empresarial** - ✅ **Monitoreo y tracking** ### **🔄 Cambios en v1.0.1-dev:** - ✅ **Nuevo constructor** con objeto de opciones - ✅ **Métodos estáticos** `create()` y `createMinimal()` - ✅ **Soporte para campos adicionales** - ✅ **Mejor documentación** con JSDoc detallado - ✅ **Tests actualizados** para el nuevo constructor - ✅ **Status codes apropiados** para cada tipo de error - ✅ **Middleware de validación Zod** completamente integrado - ✅ **Conversión automática** de errores de Zod a ValidationError - ✅ **Middleware reutilizable** para validación dinámica y base - ✅ **Soporte multi-framework** (Express, Fastify, etc.) - ✅ **Formato de respuesta estandarizado** para todos los errores - ✅ **Documentación completa** del middleware de Zod