You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
active-svelte/auditoria- ecosistema-codex.md

42 KiB

Auditoria Codex del ecosistema hacia 1.0

Fecha: 2026-05-04
Alcance: lang, cache, session, perm, auth, http.
Excluido: orca, ya auditado por separado.

Veredicto ejecutivo

El ecosistema esta bastante por encima de una 0.x normal: hay contratos tipados, separacion real entre libs, arts y svrs, factories declarativas en createActiveApp(), y una base de tests que hoy pasa para los modulos auditados.

La brecha hacia 1.0 no esta tanto en "reescribir" modulos, sino en cerrar tres frentes:

  1. Contratos publicos exactos: hay documentacion y ejemplos que todavia prometen aliases, nombres o inyecciones que el codigo ya no hace.
  2. Determinismo operativo: timr/Timers ya existe, pero http, session, cache y perm aun conservan defaults con Date.now(), Math.random() o timers nativos cuando se usan fuera del wiring ideal.
  3. Pruebas compuestas: los tests unitarios estan verdes, pero faltan historias de ecosistema donde auth, session, perm, cache y http fallan o se invalidan juntos.

Verificacion ejecutada:

npx vitest run src/arts/lang/test src/arts/cache/test src/libs/cache/test src/svrs/cache/test src/arts/session/test src/arts/perm/test src/svrs/perm/test src/arts/auth/test src/svrs/auth/test src/arts/http/test

35 test files passed
426 tests passed

Hallazgos transversales

P1 - La documentacion debe dejar de prometer APIs antiguas

Hay restos de la etapa de aliases de 4 letras y de nombres capitalizados: $cach, $sess, App.Cache, App.Sess, ejemplos sin services: { ... }, y textos que dicen que cache esta siempre presente. El codigo actual va por createActiveApp({ services }) y expone servicios lazy en minuscula (App.cache, App.session, App.perm, App.auth, App.http).

Impacto: un desarrollador nuevo no distingue que es API real y que es historia del framework. Para 1.0, esto no puede quedar en "se entiende mirando codigo"; la documentacion es parte del contrato.

Accion recomendada:

  • Hacer una pasada de docs con una regla mecanica: ningun README ni pagina src/web/routes/active/docs/** puede usar alias o propiedades que no existan en svelte.config.js y en src/arts/active-app/service-factories/**.
  • Crear tests de snippets o al menos un script que busque $cach, $sess, App.Cache, App.Sess, App.Http, App.Perm, etc.

P1 - La inyeccion entre servicios no esta igual de clara que el discurso

Las factories actuales inyectan principalmente logger, y solo session recibe tambien bus:

  • defineActiveAuth(...) recibe logger; el http debe venir en options.
  • defineActivePerm(...) recibe logger; aunque PermClientOptions acepta http?: EngineHttp, la factory no declara dependencia de http.
  • defineActiveCache(...) recibe logger; no recibe timers por defecto.
  • defineActiveSession(...) recibe logger y bus; no recibe timers para auto-refresh.
  • defineEngineHttp(...) recibe logger; no recibe timers.

Esto es coherente si el principio es "pasivo por defecto, opt-in explicito". Pero varias docs ya hablan como si Http, Cache, Bus y Timers se cablearan automaticamente entre servicios.

Accion recomendada:

  • Decidir para 1.0 una regla unica: o las factories solo reciben core deps minimas, o pueden declarar serviceDependencies.
  • Si se mantiene el modo minimalista, documentarlo sin ambiguedad: auth necesita http explicito, perm necesita endpoint o http explicito, cache no se invalida sola, session no refresca con Timers salvo que se le pase.
  • Si se quiere ergonomia enterprise, evolucionar factories para dependencias opcionales: defineActivePerm puede consumir http si existe; defineActiveCache y defineActiveSession pueden consumir timers.

P1 - Falta una suite compuesta de identidad, permisos y cache

La base verde actual no prueba suficientemente los casos que mas preocupan en una aplicacion real:

  • Usuario A abre sesion, cachea datos privados, cambia a usuario B, y B no ve cache ni permisos de A.
  • Backend comunica cambio de permisos y el cliente invalida decision cacheada antes de permitir acciones.
  • http recibe 401/403, dispara refresh/auth state, session adopta o revoca, perm y cache reaccionan.
  • Logout global revoca sesion, limpia cache actor-scoped y deja perm sin actor.
  • Password reset revoca sesiones y el cliente queda en anonymous sin datos privados residuales.

Accion recomendada:

  • Crear src/routes/test/ecosystem como harness de estas historias o moverlas a tests headless en src/arts/active-app/test/ecosystem-*.test.ts.
  • Estos tests deben usar App.Bus/presets/orquestacion cuando toque, pero el criterio es observable: snapshots finales y ausencia de datos cruzados.

P2 - Tiempo y aleatoriedad aun no son uniformes

Puntos concretos:

  • src/arts/http/retry.ts:48, :52, :64 usa Date.now(), Math.random() y setTimeout.
  • src/arts/http/timeout.ts:43, :63 usa setTimeout.
  • src/arts/cache/active-cache.svelte.ts:195 usa Date.now() si no se pasa clock.
  • src/arts/session/auto-refresh.ts:44, :45, :72 usa Date.now(), Math.random() y setInterval si no se pasa timers.
  • src/arts/perm/client.ts:291 usa Date.now() si no se pasa clock.

No es necesariamente un bug para uso aislado, pero para 1.0 la composicion createActiveApp() deberia poder inyectar Timers.clock y scheduler por defecto en los servicios que lo aceptan. Esa es una de las diferencias entre "librerias utiles" y "framework determinista".

Modulo lang

Estado

lang esta entre los modulos mas maduros. Tiene engine puro, wrapper active, resolucion de referencias #?path|fallback, extension de schema, pluralizacion, JSON helpers y una suite de tests amplia.

Refactorizaciones recomendadas

  1. Cambiar SvelteSet por Set en listeners. En src/arts/lang/active-lang.svelte.ts:34 localeListeners no alimenta templates ni estado derivado; solo se itera manualmente. Igual que se hizo en otros modulos, Set plano reduce reactividad innecesaria.
  2. Separar mutacion y composicion de schemas con nombres mas explicitos. Hoy extend(namespace, module) muta el engine activo y register(namespace, module) devuelve un engine hijo. Es potente, pero el naming puede confundir. Para 1.0 documentaria una tabla estricta: extend muta, register compone hijo.
  3. Hacer SupportedLocale menos cerrado. src/libs/lang/types.ts limita locales a una lista base. Para producto 1.0, conviene permitir cualquier BCP47 tipado como branded string o una registry generica por app.
  4. Modo estricto de interpolacion. interpolateTemplate() resuelve placeholders, pero no hay modo que falle si falta un parametro. Para 1.0 deberia existir strictInterpolation con diagnostico/log.
  5. Unificar docs de inyeccion. La factory defineActiveLang si inyecta logger via setLogger(core.logger). La documentacion debe mostrar claramente services: { lang: defineActiveLang({ schema }) } y no dar a entender que createActiveApp() siempre trae un schema real.

Ampliaciones 1.0

  • Loader asincrono de packs de idioma: loadLocale(locale) con cache y fallback.
  • Lang.tCode(code) si se adopta una capa comun de errores/codigos.
  • Integracion con formatos: resolver {{price | currency}}, {{date | datetime}} usando format.
  • Dev inspector: namespace registrados, fallback usado, claves faltantes, referencias circulares.
  • Script de validacion de schema: claves faltantes por locale, claves muertas y paths duplicados.

Modulo cache

Estado

cache tiene un runtime solido en libs/cache, wrapper svrs/cache y arts/cache reactivo. Hay politicas, scopes, tags, epochs, singleflight, stale-if-error y adapters memory/storage. La arquitectura esta bien orientada.

Refactorizaciones recomendadas

  1. Actualizar docs antiguas. Hay ejemplos que aun usan $cach o App.Cache; el alias real es $cache y la app expone App.cache solo si se declara services.cache.
  2. Inyectar clock desde App.Timers cuando se usa como servicio. createActiveCache() acepta clock, pero defineActiveCache() solo inyecta logger. Para 1.0, el default app-wired deberia usar core.timers.clock.
  3. Evitar console.warn directo en memory adapter. src/libs/cache/adapters/memory.ts usa console.warn si se crea en produccion sin onProductionWarning. En 1.0, la advertencia deberia pasar por diagnostics/logger o exigir handler explicito.
  4. Revisar clonacion de valores. memoryCacheAdapter usa structuredClone si existe y fallback JSON. El fallback rompe Date, Map, Set, BigInt, clases y valores no serializables. Para 1.0: o se documenta "valores serializables" o se expone clone?: (value) => value.
  5. Purgado escalable. La memoria purga expirados escaneando entradas. Es razonable para v0, pero para 1.0 conviene un sweeper opcional con Timers o un indice por expiracion si se esperan caches grandes.

Ampliaciones 1.0

  • Adapter L2 remoto opcional: Redis/HTTP/IndexedDB, manteniendo L1 memory.
  • Invalidacion por evento app: identity changed, tenant switched, permission changed.
  • cache.queryHttp() o helper de integracion con http para cachear respuestas con schema.
  • Metricas: hit rate, stale served, refresh failures, singleflight joins, evictions.
  • Politicas de privacy: impedir persistir scopes actor/session en adapters no seguros salvo opt-in.
  • Tests de no fuga cross-actor y cross-tenant.

Modulo session

Estado

session es de los modulos mejor testeados. Tiene engine puro, wrapper active, generacion/versionado para evitar carreras, broadcast/storage sync, eventos de bus y auto-refresh opcional. La direccion es buena.

Refactorizaciones recomendadas

  1. Actualizar naming en docs. Debe desaparecer $sess y App.Sess; el alias real es $session y el servicio es App.session.
  2. Cablear auto-refresh con Timers desde App. auto-refresh.ts ya acepta timers, pero defineActiveSession() no los inyecta. Para 1.0, si una app usa createActiveApp(), el refresh deberia poder ser determinista sin boilerplate manual.
  3. Limitar defaults nativos en modo app-wired. Date.now, Math.random y setInterval son aceptables como fallback aislado, pero no como camino principal del ecosistema.
  4. Documentar ownership frente a auth. session no debe saber de credenciales ni permisos; solo continuidad, refresh/revoke, snapshot y bus events. auth prueba identidad; perm decide permisos.
  5. Opciones explicitas para sync multi-tab. Si ya existen, deben documentarse mejor; si no, conviene broadcast: false | { channel } para entornos con privacidad estricta o tests.

Ampliaciones 1.0

  • Preset defineActiveSession({ autoRefresh: { standard: true } }) que use Timers.
  • Integracion http para refresh por 401 sin acoplar http a session: hook reusable de aplicacion.
  • Eventos canonicos para identity changed, credential refreshed, revoked, expired.
  • Tests compuestos con auth/cache/perm.
  • Modo SSR documentado: adoptar snapshot server sin doble refresh ni flicker.

Modulo perm

Estado

perm tiene mas base de servidor de la que parecia al inicio: libs/perm incluye DSL/evaluator/compiler, svrs/perm aporta engine, repository y SQL de referencia, y arts/perm aporta cliente activo. Es una buena base para 1.0, pero hay dos puntos que conviene cerrar pronto.

Refactorizaciones recomendadas

  1. Corregir compilador SQL para paths anidados. El runtime usa getPath, pero src/libs/perm/compilers/sql.ts:105 y :110 leen input.actor[expr.path] y input.context?.[expr.path]. Un path como risk.mfa o profile.department se evaluara distinto en runtime y en SQL. Para 1.0 esto debe ser P1: usar getPath() tambien en actor/context o declarar que SQL solo soporta paths planos.
  2. Preordenar policies una vez. src/libs/perm/runtime.ts ordena por prioridad en cada decision. Para volumen real, ordenar al construir runtime y mantener indices por action/resource reduce coste.
  3. Memoizar providers por decision. DefaultPermEvaluator puede llamar varias veces a relation/attribute providers con la misma key dentro de una decision. Un cache por request reduce latencia y evita multiples consultas a DB.
  4. Eliminar IDs auto-generados no estables en produccion. definePolicies/builders pueden producir IDs por contador. Para 1.0, los policies persistidos deberian exigir id estable o generar checksum determinista.
  5. Alinear factory y docs. PermClientOptions acepta http?: EngineHttp, pero defineActivePerm() no inyecta App.http. O se documenta que fetcher/http son manuales, o se declara dependencia opcional de http.

Ampliaciones 1.0

  • Persistencia oficial: migraciones SQL versionadas, repository contract tests y ejemplos Kysely/Drizzle.
  • Webhook/evento de cambio de permisos: invalidar cliente y cache de decisiones por actor/tenant.
  • what() y explain() cacheados con invalidacion por version de policies.
  • Obligations/advice con enforcement helpers, no solo datos.
  • Auditoria de decisiones: escribir permission_decision_audit desde server engine con redaccion.
  • Tests de cross-actor race: login A -> decision allow -> login B -> misma accion no reutiliza decision.

Modulo auth

Estado

auth ha avanzado mucho: hay libs/auth como lenguaje comun, svrs/auth con engine server-authoritative, SQL de referencia, password signup/signin, CSRF, recovery, device records, refresh rotation y OAuth base. arts/auth es un cliente seguro que refleja AuthCurrentView y no intenta ser autoridad.

Refactorizaciones recomendadas

  1. Completar handlers para rutas ya publicadas. src/libs/auth/consts.ts:21-30 declara rutas OAuth, MFA, devices y WebAuthn. src/arts/auth/active-auth.svelte.ts:154-166 ya llama DEVICES y DEVICE_REVOKE. Pero src/svrs/auth/handlers.ts solo enruta current, csrf, password, logout, email verification y password reset. Resultado: ActiveAuth.listDevices() y revokeDevice() apuntan a endpoints que el handler generico no sirve. Para 1.0, o se agregan handlers, o se retiran del cliente hasta estar soportados.
  2. No guardar secretos OAuth en metadata de flow. oauth-flow.ts guarda state y verifier en metadata ademas de hashes. Aunque la store sea server-side, el contrato ideal es persistir solo hash/verifier cifrado o recuperar verifier por canal seguro. Para 1.0 debe revisarse porque el documento original era estricto con secretos.
  3. PKCE challenge no debe usar hash token raw si no es base64url SHA-256 estandar. Si hashAuthToken() no produce exactamente base64url(SHA256(verifier)), el flujo OAuth no sera interoperable. El test oauth-pkce.test.ts existe, pero conviene comprobarlo contra el RFC shape.
  4. Rate-limit esta bien cableado en password/recovery/oauth, pero falta matriz. enforceAuthRateLimit() existe y se usa en flujos principales; para 1.0 hace falta tabla por metodo, key usada y politica recomendada.
  5. createDbAuthAdapter es demasiado generic para produccion. El adapter db.ts acepta repositorios con where generico basado en records TS, mientras el SQL aplana actorRef a tenant_id/actor_id. Es valido como referencia, pero 1.0 necesita un adapter/repository probado contra el SQL real, no solo un contrato abstracto.

Ampliaciones 1.0

  • Handlers completos para devices y OAuth; MFA/WebAuthn marcados como experimental si no se implementan.
  • Contract tests del SQL auth: credentials, flows, linked accounts, session bindings, refresh families.
  • Anti-enumeration tests para recovery/email verification.
  • Refresh rotation integrada end-to-end con sesion real, no solo helper unitario.
  • Device/session management completo: listar, revocar actual, revocar otro, global logout.
  • Security events hacia logger/audit con redaccion obligatoria.
  • Integracion con session, cache y perm: signin/logout/password reset invalidan lo necesario sin acoplar modulos directamente.

Modulo http

Estado

http esta muy bien codificado: engine puro, resultados tipados, hooks, retry, timeout, schema de request/response, fetch inyectable y tests amplios. Es una pieza importante para que el ecosistema no dependa de fetch crudo.

Refactorizaciones recomendadas

  1. Port de timers/retry. retry.ts y timeout.ts usan timers nativos. Para 1.0 deberia existir HttpTimerPort o integracion directa con Timers, al menos cuando se crea via defineEngineHttp().
  2. Jitter inyectable. computeRetryDelay() usa Math.random() si policy.jitter esta activo. Para tests deterministas y produccion controlada, aceptar random?: () => number.
  3. Cerrar lifecycle de timeouts. attemptTimeoutSignal() y totalTimeoutSignal() crean setTimeout; si la request termina antes, el timeout queda pendiente hasta disparar. No siempre es grave, pero en alto volumen deberia poder cancelarse.
  4. Helpers de autenticacion sin acoplar a auth. La pieza deberia ofrecer patrones genericos para beforeRequest/beforeRetry/beforeError que permitan refresh por 401, pero sin conocer session ni auth.
  5. Observabilidad de hooks. Hoy se emiten diagnosticos de retry y errores; para 1.0 conviene medir tiempo por intento, delay real, abort reason y hooks que rescatan respuestas.

Ampliaciones 1.0

  • http.with({ fetch: event.fetch }) documentado con snippets SSR reales.
  • Preset de retry empresarial: idempotentes por defecto, 429/503 con Retry-After, jitter determinista opcional.
  • Adapter/cache bridge: convertir Response en envelope cacheable con schema.
  • Circuit breaker opcional o al menos hooks para implementarlo con cache/session.
  • Tests de 401 -> refresh -> replay request; 403 -> perm invalidation; offline -> stale cache.

Roadmap recomendado hacia 1.0

Sprint 1 - Contrato publico y docs reales

  • Corregir aliases y nombres de servicios en READMEs y paginas active/docs.
  • Documentar factories reales: que inyecta cada una y que debe pasar el desarrollador.
  • Crear un test/script de docs que detecte aliases muertos y propiedades antiguas.
  • Publicar tabla "core siempre presente vs services declarados".

Sprint 2 - Determinismo e inyeccion

  • Hacer que defineActiveCache, defineActiveSession, defineActivePerm y defineEngineHttp puedan consumir core.timers cuando aplique.
  • Inyectar random en retry/session auto-refresh.
  • Mantener fallbacks nativos para uso aislado, pero no para el camino App.

Sprint 3 - Bugs de contrato

  • Completar handlers de auth para devices/OAuth o retirar esas APIs del cliente hasta estar soportadas.
  • Corregir path anidado en compilador SQL de perm.
  • Revisar OAuth PKCE y persistencia de verifier/state.
  • Alinear defineActivePerm con http real.

Sprint 4 - Tests compuestos

Escenarios minimos:

  1. Login A -> cache privado -> logout -> login B -> B no ve cache/perm de A.
  2. Permission webhook -> perm.invalidate() -> decision antigua no se reutiliza.
  3. HTTP 401 -> refresh sesion -> replay -> cache conserva solo datos validos.
  4. Password reset -> revoca sesiones -> active auth anonimo -> cache actor-scope limpia.
  5. Logout global -> session revoked -> perm sin actor -> cache limpia -> http protegido falla seguro.
  6. Tenant switch -> cache/perm invalidados por tenant.

Sprint 5 - Server readiness

  • auth y perm ya tienen SQL de referencia; convertirlo en migraciones versionadas o al menos en contract tests ejecutables.
  • cache necesita historia clara de adapter server: memory solo test/dev, storage/browser, y adapter remoto recomendado.
  • http no necesita svrs/http, pero si necesita ejemplos SSR y edge/runtime.

Prioridad resumida

Prioridad Tema Modulos Motivo
P1 Handlers auth incompletos para APIs publicas auth Cliente llama endpoints que el handler generico no enruta.
P1 SQL compiler no resuelve paths anidados igual que runtime perm Riesgo de decisiones distintas entre filtrado DB y evaluacion memory.
P1 Docs/API antiguas tras rename y service schema todos Bloquea adopcion y genera mal uso del framework.
P1 Tests compuestos cross-actor/cross-tenant auth/session/perm/cache/http Es donde aparecen fugas reales.
P2 Timers/random no unificados cache/session/perm/http Rompe determinismo en tests y trazabilidad.
P2 Persistencia/adapters DB contract-tested auth/perm/cache Necesario para apps reales.
P2 Metrics/diagnostics de runtime cache/http/perm/auth Necesario para operacion 1.0.
P3 Limpieza micro-reactiva (SvelteSet listeners) lang Pulido, bajo riesgo.

Criterio de cierre para 1.0

Yo no marcaria estos modulos como 1.0 hasta que se cumplan estas condiciones:

  • La documentacion publica compila mentalmente y con snippets: ningun alias muerto, ningun servicio inventado.
  • El camino createActiveApp({ services }) inyecta logger, bus, timers y servicios dependientes de forma explicita o documenta que no lo hace.
  • auth, session, perm, cache y http tienen al menos una suite compuesta de identidad completa.
  • auth no publica rutas/cliente que el server handler no soporte.
  • perm produce la misma decision en runtime y SQL compiler para paths soportados.
  • http y auto-refresh son testeables sin timers nativos.
  • Los adapters server de auth y perm tienen contract tests contra el modelo SQL de referencia.

Conclusion: la arquitectura es buena y la base esta verde. Lo que falta para 1.0 es menos glamour y mas cierre contractual: documentacion verdadera, wiring determinista y pruebas de historias completas. Esa es la parte que convierte el ecosistema en plataforma.


Segunda tanda: sium, storage, timer, logger, frontend, format, connection

Fecha: 2026-05-04
Alcance adicional: sium, storage, timer, logger, frontend, format, connection.
Finalidad: misma que la primera tanda, buscar refactorizaciones, ampliaciones y criterios de cierre hacia version 1.0.

Verificacion ejecutada:

npx vitest run src/arts/sium/test src/arts/storage/test src/arts/timer/test src/arts/logger/test src/arts/logger/adapters src/arts/frontend/test src/arts/format/test src/arts/format/currency/test src/arts/format/dates/test src/arts/format/numbers/test src/arts/format/units/test src/arts/connection/test

52 test files passed
686 tests passed

Hallazgos transversales de la segunda tanda

P1 - Documentacion con aliases y nombres de App obsoletos

La misma deuda aparece con fuerza en esta tanda. El codigo actual usa aliases semanticos:

$storage, $timer, $logger, $format, $connection

Pero varios README siguen usando:

$stor, $timr, $logr, $fmts, $conn

Tambien aparecen ejemplos con App.Timers, App.Format, App.Frontend, App.Storage, App.Sess, App.Lang, App.createActiveConnections() y App.setLocale(...). El contrato actual de createActiveApp({ services }) expone servicios en minuscula (App.format, App.frontend, App.storage, App.session, App.lang, App.connections) y las factories viven en $active-app/services.

Esto es P1 de documentacion contractual. Aunque el runtime este verde, una API 1.0 no puede tener docs que ensenan a importar desde aliases que ya no existen.

Accion recomendada:

  • Pasada mecanica por README y paginas docs para reemplazar aliases viejos.
  • Script de CI que falle si aparecen $stor, $timr, $logr, $fmts, $conn, App.Sess, App.Storage, App.Format, App.Frontend, App.Timers en docs publicas salvo en secciones de migracion.
  • Tabla unica por modulo: alias real, factory real, propiedad real de App.

P1 - Los servicios no comparten todavia un contrato de lifecycle uniforme

Algunos modulos siguen el contrato ActiveEngine o equivalente (snapshot, lastError, disposed, dispose idempotente). Otros son utilitarios activos sin disposed ni guardas post-dispose.

Casos relevantes:

  • storage no impide entry() ni clear() despues de dispose().
  • frontend mantiene setters operativos tras dispose().
  • format root llama dispose de submodulos sin flag propio idempotente.

Para 1.0, todo servicio declarado en createActiveApp({ services }) deberia tener una semantica uniforme:

  • dispose() idempotente.
  • Metodos mutadores despues de dispose: o no-op documentado, o error tipado.
  • Snapshot/introspection si el servicio expone estado.

P2 - El patron CodeError esta bien adoptado, pero quedan restos de nomenclatura antigua

sium, storage, timer, logger y connection ya extienden CodeError desde $libs/errs, que era una buena direccion. Pero hay comentarios y mensajes que todavia hablan de stor, timr, logr o conn. No rompe runtime, pero ensucia la identidad del ecosistema justo ahora que se abandono la regla de 4 letras.

Accion recomendada:

  • Mantener CodeError como raiz.
  • Renombrar comentarios, mensajes y catalogos internos que digan stor/timr/logr/conn si el modulo ya se llama storage/timer/logger/connection.
  • Si ErrCode usa seeds de 4 letras por decision historica, documentarlo. Si no, migrarlo antes de 1.0.

Modulo sium

Estado

sium es probablemente el modulo mas completo de esta segunda tanda. Tiene core DSL, Standard Schema, introspection, codecs, lazy, domain types de color/date/time, resolucion de issues, integracion con lang, CodeError y una suite de tests amplia.

Refactorizaciones recomendadas

  1. Resolver locale activo, no solo default inicial. createEngineSium() captura defaultLocale = options.locale ?? lang?.getDefaultLocale() ?? 'es'. Si se inyecta ActiveLang mediante defineEngineSium y luego cambia el locale de App.lang, resolveIssue() sin locale explicito seguira usando el default capturado. Para 1.0, si el lang inyectado tiene getLocale(), Sium deberia usar el locale actual, o no pasar locale a lang.t() para dejar que Lang resuelva su estado actual.
  2. Cerrar la migracion de errores legacy. src/arts/sium/errors.ts mantiene SIUM_ERRORS como catalogo legacy para strings que aun no son CodeError. La propia nota dice que quedan sitios por migrar. Para 1.0, todos los errores de construccion/encode/decode deberian tener ErrCode.
  3. Reducir fragilidad de facade manual. EngineSium lista manualmente decenas de funciones. Hay tests de barrel, pero para 1.0 conviene un snapshot de surface o generacion controlada para evitar que core gane funciones que el engine no expone.
  4. Separar issues de errores de programador en docs. La distincion existe en codigo: validate devuelve Result, decode lanza. La documentacion debe insistir en cuando usar cada una.

Ampliaciones 1.0

  • setupSium() o presets de dominio para formularios complejos.
  • Bridge oficial con lang: sium.resolveIssue(issue) siguiendo locale activo.
  • Emision opcional de diagnostics por schema path para errores frecuentes.
  • Serializacion estable de schema para devtools y documentacion automatica.
  • Contract tests con standard-schema frente a Zod/Valibot/ArkType adapters.

Modulo storage

Estado

storage esta muy bien planteado: engine sync, active wrapper con $state, adapters memory/local/session/cookie/broadcast, envelopes con version/TTL/migration, serializers y tests suficientes. Es una pieza clave para preferencias no secretas y persistencia local.

Hallazgos y refactorizaciones

  1. P1 - dispose() no cierra realmente la superficie publica. createEngineStorage() marca disposed = true, pero entry(), clear() y entries() no verifican ese estado. Despues de dispose() se puede crear un entry nuevo sobre un bus ya limpiado y registries ya dispuestos. ActiveStorage.dispose() hereda el mismo problema porque delega al engine y no guarda flag propio. Para 1.0 debe haber StorageDisposedError o no-op documentado.
  2. P2 - TTL usa Date.now() sin clock inyectable. decodeEnvelope() y encodeEnvelope() aceptan now, pero entry-runtime.ts llama sin pasar reloj. EngineStorageOptions no tiene clock. Para tests deterministas y App wiring, conviene clock?: { now(): number }, inyectado desde App.Timers.clock.
  3. P2 - dynamicEntry() depende de $effect, pero no hay defensa si se usa fuera de scope. El comentario lo advierte, pero para 1.0 conviene test y error claro si Svelte lanza fuera de componente/effect root.
  4. P2 - Top-level serializer auto-selection puede sorprender. Esta documentado: objetos con Date anidada caen a JSON y no restauran Date. Para 1.0, anadir recipes de serializer por schema o integracion con Sium.
  5. P2 - Cookies cliente no endurecidas por defecto. cookieAdapter() default secure: false, sameSite: 'lax'. Es razonable para preferencias no secretas, pero docs deben repetir que no es para secretos y que auth/session cookies no pasan por storage.

Ampliaciones 1.0

  • StorageDisposedError y guardas post-dispose.
  • Clock inyectable desde App.
  • Adapter IndexedDB async separado o modulo nuevo, porque el contrato actual es sync.
  • Encryption/redaction adapter opt-in para preferencias sensibles, sin prometer seguridad para secretos.
  • Schema serializer: entry('profile', defaults, { schema, serializer: siumSerializer(schema) }).
  • Devtools: entradas vivas, namespace, adapter, defaults conflict, TTL restante.

Modulo timer

Estado

timer esta en buen estado. Es engine puro, clock inyectable, race-safe con version/id/key, abort signal por tarea, active wrapper ligero y tests robustos. Es de las piezas mas solidas del framework.

Refactorizaciones recomendadas

  1. Actualizar docs antiguas. README sigue usando $timr y App.Timers; alias real $timer, propiedad real App.Timers solo para core si se mantiene capitalizado. En codigo actual createActiveApp() si expone core Timers, asi aqui la capitalizacion es real, pero el alias no.
  2. Consolidar exports de backoff. src/arts/timer/backoff.ts re-exporta desde $libs/timer, y index.ts tambien lo expone. No es grave, pero para 1.0 conviene una unica historia: backoff vive en libs/timer, arts/timer lo reexporta en index por conveniencia.
  3. Exponer random en helpers consumidores. computeBackoffDelay() ya acepta random, pero connection no lo expone en sus reconnect options. Para 1.0, los consumidores deben poder hacer backoff determinista.
  4. Nombrar mejor timer como clock/scheduler del ecosistema. En docs debe quedar claro que no es una utilidad de UI, sino la fuente temporal para http, session, connection, cache, orca.

Ampliaciones 1.0

  • Fake clock oficial exportado para tests de ecosistema.
  • Metrics: drift, scheduled count, cancelled count, failed count por scope.
  • cancelAll(scope) documentado como primitive de teardown por modulo.
  • Helpers para deadline/timeout con AbortSignal para que http/orca no usen timers nativos.
  • Devtools de timers activos por scope.

Modulo logger

Estado

logger es potente: Logger comun en $libs/logger, EngineLogger extiende ese contrato, transports, buffers, batching, failure routing con deniedFor, adapters Sentry/Datadog/Logtail/Loki/OTel, vitals y tests grandes. Es un pilar enterprise real.

Refactorizaciones recomendadas

  1. P1/P2 - Alinear filtro global con la regla de niveles habilitados. Los transports usan levels?: LevelConfig, que permite { [LogLevel.WARN]: { enabled: true }, ... }. Pero el engine global aun usa state.level como threshold (if (lvl < state.level) return). Si la regla final del framework es "habilitacion por nivel, no threshold tradicional", LoggerOptions deberia aceptar levels tambien a nivel global, y level quedarse como shorthand o deprecated antes de 1.0.
  2. P2 - Reducir dependencia directa de console dentro del engine. handleFailure() emite console.error ademas de crear synthetic failure entry. En un entorno enterprise puede duplicar salida o saltarse transports. Para 1.0 conviene onInternalError, internalTransport, o consoleFallback?: boolean.
  3. P2 - Inyectar clock/id factory opcional. IDs fallback usan Date.now()/Math.random(), failure throttle usa Date.now(), timers de buffer usan setTimeout. Como Logger se crea antes de Timers, no puede depender de App.Timers, pero si puede aceptar clock, idFactory y setTimeout opcionales para tests y runtimes especiales.
  4. P2 - dispose() de child logger solo advierte en DEV con console.warn. Es correcto como defensa, pero debe estar documentado en API: solo el root owns lifecycle.

Ampliaciones 1.0

  • Global levels con shorthand levelsAtLeast.
  • Redaction pipeline: campos token, password, secret, authorization, JWT-like values.
  • Correlation helpers: logger.withTrace(traceId), logger.withActor(actorRef).
  • Error bridge: si error instanceof CodeError, derivar category/module/code automaticamente.
  • Async flush result: flush(): Promise<FlushResult> para transports remotos.
  • Backpressure policy para buffers grandes: drop, block, sample.

Modulo frontend

Estado

frontend es pequeno y util: locale, dir, theme, mode, reduced motion/sound, density y aplicacion DOM via ActiveDom o helper de $libs/dom. La factory ya integra dom y lang si existen. Pero esta menos maduro que los demas modulos.

Refactorizaciones recomendadas

  1. P1 - Docs antiguas tras service schema. README afirma que createActiveApp() construye Frontend y usa App.Lang/App.Dom; ahora frontend se declara en services y se expone como App.frontend.
  2. P2 - Lifecycle incompleto. Tras dispose(), los setters (setLocale, setTheme, etc.) siguen funcionando y pueden aplicar DOM. Para 1.0 debe haber flag disposed y semantica uniforme.
  3. P2 - Persistencia de preferencias quedo fuera. La factory dice que storage persistence es responsabilidad de la app. Es una buena separacion, pero para 1.0 conviene un preset/helper oficial, porque tema/densidad/dir son caso principal de storage.
  4. P2 - Falta snapshot unico. Hay getters individuales, pero no snapshot() con { locale, dir, theme, mode, reducedMotion, reducedSound, density }. Para UI/debug/tests es mucho mas comodo.
  5. P3 - Validacion de valores. setDensity, setMode, setDir aceptan strings tipados en TS, pero runtime JS podria pasar valores invalidos. Si es API publica, conviene validar o documentar TypeScript-only.

Ampliaciones 1.0

  • snapshot() y onChange(snapshot).
  • Preset persistFrontendPreferences(storage).
  • Eventos de bus opcionales: frontend.preference.changed.
  • Media query injector para tests SSR/browser.
  • Documentar CSS contract: atributos data-theme, data-mode, dir, density, reduced motion.

Modulo format

Estado

format esta bien organizado: numbers, currency, dates y units como submodulos, engines y active wrappers, locale source comun e integracion con lang desde factory. Tests cubren cada subdominio. Es funcional y extensible.

Refactorizaciones recomendadas

  1. P1 - Docs con $fmts y App.Format. Alias real $format, servicio real App.format. Ademas hay un typo documental: import { createRates } from '$formats/currency' cuando el alias real es $format.
  2. P2 - createActiveFormat().dispose() no tiene guard idempotente propio. Los submodulos tienen runtime dispose, pero el root deberia seguir la regla general del ecosistema.
  3. P2 - Rates usa Date.now() por defecto. createRates({ now }) acepta inyeccion, pero defineActiveFormat no ofrece wiring con Timers.clock. Para 1.0, usar clock de App si se declaran rates con expiracion.
  4. P2 - Cache global de Intl.NumberFormat sin limite. engine-currency.ts mantiene formatCache module-global. En apps multi-locale/multi-currency/larga sesion puede crecer indefinidamente. Conviene LRU pequeno o cache por engine con dispose().
  5. P2 - Locale source doble puede duplicar notificaciones. createActiveFormat.setLocale() actualiza localeState y cada submodulo manualmente. Funciona, pero para 1.0 conviene una sola fuente reactiva que notifique y submodulos se sincronicen una vez.

Ampliaciones 1.0

  • Integracion con lang interpolation: formatters nombrados para {{price | currency}}.
  • Formatter registry: format.register('filesize', fn).
  • Ranges: date range, number range, relative time, list format, display names.
  • Rates provider HTTP/cache bridge con stale-if-error.
  • Unit catalog ampliado y aliases por dominio de negocio.
  • Tests por locale de alto riesgo: ar, en-US, es-AR, fr-FR, de-DE.

Modulo connection

Estado

connection mejoro mucho desde el primer audit: ya no es un monolito puro, ahora tiene piezas separadas para acks, heartbeat, reconnect, session wiring, channel registry, transport runtime, sender, serializer y active wrapper. Tambien exige TimerScheduler, que es correcto. Aun asi, es el modulo con mas riesgo operacional de esta tanda.

Hallazgos y refactorizaciones

  1. P1 - autoReauthOn existe en tipos pero no se usa. EngineConnectionsOptions declara autoReauthOn?: ConnectionAutoReauthOn, pero no aparece en engine-connections.ts, connection.ts ni presets. Es una opcion publica sin efecto. Para 1.0 hay que implementarla o eliminarla hasta que orca/presets la usen.
  2. P1 - README desactualizado. Usa $conn, App.createActiveConnections(), App.Sess, App.Bus, App.Timers. El codigo real usa $connection, defineActiveConnections, App.connections, core.timers, y el bus no se consume directamente salvo wiring/presets.
  3. P2 - WebSocket coverage es casi inexistente. websocket.test.ts solo valida error cuando WebSocket no existe. Faltan tests de open/message/close/error, binaryType, protocols, URL factory, bufferedAmount y cleanup de listeners.
  4. P2 - connection.ts sigue concentrando demasiado wiring. Aunque bajo de tamano frente al monolito anterior, sigue siendo el composition point de 10 KB con mucho cierre mutable (disposed, intentionalClose, lifecycle, detachSession, detachBrowserReconnect). Para 1.0 conviene dividir construction runtime en una factory interna que devuelva partes o un ConnectionRuntimeContext.
  5. P2 - Reauth concurrente no esta serializada. wireConnectionSession() puede disparar reauthenticate() en cambios de sesion sucesivos sin singleflight/cancelacion. Si llega refresh + external changed, pueden salir dos auth frames. Para 1.0, reauthenticate() deberia ser singleflight o tener politica.
  6. P2 - Reconnect backoff no expone random determinista. Usa computeBackoffDelay() sin pasar random; aunque el helper lo soporta, connection no lo deja configurar.
  7. P2 - Payloads de channels no validan schema. Tipado TS ayuda en compile-time, pero los frames de red son unknown. Para 1.0, una opcion por channel con Sium/StandardSchema reduciria bugs de mensajes malformados.

Ampliaciones 1.0

  • Implementar o retirar autoReauthOn.
  • ConnectionContext para acciones internas: publish diagnostics/events, timers, logger, abort signal.
  • Singleflight para connect/reconnect/reauthenticate.
  • WebSocket test suite real con mock constructor.
  • Channel schemas: channel('chat', { incoming: { message: schema }, outgoing: { send: schema } }).
  • Backpressure policies mas completas: buffer por topic, drop-oldest/drop-newest, metrics.
  • Reconnect policies documentadas: online/visible, queue/drop/replace si hay intento en vuelo.
  • Integracion con orca: reauth/disconnect como accion opt-in ante identity change, no acoplamiento directo a session/cache/perm.

Roadmap recomendado para esta segunda tanda

Sprint A - Docs y naming

  • Corregir aliases obsoletos en sium, storage, timer, logger, frontend, format, connection.
  • Reemplazar ejemplos App.X capitalizados por App.x servicios, excepto core reales (App.Logger, App.Bus, App.Timers, App.Orca) si se mantienen asi.
  • Actualizar README de connection, frontend, format y storage antes de tocarlos mas: ahora mismo son los que mas pueden confundir.

Sprint B - Lifecycle uniforme

  • storage: error/no-op post-dispose.
  • frontend: flag disposed y snapshot.
  • format: root dispose idempotente.
  • Tests de lifecycle para todos los servicios declarables.

Sprint C - Determinismo temporal

  • storage: clock en EngineStorageOptions.
  • format: rates con clock de App.
  • logger: opciones clock, idFactory, timer o documentar excepcion por ser core bootstrap.
  • connection: random inyectable para reconnect.

Sprint D - Riesgos operacionales

  • connection: implementar/eliminar autoReauthOn, singleflight de reauth, WebSocket tests.
  • logger: global levels por habilitacion si esa es la regla final.
  • sium: locale activo con Lang.
  • storage: no operar despues de dispose.

Prioridad resumida de la segunda tanda

Prioridad Tema Modulos Motivo
P1 Aliases/docs obsoletos sium/storage/timer/logger/frontend/format/connection API 1.0 no puede ensenar imports inexistentes.
P1 storage.dispose() no cierra superficie storage Permite crear entradas tras teardown.
P1 autoReauthOn sin efecto connection Opcion publica enganosa en un modulo critico.
P1/P2 Filtro global por threshold vs habilitacion por nivel logger Debe alinearse con la regla final del ecosistema.
P2 Locale activo no seguido por Sium sium/lang Validaciones pueden resolver mensajes en locale inicial.
P2 Determinismo de tiempo incompleto storage/logger/format/connection Falta clock/random/timer injection en caminos 1.0.
P2 Lifecycle incompleto frontend/format/storage Consistencia de servicios declarables.
P2 WebSocket tests escasos connection Superficie critica con cobertura baja.

Criterio de cierre 1.0 para esta tanda

  • Ningun README usa alias viejo ni propiedad App antigua.
  • Todo servicio declarable tiene lifecycle post-dispose definido y probado.
  • storage, format.rates, connection.reconnect y logger tienen historia determinista o excepcion documentada.
  • sium resuelve issues con el locale activo cuando se integra con lang.
  • connection no expone opciones muertas y tiene tests reales de WebSocket.
  • logger deja cerrada la decision global: threshold o per-level enable, pero no una mezcla confusa.
  • frontend tiene snapshot y persistencia oficial opt-in con storage.

Conclusion de la segunda tanda: timer, sium, logger y storage tienen una base muy fuerte; format esta sano pero necesita pulido de cache/locales; frontend necesita madurar contrato; connection es potente, pero debe cerrar opciones muertas, concurrencia de reauth y cobertura WebSocket antes de poder llamarse 1.0.

Powered by TurnKey Linux.