Skip to content

Arquitectura

Jose Luiz Rodrigues edited this page Sep 10, 2026 · 3 revisions

Arquitectura

palta es deliberadamente aburrida: un directorio por país, un archivo por módulo, contratos compartidos y nada más. Esa simpleza es lo que permite tener 23 países sin que el mantenimiento explote.

Estructura

.
├── src/
│   ├── [pais]/           # un directorio por país
│   │   ├── index.ts      # re-exporta los submódulos
│   │   ├── [documento].ts
│   │   ├── currency.ts
│   │   ├── phone.ts
│   │   └── zipcode.ts
│   ├── measurements/
│   │   ├── index.ts
│   │   ├── length.ts
│   │   ├── weight.ts
│   │   └── volume.ts
│   ├── detect.ts
│   ├── index.ts          # exporta los 23 namespaces, detect y measurements
│   ├── types.ts          # contratos compartidos
│   └── utils.ts
├── tests/                # espejo de src/, un archivo por módulo
├── demo/index.html
├── tsup.config.ts
├── vitest.config.ts
└── package.json

tests/ es un espejo exacto de src/. Si agregás src/xx/ruc.ts, el test va en tests/xx/ruc.test.ts.

Los contratos

Todo vive en src/types.ts. Son seis interfaces y tres uniones de tipos, y no se agregan más:

interfaz implementada por
DocumentModule todos los documentos de todos los países
CurrencyModule currency de cada país
PhoneModule phone de cada país
ZipcodeModule zipcode de cada país
MeasurementModule<U> length, weight, volume

La sexta es DetectResult, que nadie implementa: es lo que devuelve detect().

Un módulo nuevo implementa uno de estos. No se inventan contratos: si algo no encaja, la conversación es sobre el contrato, no sobre una excepción local.

Los contratos completos están en Referencia de API.

Utilidades compartidas

src/utils.ts tiene cuatro funciones y ninguna es casual:

función por qué existe
onlyDigits(v) normalizar entradas antes de validar
onlyDigitsAndK(v) el RUT chileno admite K como verificador
padLeft(v, len, char) documentos que se guardan sin ceros a la izquierda
safeStr(v) convertir null, undefined y no-strings a ''

safeStr es la pieza que hace cumplir la regla defensiva sin repetir guardas en 60 archivos. Todo módulo empieza pasando su entrada por ahí.

Cómo se resuelven los imports

src/index.ts re-exporta los 23 países como namespaces, más detect y measurements:

export * as br from './br/index.js'
export * as cl from './cl/index.js'
// ...
export * as do_ from './do/index.js'
export { detect } from './detect.js'
export * as measurements from './measurements/index.js'

do_ lleva guion bajo porque do es palabra reservada de JavaScript. El subpath de package.json no lo lleva: sigue siendo "./do".

Los imports internos usan extensión .js aunque los archivos sean .ts. Es lo que exige el resolver de ESM de Node.

Build

tsup genera ESM, CJS y declaraciones de tipos, con un entry por país más la raíz y measurements.

npm run build

Cada entry de tsup.config.ts tiene su contraparte en exports de package.json. Si agregás uno y olvidás el otro, el subpath no resuelve. Ese es el error más común al sumar un país, y por eso Agregar un país lista los cinco pasos en orden.

Tests

Los tests importan directo de src/, no del paquete construido. Corren sin haber hecho build, lo que hace el ciclo de desarrollo mucho más corto.

npm test
npm run test:coverage   # umbrales por métrica, definidos en vitest.config.ts

Los umbrales están configurados en vitest.config.ts y los aplica la CI: 94% en líneas y statements, 98% en funciones y 90% en ramas.

Puertas de calidad

comando qué verifica
npm run lint tsc --noEmit — es la única puerta estática, no hay ESLint
npm run test:coverage tests más los umbrales de cobertura
npm run build que tsup genere ESM, CJS y tipos sin errores

La CI (.github/workflows/ci.yml) corre los tres en cada push y cada PR contra main, en Node 22, y sube el reporte de cobertura como artefacto.

Ver también: Algoritmos, Contribuir.

Clone this wiki locally