@clhaas/palette-kit 0.3.0 → 0.4.0

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 (312) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +80 -87
  3. package/dist/contrast/contrast.d.ts +16 -0
  4. package/dist/contrast/contrast.js +102 -0
  5. package/dist/core/intent-registry.d.ts +11 -0
  6. package/dist/core/intent-registry.js +70 -0
  7. package/dist/core/oklch.d.ts +16 -0
  8. package/dist/core/oklch.js +56 -0
  9. package/dist/create-palette-kit.d.ts +9 -0
  10. package/dist/create-palette-kit.js +67 -0
  11. package/dist/engine/context/context.d.ts +13 -0
  12. package/dist/engine/context/context.js +37 -0
  13. package/dist/engine/level/curves.d.ts +17 -0
  14. package/dist/engine/level/curves.js +49 -0
  15. package/dist/engine/level/level.d.ts +4 -0
  16. package/dist/engine/level/level.js +13 -0
  17. package/dist/engine/relation/relation.d.ts +105 -0
  18. package/dist/engine/relation/relation.js +137 -0
  19. package/dist/engine/resolve/resolve.d.ts +36 -0
  20. package/dist/engine/resolve/resolve.js +116 -0
  21. package/dist/engine/state/state.d.ts +46 -0
  22. package/dist/engine/state/state.js +68 -0
  23. package/dist/engine/usage/fill.d.ts +9 -0
  24. package/dist/engine/usage/fill.js +9 -0
  25. package/dist/engine/usage/lines.d.ts +9 -0
  26. package/dist/engine/usage/lines.js +9 -0
  27. package/dist/engine/usage/overlays.d.ts +9 -0
  28. package/dist/engine/usage/overlays.js +9 -0
  29. package/dist/engine/usage/strategy.d.ts +56 -0
  30. package/dist/engine/usage/strategy.js +30 -0
  31. package/dist/engine/usage/visualVocabulary.d.ts +9 -0
  32. package/dist/engine/usage/visualVocabulary.js +9 -0
  33. package/dist/export/serialize.d.ts +14 -0
  34. package/dist/export/serialize.js +89 -0
  35. package/dist/export/types.d.ts +37 -0
  36. package/dist/export/types.js +31 -0
  37. package/dist/index.d.ts +3 -3
  38. package/dist/index.js +2 -2
  39. package/dist/operators/convert.d.ts +32 -0
  40. package/dist/operators/convert.js +80 -0
  41. package/dist/presets/presets.d.ts +95 -0
  42. package/dist/presets/presets.js +308 -0
  43. package/dist/types/index.d.ts +111 -187
  44. package/dist/utils/errors/errors.d.ts +17 -0
  45. package/dist/utils/errors/errors.js +22 -0
  46. package/docs/API.md +167 -0
  47. package/docs/Alpha.md +14 -0
  48. package/docs/Architecture.md +56 -0
  49. package/docs/CLI.md +22 -0
  50. package/docs/Concepts.md +73 -0
  51. package/docs/Config.md +144 -0
  52. package/docs/Diagnostics.md +22 -0
  53. package/docs/Exporters.md +33 -0
  54. package/docs/FAQ.md +59 -0
  55. package/docs/Migration.md +61 -0
  56. package/docs/Overlays.md +33 -0
  57. package/docs/README.md +60 -0
  58. package/docs/Text.md +41 -0
  59. package/docs/Tokens.md +42 -0
  60. package/docs/Usage-JSON.md +39 -0
  61. package/docs/Usage-ReactNative.md +63 -0
  62. package/docs/Usage-Web.md +66 -0
  63. package/docs/Validation.md +97 -0
  64. package/docs/Why.md +37 -0
  65. package/docs/_api-surface.md +53 -0
  66. package/docs/snippets/serialize-oklch.md +9 -0
  67. package/docs/spec.md +98 -0
  68. package/package.json +74 -59
  69. package/.codex/skills/color-pipeline-implementer/SKILL.md +0 -23
  70. package/.codex/skills/commit-message-crafter/SKILL.md +0 -63
  71. package/.codex/skills/commit-message-crafter/references/benchmarks.md +0 -20
  72. package/.codex/skills/contrast-solver-helper/SKILL.md +0 -20
  73. package/.codex/skills/exporters-builder/SKILL.md +0 -20
  74. package/.codex/skills/markdownlint-writer/SKILL.md +0 -32
  75. package/.codex/skills/phase-implementation-runbook/SKILL.md +0 -92
  76. package/.codex/skills/type-contract-auditor/SKILL.md +0 -21
  77. package/.github/skills/review-guide/SKILL.md +0 -23
  78. package/.github/skills/review-guide/references/review-guide-v0.3.md +0 -629
  79. package/.markdownlint.json +0 -4
  80. package/AGENTS.md +0 -16
  81. package/biome.json +0 -43
  82. package/dist/cli/args.d.ts +0 -12
  83. package/dist/cli/args.js +0 -56
  84. package/dist/cli/args.test.d.ts +0 -1
  85. package/dist/cli/args.test.js +0 -22
  86. package/dist/cli/codegen/__snapshots__/tokens.test.js.snap +0 -87
  87. package/dist/cli/codegen/tokens.d.ts +0 -12
  88. package/dist/cli/codegen/tokens.js +0 -139
  89. package/dist/cli/codegen/tokens.test.d.ts +0 -1
  90. package/dist/cli/codegen/tokens.test.js +0 -51
  91. package/dist/cli/config.d.ts +0 -40
  92. package/dist/cli/config.js +0 -34
  93. package/dist/cli/validate.d.ts +0 -2
  94. package/dist/cli/validate.js +0 -33
  95. package/dist/cli/validate.test.d.ts +0 -1
  96. package/dist/cli/validate.test.js +0 -40
  97. package/dist/cli.d.ts +0 -2
  98. package/dist/cli.js +0 -148
  99. package/dist/contrast/apca.d.ts +0 -2
  100. package/dist/contrast/apca.js +0 -15
  101. package/dist/contrast/apca.test.d.ts +0 -1
  102. package/dist/contrast/apca.test.js +0 -16
  103. package/dist/contrast/index.d.ts +0 -4
  104. package/dist/contrast/index.js +0 -4
  105. package/dist/contrast/scoring.d.ts +0 -4
  106. package/dist/contrast/scoring.js +0 -31
  107. package/dist/contrast/scoring.test.d.ts +0 -1
  108. package/dist/contrast/scoring.test.js +0 -148
  109. package/dist/contrast/solver.d.ts +0 -13
  110. package/dist/contrast/solver.js +0 -170
  111. package/dist/contrast/solver.test.d.ts +0 -1
  112. package/dist/contrast/solver.test.js +0 -75
  113. package/dist/contrast/types.d.ts +0 -17
  114. package/dist/contrast/types.js +0 -1
  115. package/dist/contrast/utils.d.ts +0 -4
  116. package/dist/contrast/utils.js +0 -18
  117. package/dist/contrast/wcag2.d.ts +0 -3
  118. package/dist/contrast/wcag2.js +0 -19
  119. package/dist/contrast/wcag2.test.d.ts +0 -1
  120. package/dist/contrast/wcag2.test.js +0 -17
  121. package/dist/core/createTheme.d.ts +0 -35
  122. package/dist/core/createTheme.js +0 -24
  123. package/dist/core/dx-helpers.test.d.ts +0 -1
  124. package/dist/core/dx-helpers.test.js +0 -61
  125. package/dist/core/index.d.ts +0 -2
  126. package/dist/core/index.js +0 -2
  127. package/dist/core/onSolid.test.d.ts +0 -1
  128. package/dist/core/onSolid.test.js +0 -118
  129. package/dist/core/qa.v1.test.d.ts +0 -1
  130. package/dist/core/qa.v1.test.js +0 -112
  131. package/dist/core/resolve.d.ts +0 -3
  132. package/dist/core/resolve.js +0 -8
  133. package/dist/core/resolve.test.d.ts +0 -1
  134. package/dist/core/resolve.test.js +0 -89
  135. package/dist/core/resolveMany.d.ts +0 -8
  136. package/dist/core/resolveMany.js +0 -17
  137. package/dist/core/tokenRegistry.d.ts +0 -23
  138. package/dist/core/tokenRegistry.js +0 -83
  139. package/dist/core/tokenRegistry.test.d.ts +0 -1
  140. package/dist/core/tokenRegistry.test.js +0 -133
  141. package/dist/engine/applyOperators.d.ts +0 -3
  142. package/dist/engine/applyOperators.js +0 -23
  143. package/dist/engine/context.d.ts +0 -4
  144. package/dist/engine/context.js +0 -1
  145. package/dist/engine/gamut.d.ts +0 -13
  146. package/dist/engine/gamut.js +0 -101
  147. package/dist/engine/gamut.test.d.ts +0 -1
  148. package/dist/engine/gamut.test.js +0 -23
  149. package/dist/engine/generateScale.d.ts +0 -15
  150. package/dist/engine/generateScale.js +0 -29
  151. package/dist/engine/generateScale.test.d.ts +0 -1
  152. package/dist/engine/generateScale.test.js +0 -32
  153. package/dist/engine/index.d.ts +0 -8
  154. package/dist/engine/index.js +0 -4
  155. package/dist/engine/normalize.d.ts +0 -43
  156. package/dist/engine/normalize.js +0 -403
  157. package/dist/engine/normalize.test.d.ts +0 -1
  158. package/dist/engine/normalize.test.js +0 -136
  159. package/dist/engine/onSolid.d.ts +0 -3
  160. package/dist/engine/onSolid.js +0 -110
  161. package/dist/engine/resolveBaseColor.d.ts +0 -25
  162. package/dist/engine/resolveBaseColor.js +0 -127
  163. package/dist/engine/resolveBaseColor.test.d.ts +0 -1
  164. package/dist/engine/resolveBaseColor.test.js +0 -97
  165. package/dist/export/__snapshots__/exportTheme.test.js.snap +0 -74
  166. package/dist/export/exportTheme.d.ts +0 -47
  167. package/dist/export/exportTheme.js +0 -170
  168. package/dist/export/exportTheme.test.d.ts +0 -1
  169. package/dist/export/exportTheme.test.js +0 -118
  170. package/dist/export/index.d.ts +0 -1
  171. package/dist/export/index.js +0 -1
  172. package/dist/export/serializeColor.d.ts +0 -1
  173. package/dist/export/serializeColor.js +0 -1
  174. package/dist/export/serializeColor.test.d.ts +0 -1
  175. package/dist/export/serializeColor.test.js +0 -54
  176. package/dist/export.d.ts +0 -1
  177. package/dist/export.js +0 -1
  178. package/dist/operators/emphasis.d.ts +0 -3
  179. package/dist/operators/emphasis.js +0 -113
  180. package/dist/operators/emphasis.test.d.ts +0 -1
  181. package/dist/operators/emphasis.test.js +0 -69
  182. package/dist/operators/index.d.ts +0 -3
  183. package/dist/operators/index.js +0 -2
  184. package/dist/operators/state.d.ts +0 -3
  185. package/dist/operators/state.js +0 -102
  186. package/dist/operators/state.test.d.ts +0 -1
  187. package/dist/operators/state.test.js +0 -48
  188. package/dist/operators/types.d.ts +0 -13
  189. package/dist/operators/types.js +0 -1
  190. package/dist/operators/utils.d.ts +0 -16
  191. package/dist/operators/utils.js +0 -23
  192. package/dist/presets/curves.d.ts +0 -28
  193. package/dist/presets/curves.js +0 -145
  194. package/dist/presets/index.d.ts +0 -2
  195. package/dist/presets/index.js +0 -1
  196. package/dist/presets/tokens/index.d.ts +0 -3
  197. package/dist/presets/tokens/index.js +0 -3
  198. package/dist/presets/tokens/minimal-ui.d.ts +0 -6
  199. package/dist/presets/tokens/minimal-ui.js +0 -53
  200. package/dist/presets/tokens/modern-ui.d.ts +0 -5
  201. package/dist/presets/tokens/modern-ui.js +0 -83
  202. package/dist/presets/tokens/presets.test.d.ts +0 -1
  203. package/dist/presets/tokens/presets.test.js +0 -31
  204. package/dist/presets/tokens/radixLike-ui.d.ts +0 -6
  205. package/dist/presets/tokens/radixLike-ui.js +0 -77
  206. package/dist/serialize/index.d.ts +0 -1
  207. package/dist/serialize/index.js +0 -1
  208. package/dist/serialize/normalizeOutput.d.ts +0 -6
  209. package/dist/serialize/normalizeOutput.js +0 -45
  210. package/dist/serialize/serializeColor.d.ts +0 -21
  211. package/dist/serialize/serializeColor.js +0 -178
  212. package/dist/serialize/serializeResolved.test.d.ts +0 -1
  213. package/dist/serialize/serializeResolved.test.js +0 -45
  214. package/dist/serialize.d.ts +0 -1
  215. package/dist/serialize.js +0 -1
  216. package/dist/utils/clamp.d.ts +0 -1
  217. package/dist/utils/clamp.js +0 -1
  218. package/dist/utils/index.d.ts +0 -1
  219. package/dist/utils/index.js +0 -1
  220. package/dist/utils/lerp.d.ts +0 -1
  221. package/dist/utils/lerp.js +0 -1
  222. package/dist/utils/parseColor.d.ts +0 -6
  223. package/dist/utils/parseColor.js +0 -67
  224. package/dist/utils/parseColor.test.d.ts +0 -1
  225. package/dist/utils/parseColor.test.js +0 -51
  226. package/dist/utils/smoothstep.d.ts +0 -1
  227. package/dist/utils/smoothstep.js +0 -5
  228. package/planning/phase-10-review.md +0 -550
  229. package/planning/phase-7-review.md +0 -411
  230. package/planning/phase-8-review.md +0 -669
  231. package/planning/phase-9-review.md +0 -564
  232. package/planning/roadmap-v0.3.md +0 -284
  233. package/planning/spec-serializer-v0.3.md +0 -324
  234. package/planning/spec-v0.3.md +0 -305
  235. package/src/cli/args.test.ts +0 -28
  236. package/src/cli/args.ts +0 -66
  237. package/src/cli/codegen/__snapshots__/tokens.test.ts.snap +0 -87
  238. package/src/cli/codegen/tokens.test.ts +0 -61
  239. package/src/cli/codegen/tokens.ts +0 -191
  240. package/src/cli/config.ts +0 -71
  241. package/src/cli/validate.test.ts +0 -49
  242. package/src/cli/validate.ts +0 -38
  243. package/src/cli.ts +0 -183
  244. package/src/contrast/apca.test.ts +0 -20
  245. package/src/contrast/apca.ts +0 -26
  246. package/src/contrast/index.ts +0 -4
  247. package/src/contrast/scoring.test.ts +0 -188
  248. package/src/contrast/scoring.ts +0 -48
  249. package/src/contrast/solver.test.ts +0 -147
  250. package/src/contrast/solver.ts +0 -235
  251. package/src/contrast/types.ts +0 -20
  252. package/src/contrast/utils.ts +0 -28
  253. package/src/contrast/wcag2.test.ts +0 -21
  254. package/src/contrast/wcag2.ts +0 -24
  255. package/src/core/createTheme.ts +0 -78
  256. package/src/core/dx-helpers.test.ts +0 -82
  257. package/src/core/index.ts +0 -7
  258. package/src/core/onSolid.test.ts +0 -146
  259. package/src/core/qa.v1.test.ts +0 -149
  260. package/src/core/resolve.test.ts +0 -99
  261. package/src/core/resolve.ts +0 -11
  262. package/src/core/resolveMany.ts +0 -22
  263. package/src/core/tokenRegistry.test.ts +0 -153
  264. package/src/core/tokenRegistry.ts +0 -114
  265. package/src/engine/applyOperators.ts +0 -32
  266. package/src/engine/context.ts +0 -8
  267. package/src/engine/gamut.test.ts +0 -30
  268. package/src/engine/gamut.ts +0 -144
  269. package/src/engine/generateScale.test.ts +0 -46
  270. package/src/engine/generateScale.ts +0 -48
  271. package/src/engine/index.ts +0 -8
  272. package/src/engine/normalize.test.ts +0 -222
  273. package/src/engine/normalize.ts +0 -550
  274. package/src/engine/onSolid.ts +0 -178
  275. package/src/engine/resolveBaseColor.test.ts +0 -117
  276. package/src/engine/resolveBaseColor.ts +0 -203
  277. package/src/export/__snapshots__/exportTheme.test.ts.snap +0 -74
  278. package/src/export/exportTheme.test.ts +0 -144
  279. package/src/export/exportTheme.ts +0 -251
  280. package/src/export/index.ts +0 -1
  281. package/src/export/serializeColor.test.ts +0 -73
  282. package/src/export/serializeColor.ts +0 -1
  283. package/src/export.ts +0 -1
  284. package/src/index.ts +0 -3
  285. package/src/operators/emphasis.test.ts +0 -85
  286. package/src/operators/emphasis.ts +0 -132
  287. package/src/operators/index.ts +0 -3
  288. package/src/operators/state.test.ts +0 -66
  289. package/src/operators/state.ts +0 -122
  290. package/src/operators/types.ts +0 -14
  291. package/src/operators/utils.ts +0 -44
  292. package/src/presets/curves.ts +0 -168
  293. package/src/presets/index.ts +0 -2
  294. package/src/presets/tokens/index.ts +0 -3
  295. package/src/presets/tokens/minimal-ui.ts +0 -55
  296. package/src/presets/tokens/modern-ui.ts +0 -85
  297. package/src/presets/tokens/presets.test.ts +0 -46
  298. package/src/presets/tokens/radixLike-ui.ts +0 -79
  299. package/src/serialize/index.ts +0 -1
  300. package/src/serialize/normalizeOutput.ts +0 -63
  301. package/src/serialize/serializeColor.ts +0 -260
  302. package/src/serialize/serializeResolved.test.ts +0 -57
  303. package/src/serialize.ts +0 -1
  304. package/src/types/index.ts +0 -207
  305. package/src/utils/clamp.ts +0 -2
  306. package/src/utils/index.ts +0 -1
  307. package/src/utils/lerp.ts +0 -1
  308. package/src/utils/parseColor.test.ts +0 -66
  309. package/src/utils/parseColor.ts +0 -87
  310. package/src/utils/smoothstep.ts +0 -6
  311. package/tsconfig.build.json +0 -11
  312. package/tsconfig.json +0 -15
@@ -1,669 +0,0 @@
1
- # Revisão Fase 8 — Inferência e Validações
2
-
3
- **Data**: 18 de janeiro de 2026
4
- **Revisor**: GitHub Copilot (usando review-guide-v0.3.md)
5
- **Status**: ✅ **APROVADO COM OBSERVAÇÃO CRÍTICA**
6
-
7
- ---
8
-
9
- ## 📋 Resumo Executivo
10
-
11
- A implementação da Fase 8 (Inferência e Validações) está **funcionalmente completa** com inferência robusta de `usage`, `surface` e `variant`. O código:
12
-
13
- - ✅ Infere `usage` de prefixos de role
14
- - ✅ Infere `surface` de padrões de naming
15
- - ✅ Infere `variant` de tokens semânticos
16
- - ✅ Strict mode com mensagens didáticas
17
- - ✅ Warnings únicos (não repetidos)
18
- - ✅ Testes abrangentes
19
-
20
- **⚠️ OBSERVAÇÃO CRÍTICA**:
21
-
22
- - **Warnings globais com estado mutável** (`inferenceWarnings` Set) pode causar vazamento de memória em long-running processes
23
- - **Sugestão**: Mover para contexto ou adicionar método de reset
24
-
25
- **Sugestões menores**:
26
-
27
- 1. Documentar estratégia de deduplicação de warnings
28
- 2. Adicionar teste para variant inference
29
- 3. JSDoc em funções helper de inferência
30
-
31
- ---
32
-
33
- ## ✅ CRITÉRIOS GERAIS (Todos aprovados)
34
-
35
- ### 1. Contratos e tipos ✅
36
-
37
- - ✅ **Nenhum `any` ou `unknown` injustificado**
38
- - ✅ **Tipos exportados**: `NormalizedQuery` mantido
39
- - ✅ **Sem circular imports**: `normalize.ts` → `types` apenas
40
- - ✅ **Hierarquia respeitada**: `types` → `engine`
41
- - ✅ **Breaking changes documentados**: JSDoc atualizado em `normalizeQuery`
42
-
43
- **Evidência**:
44
-
45
- ```typescript
46
- /**
47
- * Normalize a user-facing ColorQuery into a fully populated, validated structure.
48
- *
49
- * - Applies defaults for missing fields (context, surface, state, emphasis, output).
50
- * - Infers usage and surface from role naming patterns when not provided.
51
- * - In strict mode, missing required fields throw actionable errors.
52
- * - In non-strict mode, safe defaults are applied with explicit warnings.
53
- */
54
- ```
55
-
56
- ---
57
-
58
- ### 2. Qualidade de código ✅
59
-
60
- - ✅ **Build**: Código bem estruturado
61
- - ✅ **Testes**: 19 testes em `normalize.test.ts` (6 novos)
62
- - ✅ **Sem TODO/FIXME**: Nenhum encontrado
63
- - ⚠️ **Comentários**: Faltam em funções helper de inferência
64
- - ✅ **Style**: Formatação consistente
65
-
66
- **Testes adicionados**:
67
-
68
- 1. `infers surface when obvious` ✅
69
- 2. `requires surface when strict and inference fails` ✅
70
- 3. `warns when surface cannot be inferred in non-strict mode` ✅
71
- 4. Atualização de testes existentes com mock de warnings ✅
72
-
73
- **Sugestão de comentários**:
74
-
75
- ```typescript
76
- /**
77
- * Infer surface intent from role naming patterns.
78
- * Recognizes: "app.*", "surface.*", "bg.app", "bg.surface", etc.
79
- */
80
- const inferSurfaceFromRole = (role: string): SurfaceIntent | undefined => {
81
- // ... implementation
82
- };
83
-
84
- /**
85
- * Infer semantic variant from role token.
86
- * Recognizes: "success", "warning", "danger", "category:*", "chart:*"
87
- */
88
- const inferVariantFromRole = (role: string): SemanticVariant | undefined => {
89
- // ... implementation
90
- };
91
- ```
92
-
93
- ---
94
-
95
- ### 3. DX (Developer Experience) ✅
96
-
97
- - ✅ **Mensagens de erro acionáveis**:
98
-
99
- ```typescript
100
- `Usage is required for role: "${role}". Provide usage explicitly (e.g. { usage: "text" }) or use a role prefix like "text.", "icon.", "bg.", "border.", "ring."`
101
- ```
102
-
103
- - ✅ **Warnings explícitos**:
104
-
105
- ```typescript
106
- `Defaulting surface to "surface" for role: "${role}". Surface is required for role: "${role}". Provide surface explicitly (e.g. { surface: "surface" }) or use a role pattern like "bg.app", "bg.surface", "app.*"`
107
- ```
108
-
109
- - ✅ **Sem magia**: Inferência é explícita e testável
110
- - ✅ **Autocomplete**: Enums mantidos
111
-
112
- **Pontos positivos**:
113
-
114
- - Mensagens incluem exemplos práticos de correção
115
- - Warnings mostram caminho de solução
116
- - Deduplicação evita spam de logs
117
-
118
- ---
119
-
120
- ### 4. Princípios da v0.3 ✅
121
-
122
- - ✅ **Runtime-first**: Inferência no engine, não na CLI
123
- - ✅ **Serializer independente**: Não afetado
124
- - ✅ **Resolver com inferência**: Decisões explícitas via strict mode
125
- - ✅ **Sem magia oculta**: Warnings revelam inferência
126
- - ✅ **Determinismo**: Mesma entrada → mesma saída (warnings são side-effect)
127
-
128
- ---
129
-
130
- ## ✅ CRITÉRIOS ESPECÍFICOS DA FASE 8
131
-
132
- ### Inferência implementada ✅
133
-
134
- #### `usage` inferido por prefixo ✅
135
-
136
- **Evidência** (`inferUsageFromRole`, linhas 125-167):
137
-
138
- ```typescript
139
- if (normalizedRole.startsWith("bg.")) return "bg";
140
- if (normalizedRole.startsWith("text.")) return "text";
141
- if (normalizedRole.startsWith("icon.")) return "icon";
142
- // ... mais prefixos
143
- ```
144
-
145
- **Testes**:
146
-
147
- ```typescript
148
- expect(normalizeQuery({ role: "icon.primary" }).usage).toBe("icon");
149
- expect(normalizeQuery({ role: "border.muted" }).usage).toBe("border");
150
- expect(normalizeQuery({ role: "bg.canvas" }).usage).toBe("bg");
151
- ```
152
-
153
- #### `surface` inferido quando óbvio ✅
154
-
155
- **Evidência** (`inferSurfaceFromRole`, linhas 169-184):
156
-
157
- ```typescript
158
- // Padrão: "app.*", "surface.*"
159
- if (first && surfaces.includes(first as SurfaceIntent)) {
160
- return first as SurfaceIntent;
161
- }
162
-
163
- // Padrão: "bg.app", "bg.surface"
164
- if (first === "bg" && second && surfaces.includes(second as SurfaceIntent)) {
165
- return second as SurfaceIntent;
166
- }
167
- ```
168
-
169
- **Testes**:
170
-
171
- ```typescript
172
- expect(normalizeQuery({ role: "bg.app" }).surface).toBe("app");
173
- expect(normalizeQuery({ role: "bg.surface" }).surface).toBe("surface");
174
- expect(normalizeQuery({ role: "app.bg", usage: "bg" }).surface).toBe("app");
175
- ```
176
-
177
- #### `variant` inferido se omitido ✅
178
-
179
- **Evidência** (`inferVariantFromRole`, linhas 186-202):
180
-
181
- ```typescript
182
- // Reconhece variantes semânticas
183
- if (semanticVariants.includes(normalized as SemanticVariant)) {
184
- return normalized as SemanticVariant;
185
- }
186
-
187
- // Reconhece padrões especiais: "category:*", "chart:*"
188
- if (/^(category|chart):.+/.test(normalized)) {
189
- return normalized as SemanticVariant;
190
- }
191
- ```
192
-
193
- **⚠️ Teste faltando**:
194
-
195
- ```typescript
196
- it("infers variant from role token", () => {
197
- const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => undefined);
198
-
199
- try {
200
- expect(normalizeQuery({ role: "success.bg" }).variant).toBe("success");
201
- expect(normalizeQuery({ role: "warning.text" }).variant).toBe("warning");
202
- expect(normalizeQuery({ role: "danger.border" }).variant).toBe("danger");
203
- expect(normalizeQuery({ role: "category:sales.fill" }).variant).toBe("category:sales");
204
- expect(normalizeQuery({ role: "chart:revenue.stroke" }).variant).toBe("chart:revenue");
205
- } finally {
206
- warnSpy.mockRestore();
207
- }
208
- });
209
- ```
210
-
211
- ---
212
-
213
- ### Strict mode ✅
214
-
215
- #### `strict: false` - warnings + fallback ✅
216
-
217
- **Evidência** (linhas 446-456):
218
-
219
- ```typescript
220
- if (!usageValue) {
221
- if (output.strict) {
222
- throw new Error(missingUsageMessage(role));
223
- }
224
-
225
- warnInferenceOnce(
226
- `usage:${role}`,
227
- `Defaulting usage to "bg" for role: "${role}". ${missingUsageMessage(role)}`,
228
- );
229
- }
230
- ```
231
-
232
- **Testes**:
233
-
234
- ```typescript
235
- it("warns when usage cannot be inferred in non-strict mode", () => {
236
- const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => undefined);
237
-
238
- expect(normalizeQuery({ role: "brand.primary" }).usage).toBe("bg");
239
- expect(warnSpy).toHaveBeenCalledWith(
240
- expect.stringContaining('Defaulting usage to "bg"'),
241
- );
242
-
243
- warnSpy.mockRestore();
244
- });
245
- ```
246
-
247
- #### `strict: true` - erros claros ✅
248
-
249
- **Evidência**:
250
-
251
- ```typescript
252
- it("requires usage when strict and inference fails", () => {
253
- expect(() =>
254
- normalizeQuery({ role: "brand.primary", output: { strict: true } }),
255
- ).toThrowError(/Provide usage explicitly/i);
256
- });
257
-
258
- it("requires surface when strict and inference fails", () => {
259
- expect(() =>
260
- normalizeQuery({ role: "text.primary", output: { strict: true } }),
261
- ).toThrowError(/Provide surface explicitly/i);
262
- });
263
- ```
264
-
265
- #### Mensagens didáticas ✅
266
-
267
- **Evidência** (linhas 235-238):
268
-
269
- ```typescript
270
- const missingUsageMessage = (role: string) =>
271
- `Usage is required for role: "${role}". Provide usage explicitly (e.g. { usage: "text" }) or use a role prefix like "text.", "icon.", "bg.", "border.", "ring."`;
272
-
273
- const missingSurfaceMessage = (role: string) =>
274
- `Surface is required for role: "${role}". Provide surface explicitly (e.g. { surface: "surface" }) or use a role pattern like "bg.app", "bg.surface", "app.*"`;
275
- ```
276
-
277
- **Pontos positivos**:
278
-
279
- - ✅ Explica o problema
280
- - ✅ Mostra exemplo de correção
281
- - ✅ Lista padrões válidos
282
-
283
- #### Sugestões de correção ✅
284
-
285
- Todas as mensagens incluem exemplos práticos:
286
-
287
- - `{ usage: "text" }` ✅
288
- - `{ surface: "surface" }` ✅
289
- - Padrões de naming válidos ✅
290
-
291
- ---
292
-
293
- ### Validações ✅
294
-
295
- #### Queries inválidos detectados ✅
296
-
297
- **Evidência**:
298
-
299
- - Strict mode valida `usage` obrigatório
300
- - Strict mode valida `surface` obrigatório
301
- - Testes cobrem casos de erro
302
-
303
- #### Conflitos de inferência apontados ✅
304
-
305
- **Evidência** (warnings explícitos):
306
-
307
- ```typescript
308
- warnInferenceOnce(
309
- `usage:${role}`,
310
- `Defaulting usage to "bg" for role: "${role}". ${missingUsageMessage(role)}`,
311
- );
312
- ```
313
-
314
- #### Fallbacks documentados ✅
315
-
316
- **Evidência** (JSDoc atualizado):
317
-
318
- ```typescript
319
- /**
320
- * - In strict mode, missing required fields throw actionable errors.
321
- * - In non-strict mode, safe defaults are applied with explicit warnings.
322
- */
323
- ```
324
-
325
- ---
326
-
327
- ### Testes ✅
328
-
329
- #### Inferência correta em casos comuns ✅
330
-
331
- **Testes presentes**:
332
-
333
- 1. `infers usage from role prefixes` - 8 casos ✅
334
- 2. `infers surface when obvious` - 3 casos ✅
335
- 3. Background hint normalization mantida ✅
336
-
337
- #### Inferência falha gracefully ✅
338
-
339
- **Testes presentes**:
340
-
341
- 1. `warns when usage cannot be inferred in non-strict mode` ✅
342
- 2. `warns when surface cannot be inferred in non-strict mode` ✅
343
- 3. Mock de `console.warn` em testes relevantes ✅
344
-
345
- #### Strict mode valida edge cases ✅
346
-
347
- **Testes presentes**:
348
-
349
- 1. `requires usage when strict and inference fails` ✅
350
- 2. `requires surface when strict and inference fails` ✅
351
-
352
- #### Mensagens de erro testadas ✅
353
-
354
- **Evidência**:
355
-
356
- ```typescript
357
- .toThrowError(/Provide usage explicitly/i);
358
- .toThrowError(/Provide surface explicitly/i);
359
- expect(warnSpy).toHaveBeenCalledWith(
360
- expect.stringContaining('Defaulting surface to "surface"'),
361
- );
362
- ```
363
-
364
- ---
365
-
366
- ## ⚠️ OBSERVAÇÃO CRÍTICA
367
-
368
- ### Estado global mutável (`inferenceWarnings`)
369
-
370
- **Localização**: `src/engine/normalize.ts` linha 227
371
-
372
- **Problema**:
373
-
374
- ```typescript
375
- const inferenceWarnings = new Set<string>();
376
-
377
- const warnInferenceOnce = (key: string, message: string) => {
378
- if (inferenceWarnings.has(key)) return;
379
- inferenceWarnings.add(key); // ⚠️ Cresce indefinidamente
380
- console.warn(message);
381
- };
382
- ```
383
-
384
- **Impacto**:
385
-
386
- - ✅ Funciona bem em builds CLI (processo curto)
387
- - ⚠️ **Memory leak** em long-running processes (servers, watch modes)
388
- - ⚠️ Warnings nunca são resetados entre builds
389
-
390
- **Cenários problemáticos**:
391
-
392
- 1. Server-side rendering com multiple renders
393
- 2. Dev server em watch mode
394
- 3. Testes com múltiplas execuções
395
-
396
- **Sugestão 1** - Reset público:
397
-
398
- ```typescript
399
- /**
400
- * Clear inference warning cache.
401
- * Call this between independent builds or test runs.
402
- */
403
- export const resetInferenceWarnings = () => {
404
- inferenceWarnings.clear();
405
- };
406
- ```
407
-
408
- **Sugestão 2** - Context-based (melhor):
409
-
410
- ```typescript
411
- // Mover Set para dentro de createTheme ou normalizeQuery context
412
- // Evitar estado global compartilhado
413
- ```
414
-
415
- **Sugestão 3** - LRU cache:
416
-
417
- ```typescript
418
- // Limitar tamanho do Set (ex: máximo 1000 keys)
419
- const MAX_WARNINGS = 1000;
420
-
421
- const warnInferenceOnce = (key: string, message: string) => {
422
- if (inferenceWarnings.size >= MAX_WARNINGS) {
423
- inferenceWarnings.clear(); // Reset quando cheio
424
- }
425
- if (inferenceWarnings.has(key)) return;
426
- inferenceWarnings.add(key);
427
- console.warn(message);
428
- };
429
- ```
430
-
431
- **Decisão**: Não bloqueante para merge, mas deve ser endereçado em PR futuro.
432
-
433
- ---
434
-
435
- ## ⚠️ SUGESTÕES DE MELHORIA (Não bloqueantes)
436
-
437
- ### 1. JSDoc em funções helper de inferência
438
-
439
- **Localização**: `src/engine/normalize.ts` linhas 169-202
440
-
441
- **Código atual**:
442
-
443
- ```typescript
444
- const inferSurfaceFromRole = (role: string): SurfaceIntent | undefined => {
445
- // Sem JSDoc
446
- };
447
-
448
- const inferVariantFromRole = (role: string): SemanticVariant | undefined => {
449
- // Sem JSDoc
450
- };
451
- ```
452
-
453
- **Sugestão**:
454
-
455
- ```typescript
456
- /**
457
- * Infer surface intent from role naming patterns.
458
- *
459
- * Recognizes:
460
- * - Direct surface tokens: "app.*", "surface.*", "solid.*"
461
- * - Background patterns: "bg.app", "bg.surface", "bg.solid"
462
- *
463
- * @example
464
- * inferSurfaceFromRole("bg.app") // → "app"
465
- * inferSurfaceFromRole("app.bg") // → "app"
466
- * inferSurfaceFromRole("text.primary") // → undefined
467
- */
468
- const inferSurfaceFromRole = (role: string): SurfaceIntent | undefined => {
469
- // ... implementation
470
- };
471
-
472
- /**
473
- * Infer semantic variant from role token.
474
- *
475
- * Recognizes:
476
- * - Semantic variants: "success", "warning", "danger", "info"
477
- * - Custom categories: "category:sales", "category:marketing"
478
- * - Chart variants: "chart:revenue", "chart:expenses"
479
- *
480
- * @example
481
- * inferVariantFromRole("success.bg") // → "success"
482
- * inferVariantFromRole("category:sales.fill") // → "category:sales"
483
- * inferVariantFromRole("text.primary") // → undefined
484
- */
485
- const inferVariantFromRole = (role: string): SemanticVariant | undefined => {
486
- // ... implementation
487
- };
488
- ```
489
-
490
- ---
491
-
492
- ### 2. Teste para variant inference
493
-
494
- **Motivação**: Código existe mas não há teste específico
495
-
496
- **Sugestão**:
497
-
498
- ```typescript
499
- it("infers variant from role token", () => {
500
- const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => undefined);
501
-
502
- try {
503
- // Semantic variants
504
- expect(normalizeQuery({ role: "success.bg" }).variant).toBe("success");
505
- expect(normalizeQuery({ role: "warning.text" }).variant).toBe("warning");
506
- expect(normalizeQuery({ role: "danger.border" }).variant).toBe("danger");
507
- expect(normalizeQuery({ role: "info.icon" }).variant).toBe("info");
508
-
509
- // Custom categories
510
- expect(normalizeQuery({ role: "category:sales.fill" }).variant).toBe("category:sales");
511
- expect(normalizeQuery({ role: "category:marketing.bg" }).variant).toBe("category:marketing");
512
-
513
- // Chart variants
514
- expect(normalizeQuery({ role: "chart:revenue.stroke" }).variant).toBe("chart:revenue");
515
- expect(normalizeQuery({ role: "chart:expenses.fill" }).variant).toBe("chart:expenses");
516
-
517
- // No inference
518
- expect(normalizeQuery({ role: "text.primary" }).variant).toBeUndefined();
519
- } finally {
520
- warnSpy.mockRestore();
521
- }
522
- });
523
- ```
524
-
525
- ---
526
-
527
- ### 3. Documentar estratégia de deduplicação
528
-
529
- **Localização**: `src/engine/normalize.ts` linha 227-233
530
-
531
- **Sugestão**:
532
-
533
- ```typescript
534
- /**
535
- * Global cache to avoid duplicate inference warnings.
536
- *
537
- * WARNING: This Set grows indefinitely in long-running processes.
538
- * Consider calling resetInferenceWarnings() between independent builds
539
- * or moving to a context-based approach.
540
- *
541
- * @internal
542
- */
543
- const inferenceWarnings = new Set<string>();
544
-
545
- /**
546
- * Emit a warning only once per unique key.
547
- * Uses global cache to deduplicate across multiple normalizeQuery calls.
548
- *
549
- * @param key - Unique identifier for this warning (e.g., "usage:text.primary")
550
- * @param message - Warning message to display
551
- */
552
- const warnInferenceOnce = (key: string, message: string) => {
553
- if (inferenceWarnings.has(key)) return;
554
- inferenceWarnings.add(key);
555
- console.warn(message);
556
- };
557
- ```
558
-
559
- ---
560
-
561
- ### 4. Teste de integração com `createTheme`
562
-
563
- **Motivação**: Inferência foi movida de `createTheme` para `normalize`, validar integração
564
-
565
- **Sugestão**:
566
-
567
- ```typescript
568
- // Em src/core/createTheme.test.ts
569
- it("theme.color() uses inference from normalizeQuery", () => {
570
- const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => undefined);
571
-
572
- try {
573
- const theme = createTheme({
574
- seeds: { light: { neutral: "#111827", accent: "#3d63dd" }, dark: { neutral: "#111827", accent: "#3d63dd" } },
575
- });
576
-
577
- // Inferência de usage
578
- const color1 = theme.color("text.primary");
579
- expect(color1).toBeDefined();
580
-
581
- // Inferência de surface
582
- const color2 = theme.color("bg.app");
583
- expect(color2).toBeDefined();
584
-
585
- // Strict mode
586
- expect(() =>
587
- theme.color("brand.primary", { output: { strict: true } })
588
- ).toThrow(/Provide usage explicitly/i);
589
- } finally {
590
- warnSpy.mockRestore();
591
- }
592
- });
593
- ```
594
-
595
- ---
596
-
597
- ## ✅ VALIDAÇÕES NEGATIVAS
598
-
599
- ### O que NÃO deve acontecer ✅
600
-
601
- - ✅ **Inferência silenciosa incorreta**: Warnings explícitos em non-strict
602
- - ✅ **Erros crípticos**: Mensagens didáticas com exemplos
603
- - ✅ **Strict mode permissivo**: Valida corretamente
604
- - ✅ **Fallbacks inesperados**: Documentados em JSDoc e warnings
605
-
606
- ---
607
-
608
- ## 🎯 CHECKLIST FINAL
609
-
610
- ### Critérios Gerais
611
-
612
- - ✅ Contratos e tipos (JSDoc atualizado)
613
- - ✅ Qualidade de código (com sugestão de comentários)
614
- - ✅ DX (mensagens acionáveis)
615
- - ✅ Princípios v0.3
616
-
617
- ### Critérios Fase 8
618
-
619
- - ✅ Inferência implementada (`usage`, `surface`, `variant`)
620
- - ✅ Strict mode (`false`: warnings, `true`: errors)
621
- - ✅ Validações (queries inválidos, conflitos, fallbacks)
622
- - ✅ Testes (19 testes, cobertura abrangente)
623
-
624
- ### O que NÃO deve acontecer (Checklist) ✅
625
-
626
- - ✅ Sem inferência silenciosa incorreta
627
- - ✅ Sem erros crípticos
628
- - ✅ Strict mode não permissivo
629
- - ✅ Sem fallbacks inesperados
630
-
631
- ---
632
-
633
- ## 📊 VEREDICTO FINAL
634
-
635
- **Status**: ✅ **APROVADO PARA MERGE** (com observação para PR futuro)
636
-
637
- **Implementação robusta** que:
638
-
639
- - Atende todos os critérios obrigatórios da Fase 8
640
- - Possui testes abrangentes (19 testes, 6 novos)
641
- - Mensagens de erro didáticas e acionáveis
642
- - Inferência inteligente de `usage`, `surface` e `variant`
643
- - Strict mode funcional com validações claras
644
-
645
- **⚠️ Para endereçar em PR futuro** (não bloqueante):
646
-
647
- 1. **Crítico**: Resolver memory leak do `inferenceWarnings` Set global
648
- - Sugestão: Adicionar método de reset ou mover para contexto
649
- - Impacto: Long-running processes (SSR, dev servers)
650
-
651
- **Sugestões opcionais**:
652
-
653
- 2. JSDoc em funções helper de inferência
654
- 3. Teste específico para variant inference
655
- 4. Documentar estratégia de deduplicação
656
- 5. Teste de integração com `createTheme`
657
-
658
- **Parabéns pela implementação completa e bem testada!** 🎉
659
-
660
- ---
661
-
662
- ## 📚 Referências
663
-
664
- - Guia de revisão: `.github/skills/review-guide/references/review-guide-v0.3.md`
665
- - Fase 8 checklist: Linhas 439-469 do guia
666
- - Arquivos revisados:
667
- - `src/engine/normalize.ts` (+67 linhas, refactor de inferência)
668
- - `src/engine/normalize.test.ts` (+58 linhas, 6 novos testes)
669
- - `src/core/createTheme.ts` (-76 linhas, simplificação)