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:
- Contratos publicos exactos: hay documentacion y ejemplos que todavia prometen aliases, nombres o inyecciones que el codigo ya no hace.
- Determinismo operativo:
timr/Timersya existe, perohttp,session,cacheypermaun conservan defaults conDate.now(),Math.random()o timers nativos cuando se usan fuera del wiring ideal. - Pruebas compuestas: los tests unitarios estan verdes, pero faltan historias de ecosistema donde
auth,session,perm,cacheyhttpfallan 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 ensvelte.config.jsy ensrc/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(...)recibelogger; elhttpdebe venir enoptions.defineActivePerm(...)recibelogger; aunquePermClientOptionsaceptahttp?: EngineHttp, la factory no declara dependencia dehttp.defineActiveCache(...)recibelogger; no recibetimerspor defecto.defineActiveSession(...)recibeloggerybus; no recibetimerspara auto-refresh.defineEngineHttp(...)recibelogger; no recibetimers.
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:
authnecesitahttpexplicito,permnecesitaendpointohttpexplicito,cacheno se invalida sola,sessionno refresca con Timers salvo que se le pase. - Si se quiere ergonomia enterprise, evolucionar factories para dependencias opcionales:
defineActivePermpuede consumirhttpsi existe;defineActiveCacheydefineActiveSessionpueden consumirtimers.
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.
httprecibe 401/403, dispara refresh/auth state,sessionadopta o revoca,permycachereaccionan.- Logout global revoca sesion, limpia cache actor-scoped y deja
permsin actor. - Password reset revoca sesiones y el cliente queda en
anonymoussin datos privados residuales.
Accion recomendada:
- Crear
src/routes/test/ecosystemcomo harness de estas historias o moverlas a tests headless ensrc/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,:64usaDate.now(),Math.random()ysetTimeout.src/arts/http/timeout.ts:43,:63usasetTimeout.src/arts/cache/active-cache.svelte.ts:195usaDate.now()si no se pasa clock.src/arts/session/auto-refresh.ts:44,:45,:72usaDate.now(),Math.random()ysetIntervalsi no se pasatimers.src/arts/perm/client.ts:291usaDate.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
- Cambiar
SvelteSetporSeten listeners. Ensrc/arts/lang/active-lang.svelte.ts:34localeListenersno alimenta templates ni estado derivado; solo se itera manualmente. Igual que se hizo en otros modulos,Setplano reduce reactividad innecesaria. - Separar mutacion y composicion de schemas con nombres mas explicitos. Hoy
extend(namespace, module)muta el engine activo yregister(namespace, module)devuelve un engine hijo. Es potente, pero el naming puede confundir. Para 1.0 documentaria una tabla estricta:extendmuta,registercompone hijo. - Hacer
SupportedLocalemenos cerrado.src/libs/lang/types.tslimita locales a una lista base. Para producto 1.0, conviene permitir cualquier BCP47 tipado como branded string o una registry generica por app. - Modo estricto de interpolacion.
interpolateTemplate()resuelve placeholders, pero no hay modo que falle si falta un parametro. Para 1.0 deberia existirstrictInterpolationcon diagnostico/log. - Unificar docs de inyeccion. La factory
defineActiveLangsi inyecta logger viasetLogger(core.logger). La documentacion debe mostrar claramenteservices: { lang: defineActiveLang({ schema }) }y no dar a entender quecreateActiveApp()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}}usandoformat. - 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
- Actualizar docs antiguas. Hay ejemplos que aun usan
$cachoApp.Cache; el alias real es$cachey la app exponeApp.cachesolo si se declaraservices.cache. - Inyectar clock desde App.Timers cuando se usa como servicio.
createActiveCache()aceptaclock, perodefineActiveCache()solo inyectalogger. Para 1.0, el default app-wired deberia usarcore.timers.clock. - Evitar
console.warndirecto en memory adapter.src/libs/cache/adapters/memory.tsusaconsole.warnsi se crea en produccion sinonProductionWarning. En 1.0, la advertencia deberia pasar por diagnostics/logger o exigir handler explicito. - Revisar clonacion de valores.
memoryCacheAdapterusastructuredClonesi existe y fallback JSON. El fallback rompeDate,Map,Set,BigInt, clases y valores no serializables. Para 1.0: o se documenta "valores serializables" o se exponeclone?: (value) => value. - Purgado escalable. La memoria purga expirados escaneando entradas. Es razonable para v0, pero para 1.0 conviene un sweeper opcional con
Timerso 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 conhttppara 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
- Actualizar naming en docs. Debe desaparecer
$sessyApp.Sess; el alias real es$sessiony el servicio esApp.session. - Cablear auto-refresh con Timers desde App.
auto-refresh.tsya aceptatimers, perodefineActiveSession()no los inyecta. Para 1.0, si una app usacreateActiveApp(), el refresh deberia poder ser determinista sin boilerplate manual. - Limitar defaults nativos en modo app-wired.
Date.now,Math.randomysetIntervalson aceptables como fallback aislado, pero no como camino principal del ecosistema. - Documentar ownership frente a auth.
sessionno debe saber de credenciales ni permisos; solo continuidad, refresh/revoke, snapshot y bus events.authprueba identidad;permdecide permisos. - 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 useTimers. - Integracion
httppara refresh por 401 sin acoplarhttpasession: 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
- Corregir compilador SQL para paths anidados. El runtime usa
getPath, perosrc/libs/perm/compilers/sql.ts:105y:110leeninput.actor[expr.path]yinput.context?.[expr.path]. Un path comorisk.mfaoprofile.departmentse evaluara distinto en runtime y en SQL. Para 1.0 esto debe ser P1: usargetPath()tambien en actor/context o declarar que SQL solo soporta paths planos. - Preordenar policies una vez.
src/libs/perm/runtime.tsordena por prioridad en cada decision. Para volumen real, ordenar al construir runtime y mantener indices por action/resource reduce coste. - Memoizar providers por decision.
DefaultPermEvaluatorpuede 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. - Eliminar IDs auto-generados no estables en produccion.
definePolicies/builders pueden producir IDs por contador. Para 1.0, los policies persistidos deberian exigiridestable o generar checksum determinista. - Alinear factory y docs.
PermClientOptionsaceptahttp?: EngineHttp, perodefineActivePerm()no inyectaApp.http. O se documenta quefetcher/httpson manuales, o se declara dependencia opcional dehttp.
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()yexplain()cacheados con invalidacion por version de policies.- Obligations/advice con enforcement helpers, no solo datos.
- Auditoria de decisiones: escribir
permission_decision_auditdesde 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
- Completar handlers para rutas ya publicadas.
src/libs/auth/consts.ts:21-30declara rutas OAuth, MFA, devices y WebAuthn.src/arts/auth/active-auth.svelte.ts:154-166ya llamaDEVICESyDEVICE_REVOKE. Perosrc/svrs/auth/handlers.tssolo enruta current, csrf, password, logout, email verification y password reset. Resultado:ActiveAuth.listDevices()yrevokeDevice()apuntan a endpoints que el handler generico no sirve. Para 1.0, o se agregan handlers, o se retiran del cliente hasta estar soportados. - No guardar secretos OAuth en metadata de flow.
oauth-flow.tsguardastateyverifierenmetadataademas 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. - PKCE challenge no debe usar hash token raw si no es base64url SHA-256 estandar. Si
hashAuthToken()no produce exactamentebase64url(SHA256(verifier)), el flujo OAuth no sera interoperable. El testoauth-pkce.test.tsexiste, pero conviene comprobarlo contra el RFC shape. - 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. createDbAuthAdapteres demasiado generic para produccion. El adapterdb.tsacepta repositorios conwheregenerico basado en records TS, mientras el SQL aplanaactorRefatenant_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,cacheyperm: 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
- Port de timers/retry.
retry.tsytimeout.tsusan timers nativos. Para 1.0 deberia existirHttpTimerPorto integracion directa conTimers, al menos cuando se crea viadefineEngineHttp(). - Jitter inyectable.
computeRetryDelay()usaMath.random()sipolicy.jitteresta activo. Para tests deterministas y produccion controlada, aceptarrandom?: () => number. - Cerrar lifecycle de timeouts.
attemptTimeoutSignal()ytotalTimeoutSignal()creansetTimeout; si la request termina antes, el timeout queda pendiente hasta disparar. No siempre es grave, pero en alto volumen deberia poder cancelarse. - Helpers de autenticacion sin acoplar a auth. La pieza deberia ofrecer patrones genericos para
beforeRequest/beforeRetry/beforeErrorque permitan refresh por 401, pero sin conocersessionniauth. - 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
Responseen 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,defineActivePermydefineEngineHttppuedan consumircore.timerscuando aplique. - Inyectar
randomen retry/session auto-refresh. - Mantener fallbacks nativos para uso aislado, pero no para el camino App.
Sprint 3 - Bugs de contrato
- Completar handlers de
authpara 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
defineActivePermconhttpreal.
Sprint 4 - Tests compuestos
Escenarios minimos:
- Login A -> cache privado -> logout -> login B -> B no ve cache/perm de A.
- Permission webhook ->
perm.invalidate()-> decision antigua no se reutiliza. - HTTP 401 -> refresh sesion -> replay -> cache conserva solo datos validos.
- Password reset -> revoca sesiones -> active auth anonimo -> cache actor-scope limpia.
- Logout global -> session revoked -> perm sin actor -> cache limpia -> http protegido falla seguro.
- Tenant switch -> cache/perm invalidados por tenant.
Sprint 5 - Server readiness
authypermya tienen SQL de referencia; convertirlo en migraciones versionadas o al menos en contract tests ejecutables.cachenecesita historia clara de adapter server: memory solo test/dev, storage/browser, y adapter remoto recomendado.httpno necesitasvrs/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,cacheyhttptienen al menos una suite compuesta de identidad completa.authno publica rutas/cliente que el server handler no soporte.permproduce la misma decision en runtime y SQL compiler para paths soportados.httpy auto-refresh son testeables sin timers nativos.- Los adapters server de
authypermtienen 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.Timersen 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:
storageno impideentry()niclear()despues dedispose().frontendmantiene setters operativos trasdispose().formatroot 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
CodeErrorcomo raiz. - Renombrar comentarios, mensajes y catalogos internos que digan
stor/timr/logr/connsi el modulo ya se llamastorage/timer/logger/connection. - Si
ErrCodeusa 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
- Resolver locale activo, no solo default inicial.
createEngineSium()capturadefaultLocale = options.locale ?? lang?.getDefaultLocale() ?? 'es'. Si se inyectaActiveLangmediantedefineEngineSiumy luego cambia el locale deApp.lang,resolveIssue()sin locale explicito seguira usando el default capturado. Para 1.0, si ellanginyectado tienegetLocale(), Sium deberia usar el locale actual, o no pasar locale alang.t()para dejar que Lang resuelva su estado actual. - Cerrar la migracion de errores legacy.
src/arts/sium/errors.tsmantieneSIUM_ERRORScomo catalogo legacy para strings que aun no sonCodeError. La propia nota dice que quedan sitios por migrar. Para 1.0, todos los errores de construccion/encode/decode deberian tenerErrCode. - Reducir fragilidad de facade manual.
EngineSiumlista manualmente decenas de funciones. Hay tests de barrel, pero para 1.0 conviene un snapshot de surface o generacion controlada para evitar quecoregane funciones que el engine no expone. - Separar issues de errores de programador en docs. La distincion existe en codigo:
validatedevuelveResult,decodelanza. 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-schemafrente 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
- P1 -
dispose()no cierra realmente la superficie publica.createEngineStorage()marcadisposed = true, peroentry(),clear()yentries()no verifican ese estado. Despues dedispose()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 haberStorageDisposedErroro no-op documentado. - P2 - TTL usa
Date.now()sin clock inyectable.decodeEnvelope()yencodeEnvelope()aceptannow, peroentry-runtime.tsllama sin pasar reloj.EngineStorageOptionsno tieneclock. Para tests deterministas y App wiring, convieneclock?: { now(): number }, inyectado desdeApp.Timers.clock. - 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. - P2 - Top-level serializer auto-selection puede sorprender. Esta documentado: objetos con
Dateanidada caen a JSON y no restauran Date. Para 1.0, anadir recipes de serializer por schema o integracion con Sium. - P2 - Cookies cliente no endurecidas por defecto.
cookieAdapter()defaultsecure: 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 porstorage.
Ampliaciones 1.0
StorageDisposedErrory 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
- Actualizar docs antiguas. README sigue usando
$timryApp.Timers; alias real$timer, propiedad realApp.Timerssolo para core si se mantiene capitalizado. En codigo actualcreateActiveApp()si expone coreTimers, asi aqui la capitalizacion es real, pero el alias no. - Consolidar exports de backoff.
src/arts/timer/backoff.tsre-exporta desde$libs/timer, yindex.tstambien lo expone. No es grave, pero para 1.0 conviene una unica historia: backoff vive enlibs/timer,arts/timerlo reexporta en index por conveniencia. - Exponer random en helpers consumidores.
computeBackoffDelay()ya aceptarandom, peroconnectionno lo expone en sus reconnect options. Para 1.0, los consumidores deben poder hacer backoff determinista. - Nombrar mejor
timercomo clock/scheduler del ecosistema. En docs debe quedar claro que no es una utilidad de UI, sino la fuente temporal parahttp,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
AbortSignalpara quehttp/orcano 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
- 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 usastate.levelcomo threshold (if (lvl < state.level) return). Si la regla final del framework es "habilitacion por nivel, no threshold tradicional",LoggerOptionsdeberia aceptarlevelstambien a nivel global, ylevelquedarse como shorthand o deprecated antes de 1.0. - P2 - Reducir dependencia directa de
consoledentro del engine.handleFailure()emiteconsole.errorademas de crear synthetic failure entry. En un entorno enterprise puede duplicar salida o saltarse transports. Para 1.0 convieneonInternalError,internalTransport, oconsoleFallback?: boolean. - P2 - Inyectar clock/id factory opcional. IDs fallback usan
Date.now()/Math.random(), failure throttle usaDate.now(), timers de buffer usansetTimeout. Como Logger se crea antes deTimers, no puede depender deApp.Timers, pero si puede aceptarclock,idFactoryysetTimeoutopcionales para tests y runtimes especiales. - P2 -
dispose()de child logger solo advierte en DEV conconsole.warn. Es correcto como defensa, pero debe estar documentado en API: solo el root owns lifecycle.
Ampliaciones 1.0
- Global
levelscon shorthandlevelsAtLeast. - 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
- P1 - Docs antiguas tras service schema. README afirma que
createActiveApp()construyeFrontendy usaApp.Lang/App.Dom; ahorafrontendse declara enservicesy se expone comoApp.frontend. - P2 - Lifecycle incompleto. Tras
dispose(), los setters (setLocale,setTheme, etc.) siguen funcionando y pueden aplicar DOM. Para 1.0 debe haber flagdisposedy semantica uniforme. - 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. - 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. - P3 - Validacion de valores.
setDensity,setMode,setDiraceptan 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()yonChange(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
- P1 - Docs con
$fmtsyApp.Format. Alias real$format, servicio realApp.format. Ademas hay un typo documental:import { createRates } from '$formats/currency'cuando el alias real es$format. - P2 -
createActiveFormat().dispose()no tiene guard idempotente propio. Los submodulos tienen runtime dispose, pero el root deberia seguir la regla general del ecosistema. - P2 - Rates usa
Date.now()por defecto.createRates({ now })acepta inyeccion, perodefineActiveFormatno ofrece wiring conTimers.clock. Para 1.0, usar clock de App si se declaran rates con expiracion. - P2 - Cache global de Intl.NumberFormat sin limite.
engine-currency.tsmantieneformatCachemodule-global. En apps multi-locale/multi-currency/larga sesion puede crecer indefinidamente. Conviene LRU pequeno o cache por engine condispose(). - 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
langinterpolation: 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
- P1 -
autoReauthOnexiste en tipos pero no se usa.EngineConnectionsOptionsdeclaraautoReauthOn?: ConnectionAutoReauthOn, pero no aparece enengine-connections.ts,connection.tsni presets. Es una opcion publica sin efecto. Para 1.0 hay que implementarla o eliminarla hasta queorca/presets la usen. - 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. - P2 - WebSocket coverage es casi inexistente.
websocket.test.tssolo valida error cuando WebSocket no existe. Faltan tests de open/message/close/error, binaryType, protocols, URL factory, bufferedAmount y cleanup de listeners. - P2 -
connection.tssigue 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 unConnectionRuntimeContext. - P2 - Reauth concurrente no esta serializada.
wireConnectionSession()puede dispararreauthenticate()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. - P2 - Reconnect backoff no expone random determinista. Usa
computeBackoffDelay()sin pasarrandom; aunque el helper lo soporta, connection no lo deja configurar. - 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. ConnectionContextpara 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.Xcapitalizados porApp.xservicios, excepto core reales (App.Logger,App.Bus,App.Timers,App.Orca) si se mantienen asi. - Actualizar README de
connection,frontend,formatystorageantes 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 enEngineStorageOptions.format: rates con clock de App.logger: opcionesclock,idFactory,timero documentar excepcion por ser core bootstrap.connection: random inyectable para reconnect.
Sprint D - Riesgos operacionales
connection: implementar/eliminarautoReauthOn, 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.reconnectyloggertienen historia determinista o excepcion documentada.siumresuelve issues con el locale activo cuando se integra conlang.connectionno expone opciones muertas y tiene tests reales de WebSocket.loggerdeja cerrada la decision global: threshold o per-level enable, pero no una mezcla confusa.frontendtiene snapshot y persistencia oficial opt-in constorage.
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.