npm package: @jamx-framework/validator
Description: JAMX Framework â€” Schema validation with type inference
Author: Stiven-21
Latest version: 1.0.0

# @jamx-framework/validator

## Descripción
Sistema de validación de datos con inferencia de tipos para JAMX Framework. Proporciona una API fluida y type-safe para definir schemas de validación (strings, numbers, booleans, arrays, objects, unions, enums, literals) y validar datos en runtime con errores detallados. Los schemas permiten inferir automáticamente los tipos TypeScript, garantizando consistencia entre validación y tipado.

## Cómo funciona
El módulo implementa un sistema de schemas composables:

1. **Schema base**: Clase abstracta con método `parse()` que retorna `ValidationResult`
2. **Schemas específicos**: `StringSchema`, `NumberSchema`, `BooleanSchema`, `ArraySchema`, `ObjectSchema`, etc.
3. **API fluida**: `v.string()`, `v.number()`, `v.object()` para construir schemas
4. **Validación**: `validate(schema, value)` retorna resultado con `ok`, `data` o `errors`
5. **Type inference**: `Infer<typeof schema>` extrae el tipo TypeScript del schema

## Componentes principales

### API Principal (`src/index.ts`, `src/v.ts`)
- `v`: Objeto con métodos para crear schemas (`v.string()`, `v.number()`, etc.)
- `validate<T>(schema, value)`: Valida un valor contra un schema
- `parseOrThrow<T>(schema, value)`: Valida y lanza error si falla

### Schemas (`src/schemas/`)
- `StringSchema`: Validación de strings (min, max, pattern, email, url)
- `NumberSchema`: Validación de números (min, max, int, positive)
- `BooleanSchema`: Validación de booleanos
- `EnumSchema`: Validación de valores de un conjunto
- `LiteralSchema`: Validación de valores literales
- `ArraySchema`: Validación de arrays (items, min, max)
- `ObjectSchema`: Validación de objetos con shape
- `UnionSchema`: Validación de tipos unión

### Tipos (`src/types/`)
- `Schema<T>`: Interface base de un schema
- `ValidationResult<T>`: Resultado de validación (`{ ok: true, data }` o `{ ok: false, errors }`)
- `ValidationError`: Error individual con `path`, `message`, `code`

## Uso básico

### Validación simple
```typescript
import { v, validate } from '@jamx-framework/validator';

// Schema de string
const nameSchema = v.string().min(2).max(50);

// Validar
const result = validate(nameSchema, 'Alice');
if (result.ok) {
  console.log('Nombre válido:', result.data); // 'Alice'
} else {
  console.log('Errores:', result.errors);
}
```

### Validación de objetos
```typescript
import { v, validate } from '@jamx-framework/validator';

const userSchema = v.object({
  id: v.number().int().positive(),
  name: v.string().min(1).max(100),
  email: v.string().email(),
  age: v.number().min(0).max(150).optional(),
  role: v.enum(['admin', 'user', 'guest']),
});

const data = {
  id: 1,
  name: 'Alice',
  email: 'alice@example.com',
  role: 'user',
};

const result = validate(userSchema, data);
if (result.ok) {
  // result.data está tipado como:
  // { id: number; name: string; email: string; age?: number; role: 'admin' | 'user' | 'guest' }
  console.log('Usuario válido:', result.data);
}
```

### Type inference
```typescript
import { v } from '@jamx-framework/validator';

const productSchema = v.object({
  id: v.string().uuid(),
  name: v.string(),
  price: v.number().positive(),
  tags: v.array(v.string()),
});

type Product = Infer<typeof productSchema>;
// type Product = {
//   id: string;  // uuid validado como string
//   name: string;
//   price: number;
//   tags: string[];
// }
```

### Validación en API
```typescript
import { validate, parseOrThrow } from '@jamx-framework/validator';
import { v } from '@jamx-framework/validator';

const createUserSchema = v.object({
  name: v.string().min(2).max(100),
  email: v.string().email(),
  password: v.string().min(8),
});

server.post('/users', async (req, res) => {
  // Opción 1: validate
  const result = validate(createUserSchema, req.body);
  if (!result.ok) {
    res.json({ errors: result.errors }, 400);
    return;
  }

  const user = result.data; // tipado correcto
  await db.users.create(user);
  res.created(user);

  // Opción 2: parseOrThrow (más conciso)
  // const user = parseOrThrow(createUserSchema, req.body);
});
```

### Schemas complejos
```typescript
import { v } from '@jamx-framework/validator';

// Union
const statusSchema = v.union(
  v.literal('pending'),
  v.literal('active'),
  v.literal('inactive')
);
// type Status = 'pending' | 'active' | 'inactive'

// Array con validación
const tagsSchema = v.array(v.string().min(1)).max(10);

// Object con propiedades opcionales
const configSchema = v.object({
  debug: v.boolean().default(false),
  port: v.number().int().min(1).max(65535).default(3000),
  host: v.string().optional(),
});

// Nested objects
const addressSchema = v.object({
  street: v.string(),
  city: v.string(),
  zip: v.string().pattern(/^\d{5}$/),
});

const userSchema = v.object({
  name: v.string(),
  address: addressSchema.optional(),
});
```

### Validadores personalizados
```typescript
import { v } from '@jamx-framework/validator';

// Custom validator para password fuerte
const passwordSchema = v.string()
  .min(8)
  .pattern(/[A-Z]/) // al menos una mayúscula
  .pattern(/[a-z]/) // al menos una minúscula
  .pattern(/\d/)    // al menos un número
  .pattern(/[!@#$%^&*]/) // al menos un especial
  .refine((value) => {
    // validación adicional asíncrona (ej: check contra breached passwords)
    return !isBreachedPassword(value);
  }, 'Password has been breached')
  .refine((value) => {
    // otra validación
    return value !== 'password123';
  }, 'Password too common');

// Custom refine para lógica compleja
const startDateSchema = v.string().datetime();
const endDateSchema = v.string().datetime();

const dateRangeSchema = v.object({
  start: startDateSchema,
  end: endDateSchema,
}).refine(
  (data) => new Date(data.end) > new Date(data.start),
  'end date must be after start date'
);
```

### Default values
```typescript
import { v } from '@jamx-framework/validator';

const settingsSchema = v.object({
  theme: v.enum(['light', 'dark']).default('light'),
  notifications: v.boolean().default(true),
  language: v.string().default('en'),
});

// Al validar, los campos faltantes se completan con defaults
const result = validate(settingsSchema, {});
// result.data = { theme: 'light', notifications: true, language: 'en' }
```

### Transformaciones
```typescript
import { v } from '@jamx-framework/validator';

// Transformar string a number
const ageSchema = v.string()
  .pattern(/^\d+$/)
  .transform((value) => parseInt(value, 10));

// Transformar trim
const trimmedString = v.string()
  .transform((value) => value.trim());

// Transformar fecha
const dateSchema = v.string()
  .datetime()
  .transform((value) => new Date(value));
```

## API Reference

### v (Factory)

#### v.string()
```typescript
v.string(): StringSchema
```
Crea un schema de string.

#### v.number()
```typescript
v.number(): NumberSchema
```
Crea un schema de number.

#### v.boolean()
```typescript
v.boolean(): BooleanSchema
```
Crea un schema de boolean.

#### v.enum<T>()
```typescript
v.enum<T extends string>(values: readonly T[]): EnumSchema<T>
```
Crea un schema que valida que el valor esté en el conjunto.

#### v.literal<T>()
```typescript
v.literal<T extends string | number | boolean>(value: T): LiteralSchema<T>
```
Crea un schema que valida un valor literal exacto.

#### v.array<T>()
```typescript
v.array<T>(itemSchema: Schema<T>): ArraySchema<T>
```
Crea un schema de array.

#### v.object<T>()
```typescript
v.object<T extends ObjectShape>(shape: T): ObjectSchema<T>
```
Crea un schema de objeto.

#### v.union<T>()
```typescript
v.union<T>(...schemas: Schema<T>[]): UnionSchema<T>
```
Crea un schema que acepta cualquiera de los schemas dados.

### StringSchema

#### Métodos de cadena
```typescript
.min(length: number): this
.max(length: number): this
.pattern(regex: RegExp): this
.email(): this           // valida formato email
.url(): this             // valida formato URL
.uuid(): this            // valida UUID v4
.nonempty(): this        // no vacío
.optional(): this        // permite undefined
```
**Ejemplo:**
```typescript
v.string().min(3).max(100).email();
```

### NumberSchema

#### Métodos numéricos
```typescript
.min(value: number): this
.max(value: number): this
.int(): this             // debe ser entero
.positive(): this        // > 0
.negative(): this        // < 0
.nonnegative(): this     // >= 0
.nonpositive(): this     // <= 0
.optional(): this
```
**Ejemplo:**
```typescript
v.number().int().min(0).max(100);
```

### BooleanSchema
```typescript
.optional(): this
```
**Ejemplo:**
```typescript
v.boolean();
```

### EnumSchema<T>
```typescript
// Ya creado con v.enum(values)
```
Valida que el valor esté en el array de valores.

### LiteralSchema<T>
```typescript
// Ya creado con v.literal(value)
```
Valida igualdad estricta.

### ArraySchema<T>

#### Métodos
```typescript
.min(length: number): this
.max(length: number): this
.optional(): this
```
**Ejemplo:**
```typescript
v.array(v.string()).min(1).max(10);
```

### ObjectSchema<T>

#### Métodos
```typescript
.refine(
  predicate: (data: T) => boolean,
  message: string
): this
```
Añade validación personalizada.

**.optional()** (en campos individuales): Hace una propiedad opcional.

**Ejemplo:**
```typescript
v.object({
  name: v.string().min(1),
  age: v.number().optional(),
}).refine(
  (data) => data.age === undefined || data.age >= 18,
  'Must be at least 18 years old'
);
```

### UnionSchema<T>
```typescript
// Ya creado con v.union(...schemas)
```
Acepta cualquiera de los schemas.

### Schema (interface base)
```typescript
interface Schema<T> {
  parse(value: unknown): ValidationResult<T>;
  optional(): Schema<T | undefined>;
}
```

### ValidationResult<T>
```typescript
interface ValidationResult<T> {
  ok: boolean;
  data?: T;        // presente si ok === true
  errors?: ValidationError[]; // presente si ok === false
}
```

### ValidationError
```typescript
interface ValidationError {
  path: string[];   // ruta al campo, ej: ['address', 'city']
  message: string;  // mensaje de error
  code: string;     // código de error, ej: 'invalid_type', 'too_small'
}
```

## Ejemplos completos

### Formulario de login
```typescript
import { v, validate } from '@jamx-framework/validator';

const loginSchema = v.object({
  email: v.string().email(),
  password: v.string().min(8).max(100),
  remember: v.boolean().default(false),
});

type LoginData = Infer<typeof loginSchema>;

async function handleLogin(req: JamxRequest, res: JamxResponse) {
  const result = validate(loginSchema, req.body);

  if (!result.ok) {
    // errors = [{ path: ['email'], message: 'Invalid email', code: 'invalid_email' }]
    return res.json({ errors: result.errors }, 400);
  }

  const { email, password, remember } = result.data;
  const user = await authenticate(email, password);

  if (!user) {
    return res.json({ error: 'Invalid credentials' }, 401);
  }

  const token = generateToken(user, { remember });
  res.json({ token, user });
}
```

### Validación de query params
```typescript
import { v, validate } from '@jamx-framework/validator';

const paginationSchema = v.object({
  page: v.number().int().positive().default(1),
  limit: v.number().int().min(1).max(100).default(20),
  sort: v.enum(['asc', 'desc']).default('asc'),
});

server.get('/users', async (req, res) => {
  const result = validate(paginationSchema, req.query);

  if (!result.ok) {
    return res.json({ errors: result.errors }, 400);
  }

  const { page, limit, sort } = result.data;
  const users = await db.users.find({}).paginate({ page, limit, sort });
  res.json(users);
});
```

### Validación anidada
```typescript
import { v, validate } from '@jamx-framework/validator';

const addressSchema = v.object({
  street: v.string().min(1),
  city: v.string().min(1),
  zip: v.string().pattern(/^\d{5}$/),
  country: v.string().default('US'),
});

const orderSchema = v.object({
  customerId: v.string().uuid(),
  shippingAddress: addressSchema,
  billingAddress: addressSchema.optional(),
  items: v.array(
    v.object({
      productId: v.string().uuid(),
      quantity: v.number().int().positive(),
      price: v.number().positive(),
    })
  ).min(1),
  total: v.number().positive(),
}).refine(
  (data) => {
    const itemsTotal = data.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
    return Math.abs(itemsTotal - data.total) < 0.01; // tolerancia para redondeo
  },
  'Total does not match sum of items'
);

type Order = Infer<typeof orderSchema>;
```

### Validación condicional
```typescript
import { v } from '@jamx-framework/validator';

const signupSchema = v.object({
  email: v.string().email(),
  password: v.string().min(8),
  confirmPassword: v.string(),
  marketingOptIn: v.boolean().default(false),
}).refine(
  (data) => data.password === data.confirmPassword,
  'Passwords do not match'
).omit(['confirmPassword']); // eliminar campo sensible antes de usar

// O usando union para diferentes tipos de usuario
const userSchema = v.union(
  v.object({
    type: v.literal('admin'),
    adminKey: v.string(),
  }),
  v.object({
    type: v.literal('user'),
    username: v.string(),
  })
);
```

### Transformaciones y defaults
```typescript
import { v } from '@jamx-framework/validator';

const searchSchema = v.object({
  query: v.string().trim().min(1),
  page: v.string().transform(Number).default(1),
  limit: v.string().transform(Number).min(1).max(100).default(20),
  sortBy: v.string().default('createdAt'),
  filters: v.object({
    status: v.enum(['active', 'inactive']).optional(),
    category: v.string().optional(),
  }).default({}),
});

// Al validar '?query= test &page=2':
// result.data = {
//   query: 'test',
//   page: 2,
//   limit: 20,
//   sortBy: 'createdAt',
//   filters: {}
// }
```

### Validación de arrays complejos
```typescript
import { v } from '@jamx-framework/validator';

const matrixSchema = v.array(
  v.array(v.number())
).min(1).max(10).refine(
  (matrix) => matrix.every(row => row.length === matrix[0].length),
  'All rows must have same length'
);

// Validar matriz 3x3
const result = validate(matrixSchema, [[1,2,3], [4,5,6], [7,8,9]]);
```

### Schema reutilizable
```typescript
import { v } from '@jamx-framework/validator';

const emailSchema = v.string().email().trim();
const passwordSchema = v.string().min(8).max(100);

const userRegistrationSchema = v.object({
  email: emailSchema,
  password: passwordSchema,
  confirmPassword: passwordSchema,
}).refine(
  (data) => data.password === data.confirmPassword,
  'Passwords do not match'
).omit(['confirmPassword']);
```

## Flujo interno

### Validación de string
```typescript
class StringSchema extends BaseSchema<string> {
  _parse(value: unknown, path: string[]): ValidationResult<string> {
    // 1. Verificar tipo
    if (typeof value !== "string") {
      return fail([{ path, message: "Expected string", code: "invalid_type" }]);
    }

    // 2. Validar nonempty
    if (this._nonempty && value.length === 0) {
      return fail([{ path, message: "Cannot be empty", code: "too_small" }]);
    }

    // 3. Validar min
    if (this._min !== undefined && value.length < this._min) {
      return fail([{ path, message: `Min ${this._min} chars`, code: "too_small" }]);
    }

    // 4. Validar max
    if (this._max !== undefined && value.length > this._max) {
      return fail([{ path, message: `Max ${this._max} chars`, code: "too_large" }]);
    }

    // 5. Validar pattern
    if (this._pattern && !this._pattern.test(value)) {
      return fail([{ path, message: "Invalid format", code: "invalid_pattern" }]);
    }

    // 6. Validar email
    if (this._email && !this._validateEmail(value)) {
      return fail([{ path, message: "Invalid email", code: "invalid_email" }]);
    }

    // 7. Validar url
    if (this._url && !this._validateUrl(value)) {
      return fail([{ path, message: "Invalid URL", code: "invalid_url" }]);
    }

    // 8. Aplicar transformaciones y retornar éxito
    return ok(value);
  }
}
```

### Validación de objeto
```typescript
class ObjectSchema<T extends ObjectShape> extends BaseSchema<T> {
  _parse(value: unknown, path: string[]): ValidationResult<T> {
    // 1. Verificar que es objeto
    if (typeof value !== "object" || value === null || Array.isArray(value)) {
      return fail([{ path, message: "Expected object", code: "invalid_type" }]);
    }

    const obj = value as Record<string, unknown>;
    const result: Record<string, unknown> = {};
    const errors: ValidationError[] = [];

    // 2. Validar cada propiedad del shape
    for (const [key, schema] of Object.entries(this._shape)) {
      const fieldPath = [...path, key];
      const fieldResult = schema.parse(obj[key]);

      if (fieldResult.ok) {
        result[key] = fieldResult.data;
      } else {
        errors.push(...fieldResult.errors.map(err => ({
          ...err,
          path: fieldPath,
        })));
      }
    }

    // 3. Aplicar refinements
    for (const refine of this._refinements) {
      if (!refine.predicate(result)) {
        errors.push({
          path,
          message: refine.message,
          code: 'custom',
        });
      }
    }

    // 4. Retornar
    return errors.length > 0
      ? fail(errors)
      : ok(result as T);
  }
}
```

### validate()
```typescript
export function validate<T>(
  schema: Schema<T>,
  value: unknown,
): ValidationResult<T> {
  return schema.parse(value);
}
```

### parseOrThrow()
```typescript
export function parseOrThrow<T>(schema: Schema<T>, value: unknown): T {
  const result = schema.parse(value);
  if (!result.ok) {
    const messages = result.errors
      .map(e => `${e.path.join(".") || "root"}: ${e.message}`)
      .join(", ");
    throw new Error(`Validation failed: ${messages}`);
  }
  return result.data;
}
```

## Consideraciones de rendimiento

### Compilación vs runtime
- Los schemas se ejecutan en runtime (no hay compilación)
- La validación es síncrona y rápida para la mayoría de casos
- Para validación masiva, considera batch processing

### Type inference
- La inferencia de tipos es en compile-time (TypeScript)
- No hay overhead en runtime por la inferencia

### Caching
- Los schemas son inmutables una vez creados
- Puedes reutilizar schemas sin problemas
- No hay caching interno de resultados

### Profiling
```typescript
// Para medir performance
const start = Date.now();
const result = validate(largeSchema, largeObject);
console.log(`Validation took ${Date.now() - start}ms`);
```

## Testing

### Tests unitarios
```typescript
import { describe, it, expect } from 'vitest';
import { v, validate } from '@jamx-framework/validator';

describe('StringSchema', () => {
  it('should validate min length', () => {
    const schema = v.string().min(3);
    const result = validate(schema, 'ab');
    expect(result.ok).toBe(false);
    expect(result.errors[0].code).toBe('too_small');
  });

  it('should validate email', () => {
    const schema = v.string().email();
    const result = validate(schema, 'invalid-email');
    expect(result.ok).toBe(false);
    expect(result.errors[0].code).toBe('invalid_email');
  });
});

describe('ObjectSchema', () => {
  it('should validate nested objects', () => {
    const schema = v.object({
      user: v.object({
        name: v.string(),
      }),
    });

    const result = validate(schema, { user: { name: 'Alice' } });
    expect(result.ok).toBe(true);
    expect(result.data.user.name).toBe('Alice');
  });
});
```

### Tests con type inference
```typescript
import { v, Infer } from '@jamx-framework/validator';

const schema = v.object({
  id: v.string().uuid(),
  count: v.number().int().positive(),
});

type Data = Infer<typeof schema>;

// TypeScript sabe que Data es:
// { id: string; count: number }

function process(data: Data) {
  // data.id es string
  // data.count es number
}
```

## Seguridad

### Sanitización
El validador NO sanitiza datos, solo valida. Si necesitas sanitización, hazla después:

```typescript
const schema = v.string().min(1);
const result = validate(schema, req.body.name);
if (result.ok) {
  const sanitized = sanitizeHtml(result.data); // sanitizar manualmente
}
```

### Inyección
La validación previene algunos ataques (ej: tipo de dato incorrecto), pero no sustituye otras medidas de seguridad (prepared statements, escaping, etc.).

### DoS
Validar inputs muy grandes puede ser costoso. Considerar límites:

```typescript
const largeString = v.string().max(10_000); // límite de 10KB
```

## Limitaciones

### Sin async validation
Los schemas son síncronos. Para validación asíncrona (ej: check en DB), usa `refine` con función async pero debes manejar manualmente:

```typescript
// No soportado nativamente
const schema = v.string().refine(async (value) => {
  const exists = await db.userExists(value);
  return !exists;
});

// Alternativa: validar después
const result = validate(v.string(), email);
if (result.ok && !(await db.userExists(result.data))) {
  // error
}
```

### No soporta todos los tipos
Solo soporta: string, number, boolean, enum, literal, array, object, union.
Faltan: Date, RegExp, Function, etc. Usa transformaciones:

```typescript
const dateSchema = v.string().datetime().transform((v) => new Date(v));
```

### Mensajes de error fijos
Los mensajes de error están hardcodeados (ej: "Expected string"). Para mensajes personalizados, usa `refine`:

```typescript
const schema = v.string().refine(
  (v) => v === 'admin' || v === 'user',
  'Must be admin or user'
);
```

### Sin validación de referencias circulares
Los objetos con referencias circulares fallarán. Usa `v.any()` para campos problemáticos.

## Buenas prácticas

### 1. Definir schemas una vez
```typescript
// ✅ Bien: schema reutilizable
const userSchema = v.object({ ... });
export { userSchema };

// ❌ No: crear schema en cada validación
function handler(req) {
  const schema = v.object({ ... }); // crea nuevo schema cada vez
  validate(schema, req.body);
}
```

### 2. Usar type inference
```typescript
// ✅ Bien: inferir tipo
const productSchema = v.object({ ... });
type Product = Infer<typeof productSchema>;

// ❌ No: duplicar tipos
interface Product {
  id: string;
  name: string;
}
const productSchema: Schema<Product> = v.object({ ... });
```

### 3. Validar early
```typescript
// ✅ Bien: validar al inicio del handler
server.post('/users', async (req, res) => {
  const result = validate(userSchema, req.body);
  if (!result.ok) {
    return res.json({ errors: result.errors }, 400);
  }
  // ... resto del handler con datos validados
});
```

### 4. Usar defaults para campos opcionales
```typescript
const configSchema = v.object({
  debug: v.boolean().default(false),
  port: v.number().default(3000),
});
```

### 5. Refine para lógica compleja
```typescript
const dateRangeSchema = v.object({
  start: dateSchema,
  end: dateSchema,
}).refine(
  (data) => data.end > data.start,
  'end must be after start'
);
```

### 6. Omitir campos sensibles
```typescript
const passwordSchema = v.object({
  password: v.string().min(8),
  confirmPassword: v.string(),
}).refine(
  (data) => data.password === data.confirmPassword,
  'Passwords do not match'
).omit(['password', 'confirmPassword']); // eliminar antes de usar
```

## Integración con otros paquetes

### Con `@jamx-framework/server`
```typescript
import { validate } from '@jamx-framework/validator';
import { v } from '@jamx-framework/validator';

const userSchema = v.object({
  name: v.string().min(1),
  email: v.string().email(),
});

server.post('/users', async (req, res) => {
  const result = validate(userSchema, req.body);
  if (!result.ok) {
    return res.json({ errors: result.errors }, 400);
  }

  const user = result.data;
  await db.users.create(user);
  res.created(user);
});
```

### Con `@jamx-framework/auth`
```typescript
import { validate } from '@jamx-framework/validator';

const loginSchema = v.object({
  email: v.string().email(),
  password: v.string(),
});

server.post('/auth/login', async (req, res) => {
  const credentials = parseOrThrow(loginSchema, req.body);
  const user = await auth.authenticate(credentials);
  const token = await auth.createToken(user);
  res.json({ token, user });
});
```

### Con `@jamx-framework/db`
```typescript
import { validate } from '@jamx-framework/validator';

const insertSchema = v.object({
  name: v.string(),
  email: v.string().email(),
  age: v.number().optional(),
});

server.post('/users', async (req, res) => {
  const data = parseOrThrow(insertSchema, req.body);
  const user = await db.users.insert(data);
  res.created(user);
});
```

### Con `@jamx-framework/config`
```typescript
import { validate } from '@jamx-framework/validator';
import { v } from '@jamx-framework/validator';

const configSchema = v.object({
  server: v.object({
    port: v.number().int().min(1).max(65535).default(3000),
    host: v.string().default('localhost'),
  }),
  database: v.object({
    url: v.string().url(),
  }).optional(),
});

// Validar configuración cargada
const config = await configManager.load('./');
const result = validate(configSchema, config);
if (!result.ok) {
  throw new Error(`Invalid config: ${JSON.stringify(result.errors)}`);
}
```

## Preguntas frecuentes

### ¿Cómo validar arrays de objetos?
```typescript
const itemsSchema = v.array(
  v.object({
    id: v.string().uuid(),
    quantity: v.number().int().positive(),
  })
).min(1);

validate(itemsSchema, [{ id: '...', quantity: 2 }]);
```

### ¿Cómo hacer un campo opcional?
```typescript
const schema = v.object({
  requiredField: v.string(),
  optionalField: v.string().optional(), // puede ser undefined
});
```

### ¿Cómo omitir campos de la salida?
```typescript
const schema = v.object({
  password: v.string(),
  confirmPassword: v.string(),
}).omit(['password', 'confirmPassword']);
```

### ¿Cómo validar fechas?
```typescript
const dateSchema = v.string()
  .datetime() // formato ISO
  .transform((v) => new Date(v)); // transformar a Date
```

### ¿Cómo combinar múltiples validadores?
```typescript
const schema = v.string()
  .min(3)
  .max(100)
  .pattern(/^[a-zA-Z]+$/)
  .email();
```

### ¿Cómo crear schema de tupla?
```typescript
// Para tuple exacta [string, number, boolean]
const tupleSchema = v.array(v.union(v.string(), v.number(), v.boolean()))
  .length(3); // no hay .length(), usar refine
// o
const tupleSchema = v.object({
  0: v.string(),
  1: v.number(),
  2: v.boolean(),
});
```

### ¿Cómo validar null?
```typescript
// v.null() no existe, usar union
const nullableString = v.union(v.string(), v.null());
// o
const nullableString = v.string().optional(); // undefined o string
```

### ¿Cómo validar cualquier tipo?
```typescript
const anySchema = v.any(); // no existe, usar v.literal(undefined) o omitir validación
// Para "cualquier cosa", no uses schema o usa v.object({}) sin propiedades
```

## Referencia rápida

### Crear schemas
```typescript
v.string()
v.number()
v.boolean()
v.enum(['a', 'b', 'c'])
v.literal('fixed')
v.array(itemSchema)
v.object({ prop: schema })
v.union(schema1, schema2)
```

### Métodos comunes
```typescript
// String
.min(n) .max(n) .pattern(regex) .email() .url() .uuid() .nonempty() .optional()

// Number
.min(n) .max(n) .int() .positive() .negative() .nonnegative() .optional()

// Array
.min(n) .max(n) .optional()

// Object
.refine(predicate, message) .optional() (por campo)
.omit(['field1', 'field2']) // eliminar campos

// Todos
.transform(fn) // transformar valor
```

### Validar
```typescript
validate(schema, value) // retorna ValidationResult
parseOrThrow(schema, value) // retorna valor o lanza
```

### Inferir tipo
```typescript
type MyType = Infer<typeof mySchema>;
```

## Archivos importantes
- `src/index.ts` - Punto de entrada
- `src/v.ts` - API fluida
- `src/validate.ts` - Funciones de validación
- `src/schemas/` - Implementaciones de schemas
- `src/types/` - Tipos de resultado
- `tests/unit/schemas/` - Tests de schemas
- `tests/unit/validators/validate.test.ts` - Tests de validate

## Dependencias
- `@types/node` - Tipos de Node.js
- `vitest` - Testing
- `rimraf` - Limpieza

## Scripts del paquete
- `pnpm build` - Compila TypeScript
- `pnpm dev` - Watch mode
- `pnpm test` - Tests unitarios
- `pnpm test:watch` - Tests en watch
- `pnpm type-check` - Verificar tipos
- `pnpm clean` - Limpiar build

## Comparación con Zod

| Característica | JAMX Validator | Zod |
|----------------|----------------|-----|
| API fluida      | Sí             | Sí  |
| Type inference  | Sí             | Sí  |
| Transformations | Sí             | Sí  |
| Default values  | Sí             | Sí  |
| Refine/custom   | Sí             | Sí  |
| Async validation | No             | Sí  |
| Branded types   | No             | Sí  |
| Lazy schemas    | No             | Sí  |
| Tamaño          | Pequeño        | Medio |

## Roadmap futuro
- [ ] Validación asíncrona (refine async)
- [ ] Branded types para tipos nominales
- [ ] Lazy schemas para recursión
- [ ] Más métodos: .length(), .includes(), .startsWith(), .endsWith()
- [ ] Schema para Date, RegExp, Function
- [ ] Validación de discriminated unions mejorada
- [ ] Error messages templates
- [ ] Internationalización de errores
- [ ] Plugin system para custom validators

## Conclusión
`@jamx-framework/validator` es una biblioteca ligera y type-safe para validación de datos en JAMX. Su API fluida y composable permite definir schemas complejos con inferencia de tipos, ideal para validar requests de API, configuraciones, y cualquier dato en runtime.