20 KiB
Plan de Refactor Técnico — 2025-11-01
Este documento describe un plan de refactor seguro, incremental y medible para estabilizar tipado, reducir duplicación, mejorar estructura y preparar la base para evolución a largo plazo. Se prioriza no añadir dependencias externas y mantener el comportamiento actual cubierto por tests.
Objetivos
- Reducir duplicación (fechas/horas, validaciones, helpers de tests, cripto).
- Endurecer tipado y contratos en TypeScript (mientras mantenemos compatibilidad).
- Clarificar capas (utils compartidos, servicios, API web, UI SvelteKit, tests).
- Estabilizar convenciones (UTC, formato SQLite, alias/imports, nombres).
- Mantener comportamiento: cambios sin funcionalidad nueva, validados por tests.
Hallazgos clave (del diagnóstico inicial)
-
Archivos grandes:
- apps/web/src/lib/ui/data/TaskItem.svelte ~911 LOC (punto caliente claro).
- Varias páginas Svelte de 250–335 LOC (AppShell, /app, /app/groups, etc.).
-
Duplicación evidente:
- Formateo SQL-UTC con
toISOString().replace('T',' ').replace('Z','')aparece 35+ veces en app y tests. normalizeTimeduplicado en API y page.server.ymdUTCyaddMonthsUTCdefinidas 3 veces en rutas ICS y también en tests.sha256Hexreimplementado en múltiples tests pese a existir ensrc/utils/crypto.ts.- SimulatedResponseQueue replicado en varios tests.
- Formateo SQL-UTC con
-
Tipado/infra:
- tsc falla en apps/web por
$liby./$types(faltan tipos generados SvelteKit) y por DOM (navigator/document) y Bun types (HeadersInit/fetch). - apps/web/vite.config.ts incompatibilidad de tipos (UserConfig).
- Errores “unknown” por resultados de SQLite (faltan tipos/casts).
- Inconsistencias null vs undefined (p. ej., groupColor, whatsapp utils).
- tsc falla en apps/web por
-
Cobertura mejorable:
- webhook-manager (~32%), maintenance (~17%), contacts (~26%), onboarding (~30%), response-queue (~50%).
-
Organización:
- Inyección de DB mediante propiedades estáticas dispersas; funcional pero frágil. Falta un “locator” central.
Fase 1 — Diagnóstico asistido (Completada)
Comandos ejecutados con éxito el 2025-11-01; a continuación se conservan para referencia y repetibilidad:
git ls-files '*.ts' '*.tsx' '*.svelte' | xargs wc -l | sort -nr | head -n 30
git grep -n "toISOString().replace('T', ' ').replace('Z', '')"
git grep -n "normalizeTime(" && git grep -n "ymdUTC(" && git grep -n "addMonthsUTC(" && git grep -n "isValidYmd("
git grep -n "sha256Hex(" && git grep -n "SimulatedResponseQueue"
bunx tsc --noEmit --pretty false
bun test --coverage
Resultados esperados después del refactor: disminución drástica de duplicados, tsc limpio por paquete, y cobertura estable o al alza.
Progreso
- 2025-11-01:
- Documento creado y versionado (commit
a104b69). - Diagnóstico ejecutado (wc -l, git grep, tsc, coverage) y hallazgos consolidados en este documento.
- Pendiente iniciar Lote 0 (Infra de typecheck): a la espera de revisar tsconfig raíz, apps/web/tsconfig y apps/web/vite.config.
- Documento creado y versionado (commit
- 2025-11-02:
- Lote 1 completado: util canónica de fechas/UTC creada (src/utils/datetime.ts) y wrapper web (apps/web/src/lib/server/datetime.ts); migración de rutas ICS y reemplazos en producción (hooks, login, complete/uncomplete, calendar tokens, dev-seed, response-queue, maintenance, group-sync, migrator).
- Commits relevantes:
f4f7d95,882f5c9,c1f12ff,df27161,a0f35b8. - Tests verdes. Cobertura actual: 85.91% funciones / 82.12% líneas.
- 2025-11-07:
- Lote 0 completado: scripts
typecheck:coreytypecheck:webconfigurados y verificados. Se utilizatsconfig.core.jsonpara aislar el typecheck del core con reglas laxas, mientras que la web usa su propia configuración de SvelteKit. Los shims ensrc/types/shims.d.tsresuelven conflictos de tipos entre Bun y el DOM. - Verificación exitosa:
bun run typecheck:coreybun run typecheck:webse ejecutan sin errores.
- Lote 0 completado: scripts
- 2025-11-09:
- 2025-11-10:
- Lote 3 completado: endurecimiento de tipos (noImplicitAny, exactOptionalPropertyTypes, strictNullChecks), centralización de normalizeTime y tipado ligero de SQLite en servicios; inclusión de servicios en typecheck; limpieza de as any en puntos clave.
- Verificación: typecheck core/web limpios y tests verdes.
- Lote 4 completado: ICS central y rutas homogéneas; rate limiting por token; títulos “Wtask.org Tareas – Personal/Grupo/Agregado” y PRODID “-/Wtask.org//ICS 1.0//ES”; eventos de día completo; ETag/304 y Cache-Control; actualización de last_used_at solo en 200.
- Commit relevante:
234053c. - Lote 5 completado: división de TaskItem en subcomponentes (SVGs a iconos, TaskDueBadge, TaskAssignees, TaskCompleteButton, TaskActions, TaskText, TaskMeta); sin cambios funcionales; tests verdes y typecheck web limpio.
Estado actual (2025-11-10)
- Fase 1 — Diagnóstico asistido: Completada.
- Fase 2 — Plan de refactor por lotes: En curso.
- Lote 0 — Infra de typecheck: Completado.
- Lote 1 — Utilidades de fecha/hora y validaciones: Completado.
- Lote 2 — Helpers de test y cripto: Completado.
- Lote 3 — Tipos y endurecimiento suave: Completado.
- Lote 4 — ICS central y rutas homogéneas: Completado.
- Lote 5 — Svelte: dividir componentes grandes: Completado.
- Lote 5.5 — Refactor de servicios grandes (god classes): Pendiente.
- Lote 6 — DB Locator / DI ligera: Pendiente.
- Lote 7 — Cobertura en módulos flojos: Pendiente.
Fase 2 — Plan de refactor por lotes (PRs pequeñas y seguras)
Cada lote incluye objetivo, cambios, métricas y comprobaciones. Mantener tests verdes en cada paso.
Lote 0 — Infra de typecheck (preparación) - Completado
- Objetivo:
- Separar el chequeo de tipos por paquete (core vs web) para aislar errores de SvelteKit/Bun/DOM.
- Cambios:
- Scripts:
typecheck:core(tsc -p tsconfig.core.json),typecheck:web(apps/web: svelte-kit sync + tsc). - En raíz, excluir apps/web del typecheck general si hace falta hasta que esté sincronizado.
- Ajustar lib/types: en core, definir tipos mínimos para HeadersInit/fetch si no se quiere añadir bun-types; en web, asegurar DOM y tipos SvelteKit (via
svelte-kit sync). - Revisar apps/web/vite.config.ts para que encaje con tipos
UserConfig.
- Scripts:
- Métricas:
bunx tsc --noEmitlimpio por paquete.- Desaparecen errores
$liby./$typestrassvelte-kit sync.
- Comprobaciones:
cd apps/web && bunx svelte-kit sync && bunx tsc --noEmit.
Lote 1 — Utilidades de fecha/hora y validaciones - Completado
- Objetivo:
- Centralizar y unificar: SQL-UTC, validadores YMD y HH:mm, utilidades ICS (ymdUTC, addMonthsUTC).
- Cambios:
- Crear helpers canónicos (por ejemplo,
toIsoSqlUTC,isValidYmd,normalizeTime,ymdUTC,addMonthsUTC) en un módulo compartido (core o shared) y exportarlos también para web si aplica. - Reemplazar todas las ocurrencias del patrón
toISOString().replace(...). - Unificar las 3 rutas ICS para usar las mismas utilidades.
- Crear helpers canónicos (por ejemplo,
- Métricas:
git grep "toISOString().replace('T', ' ').replace('Z', '')"→ 1 referencia (en el helper) o 0 si se encapsula internamente.git grep "ymdUTC("y"addMonthsUTC("solo en helpers + imports.
- Comprobaciones:
- Tests ICS y API siguen verdes.
Lote 2 — Helpers de test y cripto - Completado
- Objetivo:
- Eliminar duplicación en tests.
- Cambios:
- Reemplazar implementaciones locales de
sha256Hexpor import desdesrc/utils/crypto. - Centralizar SimulatedResponseQueue en
tests/helpers/queue.tsy reusarlo. - Unificar helpers de fechas en tests (
toIsoSql,ymdUTC,addDays) entests/helpers/dates.ts.
- Reemplazar implementaciones locales de
- Métricas:
git grep -n "async function sha256Hex"en tests → 0.git grep -n "SimulatedResponseQueue"→ 1 implementación única.
- Comprobaciones:
bun test --coverageestable o mejor.
Lote 3 — Tipos y endurecimiento suave - Completado
- Objetivo:
- Bajar ruido de TS con cambios mínimos, sin funcionalidad nueva.
- Cambios:
- Resolver
undefinedvsnullen utils como groupColor/whatsapp. - Corregir
HeadersInit/fetchen servicios (group-sync, webhook-manager, contacts) para encajar con tipos. - Añadir refinamientos y cheques donde TS marca “posiblemente undefined”.
- Tipar resultados de SQLite
.get()/.all()en tests y servicios.
- Resolver
- Métricas:
bunx tsc --noEmitlimpio en core.
- Comprobaciones:
- Tests verdes.
Lote 4 — ICS central y rutas homogéneas - Completado
- Objetivo:
- Consolidar construcción de ICS (escape, folding, etag) en un módulo central.
- Cambios:
- Mover
ymdUTC/addMonthsUTCal móduloapps/web/src/lib/server/ics.tsy usarlos desde aggregate/group/personal. - Mantener helpers de escape/folding/etag en el mismo módulo.
- Mover
- Métricas:
git grep -n "function ymdUTC"y"function addMonthsUTC"solo en ics.ts.
- Comprobaciones:
- Tests ICS (
tests/web/ics.*) verdes.
- Tests ICS (
Lote 5 — Svelte: dividir componentes grandes
- Objetivo:
- Reducir complejidad de UI; mejorar mantenibilidad.
- Cambios:
- Dividir
TaskItem.svelte(~911 LOC) en subcomponentes: Header/Status, Assignees, DueBadge, Actions, Metadata, etc. - Revisar
AppShelly/app/+pagepara separar lógica de datos y presentación (stores y load).
- Dividir
- Métricas:
TaskItem.svelte< 300 LOC.
- Comprobaciones:
- Pruebas E2E web verdes.
Lote 5.5 — Refactor de servicios grandes (god classes)
-
Objetivo general:
- Reducir acoplamiento y responsabilidades múltiples en servicios grandes para mejorar testabilidad, DX y preparar Lote 6 (locator/DI) sin cambios funcionales.
- Mantener APIs públicas y rutas de import estables, delegando a nuevos módulos internos.
-
Alcance y orden de PRs:
-
PR 5.5-a — ResponseQueue (673 LOC)
- Motivos: mezcla de repositorio (SQLite), backoff, envío HTTP (texto/reacciones), idempotencia, limpieza/retención, métricas y lógica de onboarding.
- Cambios clave:
- Extraer cliente Evolution (HTTP):
- Nuevo módulo:
src/clients/evolution.tsconbuildHeaders(),sendText(payload),sendReaction(payload)y manejo de errores/logging.
- Nuevo módulo:
- Tipar metadata y parsers:
- Tipo
QueueMetadata = { kind: 'onboarding' | 'reaction'; ... }, helpersparseQueueMetadata(),isReactionJob().
- Tipo
- Aislar limpieza/retención:
- Nuevo módulo:
src/services/queue/cleanup.tsconrunCleanupOnce(),startCleanupScheduler(),stopCleanupScheduler().
- Nuevo módulo:
- ResponseQueue queda como orquestador (claim → enviar → marcar), delegando a EvolutionClient y Cleanup.
- Extraer cliente Evolution (HTTP):
- Archivos a consultar:
src/services/response-queue.ts(origen),src/utils/datetime.ts,src/services/metrics.ts,src/services/identity.ts,src/utils/whatsapp.ts,src/db/migrations/index.ts(esquemaresponse_queue), teststests/unit/services/response-queue.test.ts.
- Métricas de aceptación:
src/services/response-queue.ts< 350 LOC.- No variación en defaults (
MAX_ATTEMPTS,REACTIONS_MAX_ATTEMPTS, backoff). - Idempotencia de reacciones (24h) intacta.
- Comprobaciones y comandos:
bunx tsc -p tsconfig.core.json --noEmit --pretty false bun test --coverage git grep -n "sendReaction\\|sendText" src
-
PR 5.5-c — TaskService (708 LOC)
- Motivos: mezcla de creación (códigos display), queries de listado/contadores, reglas de negocio (claim/unassign), y reacción al completar.
- Cambios clave:
- Extraer generación de display_code:
- Nuevo:
src/tasks/display-code.tsconpickNextDisplayCode(db).
- Nuevo:
- Extraer reacción al completar:
- Nuevo:
src/tasks/complete-reaction.tsconenqueueCompletionReactionIfEligible(db, taskId).
- Nuevo:
- Extraer mapeos a DTO:
- Nuevo:
src/tasks/mappers.tsconmapRowToTask(),mapRowsToListItem().
- Nuevo:
TaskService.createTask/completeTask/list*delegan en estos helpers.
- Extraer generación de display_code:
- Archivos a consultar:
src/tasks/service.ts(origen),src/services/allowed-groups.ts,src/services/response-queue.ts(reacción),src/utils/whatsapp.ts,src/db/migrations/index.ts, teststests/unit/tasks/*.test.ts,tests/unit/services/command.*.test.ts.
- Métricas de aceptación:
src/tasks/service.ts< 500 LOC.- TTL y gating de reacciones sin cambios de comportamiento.
- Comprobaciones y comandos:
bunx tsc -p tsconfig.core.json --noEmit --pretty false bun test --coverage git grep -n "display_code" src
-
PR 5.5-b — GroupSyncService (1310 LOC)
- Motivos: agrupa API (Evolution), parseos, upsert/caché, scheduling, reconciliación de miembros, allowed-groups y publicación de onboarding.
- Cambios clave:
- Extraer acceso Evolution API:
- Nuevo:
src/services/group-sync/api.tsconfetchGroups(),fetchMembers(groupId)y parseos robustos.
- Nuevo:
- Extraer repositorio/caché:
- Nuevo:
src/services/group-sync/repo.tsconupsertGroups(),ensureGroupExists(),getActiveCount(),cacheActiveGroups(),listActiveMemberIds(), getters/settersonboarding_prompted_at. - Nuevo:
src/services/group-sync/cache.tspara laMap<string,string>y helpers.
- Nuevo:
- Extraer reconciliación:
- Nuevo:
src/services/group-sync/reconcile.tsconreconcileGroupMembers(db, groupId, snapshot, nowIso?).
- Nuevo:
- Mover publicación de mensajes de cobertura de alias a OnboardingService:
- Añadir método en
src/services/onboarding.ts:publishGroupCoveragePrompt(db, groupId, ratio, options).
- Añadir método en
GroupSyncServicequeda como fachada (APIs públicas y schedulers), delegando internamente.
- Extraer acceso Evolution API:
- Archivos a consultar:
src/services/group-sync.ts(origen),src/services/allowed-groups.ts,src/services/identity.ts,src/services/metrics.ts,src/utils/datetime.ts,src/db/migrations/index.ts, teststests/unit/services/group-sync.*.test.ts.
- Métricas de aceptación:
src/services/group-sync.ts≈ 600–700 LOC (fase 1), sin cambios funcionales.- Caché y métricas sin variaciones de semántica.
- Comprobaciones y comandos:
bunx tsc -p tsconfig.core.json --noEmit --pretty false bun test --coverage git grep -n "fetchAllGroups\\|participants" src/services/group-sync*
-
PR 5.5-d — WebhookServer (665 LOC)
- Motivos:
src/server.tsmezcla handlers HTTP (/metrics, /health), bootstrap y manejo del webhook (message upsert). - Cambios clave:
- Controladores HTTP dedicados:
- Nuevo:
src/http/metrics.ts(render y headers). - Nuevo:
src/http/health.ts(cálculo payload; usaGroupSyncService.getSecondsUntilNextGroupSync()). - Nuevo:
src/http/webhook-handler.tsconhandleWebhookRequest(req)yhandleMessageUpsert(data). - Nuevo:
src/http/bootstrap.tsconstartServices()(webhook-manager, schedulers, queues, maintenance).
- Nuevo:
src/server.tsqueda como wire-up (validateEnv → migrate → Bun.serve) y reexporta fachadaWebhookServerpara no romper tests.
- Controladores HTTP dedicados:
- Archivos a consultar:
src/server.ts(origen),src/services/webhook-manager.ts,src/services/response-queue.ts,src/services/maintenance.ts,src/services/reminders.ts,src/services/group-sync.ts,src/services/admin.ts,src/services/allowed-groups.ts,src/services/contacts.ts,src/services/command.ts.
- Métricas de aceptación:
src/server.ts< 350 LOC.- Rutas/contratos HTTP y logs sin cambios de comportamiento.
- Comprobaciones y comandos:
bunx tsc -p tsconfig.core.json --noEmit --pretty false bun test --coverage git grep -n "handleRequest(\\|handleMessageUpsert" src
- Motivos:
-
-
Métricas de aceptación (global Lote 5.5):
- Tests verdes y sin cambios funcionales observables.
- Reducción de LOC por fichero objetivo:
- response-queue.ts < 350, tasks/service.ts < 500, group-sync.ts ≈ 600–700, server.ts < 350.
bunx tsc --noEmitlimpio en core.- Cobertura igual o superior en piezas extraídas (api/reconcile/cleanup/complete-reaction).
-
Riesgos y mitigación:
- Dividir módulos puede introducir imports cíclicos:
- Mitigación: módulos nuevos sin importar servicios de alto nivel; inyectar
dbcomo parámetro; reexportar desde fachada.
- Mitigación: módulos nuevos sin importar servicios de alto nivel; inyectar
- Cambios en logging o tiempos:
- Mitigación: mantener mensajes/claves existentes; conservar defaults; validar en tests de integración.
- Dividir módulos puede introducir imports cíclicos:
Lote 6 — DB Locator / DI ligera
- Objetivo:
- Simplificar inyección de DB en runtime y tests.
- Cambios:
- Crear un “dbLocator” (getDb/setDb) central y migrar servicios a usarlo progresivamente, manteniendo compatibilidad con propiedades estáticas mientras dura la transición.
- Unificar
resolveDbAbsolutePathentre core y web (una sola fuente).
- Métricas:
- Sin cambios de comportamiento; tests de servicios verdes.
Lote 7 — Cobertura en módulos flojos
- Objetivo:
- Aumentar cobertura en módulos con lógica y baja cobertura (webhook-manager, maintenance, contacts, onboarding, response-queue).
- Cambios:
- Añadir pruebas de ramas de error y caminos menos transitados.
- Métricas:
- Cobertura de líneas > 80% en esos módulos.
Convenciones acordadas
-
Fechas/horas:
- Persistir en UTC en SQLite con formato ISO-SQL “YYYY-MM-DD HH:MM:SS[.SSS]”.
- Un solo helper para serializar/deserializar timestamps.
- Validadores canónicos: YMD (YYYY-MM-DD), HH:mm (00–23:00–59).
-
Tipado:
- TS estricto por módulos de forma incremental:
strict,noImplicitAny,noUncheckedIndexedAccess,exactOptionalPropertyTypes(activación progresiva). - Resultados SQLite tipados (interfaces pequeñas y casts controlados).
- TS estricto por módulos de forma incremental:
-
Organización:
- Helpers compartidos (fecha/validaciones/cripto) en módulo único (o shared).
- Tests reusan helpers de
tests/helpers.
-
SvelteKit:
svelte-kit syncantes detscpara generar tipos.- Rutas ICS homogéneas con helpers de
lib/server/ics.
Riesgos y mitigación
- Fechas/UTC: posibles regresiones en límites y recordatorios.
- Mitigación: snapshots/fixtures antes/después de serializaciones, comparar salidas ICS.
- DI de DB: transición puede generar inconsistencias si se mezcla estático/locator.
- Mitigación: migración gradual y adaptador de compatibilidad temporal.
- Tipado estricto: ruido inicial.
- Mitigación: endurecer flags por paquete/módulo en varias PRs pequeñas.
Métricas de aceptación por lote
- Tests verdes y sin cambios funcionales.
- Reducción de duplicados medible (git grep antes/después).
bunx tsc --noEmitlimpio en el ámbito del lote.- Cobertura estable o en subida en módulos afectados.
Siguientes pasos prácticos
- Estabilizar typecheck (Lote 0): Completado.
- Unificar fechas/validaciones (Lote 1): Completado.
- Centralizar helpers de tests y cripto (Lote 2): Completado.
- Endurecer tipos en core (Lote 3): Completado.
- Consolidar ICS (Lote 4): Completado.
- Dividir TaskItem y revisar AppShell (Lote 5): Completado.
- DI ligera para DB (Lote 6).
- Aumentar cobertura en módulos flojos (Lote 7).
Información adicional a recopilar (para Lote 0)
- tsconfig.json (raíz)
- apps/web/tsconfig.json
- apps/web/vite.config.ts
Con estos 3 archivos podremos definir cambios mínimos para tener un typecheck limpio por paquetes sin introducir dependencias externas.