Plataforma técnica · Orchestrator
Orchestrator (Backend)
Orchestrator
Orchestrator es la API REST del ecosistema Nostromo. Construido en Node.js + Express + TypeScript, expone los endpoints que consume Sevastopol y centraliza autenticación, autorización, resolución de tenant y reglas de negocio.
Esta página cubre lo que vive dentro del Orchestrator: stack, capas, multi-tenancy, contratos de API y catálogo de dominios. Para la visión integral del ecosistema ver Arquitectura del Sistema; para levantar el entorno ver Setup Local.
Solo dependencias conceptuales — las versiones vigentes viven en orchestrator/package.json.
| Capa | Paquetes |
|---|---|
| HTTP | express, cors, helmet, morgan, cookie-parser, express-rate-limit |
| Validación | express-validator |
| Persistencia | pg (node-postgres), mongodb (sistema de agentes y tareas) |
| Auth | jsonwebtoken, bcryptjs |
| Documentos | puppeteer, docxtemplater, pizzip, qrcode |
| Diagnóstico | systeminformation |
| Utilidades | dotenv, date-fns |
| Testing (dev) | jest, supertest, tsconfig-paths |
| Lenguaje (dev) | typescript |
Runtime: Node.js 20+. Gestor: pnpm (workspace Accounting/).
Variables de Entorno
Section titled “Variables de Entorno”orchestrator/.env mínimo para desarrollo:
NODE_ENV=developmentPORT=8000
# PostgreSQL (Mother)PGHOST=localhostPGPORT=5432PGUSER=postgresPGPASSWORD=$DB_PASSWORDCOMMAND_DB=nostromo_commandCOMMON_DB=nostromo_common
# AuthJWT_SECRET=$JWT_SECRETJWT_EXPIRES_IN=8h
# StorageLIQUIDACIONES_DIR=./storage/liquidacionesLos orígenes CORS están hardcodeados en app.ts (no se leen de env): localhost:4320, localhost:4321, localhost:4322.
En producción NODE_ENV=production activa el redirect HTTP→HTTPS y el header Strict-Transport-Security (HSTS).
El detalle completo y los pasos de creación de bases viven en Setup Local.
Estructura del Proyecto
Section titled “Estructura del Proyecto”Directoryorchestrator/
Directorysrc/
- app.ts —
createApp()factoría Express - server.ts — arranque HTTP
Directorylib/
- db.ts —
centralPool,commonPool,getTenantPool() - rbac.ts — control de acceso por rol
- tenantResolver.ts —
getDatabaseNameForUser() - accessControl.ts — verificaciones de permisos
- audit.ts —
auditMiddleware(log de mutaciones)
- db.ts —
Directorymiddleware/
- auth.ts —
authenticateToken,AuthenticatedRequest - errorHandler.ts —
AppError,errorHandler,notFoundHandler,asyncHandler - metrics.ts —
metricsMiddleware,metricsHandler - rateLimiter.ts — limitador por rol
- validation.ts — helpers para
express-validator
- auth.ts —
Directorydomain/ — un subdirectorio por contexto (DDD)
- …
Directoryroutes/ — un subdirectorio por dominio HTTP
- …
Directorycontrollers/ — controllers híbridos (ej.
EtlController)- …
Directoryservices/agent/ — sistema de agentes y registry
- …
Directorytest/ —
*.unit.test.ts·*.integration.test.ts·*.e2e.test.ts- …
Directoryscripts/ — utilidades CLI (
scan-manual-cuentas,seed-manual-cuentas)- …
- app.ts —
Directoryassets/
- …
Directorystorage/liquidaciones/ — PDFs generados
- …
- package.json
- tsconfig.json
- jest.config.js
Alias TypeScript (tsconfig.json): @/lib/*, @/middleware/*, @/domain/*, @/routes/*.
Factoría de Aplicación
Section titled “Factoría de Aplicación”createApp() retorna una instancia de Express ya configurada, sin iniciar el servidor — esto permite importarla en tests sin abrir puertos. server.ts se limita a createApp().listen(PORT).
Pipeline de Middleware
Section titled “Pipeline de Middleware”El orden importa: seguridad → CORS → logging → parsing → tracing → métricas → audit → rutas → 404 → error handler.
flowchart LR
REQ["Petición HTTP"] --> TP["trust proxy = 1"]
TP --> HTTPS["HTTPS redirect + HSTS<br/>(prod)"]
HTTPS --> HEL["helmet()<br/>+ CSP estricta"]
HEL --> CRS["cors() + cookies"]
CRS --> MOR["morgan('combined')"]
MOR --> EJS["express.json (2mb)"]
EJS --> CKP["cookieParser"]
CKP --> CC["Cache-Control no-store<br/>(/api/*)"]
CC --> RID["requestIdMiddleware"]
RID --> MET["metricsMiddleware"]
MET --> AUD["auditMiddleware"]
AUD --> RT["Rutas (/api/*, /health, /metrics)"]
RT --> AUTH["authenticateToken<br/>(por router protegido)"]
AUTH --> HDL["Handler"]
HDL --> NF["notFoundHandler"]
NF --> ERR["errorHandler global"]
ERR --> RES["Respuesta HTTP"] | Middleware | Propósito |
|---|---|
trust proxy = 1 | Resolver IP real detrás de Nginx/Cloudflare (requerido por el rate limiter). |
| HTTPS redirect + HSTS | En NODE_ENV=production: 301 a https:// y header Strict-Transport-Security. |
helmet({ contentSecurityPolicy }) | CSP estricta: defaultSrc 'self', scriptSrc 'self', frameAncestors 'none'. |
cors({ credentials }) | Acepta localhost:4320–4322 con cookies. |
morgan('combined') | Log estilo Apache por petición a stdout. |
express.json({ limit }) | Parsear JSON hasta 2 MB. |
cookieParser() | Habilita req.cookies (cookie sid). |
Cache-Control no-store | Aplicado a todo /api/* — evita 304 sin body en clientes fetch. |
requestIdMiddleware | Asigna requestId (UUID) y lo expone para tracing y errorHandler. |
metricsMiddleware | Cuenta peticiones y latencias; excluye /health y /metrics. |
auditMiddleware | Loguea operaciones mutantes; excluye /health, /metrics, /api/auth/login. |
authenticateToken | Valida JWT y resuelve req.user (aplicado por router protegido). |
notFoundHandler | 404 con errorCode: NOT_FOUND y requestId. |
errorHandler | Captura AppError y desconocidos, formato JSON consistente, oculta stack en prod. |
Rate Limiter por rol
Section titled “Rate Limiter por rol”Configurado en app.ts (actualmente deshabilitado en desarrollo, listo para producción):
| Rol | Peticiones / 15 min |
|---|---|
SUPER_ADMIN | 5000 |
ADMIN | 2000 |
| Autenticado | 1000 |
| Anónimo | 500 |
Salta /health. Respuesta de límite excedido sigue el shape estándar de error.
Health Check
Section titled “Health Check”curl http://localhost:8000/health# → {"status":"OK","service":"orchestrator"}Ruta pública. No consulta la base de datos: solo confirma que el proceso responde. El rate limiter y el audit la excluyen.
Métricas
Section titled “Métricas”curl http://localhost:8000/metricsEndpoint con métricas en formato Prometheus generadas por metricsMiddleware. Pensado para sondas de monitoring externo.
Static files
Section titled “Static files”/files/liquidaciones/<archivo>.pdf sirve los PDFs generados desde LIQUIDACIONES_DIR con Cache-Control: public, max-age=31536000, immutable.
Multi-Tenancy
Section titled “Multi-Tenancy”Modelo database-per-tenant. Tres tipos de pool en lib/db.ts:
flowchart LR
subgraph PM["Pools"]
CP["centralPool"]
CMP["commonPool"]
GET["getTenantPool(db)"]
MAP["Map<string, Pool><br/>(tenantPools)"]
GET --> MAP
end
subgraph DB["Bases de Datos"]
CMD[("nostromo_command")]
COM[("nostromo_common")]
T1[("nostromo_60004317")]
T2[("nostromo_70001234")]
end
CP --> CMD
CMP --> COM
MAP --> T1
MAP --> T2 | Pool | Tipo | Base | Contenido |
|---|---|---|---|
centralPool | Singleton | nostromo_command | Usuarios, sesiones, tenants registrados, monitoreo. |
commonPool | Singleton | nostromo_common | Parámetros compartidos (monedas, AFP, IUSC, AFC). |
tenantPools | Cacheados | nostromo_<rut_sin_dv> | Negocio aislado por empresa. |
getTenantPool(dbName) reutiliza la conexión si ya existe en el Map; si no, crea un new Pool(config) y lo cachea.
Resolución de Tenant
Section titled “Resolución de Tenant”sequenceDiagram
autonumber
participant R as Router
participant A as authenticateToken
participant T as tenantResolver
participant P as getTenantPool
participant DB as PostgreSQL
R->>A: Validar cookie sid
A-->>R: req.user = { userId, role, ... }
R->>T: getDatabaseNameForUser(user, req)
T->>T: Verificar permisos RBAC
T-->>R: nombre de tenantDb
R->>P: getTenantPool(tenantDb)
alt Pool cacheado
P-->>R: Pool existente
else Cache miss
P->>DB: new Pool(config)
P-->>R: Pool nuevo
end
R->>DB: pool.query(sql, params)
DB-->>R: Resultado El nombre de tenant del usuario se persiste en nostromo_command.users.tenant_db. Sin ese vínculo, getDatabaseNameForUser devuelve 403 Forbidden.
Capas del Dominio
Section titled “Capas del Dominio”El Orchestrator separa cada petición en cuatro capas con responsabilidades disjuntas. El dominio Remuneraciones es el caso más completo:
flowchart LR
subgraph R["Route"]
ER["express.Router()"]
GET["GET /"]
POSTG["POST /generate"]
POSTP["POST /preview"]
end
subgraph S["Service"]
GP["generatePayroll()"]
PP["previewPayroll()"]
MAP["mapContextToInput()"]
end
subgraph E["Engine (puro)"]
ENG["PayrollEngine.calculate()"]
BASE["BaseSalaryCalculator"]
GRAT["GratificationCalculator"]
SOC["SocialLawsCalculator"]
TAX["TaxCalculator"]
PRO["ProrrataCalculator"]
end
subgraph REP["Repository"]
CTX["getPayrollContext()"]
SAVE["savePayroll()"]
LIST["list()"]
FBID["findById()"]
end
subgraph DB["PostgreSQL"]
LIQ["remuneraciones.liquidaciones<br/>liquidaciones_detalle"]
VLIQ["v_liquidaciones_departamento<br/>v_liquidaciones_detalle_completo"]
PAR["parametros.*"]
end
ER --> GET & POSTG & POSTP
POSTG --> GP
POSTP --> PP
GET --> LIST
GP --> CTX --> MAP --> ENG
GP --> SAVE
ENG --> BASE & GRAT & SOC & TAX & PRO
SAVE --> LIQ
FBID --> LIQ
LIST --> VLIQ
CTX --> PAR | Capa | Archivo | Responsabilidad | Acceso a DB | Testabilidad |
|---|---|---|---|---|
| Route | payroll.ts | Parsear request, validar params, formato JSON. | Solo vía Service. | E2E (Supertest) |
| Service | PayrollService.ts | Orquestar Repository → Engine → Repository. | Solo vía Repository. | Integration |
| Engine | PayrollEngine.ts | Cálculo puro. Función estática sin efectos. | Ninguno. | Unit (rápido) |
| Repository | PayrollRepository.ts | Queries SQL y mapeo a objetos del dominio. | Directo (Pool). | Integration |
Flujo de POST /api/remuneraciones/payroll/generate
Section titled “Flujo de POST /api/remuneraciones/payroll/generate”- Route parsea
contrato_idyperiodo_mes, resuelve la base del tenant. - Service.generatePayroll llama
Repository.getPayrollContext→mapContextToInput→Engine.calculate→Repository.savePayroll. - Repository.getPayrollContext lee empleados, contratos, asistencia, indicadores, topes y tramos de impuesto en paralelo (
Promise.all). - Engine.calculate ejecuta los 5 calculadores en secuencia y retorna
PayrollResult. - Repository.savePayroll inserta cabecera en
liquidacionesy líneas enliquidaciones_detalledentro de una transacción. - Route retorna JSON
snake_caseconid_liquidacion,total_liquido,total_haberes,total_descuentos.
Patrones de API
Section titled “Patrones de API”Estructura de un router
Section titled “Estructura de un router”Patrón vigente con asyncHandler (envuelve errores async automáticamente) y custom error classes:
import express from "express";import { getTenantPool } from "@/lib/db";import { authenticateToken, AuthenticatedRequest } from "@/middleware/auth";import { asyncHandler, NotFoundError } from "@/middleware/errorHandler";import { getDatabaseNameForUser } from "@/lib/tenantResolver";import { PayrollRepository } from "@/domain/payroll/PayrollRepository";
const router = express.Router();router.use(authenticateToken);
router.get( "/", asyncHandler(async (req: AuthenticatedRequest, res) => { const db = await getDatabaseNameForUser(req.user, req); const pool = getTenantPool(db); const rows = await PayrollRepository.list(pool, parseFilters(req.query)); res.json(rows); }),);
router.get( "/:id", asyncHandler(async (req: AuthenticatedRequest, res) => { const pool = getTenantPool(await getDatabaseNameForUser(req.user, req)); const row = await PayrollRepository.findById(pool, req.params.id); if (!row) throw new NotFoundError("Liquidación", req.params.id); res.json(row); }),);
export default router;asyncHandler evita el try/catch en cada handler: cualquier rechazo se propaga al errorHandler global. Las custom error classes (BadRequestError, UnauthorizedError, ForbiddenError, NotFoundError, ConflictError, ValidationError) viven en middleware/errorHandler.ts y traducen automáticamente a su statusCode y errorCode.
Convenciones de nomenclatura
Section titled “Convenciones de nomenclatura”| Lado | Convención | Ejemplo |
|---|---|---|
| Variables internas | camelCase | totalHaberes, sueldoBase |
| Columnas PostgreSQL | snake_case | total_haberes, sueldo_base |
| Respuestas JSON | snake_case | { "total_haberes": 1500000 } |
La respuesta replica la forma de la base: evita una capa de transformación y simplifica el binding en Sevastopol.
Paginación
Section titled “Paginación”Todas las rutas de listado soportan limit y offset como query params:
GET /api/employees?limit=100&offset=200Defaults: limit=2000, offset=0. El handler hace parseInt defensivo.
Filtros temporales
Section titled “Filtros temporales”Listados por período aceptan año y mes (alias anio, year, month por compatibilidad):
GET /api/remuneraciones/payroll?año=2026&mes=5Códigos de estado HTTP
Section titled “Códigos de estado HTTP”| Código | Uso | Ejemplo |
|---|---|---|
| 200 | GET/PUT exitoso | Listar liquidaciones. |
| 201 | POST exitoso (crea recurso) | Crear un tenant. |
| 204 | DELETE exitoso | Eliminar registro. |
| 400 | Payload inválido | Faltan campos requeridos. |
| 401 | Token faltante o inválido | Sin cookie sid. |
| 403 | Token válido pero sin permisos | Rol insuficiente o tenant no asignado. |
| 404 | Recurso no existe | ID no encontrado. |
| 500 | Error del servidor | Fallo de conexión a base de datos. |
| 501 | Vista o feature no disponible | Vista materializada no creada aún. |
Formato de respuesta de error
Section titled “Formato de respuesta de error”errorHandler produce siempre el mismo shape:
{ "success": false, "error": "NOT_FOUND", "message": "Liquidación con id 'abc-123' no encontrado", "requestId": "5f1e1d2a-...", "timestamp": "2026-05-23T12:00:00.000Z", "path": "/api/remuneraciones/payroll/abc-123", "method": "GET"}En NODE_ENV !== 'production' la respuesta agrega stack y context. En errores 5xx producción, message se reemplaza por "An unexpected error occurred" para no filtrar internals.
Errores de ValidationError incluyen además un campo errors con el detalle por campo (formato express-validator).
Catálogo de Dominios
Section titled “Catálogo de Dominios”Cada dominio vive en src/domain/<contexto>/ con su trío Service + Repository + (opcional) Engine/Calculator. La página de detalle de cada uno explica entidades, calculadoras y endpoints.
| Dominio | Carpeta | Endpoints principales |
|---|---|---|
| Remuneraciones | domain/payroll/ y otros | /api/remuneraciones/payroll, /api/employees, /api/contracts, /api/attendance, /api/vacations, /api/permissions, /api/honorarios, /api/finiquitos, /api/previsiones, /api/afp, /api/isapre, /api/apv_contracts, /api/isapre_contracts, /api/cargos, /api/working_day, /api/generate-pdf |
| Operaciones SII | domain/operaciones/ | /api/operaciones, /api/accounting, /api/etl — compras, ventas, boletas, ETL mensual. |
| Activo Fijo | domain/activo-fijo/ | /api/activo-fijo — contabilización y depreciación. |
| Inventario | domain/inventario/ | /api/inventario. |
| Gastos | domain/gastos/ | /api/gastos. |
| Financieros | domain/financieros/ | /api/financieros — instrumentos, cuentas bancarias. |
| Declaraciones | domain/declaraciones/ | /api/declaraciones/f29. |
| Declaraciones Juradas | domain/declaraciones_juradas/ | /api/declaraciones-juradas/1887, /api/declaraciones-juradas/1879. |
| Ciclo Contable | domain/cicloContable/ | /api/ciclo-contable — apertura, cierre, ajustes. |
| Reportes | domain/reportes/ | /api/reportes. |
| Manual de Cuentas | domain/internal/ + más | /api/manual-cuentas, /api/command/manual-cuentas, /api/internal/manual-cuentas. |
| Administración | domain/{company,capital,legal-representatives,system-config,configContable}/ | /api/admin/company, /api/admin/capital, /api/admin/representatives, /api/admin/system-config, /api/admin/config-contable, /api/admin/chart-of-accounts. |
| Common | domain/common/ | /api/parameters, /api/afc, /api/impuesto_2cat. |
| Command | domain/command/, domain/auth/ | /api/auth, /api/admin, /api/tenant, /api/tenant-db, /api/sessions, /api/menu, /api/monitoring, /api/command/plan-cuentas. |
| Agentes | services/agent/ | /api/agent/tasks, /api/agent/registry — orquestación de agentes AI internos. |
| Cache | domain/cache/ | Cache compartido por servicios. |
Mapa completo de rutas
Section titled “Mapa completo de rutas”Generado contra src/app.ts. Las bases listadas son las relevantes; cada handler resuelve el tenantPool cuando aplica.
Command
Section titled “Command”| Prefijo | Archivo | Base / Schema |
|---|---|---|
/api/auth | routes/command/auth.ts | nostromo_command.users · sessions |
/api/admin | routes/command/users.ts | nostromo_command.users |
/api/sessions | routes/command/sessions.ts | nostromo_command.sessions |
/api/tenant | routes/command/tenant.ts | nostromo_command.tenants |
/api/tenant-db | routes/command/tenant-db.ts | nostromo_command.tenant_databases |
/api/menu | routes/command/menu.ts | Menús dinámicos |
/api/monitoring | routes/command/monitoring.ts | Métricas del sistema |
/api/command/plan-cuentas | routes/command/plan-cuentas.ts | Plan contable canónico |
/api/command/manual-cuentas | routes/command/manual-cuentas.ts | Manual de cuentas (admin) |
/api/internal/manual-cuentas | routes/internal/manual-cuentas.ts | Export read-only (token interno) |
/api/agent/tasks | routes/command/agent-tasks.ts | Sistema de agentes (MongoDB) |
/api/agent/registry | routes/command/registry.ts | Registry de agentes |
Common
Section titled “Common”| Prefijo | Archivo | Base / Schema |
|---|---|---|
/api/parameters | routes/common/parameters.ts | parametros.indicadores |
/api/afc | routes/common/afc.ts | parametros.afc |
/api/impuesto_2cat | routes/common/impuesto_2cat.ts | parametros.impuesto_2cat |
Operaciones SII y ETL
Section titled “Operaciones SII y ETL”| Prefijo | Archivo | Base / Schema |
|---|---|---|
/api/operaciones | routes/operaciones/ | operaciones_sii.* |
/api/accounting | routes/accounting.ts | operaciones_sii.* |
/api/etl | routes/etlRoutes.ts (+ controllers/EtlController.ts) | Pipeline ETL mensual |
Remuneraciones
Section titled “Remuneraciones”| Prefijo | Archivo | Base / Schema |
|---|---|---|
/api/employees | routes/remuneraciones/employees.ts | remuneraciones.empleados |
/api/contracts | routes/remuneraciones/contracts.ts | remuneraciones.contratos |
/api/cargos | routes/remuneraciones/cargos.ts | remuneraciones.cargos |
/api/departments | routes/remuneraciones/departments.ts | remuneraciones.departamentos |
/api/attendance | routes/remuneraciones/attendance.ts | remuneraciones.asistencia_dia |
/api/working_day | routes/remuneraciones/working_day.ts | remuneraciones.jornadas |
/api/vacations | routes/remuneraciones/vacations.ts | remuneraciones.vacaciones |
/api/permissions | routes/remuneraciones/permissions.ts | remuneraciones.permisos |
/api/afp | routes/remuneraciones/afp.ts | remuneraciones.afp |
/api/isapre | routes/remuneraciones/isapre.ts | remuneraciones.isapre |
/api/apv_contracts | routes/remuneraciones/apv_contracts.ts | remuneraciones.contrato_apv |
/api/isapre_contracts | routes/remuneraciones/isapre_contracts.ts | remuneraciones.contrato_isapre |
/api/remuneraciones/payroll | routes/remuneraciones/payroll.ts | remuneraciones.liquidaciones |
/api/previsiones | routes/remuneraciones/previsiones.ts | Agregaciones de seguridad social |
/api/honorarios | routes/remuneraciones/honorarios.ts | remuneraciones.honorarios |
/api/finiquitos | routes/remuneraciones/finiquitos.ts | remuneraciones.finiquitos |
/api/generate-pdf | routes/remuneraciones/generate-pdf.ts | Generación de PDF |
Administración
Section titled “Administración”| Prefijo | Archivo | Base / Schema |
|---|---|---|
/api/admin/chart-of-accounts | routes/admin/chart-of-accounts.ts | administracion.plan_contable |
/api/admin/company | routes/admin/company.ts | administracion.empresas |
/api/admin/capital | routes/admin/capital.ts | administracion.capital |
/api/admin/representatives | routes/admin/representatives.ts | administracion.representantes_legales |
/api/admin/system-config | routes/admin/system-config.ts | administracion.system_config |
/api/admin/config-contable | routes/admin/config-contable.ts | Configuración contable |
Otros dominios
Section titled “Otros dominios”| Prefijo | Archivo | Base / Schema |
|---|---|---|
/api/activo-fijo | routes/activo-fijo/ | activo_fijo.* |
/api/inventario | routes/inventario/ | inventario.* |
/api/gastos | routes/gastos/ | gastos.* |
/api/financieros | routes/financieros/ | financieros.* |
/api/declaraciones/f29 | routes/declaraciones/f29.ts | declaraciones.f29 |
/api/declaraciones-juradas/1887 | routes/declaraciones_juradas/dj1887.ts | declaraciones_juradas.dj1887 |
/api/declaraciones-juradas/1879 | routes/declaraciones_juradas/dj1879.ts | declaraciones_juradas.dj1879 |
/api/ciclo-contable | routes/ciclo-contable/ | ciclo_contable.* |
/api/reportes | routes/reportes/ | Vistas y reportes derivados |
/api/manual-cuentas | routes/manual-cuentas/ | Manual de cuentas (lectura) |
Sistema
Section titled “Sistema”| Endpoint | Tipo | Propósito |
|---|---|---|
/health | Sonda | {"status":"OK","service":"orchestrator"}. |
/metrics | Prometheus | Métricas internas vía metricsHandler. |
/files/liquidaciones/<pdf> | Static | PDFs servidos con cache inmutable. |
Observabilidad
Section titled “Observabilidad”| Mecanismo | Qué provee |
|---|---|
morgan('combined') | Log estilo Apache de cada petición HTTP a stdout. |
requestIdMiddleware | UUID por petición, propagado al errorHandler y a respuestas de error. |
metricsMiddleware | Recolecta contadores y latencias; expuestos en /metrics (Prometheus). |
auditMiddleware | Loguea operaciones mutantes (POST/PUT/PATCH/DELETE) con user, path, params. |
/health | Sonda de liveness; no toca la base ni atraviesa rate limit. |
/api/monitoring | Endpoints internos del dominio Command (carga del sistema, uptime, sesiones). |
errorHandler | Centraliza el log: 5xx → console.error con stack; 4xx → console.warn con ctx. |
Los logs de stdout/stderr quedan a cargo del runtime (Docker, systemd o Cloudflare). No hay sink de logs externo configurado por defecto.
Testing
Section titled “Testing”Tres tipos según sufijo de archivo:
| Tipo | Sufijo | Qué cubre | Velocidad |
|---|---|---|---|
| Unit | *.unit.test.ts | Funciones puras (Engine, Calculators). Sin DB. | <100ms |
| Integration | *.integration.test.ts | Repository contra DB real o transacciones. | 100–1000ms |
| E2E | *.e2e.test.ts | Ciclo HTTP completo vía supertest. | 1–5s |
Cobertura esperada:
- Engines (lógica de negocio): 90%+.
- Endpoints críticos (auth, generación de nómina): 100%.
- Repository (queries SQL): 70%+.
Detalle de runners y configuración en Testing.
Scripts pnpm
Section titled “Scripts pnpm”Desde la raíz del workspace Accounting/ (preferido) o pnpm --dir orchestrator ...:
| Comando | Propósito |
|---|---|
pnpm dev:orchestrator | Hot reload (nodemon -r tsconfig-paths/register). |
pnpm --dir orchestrator build | Compilar TypeScript a dist/. |
pnpm --dir orchestrator start | Servidor de producción (node dist/server.js). |
pnpm --dir orchestrator test | Todos los tests (jest). |
pnpm --dir orchestrator test:unit | Solo unit tests (*.unit.test.ts). |
pnpm --dir orchestrator test:integration | Solo integration tests. |
pnpm --dir orchestrator test:domain | Tests de la capa de dominio (src/domain/). |
pnpm --dir orchestrator test:payroll | Tests del dominio Remuneraciones. |
pnpm --dir orchestrator test:watch | Modo watch. |
pnpm --dir orchestrator lint | ESLint sobre src/**/*.ts. |
pnpm --dir orchestrator lint:fix | ESLint con --fix. |
pnpm --dir orchestrator scan:manual-cuentas | CLI: inspeccionar manual de cuentas. |
pnpm --dir orchestrator seed:manual-cuentas | CLI: sembrar manual de cuentas. |