codigo0/.cursorrules

581 lines
19 KiB
Plaintext
Raw Normal View History

# Cursor Rules - EMERGES TES
## Arquitectura Clean Architecture + TypeScript + PostgreSQL
**Última actualización:** 2025-01-29
**Versión:** 3.0
---
## 🎯 DECISIONES TÉCNICAS CONSOLIDADAS
### 1. Value Objects (Híbrido)
- ✅ Entidades usan **tipos simples** (`ContentStatusType`, `ContentPriorityType`)
- ✅ Value Objects (`ContentStatus`, `ContentPriority`) se usan en **servicios** para validación
- ✅ Domain Layer mantiene entidades como POJOs inmutables
### 2. Serialización (Mappers)
- ✅ **Mappers separados** en `infrastructure/mappers/`
- ✅ Domain Layer NO tiene métodos `toJSON`/`fromJSON`
- ✅ Mappers convierten entre Domain ↔ Persistence
### 3. IDs (Application Layer)
- ✅ UUIDs generados en **Application Layer** (Use Cases)
- ✅ Pasados como parámetro a métodos `create` de entidades
- ✅ Permite inyección de IDs en tests
### 4. Validación (Híbrido)
- ✅ **Validaciones básicas** en métodos `create` de entidades (formato, longitud)
- ✅ **Validaciones complejas** en Application Services (unicidad, dependencias)
- ✅ **Zod** en Application Layer para validar esquemas de entrada
### 5. Fechas (ISO 8601 Strings)
- ✅ Usar `string` con formato ISO 8601: `"2025-01-25T10:00:00Z"`
- ✅ NO usar `Date` nativo en entidades
- ✅ Mappers convierten strings ↔ PostgreSQL TIMESTAMPTZ
### 6. Arrays (readonly T[])
- ✅ Usar `readonly string[]` para arrays inmutables
- ✅ NO usar `ReadonlyArray<T>` (más verboso)
- ✅ Mantener inmutabilidad por defecto
### 7. Opcionales (Híbrido)
- ✅ `?` para campos opcionales: `readonly description?: string`
- ✅ `| null` cuando null tiene significado: `readonly validatedAt: string | null`
- ✅ Distinguir entre "no proporcionado" vs "explícitamente null"
### 8. Errores (Personalizados)
- ✅ Usar `DomainError`, `ValidationError`, `BusinessRuleError`
- ✅ NO usar errores genéricos de JavaScript
- ✅ Errores con código y contexto
### 9. Versionado (Números Enteros)
- ✅ `version: number` y `latestVersion: number`
- ✅ NO usar semantic versioning (`"1.0.0"`)
- ✅ Incrementales simples para comparación fácil
### 10. JSONB (Union Types)
- ✅ Union types para `content`: `ProtocolContent | GuideContent | ManualContent`
- ✅ NO usar `Record<string, unknown>` genérico
- ✅ Type safety completo con narrowing automático
---
## 📐 ARQUITECTURA
### Estructura de Capas
```
domain/ → Entidades, Value Objects, Repository Interfaces
application/ → Services, Use Cases, DTOs
infrastructure/ → Repository Implementations, Mappers, Database
presentation/ → Routes, Middleware, Validators (Zod)
shared/ → Types, Errors, Utils
```
### Reglas de Dependencias
- ✅ **Regla de Dependencia:** Las dependencias deben apuntar siempre hacia adentro (hacia el Dominio)
- ✅ **Ninguna capa interna puede conocer detalles de una capa externa**
- ✅ Domain: NO depende de nadie
- ✅ Application: Solo depende de Domain
- ✅ Infrastructure: Depende de Domain y Application
- ✅ Presentation: Depende de Application y Domain
---
## 🔒 REGLAS DE CÓDIGO
### TypeScript
- ✅ **PROHIBIDO el uso de `any`** - Todos los tipos deben ser estrictos y explícitos
- ✅ **Tipado Estricto:** Todos los tipos deben estar definidos explícitamente en `/types` o dentro del dominio
- ✅ Usar tipos explícitos, evitar `any`
- ✅ Usar `readonly` para propiedades inmutables
- ✅ Preferir `interface` sobre `type` para objetos extensibles
- ✅ Usar `type` para unions, intersections, primitives
- ✅ NO usar `@ts-ignore` sin comentario explicativo
- ✅ **Higiene de datos:** Si un tipo no puede ser inferido, definirlo explícitamente
- ✅ **Tipos en dominio:** Tipos de dominio deben estar en `domain/types/` o dentro de entidades
### Entidades de Dominio
- ✅ Todas las propiedades `readonly`
- ✅ Tipos simples (no clases) en interfaces
- ✅ Métodos estáticos `create()` para construcción
- ✅ Validaciones básicas en `create()`
- ✅ IDs recibidos como parámetro (no generados internamente)
```typescript
// ✅ CORRECTO
interface ContentItem {
readonly id: string;
readonly title: string;
readonly status: ContentStatusType; // Tipo simple
readonly createdAt: string; // ISO 8601
readonly tags: readonly string[]; // Array inmutable
}
static create(
id: string, // ID inyectado
title: string,
// ...
): ContentItem {
// Validación básica
if (!title || title.trim().length === 0) {
throw new ValidationError('Título es obligatorio');
}
return { id, title: title.trim(), ... };
}
```
### Value Objects
- ✅ Clases con constructor privado
- ✅ Métodos estáticos `fromString()`, `create()`
- ✅ Métodos `toString()`, `equals()`, `canTransitionTo()`
- ✅ Usados en Services, NO en entidades
```typescript
// ✅ CORRECTO
export class ContentStatus {
private constructor(private readonly value: string) {}
static fromString(value: string): ContentStatus {
// Validación
return new ContentStatus(value);
}
toString(): string {
return this.value;
}
}
```
### Mappers
- ✅ En `infrastructure/mappers/`
- ✅ Métodos estáticos `toDomain()` y `toPersistence()`
- ✅ Conversión de tipos (string ↔ Date, snake_case ↔ camelCase)
- ✅ **PROHIBIDO:** Pasar entidades de base de datos directamente a la UI
- ✅ **OBLIGATORIO:** Implementar Mappers en capa de infraestructura para traducir datos de persistencia a entidades de dominio
- ✅ **Validación Zod:** Todos los mappers deben validar datos con Zod antes de convertir a dominio
```typescript
// ✅ CORRECTO - Con validación Zod
import { z } from 'zod';
const ContentItemRowSchema = z.object({
id: z.string().min(1),
status: z.string(),
created_at: z.string(),
// ...
});
class ContentItemMapper {
static toDomain(row: unknown): ContentItem {
// Validar con Zod antes de convertir
const validated = ContentItemRowSchema.parse(row);
return {
id: validated.id,
status: validated.status as ContentStatusType,
createdAt: validated.created_at, // Ya es string ISO
// ...
};
}
static toPersistence(item: ContentItem): Record<string, unknown> {
return {
id: item.id,
status: item.status,
created_at: item.createdAt, // String ISO
// ...
};
}
}
```
### Validación
- ✅ Zod en Application Layer para esquemas de entrada
- ✅ Validaciones básicas en Domain (`create()`)
- ✅ Validaciones complejas en Application Services
- ✅ Mensajes de error claros y específicos
### Errores
- ✅ Usar `DomainError`, `ValidationError`, `BusinessRuleError`
- ✅ Incluir código y contexto
- ✅ NO silenciar errores con `catch` vacío
- ✅ **Early Returns para errores:** Usar retornos tempranos en lugar de anidar condiciones
```typescript
// ✅ CORRECTO
throw new ValidationError('Título es obligatorio', {
field: 'title',
value: title
});
throw new BusinessRuleError('Slug ya existe', {
slug,
existingId: existing.id
});
```
### Funciones
- ✅ Máximo 20-30 líneas
- ✅ Una sola responsabilidad
- ✅ Nombres descriptivos
- ✅ Parámetros máximo 3-4, usar objetos si hay más
- ✅ **Early Returns obligatorios:** Usar retornos tempranos para manejar condiciones de error o datos no definidos
- ✅ **Regla de los 15 minutos:** Si la lógica no puede entenderse en 15 minutos, simplificar o dividir en módulos más pequeños
### Base de Datos
- ✅ Usar parámetros preparados (nunca concatenar SQL)
- ✅ Validar datos antes de insertar/actualizar
- ✅ Usar transacciones para operaciones múltiples
- ✅ Índices apropiados para queries frecuentes
---
## 📁 CONVENCIONES DE ARCHIVOS
### Nomenclatura
- **Archivos TypeScript:** `kebab-case.ts` o `kebab-case.tsx` para componentes React
- **Componentes React:** `kebab-case.tsx` (ej. `rcp-protocol-view.tsx`, `drug-card.tsx`)
- **Carpetas:** `kebab-case` (siempre)
- **Tipos/Interfaces:** `PascalCase`
- **Funciones:** `camelCase`
- **Constantes:** `UPPER_SNAKE_CASE`
- **Event Handlers:** Prefijar con `handle` (ej. `handleClick`, `handleSubmit`)
### Estructura
```
domain/entities/
└── ContentItem.ts # Entidad de dominio
application/services/
└── ContentService.ts # Servicio de aplicación
infrastructure/repositories/
└── ContentRepository.ts # Repositorio de infraestructura
infrastructure/mappers/
└── ContentItemMapper.ts # Mapper de infraestructura
presentation/routes/
└── content.ts # Rutas Express
```
---
## 🧪 TESTING
### Estructura
- ✅ Tests unitarios en `tests/unit/`
- ✅ Tests de integración en `tests/integration/`
- ✅ Tests de API en `tests/api/`
- ✅ Mocks en `tests/mocks/`
- ✅ Fixtures en `tests/fixtures/`
### Buenas Prácticas
- ✅ Un test por caso de uso
- ✅ Tests independientes
- ✅ Usar mocks para dependencias externas
- ✅ Arrange-Act-Assert claro
- ✅ Cobertura mínima: 80%
---
## 🚫 ANTI-PATRONES A EVITAR
### ❌ NO hacer:
- Funciones de más de 30 líneas
- Mutar objetos directamente (usar inmutabilidad)
- Validar solo en frontend
- Usar `any` en TypeScript
- Código duplicado (DRY)
- Imports no usados
- Código comentado (eliminar o documentar)
- Catch vacío sin logging
- SQL concatenado (usar parámetros)
- Tests que dependen de otros tests
- Métodos `toJSON`/`fromJSON` en entidades
- Generar IDs dentro de entidades
- Usar `Date` nativo en entidades
- Usar `ReadonlyArray<T>` (usar `readonly T[]`)
- **Componentes de fetching sin estados de loading/error**
- **Acceder a propiedades sin verificar que el objeto existe** (usar optional chaining o guard clauses)
- **Código que requiere más de 15 minutos para entender** (simplificar o dividir)
- **Pasar entidades de BD directamente a UI** (usar mappers)
- **Casos de uso que invocan otros casos de uso** (lógica circular)
- **Event handlers sin prefijo `handle`**
- **Nomenclatura incorrecta** (usar kebab-case para componentes)
- **TODOs, FIXMEs, comentarios de cierre** en código final
- **Commits no convencionales** o mensajes >60 caracteres
- **Falta de validación Zod** en entradas externas
---
## 📚 PATRONES RECOMENDADOS
### Repository Pattern
```typescript
interface IContentRepository {
findById(id: string): Promise<ContentItem | null>;
findAll(filters: ContentFilters): Promise<{ items: ContentItem[]; total: number }>;
save(content: ContentItem): Promise<ContentItem>;
delete(id: string): Promise<void>;
}
```
### Service Layer
```typescript
class ContentService {
constructor(
private readonly repository: IContentRepository,
private readonly mapper: ContentItemMapper
) {}
async createContent(input: CreateContentDTO): Promise<ContentItem> {
// 1. Validar con Zod
const validated = createContentSchema.parse(input);
// 2. Validaciones complejas
if (await this.repository.existsBySlug(validated.slug)) {
throw new BusinessRuleError('Slug ya existe');
}
// 3. Generar ID
const id = randomUUID();
// 4. Crear entidad (validaciones básicas aquí)
const content = ContentItem.create(id, validated.title, ...);
// 5. Persistir
return await this.repository.save(content);
}
}
```
### Use Case Pattern
```typescript
class CreateContentUseCase {
constructor(
private readonly repository: IContentRepository,
private readonly validator: ContentValidator
) {}
async execute(input: CreateContentDTO): Promise<ContentItem> {
// Orquestación del caso de uso
}
}
```
---
## 🔍 CODE REVIEW CHECKLIST
Antes de hacer commit, verificar:
- [ ] Funciones <30 líneas
- [ ] Validación con Zod en todos los inputs
- [ ] Tests escritos y pasando
- [ ] Sin código duplicado
- [ ] Sin imports no usados
- [ ] **Sin `any` en TypeScript** (PROHIBIDO)
- [ ] Entidades inmutables (`readonly`)
- [ ] Manejo de errores apropiado
- [ ] Logs de auditoría donde corresponda
- [ ] Documentación de funciones complejas
- [ ] IDs generados en Application Layer
- [ ] Fechas como strings ISO 8601
- [ ] Arrays como `readonly T[]`
- [ ] Mappers separados en Infrastructure
- [ ] **Componentes de fetching tienen estados de loading y error**
- [ ] **Early returns para condiciones de error y datos undefined**
- [ ] **Código comprensible en menos de 15 minutos** (si no, simplificar)
- [ ] **Guard clauses aplicadas antes de acceder a propiedades**
- [ ] **Nomenclatura kebab-case para componentes React**
- [ ] **Event handlers prefijados con `handle`**
- [ ] **Mappers validan con Zod antes de convertir a dominio**
- [ ] **Casos de uso no invocan otros casos de uso**
- [ ] **Accesibilidad AA cumplida (roles ARIA, navegación por teclado)**
- [ ] **Semántica HTML apropiada**
- [ ] **Sin TODOs ni marcadores en código final**
- [ ] **Commits convencionales con mensajes <60 caracteres**
---
## 📖 REFERENCIAS
- SPEC.md: Arquitectura completa del proyecto
- Clean Architecture: Robert C. Martin
- Domain-Driven Design: Eric Evans
- Zod: https://zod.dev
---
## 🛡️ REGLAS DE SEGURIDAD Y ROBUSTEZ
### Higiene de Datos
- ✅ **PROHIBIDO el uso de `any`** - Todos los tipos deben ser estrictos
- ✅ Validar todos los datos de entrada antes de procesarlos
- ✅ Usar tipos explícitos en lugar de inferencia cuando hay ambigüedad
- ✅ Verificar que arrays/objetos existen antes de acceder a propiedades
### Estados Obligatorios en Fetching
- ✅ **OBLIGATORIO:** Todos los hooks/componentes que obtienen datos deben manejar:
- Estado `loading` (mientras se cargan los datos)
- Estado `error` (si falla la carga)
- Estado `success` (datos disponibles)
- Estado `not_found` (recurso no encontrado)
- ✅ Usar Discriminated Unions para type safety
- ✅ Renderizar componentes de fallback (`PageLoader`, `NotFound`) según el estado
- ✅ **Estados de Carga y Error:** Todos los componentes que realicen obtención de datos deben incluir obligatoriamente estados de loading y error
### Andragogía clínica y Stress-Ready Design (112/061)
La interfaz debe actuar como **socio cognitivo**, no como manual digital: reducir carga cognitiva y respetar la autonomía del facultativo en emergencias.
- ✅ **Orientación al problema:** Estructurar por problemas clínicos (ej. "Parada Cardiorrespiratoria"); contenido accionable ("qué hacer") en menos de 2 clics.
- ✅ **Autonomía:** Navegación no lineal (saltar a dosis/algoritmos sin secuencia rígida); Offline-First cuando sea posible.
- ✅ **Experiencia previa:** Terminología y modelos familiares (estándares clínicos); resúmenes visuales + detalle expandible para quien lo necesite.
- ✅ **Procesamiento sensorial / estrés:** Jerarquía visual (Lookability): elementos críticos (alertas, dosis) visualmente dominantes; multimodalidad (señales auditivas, diagramas claros) cuando aplique.
- ✅ **Simulación y maestría:** Modos de práctica/simulación y feedback inmediato (checklists, confirmaciones) para transferir habilidades al mundo real.
Referencia completa: `docs/ANDRAGOGIA_STRESS_READY_112.md`
### Seguridad con Early Returns
- ✅ **SIEMPRE usar retornos tempranos** para manejar:
- Datos `undefined` o `null`
- Condiciones de error
- Validaciones fallidas
- Estados de carga
- ✅ Evitar anidar condiciones profundamente
- ✅ Aplicar guard clauses al inicio de funciones/componentes
```typescript
// ✅ CORRECTO - Early returns
function processData(data: Data | undefined): Result {
if (!data) return { error: 'Datos no disponibles' };
if (!data.id) return { error: 'ID requerido' };
if (data.status === 'error') return { error: data.message };
// Solo aquí procesamos datos válidos
return { success: true, result: transform(data) };
}
// ❌ INCORRECTO - Anidación profunda
function processData(data: Data | undefined): Result {
if (data) {
if (data.id) {
if (data.status !== 'error') {
return { success: true, result: transform(data) };
}
}
}
return { error: 'Error' };
}
```
### Regla de los 15 Minutos
- ✅ **Código debe ser comprensible en 15 minutos**
- ✅ Si una función/componente es demasiado compleja:
- Dividirla en funciones más pequeñas
- Extraer lógica a hooks personalizados
- Crear componentes intermedios
- Documentar la lógica compleja con comentarios claros
- ✅ **Aplicar a IA:** Si Cursor propone una lógica que no puede ser comprendida por un humano en menos de 15 minutos, debe ser simplificada o dividida en submódulos
- ✅ Evitar "deuda de comprensión" - código que solo el autor entiende
```typescript
// ✅ CORRECTO - Función simple y clara
function calculateTotal(items: Item[]): number {
if (!items || items.length === 0) return 0;
return items.reduce((sum, item) => sum + item.price, 0);
}
// ❌ INCORRECTO - Demasiado compleja, requiere más de 15 min para entender
function processComplexData(data: any): any {
// 50 líneas de lógica compleja mezclada...
}
```
---
## 📝 GOBERNANZA DEL CÓDIGO IA
### Higiene de Commits
- ✅ **Commits Convencionales:** Usar siempre formato convencional (`feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`)
- ✅ **Mensajes Cortos:** Mantener los mensajes de commit por debajo de 60 caracteres
- ✅ **Descripción Opcional:** Si se necesita más contexto, usar cuerpo del commit después de línea vacía
```bash
# ✅ CORRECTO
feat: añadir guard clauses en Farmacos.tsx
fix: corregir acceso inseguro a drug.id
docs: actualizar SPEC.md con decisiones técnicas
# ❌ INCORRECTO
Added guard clauses to Farmacos component # ❌ No convencional
fix: corregir el problema de acceso inseguro a la propiedad id del objeto drug que causaba errores de runtime # ❌ >60 caracteres
```
### Documentación Continua
- ✅ **Actualizar SPEC.md:** Por cada cambio mayor, actualizar `SPEC.md` con decisiones técnicas
- ✅ **Registro de Decisiones:** Mantener ADL (Architecture Decision Log) para decisiones arquitectónicas importantes
- ✅ **Memoria del Proyecto:** La IA debe mantener la "memoria" del proyecto actualizando documentación
### Prohibición de Marcadores
- ❌ **NO dejar TODOs** en código final
- ❌ **NO dejar comentarios de llaves de cierre** (ej. `} // end function`)
- ❌ **NO dejar marcadores de posición** (ej. `// FIXME`, `// HACK`, `// XXX`)
- ✅ Si hay trabajo pendiente, crear ticket o documentar en SPEC.md
```typescript
// ❌ INCORRECTO - Marcadores prohibidos
function calculateDose() {
// TODO: añadir validación de peso
return weight * dose;
} // end calculateDose
// ✅ CORRECTO - Código limpio
function calculateDose(weight: number, dose: number): number {
if (weight <= 0) throw new ValidationError('Peso debe ser positivo');
return weight * dose;
}
```
### Checklist antes de aceptar cambios (IA)
**Antes de que el usuario acepte cualquier cambio propuesto, la IA debe verificar y responder explícitamente:**
1. **Clean Architecture:** ¿El código cumple con la Clean Architecture y las capas definidas? (Domain → Application → Infrastructure → Presentation; sin dependencias invertidas.)
2. **Pruebas:** ¿Se han incluido pruebas unitarias con una cobertura mínima del 80% para el código nuevo o modificado?
3. **Andragogía / UX:** ¿La interfaz reduce la latencia cognitiva siguiendo los principios de andragogía? (Información crítica visible, estados loading/error, fallbacks.)
4. **Seguridad / PII:** ¿Se han enmascarado los datos sensibles (PII) y evitado claves API en texto plano?
- ✅ Documento completo: `docs/CHECKLIST_ANTES_ACEPTAR_CAMBIOS.md`
- ✅ Si algún punto no se cumple, indicarlo y proponer corrección o excepción documentada.
---
**Fin de Cursor Rules**