reactigoded 1.0.0-beta.26 → 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +183 -6
  2. package/README.md +17 -5
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,17 +7,194 @@ versionado [SemVer](https://semver.org/lang/es/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ _Sin cambios desde `1.0.0-rc.1`._
11
+
12
+ ## [1.0.0-rc.1] — 2026-07-19 · **API pública congelada**
13
+
14
+ Primer release candidate. **La superficie pública de 1.0 queda congelada**: a partir de
15
+ aquí, cambiarla exige un bump MAJOR. Cierra el gate `claudegate6` (auditoría con cruce de
16
+ dos auditores independientes A+B sobre `beta.26`) — 2 BLOCKER, 3 HIGH, 6 MEDIUM y 4 LOW.
17
+
18
+ Este documento declara también **lo que NO se hizo y por qué**: en un release de freeze,
19
+ los residuales declarados valen más que una lista solo de logros.
20
+
10
21
  ### Cambiado — BREAKING (última ventana antes de 1.0)
11
22
 
12
- - Renombres de API pública para eliminar colisiones de nombre antes de congelar 1.0:
13
- - Clases `.ig-tooltip-color-*` → `.ig-tooltip-*` (el infijo `-color-` era dialecto local; la doc ya prometía la forma sin él).
14
- - Clase `.ig-navbar-brand` → `.ig-navbar-logo` y componente `NavbarBrand` → `NavbarLogo` (`brand` colisionaba con el eje de rol de color; el slot es el logo).
15
- - Clases `.ig-input-error`/`.ig-input-success` → `.ig-input-invalid`/`.ig-input-valid` y valores del prop `state` (`"invalid"`/`"valid"`), alineados con `aria-invalid`.
23
+ Renombres de API pública para eliminar colisiones de nombre antes de congelar. Se hacen
24
+ ahora porque el paquete tenía **0 consumidores en npm**; después de 1.0 costarían un major.
25
+
26
+ - Clases `.ig-tooltip-color-*` → `.ig-tooltip-*` (el infijo `-color-` era dialecto local; la doc ya prometía la forma sin él).
27
+ - Clase `.ig-navbar-brand` → `.ig-navbar-logo` y componente `NavbarBrand` → **`NavbarLogo`** (`brand` colisionaba con el eje de rol de color; el slot es el logo).
28
+ - Clases `.ig-input-error`/`.ig-input-success` → `.ig-input-invalid`/`.ig-input-valid` y valores del prop `state` (`"invalid"`/`"valid"`), alineados con `aria-invalid`.
29
+
30
+ Además, 4 correcciones de documentación de clases que **se documentaban pero no shippean**:
31
+ `ig-btn-md`, `ig-timeline-dot-default`, `ig-step-interactive`, `ig-text-on-cinis`.
16
32
 
17
33
  ### Añadido
18
34
 
19
- - **Garantía `@server-safe` validada contra Vercel Edge REAL (#18)**: el catálogo de globals permitidos se medía contra `@edge-runtime/vm` (sandbox sobre Node que filtra globals Node-shared, ~95% fiel). Un probe de deploy real a Vercel Edge producción (`scripts/runtime-oracle/vercel/`, `typeof <bare>` — el único test fiel dado que el objeto-global de Edge es exótico) cerró ese ~5%: **3 globals que `@edge-runtime/vm` reportaba presentes por fuga de Node (`WeakRef`, `FinalizationRegistry`, `DOMException`) NO existen en Vercel Edge real** → restados de `SAFE_GLOBALS` (nuevo set `EDGE_MISSING_REAL`). Cierra un falso-negativo latente (0 módulos los usaban) que habría dejado pasar un `new WeakRef(...)` bare que crashea en producción Edge. Premisas del catálogo (createObjectURL/WASM/eval/elu) confirmadas 6/6 en el Edge real.
20
- - **Freeze de API pública (§5.13)**: la superficie estable de 1.0 (clases de componente, data-attributes de estado, tokens Tier-2) se congela en `src/_audit/public-api-names.json`, protegida por el gate `scripts/check-public-api-names.mjs` (`⊆ dist`, encadenado en `verify:unit`). Editar el freeze exige bump MAJOR. La capa utility (`state.css`) y los tokens Tier-1/Tier-3 quedan fuera del freeze **por declaración explícita** (ver CSSAPI.mdx + DesignTokens.mdx).
35
+ - **Freeze de API pública (§5.13)**. La superficie estable de 1.0 se congela en
36
+ `src/_audit/public-api-names.json`: **332 clases** de componente, **2 classHooks**,
37
+ **6 data-attributes** de estado y **37 tokens Tier-2**. Protegido por el gate
38
+ `scripts/check-public-api-names.mjs` (verifica `JSON ⊆ dist`, encadenado en `verify:unit`
39
+ **post-build**). Editar el freeze exige **bump MAJOR** + entrada aquí.
40
+ **Alcance declarado del gate: solo integridad.** Caza el rename *accidental* que olvida
41
+ actualizar el contrato; **no** impide el rename deliberado — así es exactamente como se
42
+ renombra, a propósito y con major.
43
+ **Fuera del freeze por declaración explícita** (no por silencio): la **capa utility** de
44
+ `state.css` (`ig-bg-*`, `ig-text-*`, `ig-flex`, `ig-gap-*`…), por ser opt-in y
45
+ **experimental** — su vocabulario puede evolucionar sin major hasta que se declare estable
46
+ aquí; y los **tokens Tier-1 y Tier-3**, porque una escala conserva la libertad de perder un
47
+ escalón sin major. Documentado en `docs/CSSAPI.mdx` y `docs/DesignTokens.mdx`.
48
+ - **Publicación en npm.** El paquete existe en el registro (`reactigoded`). Ver
49
+ *Limitaciones conocidas* para el estado de los dist-tags.
50
+ - **Pipeline de release automatizado** vía **Trusted Publishing (OIDC)**: sin tokens
51
+ almacenados, sin 2FA/OTP, con **provenance firmada** automáticamente. Se dispara al
52
+ pushear un tag `v*` y publica tras el `verify` completo, registrando versión, dist-tag y
53
+ commit en la GitHub Release.
54
+
55
+ ### Corregido
56
+
57
+ **BLOCKER**
58
+
59
+ - **Gate `@server-safe`: de denylist frágil a fail-closed.** `CLIENT_GLOBALS` era una
60
+ **denylist de ~46 nombres**, pero `lib.dom.d.ts` declara ~826 globals client-only que
61
+ lanzan `ReferenceError` en Node: **~780 pasaban en silencio** (`HTMLElement`, `self`,
62
+ `CSS`, `instanceof Element`…), incluso por ruta transitiva. Reemplazado por una
63
+ **whitelist fail-closed** (`SAFE_GLOBALS` = builtins ES ∪ globals de Node, menos
64
+ denegaciones intencionales y overclaims verificados), anclada al engine mínimo (Node
65
+ 22.12) mediante la matriz CI. Ahora un global DOM nuevo **se caza solo**: un falso
66
+ positivo es ruido corregible, no un crash en producción SSR.
67
+ - **Marcador `@server-safe` anidado = fail-open silencioso.** La detección solo miraba los
68
+ statements top-level, así que un marcador en posición anidada no marcaba **y no avisaba**.
69
+ Ahora recorre el AST completo y **lanza** en posición no soportada (fail-loud).
70
+
71
+ **HIGH**
72
+
73
+ - **`Slot` descartaba children en silencio.** Con más de un hijo renderizaba solo el
74
+ primero, borrando CTAs y nodos accesibles sin aviso. Ahora devuelve `null`: fallo
75
+ observable en vez de pérdida silenciosa.
76
+ - **`composeRefs` descartaba los cleanups de refs de React 19.** Un callback ref que
77
+ devuelve función de limpieza nunca la ejecutaba al desmontar (y recibía un `ref(null)`
78
+ inesperado), **fugando observers**. Retorno ensanchado a `void | (() => void)` con
79
+ recolección y combinación de cleanups.
80
+ - **`ContrastPairs` con aserción unidireccional.** Solo comprobaba el límite inferior, así
81
+ que un drift **hacia arriba** (cardinales separándose) pasaba silencioso. Ahora es una
82
+ banda de dos lados, más un assert fail-loud de que todo cardinal del allowlist esté
83
+ mapeado a variante.
84
+
85
+ **MEDIUM**
86
+
87
+ - `clsx` faltaba en el **comando de install del README**: quien lo siguiera y lo omitiera se
88
+ encontraba `reactigoded/cn` reventando con `ERR_MODULE_NOT_FOUND`. *(El packaging no se
89
+ toca: `clsx` es peer externalizada por decisión cerrada de beta.24.)*
90
+ - **133 `.d.ts.map` colgantes**: apuntaban a `../src/*.ts`, que **no se publica** (`files` no
91
+ incluye `src`), rompiendo el go-to-definition del consumer y engordando el tarball.
92
+ `declarationMap: false` — **decisión final**.
93
+ - **`consumer-pack` validaba 2 de 36 exports** server-safe desde el tarball; el bug que
94
+ motivó el gate (259 errores TS2834 bajo NodeNext) seguía latente en los otros 34. Ahora
95
+ materializa los 36 en ambas resoluciones (Bundler y NodeNext).
96
+ - **Rutas UNC** (`//host/share`) colapsaban a `/` en el gate, rompiendo la ejecución en esos
97
+ paths. Fix simétrico al del drive-letter.
98
+ - **Crash de `attw` en ARM64** — resuelto upstream y verificado en vivo.
99
+ - **~106 líneas de warnings en stderr** durante los tests. Política nueva: solo se permiten
100
+ los dev-warnings propios del DS; cualquier otro `console` **falla el test**. stderr = 0.
101
+
102
+ **LOW**
103
+
104
+ - **ERESOLVE en `npm ci` plano** (eslint 10 vs plugins con peer `^9`): cerrado con
105
+ `overrides` quirúrgico. `--legacy-peer-deps` **eliminado de los 3 jobs de CI**.
106
+ - **`npm run verify` ≠ CI**: `test:no-dev-warns` corría en CI pero no en `verify`, así que
107
+ `prepublishOnly` podía saltárselo. Encadenado.
108
+ - **`build-storybook` pasaba con warnings**: glob `.mdx` vacío eliminado y umbral de chunk
109
+ ajustado. *(Ver Diferido para el tercer warning, que no se toca a propósito.)*
110
+ - **`npm publish --dry-run` inutilizable**: npm propaga `npm_config_dry_run` a los comandos
111
+ anidados, así que el `npm pack` del gate anunciaba el tarball **sin escribirlo** →
112
+ el ensayo previo a una operación irreversible fallaba siempre. Neutralizada la herencia.
113
+
114
+ ### Garantía `@server-safe` validada contra Vercel Edge **real**
115
+
116
+ El catálogo de globals permitidos se derivaba de `@edge-runtime/vm`, un sandbox sobre Node
117
+ que **filtra globals Node-shared** (~95% fiel). Un probe desplegado a Vercel Edge de
118
+ producción cerró ese ~5%:
119
+
120
+ - **3 falsos negativos cazados** — `WeakRef`, `FinalizationRegistry` y `DOMException`
121
+ aparecían como presentes por la fuga de Node, pero **no existen en Vercel Edge**. Restados
122
+ de `SAFE_GLOBALS` (set nuevo `EDGE_MISSING_REAL`). Cierra un fallo latente que habría
123
+ dejado pasar un `new WeakRef(...)` que crashea en producción.
124
+ - **Premisas confirmadas** (`createObjectURL`/`revokeObjectURL` lanzan, `WebAssembly.compile`
125
+ lanza `CompileError`, `new Function` lanza `EvalError`, `performance.eventLoopUtilization`
126
+ ausente) y **0 fail-opens** en los 22 guards browser-only.
127
+ - **Validado cross-región**: `lhr1` vs `iad1`, **0 diferencias en 1489 puntos** medidos → lo
128
+ pineado es propiedad del *runtime*, no de una región.
129
+
130
+ Medición reproducible en `scripts/runtime-oracle/vercel/`.
131
+
132
+ ### `exactOptionalPropertyTypes` — las 2 fronteras
133
+
134
+ 307 props públicas se ensancharon a `?: T | undefined` para consumers con
135
+ `exactOptionalPropertyTypes: true`. **Dos clases quedan fuera a propósito**, porque
136
+ ensancharlas rompería su semántica:
137
+
138
+ 1. **Discriminantes con literal `?: undefined`** (3): `NavbarLogo`, `SidebarItem` y
139
+ `MenuItem` — el `undefined` literal *es* lo que activa la rama `button`/`div` del
140
+ discriminated union.
141
+ 2. **Exclusiones con `?: never`** (8): en `Accordion`, `Avatar` (×3), `BreadcrumbItem` y
142
+ `useControllableState` (×3) — `never` *es* el mecanismo de exclusión de variantes.
143
+
144
+ Inventario completo: `node scripts/eopt-classify.mjs`.
145
+
146
+ ### Limitaciones conocidas
147
+
148
+ - **Paquete ESM-only.** Desde CJS hay que usar `import()` dinámico. El engine mínimo es Node
149
+ `>=22.12`.
150
+ - **`npm install reactigoded` devuelve `1.0.0-beta.26`, no el rc.1.** El dist-tag `latest`
151
+ quedó apuntando a la beta en la primera publicación y **npm no permite retirarlo**
152
+ (responde 400). `--tag rc` no lo mueve. Para el rc.1: `npm install reactigoded@rc`. Se
153
+ corrige solo al publicar `1.0.0` final.
154
+ - **Go-to-definition aterriza en los `.d.ts`**, no en el `.ts` original (consecuencia de
155
+ `declarationMap: false`). Shippear `src` + maps es aditivo y se evalúa post-1.0.
156
+ - **Falsos positivos conocidos del gate `@server-safe`** — todos *fail-closed*, ninguno
157
+ oculta código ejecutable: `process.env` con guard `typeof` se flagea (workaround:
158
+ `import.meta.env`); 10 globals de streams/eventos presentes en Edge real siguen flageados
159
+ por sobre-estrictez deliberada; `console.clear` se flagea aunque funciona.
160
+ - **El marcador `@server-safe` debe ir en su propia línea.** Prosa *antes* del marcador en la
161
+ misma línea no marca **y no avisa** (residual declarado abajo).
162
+ - **Contraste `axis` vs `kobalium` en tema oscuro**: ΔE `0.0522`, allowlisteado
163
+ conscientemente. Pueden confundirse si quedan adyacentes.
164
+ - **CSP estricta con el Storybook del DS**: el script propio está 100% externalizado, pero
165
+ Storybook inyecta upstream un `<script>` inline. Requiere `'unsafe-inline'` o un nonce.
166
+
167
+ ### Diferido y residual — con su razón
168
+
169
+ **No hecho a propósito** (no es deuda olvidada):
170
+
171
+ - **Coverage de `perceptual-allowlist.json?import`** — *verificado no-issue*. Lo excluido es
172
+ un JSON de **datos, no código**; coverage **no es gate**; y en vitest 4
173
+ `coverageConfigDefaults.exclude` viene vacío, así que setearlo habría borrado los excludes
174
+ built-in y **deflactado** el número. Arreglarlo era un falso arreglo.
175
+ - **Tercer warning de `build-storybook`** (`Skipping docgen for preview.tsx`) — es Storybook
176
+ informando de que saltó **correctamente** un fichero de configuración. Silenciarlo exigiría
177
+ parchear un plugin minificado.
178
+
179
+ **Residual por diseño** (fronteras declaradas, no huecos):
180
+
181
+ - **Data-flow / provenance** — el gate no sigue valores a través de llamadas, parámetros,
182
+ fronteras cross-módulo ni round-trips de representación. Cazar solo el subconjunto
183
+ sintáctico obvio sería **falsa cobertura**.
184
+ - **Frontera del eval-sink** — se caza el *token presente en su sitio*; el **token
185
+ ensamblado** (`"a"+"b"`, `fromCharCode`, `join`) es residual: hay infinitas escrituras
186
+ equivalentes y cazar una sería teatro. Con cláusula de caducidad si `@server-safe` pasara a
187
+ ser frontera de confianza sobre código no auditado.
188
+ - **Prosa antes del marcador en la misma línea** — se tolera en silencio porque el
189
+ discriminador intención-vs-mención dentro de prosa es genuinamente ambiguo
190
+ (`/** not yet @server-safe */` es una mención legítima que no debe lanzar).
191
+
192
+ **Diferido a releases posteriores**: `process.env` con guard (rc.2); derivación sistemática
193
+ de globals Edge con las patas **workerd** y **Deno** (la de Vercel Edge ya está cerrada);
194
+ re-derivar `EDGE_MISSING_GLOBALS` desde Edge real para retirar la receta basada en
195
+ `@edge-runtime/vm`; `typescript@7` como autoritativo (bloqueado por `typescript-eslint` y
196
+ porque el port nativo no expone la Compiler API que usan los gates AST); `attw` como gate de
197
+ CI; go-to-definition al source.
21
198
 
22
199
  ## [1.0.0-beta.26] — 2026-05-29 (bloque claudegate5 / beta.27 cerrado)
23
200
 
package/README.md CHANGED
@@ -4,7 +4,9 @@ Design system de **igoded** — 32 componentes React 19 + TypeScript estricto
4
4
  sobre un CSS modular utility-first state-driven (`tokens` / `base` /
5
5
  `components` + `reset` opt-in + `state` opt-in).
6
6
 
7
- > **Estado**: `1.0.0-beta.25`.
7
+ > **Estado**: pre-1.0 — la versión publicada vive en
8
+ > [Instalación](#instalación), no aquí (duplicarla derivaba en cada bump:
9
+ > este banner decía `beta.25` con el paquete ya en `beta.26`).
8
10
  > Paleta cardinal estable: 7 cardinales con geometría OKLCH dual
9
11
  > (L_lux≈0.32 / L_nox≈0.84 / ΔH≤10°), todos AAA contra los 5 fondos
10
12
  > del tema en ambos modos. El cardinal `info` se llama internamente
@@ -18,9 +20,19 @@ sobre un CSS modular utility-first state-driven (`tokens` / `base` /
18
20
 
19
21
  ## Instalación
20
22
 
21
- > **Nota**: el paquete aún no está publicado a npm. La instalación
22
- > se hace vía clone + `npm link` mientras tanto. La publicación al
23
- > registro está prevista pero sin fecha confirmada.
23
+ > **Release candidate `1.0.0-rc.1`** la API pública está **congelada**
24
+ > (§5.13, protegida por gate): cambiarla exige un bump MAJOR.
25
+
26
+ ```bash
27
+ npm install reactigoded@rc react react-dom @floating-ui/react clsx
28
+ ```
29
+
30
+ > ⚠️ **El `@rc` es necesario.** El dist-tag `latest` quedó apuntando a
31
+ > `1.0.0-beta.26` en la primera publicación y npm no permite retirarlo, así que
32
+ > `npm install reactigoded` **a secas devuelve la beta**. Se corrige solo al
33
+ > publicar `1.0.0` final. Ver CHANGELOG → *Limitaciones conocidas*.
34
+
35
+ Para trabajar **sobre** la librería (o probar cambios sin publicar):
24
36
 
25
37
  ```bash
26
38
  git clone https://github.com/ivangc1/reactigoded.git
@@ -41,7 +53,7 @@ internamente `Tooltip` (y futuros `Popover`, `HoverCard`, etc.).
41
53
  Instálalo **siempre** junto a la librería:
42
54
 
43
55
  ```bash
44
- npm install reactigoded react react-dom @floating-ui/react clsx
56
+ npm install reactigoded@rc react react-dom @floating-ui/react clsx
45
57
  ```
46
58
 
47
59
  **`clsx` (^2.1) también es peer-dep requerido** — lo usa el helper `cn`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reactigoded",
3
- "version": "1.0.0-beta.26",
3
+ "version": "1.0.0-rc.1",
4
4
  "description": "igoded design system — componentes React + CSS utility-first state-driven",
5
5
  "license": "MIT",
6
6
  "author": "ivangc1",