25 KiB
AUDIT_OPENCODE
Resumen ejecutivo
El ecosistema Active es un framework propio sólido y bien diseñado (8.2/10). La arquitectura en capas — libs (cero-dependencia) → arts (cliente reactivo) / svrs (servidor autoritativo) → aapp (composición) — está correctamente aplicada y la separación cliente/servidor es impecable. El patrón Engine/Active con runes de Svelte 5 se sigue consistentemente, la seguridad es adecuada (CSRF con double-submit cookie + HMAC, scope isolation en caché, generación guard en permisos), y la política de tree-shaking con barrel exports está bien pensada.
Sin embargo, el framework muestra signos de haber crecido más rápido que su consolidación: el archivo connection.ts tiene 865 líneas y merece ser partido, engine-auth.ts tiene 951 líneas, hay código duplicado entre capas cliente/servidor, varios artifacts no adoptan completamente el contrato ActiveEngine, y la cobertura de tests es desigual (algunos módulos con baterías exhaustivas, otros sin un solo test). La documentación de diseño (DESIGN_CONN.md) referencia archivos que ya no existen.
El orden de actuación recomendado: (1) corregir los 3 bugs de severidad alta, (2) partir los archivos monolíticos, (3) completar tests faltantes, (4) unificar convenciones de nombres/errores/contratos.
Hallazgos críticos
HC-1: Http engine nunca se libera en ActiveApp.dispose() — fuga de recursos
- Archivo:
src/arts/aapp/active-app.svelte.ts:110,264 - Severidad: Alta | Clasificación: bug confirmado | Esfuerzo: Bajo (1 línea)
- Explicación:
createEngineHttp()se construye en línea 110 peroHttp.dispose()no aparece en el cascade dedispose()(líneas 264-286). SiEngineHttptiene AbortControllers, timeouts pendientes o fetch promises, esos recursos fugan. El orden correcto es Cache → Timers → Http → Frontend → Dom → Formats → Storage → Lang → Logger. - Propuesta: Añadir
Http.dispose()entreTimers.dispose()yteardownPersistence().
HC-2: connection.ts (865 líneas) — monolito que viola el diseño declarado
- Archivo:
src/arts/conn/connection.ts - Severidad: Alta | Clasificación: refactor | Esfuerzo: Alto (4-6h)
- Explicación: El archivo contiene state machine, transport lifecycle, heartbeat, reconnect, auth, buffering, channels, session bridge y browser lifecycle.
DESIGN_CONN.md:440-457declara explícitamente archivos separados (reconnect.ts,heartbeat.ts,backpressure.ts,ack.ts) que no existen. El diseño original se consolidó en un solo archivo, dificultando el mantenimiento y testing aislado. - Propuesta: Extraer
reconnect.ts(líneas 332-357, 670-710),heartbeat.ts(359-392),buffer.ts(431-456),auth.ts(504-541),request.ts(543-577). Mantenerconnection.tscomo orquestador.
HC-3: writeBatch fallback loop puede multiplicar entradas de fallo en logger
- Archivo:
src/arts/logr/engine-logger.ts:377 - Severidad: Alta | Clasificación: bug confirmado | Esfuerzo: Bajo (30min)
- Explicación: Cuando
writeBatchno está definido,flushTransportllama awriteOnepor cada item en el buffer. Si el transport falla en cadawriteOne, se crea una entrada sintética de fallo POR CADA ITEM. SinfailureThrottleMs, esto multiplica el volumen de logs catastróficamente. - Propuesta: Registrar fallo a nivel de flush — si
writeOnefalla durante un flush batch, detener iteración y emitir una sola entrada de fallo para el batch.
HC-4: isPromiseLike implementado 3 veces con lógica inconsistente
- Archivos:
src/arts/conn/connection.ts:128vssrc/arts/sium/core/internals.ts:23vssrc/libs/standard-schema.ts:72 - Severidad: Alta | Clasificación: bug confirmado | Esfuerzo: Bajo (15min)
- Explicación: La versión en
connection.tsusa'then' in valueque retornatruepara objetos como{ then: 42 }que NO son thenables, causando quesendFramehagaawaitde un no-promise. Las otras dos versiones usantypeof value.then === 'function'que es correcto. Tres implementaciones con firmas diferentes. - Propuesta: Todas las implementaciones deben importar desde
$libs/standard-schema. Eliminar las locales.
Hallazgos medios
HM-1: Can.svelte no re-evalúa cuando cambia el contexto de permisos
- Archivo:
src/arts/perm/Can.svelte:30-49 - Severidad: Media | Clasificación: bug confirmado | Esfuerzo: Bajo
- Explicación: El
$effectdepende deaction,resource,context,optimistic— pero NO delpermissionscontext. Si se llamasetPermissionsContext()después de montar<Can/>, el componente no re-evalúa. - Propuesta: Leer
permissions.currentSnapshot.versiondentro del effect como dependencia reactiva.
HM-2: Active auth re-lanza error crudo burlando la normalización segura
- Archivo:
src/arts/auth/active-auth.svelte.ts:185-186 - Severidad: Media | Clasificación: bug confirmado / seguridad | Esfuerzo: Bajo
- Explicación:
catch (error) { lastError = normalizeClientError(error); throw error; }— re-lanza el error original, que puede contener stack traces o datos internos. Si el caller captura directamente en vez de leerAuth.lastError, recibe el error inseguro. - Propuesta: Lanzar
AuthInvalidResponseErroroAuthRequestFailedErrorcon el mensaje normalizado, no el error original.
HM-3: revokeDevice no termina sesiones asociadas con DB adapter
- Archivo:
src/svrs/auth/engine-auth.ts:513-532 - Severidad: Media | Clasificación: bug confirmado / seguridad | Esfuerzo: Medio
- Explicación:
revokeDevice()en el adapter de memoria sí revoca session bindings, pero el DB adapter (db.ts:108-112) solo actualiza el registro del dispositivo — las sesiones bindings quedan activas. Un dispositivo revocado podría mantener sesiones válidas. - Propuesta: Mover la lógica de revocación de session bindings al engine (no al adapter). Llamar
sess.end()para sesiones asociadas al dispositivo revocado.
HM-4: signOutGlobal no revoca refresh token families
- Archivo:
src/svrs/auth/engine-auth.ts:306-334 - Severidad: Media | Clasificación: bug confirmado / seguridad | Esfuerzo: Bajo
- Explicación:
signOutGlobalrevoca session bindings pero NO las refresh token families del actor. Un refresh token emitido antes del logout global podría potencialmente rotar a nuevas sesiones.refresh-rotation.tstienerevokeRefreshFamilypero no se llama. - Propuesta: Añadir
store.revokeRefreshFamily()durantesignOutGlobal.
HM-5: mono-lang.svelte.ts register() retorna tipo falseado
- Archivo:
src/arts/lang/mono-lang.svelte.ts:139-142 - Severidad: Media | Clasificación: bug confirmado | Esfuerzo: Bajo
- Explicación:
register()creaActiveLang<LangNode>pero lo casteaas unknown as ActiveLang<LangNode & { [K in NS]: M }>. La instancia retornada no tiene conocimiento real del namespace —lang.t('shop.product')devolvería el path literal, no una traducción. - Propuesta: Hacer que mono-lang's
registerrealmente mergee módulos en un schema interno, o tipar el retorno comoActiveLang<LangNode>sin pretensión de type safety.
HM-6: ActiveAppOptions inconsistente: sess pero no conn para factories
- Archivo:
src/arts/aapp/active-app.svelte.ts:155-261 - Severidad: Media | Clasificación: simplificación / coherencia | Esfuerzo: Medio
- Explicación: El constructor acepta
sesscomo opción cononSignedOut(), peroconn(Connections) no tiene opción equivalente para inyectar configuración inicial. Esto fuerza a llamarApp.createActiveConnections()sin poder preconfigurar. La asimetría consess/auth/permrompe el patrón de factories. - Propuesta: Aceptar
connections?: Omit<ConnectionsOptions, 'timers' | 'logger'>enActiveAppOptions.
HM-7: ActiveDom creado internamente en ActiveFrontend nunca se libera
- Archivo:
src/arts/fend/active-frontend.svelte.ts:82,224-229 - Severidad: Media | Clasificación: bug confirmado | Esfuerzo: Bajo
- Explicación: Cuando
applyDom === true(default) y no se pasadom, se creaActiveDominterno que adjunta unresizelistener awindow.ActiveFrontend.dispose()no llama adom.dispose(), filtrando el listener hasta que se cierre la página. - Propuesta: Guardar referencia al
domcreado internamente y llamardom.dispose()en el métododispose().
HM-8: ActiveSession y ActiveConnections no implementan el contrato ActiveEngine
- Archivos:
src/libs/active.ts,src/arts/sess/,src/arts/conn/ - Severidad: Media | Clasificación: refactor / coherencia | Esfuerzo: Bajo
- Explicación:
ActiveEngine<TSnapshot, TError>es implementado porActiveAuth,ActivePermissions,ActiveCachepero NO porActiveSessionniActiveConnections— ambos tienenloading,lastError,snapshot(),dispose()yonChange(). El contrato está a medio adoptar. - Propuesta: Extender
ActiveSessionyActiveConnectionsconActiveEngineo eliminar el contrato parcial y documentar que es solo para "network-augmented" artifacts.
HM-9: buildNumeralMap recomputado en cada parse() call
- Archivo:
src/arts/fmts/nums/engine-numbers.ts:37-44,119-123 - Severidad: Media | Clasificación: optimización | Esfuerzo: Bajo
- Explicación: Cada llamada a
parse()invocabuildNumeralMap(locale)que creaIntl.NumberFormat, formatea un número constante y construye unMapiterando caracteres. Este mapa es constante por locale. - Propuesta: Cachear el numeral map por locale, similar al
formatCache.
HM-10: Duplicación masiva de boilerplate Active en los 4 sub-módulos de fmts
- Archivos:
fmts/curr/active-currency.svelte.ts,fmts/dates/active-dates.svelte.ts,fmts/nums/active-numbers.svelte.ts,fmts/unts/active-units.svelte.ts - Severidad: Media | Clasificación: refactor | Esfuerzo: Medio
- Explicación: Cuatro archivos comparten ~80% de estructura idéntica:
version = $state(0),SvelteSetpara listeners,notifyPreferences(),syncLocale(),unsubscribeLocale,dispose(). ~100 líneas cada uno con ~60 líneas de boilerplate. - Propuesta: Crear helper genérico
createReactiveSubEngine<E>(engine, subs)enfmts/helpers.ts. Cada wrapper bajaría a ~30 líneas.
HM-11: unref pattern duplicado 3 veces
- Archivos:
src/arts/http/retry.ts:68,src/arts/http/timeout.ts:52,68 - Severidad: Media | Clasificación: refactor | Esfuerzo: Bajo
- Explicación:
(id as unknown as { unref?: () => void }).unref?.()aparece 3 veces. - Propuesta: Extraer a
tryUnref(handle: unknown)en$libs/timers.
Hallazgos menores
HL-1: libs/times/index.ts — módulo vacío (dead code)
- Archivo:
src/libs/times/index.ts - Severidad: Baja | Clasificación: bug confirmado | Esfuerzo: Bajo
- Propuesta: Poblar con utilidades de tiempo o eliminar el directorio y alias.
HL-2: resolveDir duplicado entre arts/fend/locale-defaults.ts y libs/dom/locale.ts
- Archivos:
src/arts/fend/locale-defaults.ts:8-10,src/libs/dom/locale.ts:1-5 - Clasificación: refactor | Esfuerzo: Bajo
- Propuesta: Mover
resolveDiryRTL_LOCALESalibs/dom/locale.ts. Re-exportar desde fend.
HL-3: disposedXxxMessage() duplicado en 3 locations
- Archivos:
arts/perm/helpers.ts:3-5,svrs/perm/helpers.ts:3-5,svrs/cach/helpers.ts:3-5 - Clasificación: refactor | Esfuerzo: Bajo
- Propuesta: Extraer a
$libs/activecomodisposedMessage(artifact, method).
HL-4: isLangBranch vive en helpers.ts pero pertenece a guards.ts
- Archivo:
src/arts/lang/helpers.ts:171-179 - Clasificación: refactor | Esfuerzo: Bajo
- Propuesta: Mover a
guards.ts.
HL-5: toError helper duplicado
- Archivos:
src/arts/sess/engine-session.ts:792-794 - Clasificación: refactor | Esfuerzo: Bajo
- Propuesta: Mover a
$libs/reactive/utils.
HL-6: NodeJS.Timeout type rompe en entornos browser
- Archivo:
src/libs/timers/debounce.ts:3 - Clasificación: bug confirmado | Esfuerzo: Bajo
- Propuesta: Usar
ReturnType<typeof setTimeout>.
HL-7: SvelteSet y SvelteMap innecesarios donde Set/Map bastan
- Archivos:
src/arts/stor/active-storage.svelte.ts:46,115 - Clasificación: optimización | Esfuerzo: Bajo
- Explicación:
listenersyuserSubssolo se iteran imperativamente (.forEach,.values()), nunca en$derivedo template. La reactividad de SvelteSet/Map no se aprovecha. - Propuesta: Reemplazar con
SetyMapplanos.
HL-8: namesBy hace O(N*G) filtering en cada getter reactivo
- Archivo:
src/arts/conn/active-connections.svelte.ts:36-76 - Clasificación: optimización | Esfuerzo: Bajo
- Propuesta: Precomputar arrays categorizados con un solo
$derived.
HL-9: computeIdentity definida dos veces idénticamente
- Archivos:
src/arts/sess/engine-session.ts:188-191,src/arts/sess/active-session.svelte.ts:31-34 - Clasificación: refactor | Esfuerzo: Bajo
- Propuesta: El wrapper active debe delegar a
engine.identityen vez de recomputar.
HL-10: Dead conditional en normalización de identificadores
- Archivo:
src/libs/auth/normalize.ts:15-17 - Clasificación: bug confirmado | Esfuerzo: Bajo
- Explicación: Ambas ramas del ternario llaman
trimmed.toLocaleLowerCase(). El condicional está muerto. - Propuesta: Eliminar condicional o aplicar normalización diferente por rama.
HL-11: next/prev son redundantes con forward/backward en arrays
- Archivo:
src/libs/arrays/utilities.ts:89-169 - Clasificación: refactor | Esfuerzo: Bajo
- Propuesta: Reimplementar
next/prevcomo wrappers deforward(array, index, 1, loop).
HL-12: libs/http/index.ts y todas las barrels de libs/* usan export *
- Archivos:
src/libs/*/index.ts - Severidad: Baja | Clasificación: simplificación / coherencia
- Explicación: El
arts/README.mdafirma que "All barrels use named re-exports" — esto es falso para toda la capalibs/. Para libs de utilidades es aceptable, pero paralibs/auth(400+ líneas de tipos),libs/perm,libs/cachpenaliza el tree-shaking. - Propuesta: Actualizar README para reflejar la realidad: "libs barrels usan
export *; arts barrels usan named re-exports." Opcional: convertir las libs grandes a named re-exports.
HL-13: libs/numbers/utilities.ts — parámetro confuso numerator
- Archivo:
src/libs/numbers/utilities.ts:24 - Clasificación: refactor | Esfuerzo: Bajo
- Propuesta: Renombrar a
mod(value: number, modulus: number).
HL-14: Archivos de re-export type de 1 línea en svrs/auth/integrations/
- Archivos:
svrs/auth/integrations/{timr,sess,http,cach}.ts(1 línea cada uno) - Clasificación: simplificación | Esfuerzo: Bajo
- Propuesta: Eliminar archivos intermedios. Re-exportar directamente desde
svrs/auth/index.ts.
Refactorizaciones recomendadas
| # | Descripción | Archivo(s) | Esfuerzo |
|---|---|---|---|
| R1 | Partir connection.ts (865 líneas) en módulos separados |
src/arts/conn/connection.ts |
Alto |
| R2 | Partir engine-auth.ts (951 líneas) extrayendo password flow, session binding, OAuth |
src/svrs/auth/engine-auth.ts |
Alto |
| R3 | Extraer boilerplate Active de fmts en createReactiveSubEngine() |
src/arts/fmts/*/active-*.svelte.ts |
Medio |
| R4 | Extraer getLocale/setLocale duplicado en 4 engines fmts |
src/arts/fmts/*/engine-*.ts |
Medio |
| R5 | Unificar resolveDir + Direction en libs/dom/locale.ts |
fend/locale-defaults.ts, libs/dom/locale.ts | Bajo |
| R6 | Extraer disposedXxxMessage() a helper compartido |
perm/helpers.ts, cach/helpers.ts | Bajo |
| R7 | Mover readField, toError, escapeId a libs/ |
sess/engine-session.ts, adom/roving-focus-group | Bajo |
| R8 | Convertir libs/auth/index.ts de export * a named re-exports |
src/libs/auth/index.ts |
Medio |
| R9 | Actualizar DESIGN_CONN.md para reflejar la implementación real |
src/arts/conn/DESIGN_CONN.md |
Medio |
| R10 | Alinear activeEngine contract: extender ActiveSession/ActiveConnections |
src/libs/active.ts |
Bajo |
Simplificaciones recomendadas
| # | Descripción | Archivo | Esfuerzo |
|---|---|---|---|
| S1 | Eliminar close() duplicado de engine-connections (alias de closeConnection) |
src/arts/conn/engine-connections.ts:163-165 |
Bajo |
| S2 | Eliminar FormatsLocaleSource (type alias muerto de LocaleSource) |
src/arts/fmts/types.ts:10 |
Bajo |
| S3 | Consolidar AUTO_VALUE/AUTO_CURRENCY/AUTO_UNIT_SYSTEM (todos son 'auto') |
fmts/consts.ts, curr/consts.ts, unts/consts.ts |
Bajo |
| S4 | Reemplazar SvelteSet/SvelteMap innecesarios con Set/Map |
active-storage.svelte.ts:46,115 |
Bajo |
| S5 | Eliminar archivos de 1 línea en svrs/auth/integrations/* |
svrs/auth/integrations/ |
Bajo |
| S6 | Eliminar svrs/auth/context.ts (usado solo en test page) |
svrs/auth/context.ts |
Bajo |
| S7 | noop() debería aceptar rest args para compatibilidad universal |
libs/funcs/noop.ts:4 |
Bajo |
| S8 | Simplificar subscribe() delegando a addTransport en logger |
src/arts/logr/engine-logger.ts:543-551 |
Bajo |
Optimizaciones recomendadas
| # | Descripción | Archivo | Esfuerzo |
|---|---|---|---|
| O1 | Cachear buildNumeralMap por locale (recomputado en cada parse()) |
fmts/nums/engine-numbers.ts:119-123 |
Bajo |
| O2 | Precomputar namesBy en un solo $derived en vez de 5 filtros O(N) |
conn/active-connections.svelte.ts:36-76 |
Bajo |
| O3 | getLogs clona todas las entradas antes de filtrar — filtrar primero |
logr/engine-logger.ts:473-490 |
Bajo |
| O4 | Object.keys(globalContext).length > 0 aloca array en hot path |
logr/engine-logger.ts:194-199 |
Bajo |
| O5 | sameSnapshot usa JSON.stringify en cada cambio externo — shortcut con generation |
sess/engine-session.ts:786-789 |
Bajo |
| O6 | refines.ts regex() clona RegExp innecesariamente sin flags g/y |
sium/core/refines.ts:229 |
Bajo |
| O7 | cookieAdapter.get() re-parsea document.cookie en cada lectura |
stor/adapters/cookie.ts:94-96 |
Medio |
| O8 | Timers sin cancelar en body-scroll-lock durante HMR reloads |
adom/body-scroll-lock.svelte.ts:150-169 |
Bajo |
| O9 | $effect sin debounce en Can.svelte para cambios rápidos de props |
perm/Can.svelte:30-48 |
Bajo |
| O10 | decisionKey() llamada incluso para cache hits — diferir tras cache miss |
perm/client.ts:252-254 |
Bajo |
Incoherencias de arquitectura
-
Contrato
ActiveEnginea medio adoptar —ActiveAuth,ActivePermissions,ActiveCachelo implementan;ActiveSessionyActiveConnectionsno, aunque cumplen estructuralmente. O se adopta universalmente o se elimina. -
Clases vs factories en
adom—BodyScrollLock,DOMContext,RovingFocusGroupson clases connew; el resto del ecosistema usacreateEngine*/createActive*. Inconsistencia de API. -
Naming plural vs singular —
createEngineTimers(plural) vscreateEngineHttp(singular). Solotimrusa plural. -
Errores: clases vs string-templates —
timr/http/conn/permusan clases Error con type guards;fmtsusa string factories. Inconsistente paracatchprogramático. -
Patrón de errores
disposed—CachDisposedError,PermDisposedError,AuthDisposedErrorvsSTORAGE_ERRORS.DISPOSED(string). Sin patrón unificado. -
DESIGN_CONN.mdreferencia archivos inexistentes —reconnect.ts,heartbeat.ts,backpressure.ts,ack.ts,presence.ts,app-integration.tsno existen. El diseño se consolidó sin actualizar la documentación. -
libs/times/— alias en config pero directorio vacío — el alias$libs/timesresuelve a unindex.tsde 0 bytes. -
AappAlreadyCreatedErrorno se usa para sesión —createActiveSession()lanzaSessAlreadyCreatedError, noAappAlreadyCreatedErrorcomo los demás factories. -
Sin
DESIGN_*.mdpara http, fmts, stor — solotimryconntienen documentos de diseño detallados.
Tests faltantes
Sin tests (crítico)
| Módulo | Archivos sin tests |
|---|---|
libs/timers |
backoff.ts, debounce.ts (0 tests) |
arts/conn |
active-connections.svelte.ts (sin archivo de test) |
arts/conn |
websocket.ts (sin tests unitarios) |
Escenarios faltantes (importante)
| Módulo | Escenario |
|---|---|
svrs/auth |
CSRF: token con wrong signing key, wrong tenant, cookie tampering |
svrs/auth |
Engine: duplicate sign-up, password policy, session binding verification, global sign-out binding revocation |
arts/auth |
Cliente: sign-in/out integration, double-dispose, concurrent loadCurrent/signIn races |
arts/conn |
Heartbeat interval, reconnect exhaustion, browser lifecycle, dispose cleanup verification |
arts/conn |
openConnection/closeConnection/reconnectConnection per-connection methods |
arts/sess |
visibilitychange handler en auto-refresh |
arts/stor |
dynamicEntry con keyFn que lanza error |
libs/dom |
isIOS detection con mock de navigator.userAgent |
libs/arrays |
getNextMatch con edge cases (empty values, spaces, cycling) |
Preguntas abiertas
-
¿Debe
ActiveEngineser contrato universal o solo para artifacts con side-effects? — Actualmente a medio adoptar. O se extiende a Session/Connections o se documenta como específico de "network-augmented" artifacts. -
¿Mantener clases en
adomo migrar a factories? —BodyScrollLock,DOMContext,RovingFocusGroupusannew; el resto usacreate*(). La inconsistencia actual confunde. -
¿Cuál es el plan para
libs/times/? — Directorio vacío con alias en config. ¿Se puebla con duration math,delay(),sleep()o se elimina? -
¿Nivel de madurez de OAuth y MFA? — El README dice "no deben documentarse como production-ready".
verifyMfaChallengesiempre lanza error. OAuth tiene incompatibilidad con DB adapter. ¿Roadmap? -
¿Estándar de idioma para documentación? —
adom/README.mdestá en español,stor/README.mden inglés. Sin estándar definido. -
¿Mover validación de sesión en SSR al engine? —
readSessionFromCookiesno valida schema; depende del caller pasar poradoptServer. ¿Debería el helper ser más defensivo? -
¿Estrategia de barrels? — El README dice "named re-exports" para todos los barrels pero
libs/*usaexport *. ¿Actualizar README o convertir libs?
Veredicto
El ecosistema Active es un framework sólido, bien diseñado y con fundamentos arquitectónicos excelentes. La separación en capas, el patrón Engine/Active, la política de tree-shaking, el aislamiento de scope en caché y permisos, y la implementación de CSRF son de calidad profesional.
Lo que frena la calidad hoy:
-
Deuda de consolidación — Archivos monolíticos (
connection.ts865 líneas,engine-auth.ts951 líneas) que contradicen su propio diseño documentado. La duplicación de boilerplate entre sub-módulos de fmts y entre capas cliente/servidor indica que el framework creció sin pausas de refactorización. -
Cobertura de tests desigual — Algunos módulos tienen baterías exhaustivas (50 tests en
engine-timers.test.ts, 622 líneas enengine-http.test.ts); otros tienen cero tests (libs/timers,active-connections,websocket). Las áreas sin tests son precisamente donde hay más bugs potenciales (conexiones, reconexión, heartbeats). -
Convenciones inconsistentes — Nombres plural/singular, clases vs factories, errores clase vs string, contrato
ActiveEnginea medio adoptar. Esto crea fricción para nuevos contribuidores y hace que el código parezca menos cohesionado de lo que realmente es.
Orden de actuación recomendado:
- Semana 1-2 — Corrección de bugs: HC-1 (Http dispose), HC-3 (writeBatch loop), HC-4 (isPromiseLike), HM-1 (Can reactivity), HM-2 (auth error re-throw)
- Semana 3-4 — Refactors estructurales: Partir
connection.ts, extraer boilerplate fmts, añadir Http.dispose() - Semana 5-6 — Seguridad: HM-3 (revokeDevice DB), HM-4 (signOutGlobal refresh families), HM-6 (session fixation docs)
- Semana 7-8 — Tests: Añadir tests para
libs/timers,active-connections, heartbeat, CSRF edge cases - Mes 2-3 — Unificación: Adoptar
ActiveEngineuniversalmente o eliminarlo, unificar naming (plural→singular en timr), estandarizar errores (clases everywhere), actualizar DESIGN_CONN.md
Lo que está excepcionalmente bien:
- Separación cliente/servidor: cero imports cruzados
$arts↔$svrs(fuera de tests de integración) - CSRF: double-submit cookie + HMAC-SHA256 + timing-safe comparison +
__Host-prefix - Scope isolation en caché:
SCOPE_ACTOR,SCOPE_TENANT,SCOPE_PERMISSIONcon hash en key - Generation guard en permisos: previene escritura de resultados stale en snapshots posteriores
- Sistema de timers deterministas con fake clock injection para testing
- Tree-shaking: separación
.svelte.tsvs.ts, barrels con named re-exports enarts/ - Documentación de diseño:
DESIGN_TIMR.md(1530 líneas) yDESIGN_CONN.md(1770 líneas) son ejemplares