Validate persisted Eidos configs

active-uix
dev 5 months ago
parent 3c115ce093
commit bfd3b4020e

@ -2,6 +2,27 @@
Fecha de corte: 2026-05-16. Rama: `active-uix`. Fecha de corte: 2026-05-16. Rama: `active-uix`.
Actualizacion Eidos ecosystem 2026-05-16:
- Correccion de alcance: no seguir tocando demos/rutas ni
`src/uix/eidos/components/*` sin orden explicita. El foco vigente es el
ecosistema Eidos fuera de componentes: `ActiveEidos`, `eidos/lib`,
contrato/generacion/persistencia, tests de runtime y documentacion de capa.
- Persistencia reforzada: `readEidosConfigFromDocument(...)` y
`parseEidosConfigFromJson(...)` ya no aceptan solamente un envelope valido;
al convertirlo en runtime validan tambien las `options` contra el contrato
completo de `EidosConfig` y lanzan `EidosConfigValidationError` si el tema
persistido no es usable.
- `parseEidosConfigDocument(...)` queda definido como parser de envelope
versionado (`kind/version/options`), no como validador semantico del tema.
- Guardia anadida: el contrato CSS de Eidos debe estar alineado con los
renderers generados; cada token `static` debe aparecer en `renderStaticCss()`
y cada token `theme` en `renderThemeCss('base-light')`.
- Validado:
- `npx vitest run src/uix/eidos/active-eidos-config.test.ts` -> 26 tests OK.
- `npx vitest run src/uix/eidos` -> 7 archivos, 65 tests OK.
- `npm run check` -> 0 errores, 0 warnings.
Actualizacion permutation runner 2026-05-16: Actualizacion permutation runner 2026-05-16:
- Segunda tanda de demos `/uix/components/*` instrumentada con - Segunda tanda de demos `/uix/components/*` instrumentada con

@ -349,6 +349,13 @@ con versiones no soportadas. La app decide dónde guardar ese documento
(prefs, backend, archivo, etc.) y qué metadata de producto lo envuelve (prefs, backend, archivo, etc.) y qué metadata de producto lo envuelve
(`name`, owner, timestamps). (`name`, owner, timestamps).
Al hidratar un documento como runtime (`config: document`,
`readEidosConfigFromDocument(...)` o `parseEidosConfigFromJson(...)`), Eidos
valida también las `options` contra el contrato completo de `EidosConfig`.
`parseEidosConfigDocument(...)` queda como parser de envelope: confirma
`kind/version/options`, pero no convierte ese envelope en configuracion
visual usable por si solo.
Integracion normal dentro de un arbol con `ActiveUix`: Integracion normal dentro de un arbol con `ActiveUix`:
```ts ```ts

@ -12,6 +12,7 @@ import {
EidosThemeNotFoundError, EidosThemeNotFoundError,
EidosConfigValidationError, EidosConfigValidationError,
extendEidosConfig, extendEidosConfig,
parseEidosConfigFromJson,
parseEidosConfigDocument, parseEidosConfigDocument,
STATIC_CONTROL_HEIGHT, STATIC_CONTROL_HEIGHT,
STATIC_DENSITY, STATIC_DENSITY,
@ -58,6 +59,10 @@ function createEidosFromJson(json: string) {
return createEidosFromDocument(parseEidosConfigDocument(json)) return createEidosFromDocument(parseEidosConfigDocument(json))
} }
function cssVariableDeclarations(css: string): Set<string> {
return new Set([...css.matchAll(/--[a-z0-9_-]+(?=\s*:)/g)].map(([name]) => name))
}
function colorScale(seed: string): ColorScale { function colorScale(seed: string): ColorScale {
return Object.fromEntries( return Object.fromEntries(
COLOR_SCALE_STEPS.map((step) => [step, `${seed}-${step}`]) COLOR_SCALE_STEPS.map((step) => [step, `${seed}-${step}`])
@ -299,6 +304,18 @@ describe('ActiveEidos config', () => {
expect(() => parseEidosConfigDocument('{')).toThrow(EidosConfigDocumentError) expect(() => parseEidosConfigDocument('{')).toThrow(EidosConfigDocumentError)
}) })
it('rejects persisted documents with invalid Eidos config options', () => {
const invalid = structuredClone(createThemeBaseEidos().toDocument()) as EidosConfigDocument
;(invalid.options.semantics.color.roles as Record<string, unknown>).primary =
'missing-scale'
expect(() => createEidosFromDocument(invalid)).toThrow(EidosConfigValidationError)
expect(() => parseEidosConfigFromJson(JSON.stringify(invalid))).toThrow(
EidosConfigValidationError
)
})
it('ships typography families separately from typography styles', () => { it('ships typography families separately from typography styles', () => {
const typography = createThemeBaseEidos().snapshot().primitives.typography! const typography = createThemeBaseEidos().snapshot().primitives.typography!
@ -620,6 +637,20 @@ describe('ActiveEidos config', () => {
expect(css).not.toContain('--size-full-control-height') expect(css).not.toContain('--size-full-control-height')
}) })
it('keeps the generated CSS renderers aligned with the CSS contract', () => {
const eidos = createThemeBaseEidos()
const contract = eidos.getCssContract()
const staticDeclarations = cssVariableDeclarations(eidos.renderStaticCss())
const themeDeclarations = cssVariableDeclarations(eidos.renderThemeCss('base-light'))
for (const token of contract.static) {
expect(staticDeclarations.has(token.cssVar), token.cssVar).toBe(true)
}
for (const token of contract.theme) {
expect(themeDeclarations.has(token.cssVar), token.cssVar).toBe(true)
}
})
it('renders contract-aware runtime CSS variables', () => { it('renders contract-aware runtime CSS variables', () => {
const eidos = createThemeBaseEidos() const eidos = createThemeBaseEidos()
const css = eidos.renderCssVariables( const css = eidos.renderCssVariables(

@ -57,11 +57,15 @@ export function snapshotEidosConfig(options: EidosConfig): EidosConfigSnapshot {
} }
export function readEidosConfigFromDocument(document: unknown): EidosConfig { export function readEidosConfigFromDocument(document: unknown): EidosConfig {
return snapshotEidosConfig(readEidosConfigDocumentEnvelope(document).options) const config = snapshotEidosConfig(readEidosConfigDocumentEnvelope(document).options)
assertValidEidosConfig(config)
return config
} }
export function parseEidosConfigFromJson(json: string): EidosConfig { export function parseEidosConfigFromJson(json: string): EidosConfig {
return snapshotEidosConfig(parseEidosConfigDocumentEnvelope(json).options) const config = snapshotEidosConfig(parseEidosConfigDocumentEnvelope(json).options)
assertValidEidosConfig(config)
return config
} }
export function createEidosConfigDocumentFromConfig(options: EidosConfig): EidosConfigDocument { export function createEidosConfigDocumentFromConfig(options: EidosConfig): EidosConfigDocument {

Loading…
Cancel
Save

Powered by TurnKey Linux.