@tenphi/tasty 2.0.2 → 2.0.4

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 (321) hide show
  1. package/dist/{ssr/async-storage.js → async-storage-B7_o6FKt.js} +2 -2
  2. package/dist/async-storage-B7_o6FKt.js.map +1 -0
  3. package/dist/{ssr/collector.d.ts → collector-CkZ517g4.d.ts} +3 -3
  4. package/dist/{ssr/collector.js → collector-DXqvGOb1.js} +5 -10
  5. package/dist/collector-DXqvGOb1.js.map +1 -0
  6. package/dist/config-5jzS6k6B.js +10005 -0
  7. package/dist/config-5jzS6k6B.js.map +1 -0
  8. package/dist/config-DknGsfMo.d.ts +857 -0
  9. package/dist/{ssr/context.js → context-CkSg-kDT.js} +11 -3
  10. package/dist/context-CkSg-kDT.js.map +1 -0
  11. package/dist/core/index.d.ts +5 -34
  12. package/dist/core/index.js +5 -26
  13. package/dist/core-CtU6-9OC.js +1507 -0
  14. package/dist/core-CtU6-9OC.js.map +1 -0
  15. package/dist/{zero/extractor.js → css-writer-DHkX0JuE.js} +74 -11
  16. package/dist/css-writer-DHkX0JuE.js.map +1 -0
  17. package/dist/{ssr/format-global-rules.js → format-global-rules-Dbc_1tc3.js} +2 -2
  18. package/dist/format-global-rules-Dbc_1tc3.js.map +1 -0
  19. package/dist/format-rules-DH13ewDu.js +143 -0
  20. package/dist/format-rules-DH13ewDu.js.map +1 -0
  21. package/dist/{ssr/hydrate.js → hydrate-C1Gv-DoS.js} +3 -3
  22. package/dist/hydrate-C1Gv-DoS.js.map +1 -0
  23. package/dist/{styles/types.d.ts → index-PzENbpAq.d.ts} +701 -5
  24. package/dist/index-o7zV2yCr.d.ts +1561 -0
  25. package/dist/index.d.ts +5 -51
  26. package/dist/index.js +728 -35
  27. package/dist/index.js.map +1 -0
  28. package/dist/keyframes-b7X3UxDV.js +587 -0
  29. package/dist/keyframes-b7X3UxDV.js.map +1 -0
  30. package/dist/{utils/merge-styles.d.ts → merge-styles-C7KTy7MY.d.ts} +3 -3
  31. package/dist/{utils/merge-styles.js → merge-styles-Tgo3BbL2.js} +3 -4
  32. package/dist/merge-styles-Tgo3BbL2.js.map +1 -0
  33. package/dist/{utils/resolve-recipes.js → resolve-recipes-Ca2-5CxM.js} +4 -6
  34. package/dist/resolve-recipes-Ca2-5CxM.js.map +1 -0
  35. package/dist/ssr/astro-client.js +1 -1
  36. package/dist/ssr/astro.js +4 -4
  37. package/dist/ssr/index.d.ts +44 -4
  38. package/dist/ssr/index.js +4 -4
  39. package/dist/ssr/next.d.ts +1 -1
  40. package/dist/ssr/next.js +6 -6
  41. package/dist/ssr/next.js.map +1 -1
  42. package/dist/static/index.d.ts +91 -5
  43. package/dist/static/index.js +49 -3
  44. package/dist/static/index.js.map +1 -0
  45. package/dist/zero/babel.d.ts +1 -1
  46. package/dist/zero/babel.js +10 -6
  47. package/dist/zero/babel.js.map +1 -1
  48. package/dist/zero/index.d.ts +67 -3
  49. package/dist/zero/index.js +1 -2
  50. package/docs/pipeline.md +204 -50
  51. package/package.json +3 -3
  52. package/dist/_virtual/_rolldown/runtime.js +0 -7
  53. package/dist/chunks/cacheKey.d.ts +0 -1
  54. package/dist/chunks/cacheKey.js +0 -77
  55. package/dist/chunks/cacheKey.js.map +0 -1
  56. package/dist/chunks/definitions.d.ts +0 -37
  57. package/dist/chunks/definitions.js +0 -258
  58. package/dist/chunks/definitions.js.map +0 -1
  59. package/dist/chunks/index.d.ts +0 -1
  60. package/dist/chunks/renderChunk.d.ts +0 -1
  61. package/dist/chunks/renderChunk.js +0 -59
  62. package/dist/chunks/renderChunk.js.map +0 -1
  63. package/dist/compute-styles.d.ts +0 -31
  64. package/dist/compute-styles.js +0 -322
  65. package/dist/compute-styles.js.map +0 -1
  66. package/dist/config.d.ts +0 -407
  67. package/dist/config.js +0 -591
  68. package/dist/config.js.map +0 -1
  69. package/dist/counter-style/index.js +0 -51
  70. package/dist/counter-style/index.js.map +0 -1
  71. package/dist/debug.d.ts +0 -89
  72. package/dist/debug.js +0 -453
  73. package/dist/debug.js.map +0 -1
  74. package/dist/font-face/index.js +0 -63
  75. package/dist/font-face/index.js.map +0 -1
  76. package/dist/hooks/index.d.ts +0 -7
  77. package/dist/hooks/useCounterStyle.d.ts +0 -36
  78. package/dist/hooks/useCounterStyle.js +0 -65
  79. package/dist/hooks/useCounterStyle.js.map +0 -1
  80. package/dist/hooks/useFontFace.d.ts +0 -45
  81. package/dist/hooks/useFontFace.js +0 -66
  82. package/dist/hooks/useFontFace.js.map +0 -1
  83. package/dist/hooks/useGlobalStyles.d.ts +0 -46
  84. package/dist/hooks/useGlobalStyles.js +0 -88
  85. package/dist/hooks/useGlobalStyles.js.map +0 -1
  86. package/dist/hooks/useKeyframes.d.ts +0 -58
  87. package/dist/hooks/useKeyframes.js +0 -55
  88. package/dist/hooks/useKeyframes.js.map +0 -1
  89. package/dist/hooks/useProperty.d.ts +0 -81
  90. package/dist/hooks/useProperty.js +0 -96
  91. package/dist/hooks/useProperty.js.map +0 -1
  92. package/dist/hooks/useRawCSS.d.ts +0 -22
  93. package/dist/hooks/useRawCSS.js +0 -103
  94. package/dist/hooks/useRawCSS.js.map +0 -1
  95. package/dist/hooks/useStyles.d.ts +0 -40
  96. package/dist/hooks/useStyles.js +0 -31
  97. package/dist/hooks/useStyles.js.map +0 -1
  98. package/dist/injector/index.d.ts +0 -182
  99. package/dist/injector/index.js +0 -185
  100. package/dist/injector/index.js.map +0 -1
  101. package/dist/injector/injector.d.ts +0 -198
  102. package/dist/injector/injector.js +0 -651
  103. package/dist/injector/injector.js.map +0 -1
  104. package/dist/injector/sheet-manager.d.ts +0 -132
  105. package/dist/injector/sheet-manager.js +0 -699
  106. package/dist/injector/sheet-manager.js.map +0 -1
  107. package/dist/injector/types.d.ts +0 -235
  108. package/dist/keyframes/index.js +0 -206
  109. package/dist/keyframes/index.js.map +0 -1
  110. package/dist/parser/classify.js +0 -319
  111. package/dist/parser/classify.js.map +0 -1
  112. package/dist/parser/const.js +0 -60
  113. package/dist/parser/const.js.map +0 -1
  114. package/dist/parser/lru.js +0 -109
  115. package/dist/parser/lru.js.map +0 -1
  116. package/dist/parser/parser.d.ts +0 -25
  117. package/dist/parser/parser.js +0 -115
  118. package/dist/parser/parser.js.map +0 -1
  119. package/dist/parser/tokenizer.js +0 -69
  120. package/dist/parser/tokenizer.js.map +0 -1
  121. package/dist/parser/types.d.ts +0 -51
  122. package/dist/parser/types.js +0 -46
  123. package/dist/parser/types.js.map +0 -1
  124. package/dist/pipeline/conditions.d.ts +0 -134
  125. package/dist/pipeline/conditions.js +0 -406
  126. package/dist/pipeline/conditions.js.map +0 -1
  127. package/dist/pipeline/exclusive.js +0 -382
  128. package/dist/pipeline/exclusive.js.map +0 -1
  129. package/dist/pipeline/index.d.ts +0 -55
  130. package/dist/pipeline/index.js +0 -708
  131. package/dist/pipeline/index.js.map +0 -1
  132. package/dist/pipeline/materialize.js +0 -1157
  133. package/dist/pipeline/materialize.js.map +0 -1
  134. package/dist/pipeline/parseStateKey.d.ts +0 -15
  135. package/dist/pipeline/parseStateKey.js +0 -446
  136. package/dist/pipeline/parseStateKey.js.map +0 -1
  137. package/dist/pipeline/simplify.js +0 -690
  138. package/dist/pipeline/simplify.js.map +0 -1
  139. package/dist/pipeline/warnings.js +0 -18
  140. package/dist/pipeline/warnings.js.map +0 -1
  141. package/dist/plugins/index.d.ts +0 -2
  142. package/dist/plugins/okhsl-plugin.d.ts +0 -35
  143. package/dist/plugins/okhsl-plugin.js +0 -97
  144. package/dist/plugins/okhsl-plugin.js.map +0 -1
  145. package/dist/plugins/types.d.ts +0 -87
  146. package/dist/properties/index.js +0 -222
  147. package/dist/properties/index.js.map +0 -1
  148. package/dist/properties/property-type-resolver.d.ts +0 -24
  149. package/dist/properties/property-type-resolver.js +0 -90
  150. package/dist/properties/property-type-resolver.js.map +0 -1
  151. package/dist/rsc-cache.js +0 -79
  152. package/dist/rsc-cache.js.map +0 -1
  153. package/dist/ssr/async-storage.d.ts +0 -17
  154. package/dist/ssr/async-storage.js.map +0 -1
  155. package/dist/ssr/collect-auto-properties.js +0 -58
  156. package/dist/ssr/collect-auto-properties.js.map +0 -1
  157. package/dist/ssr/collector.js.map +0 -1
  158. package/dist/ssr/context.js.map +0 -1
  159. package/dist/ssr/format-global-rules.js.map +0 -1
  160. package/dist/ssr/format-keyframes.js +0 -69
  161. package/dist/ssr/format-keyframes.js.map +0 -1
  162. package/dist/ssr/format-property.js +0 -49
  163. package/dist/ssr/format-property.js.map +0 -1
  164. package/dist/ssr/format-rules.js +0 -73
  165. package/dist/ssr/format-rules.js.map +0 -1
  166. package/dist/ssr/hydrate.d.ts +0 -29
  167. package/dist/ssr/hydrate.js.map +0 -1
  168. package/dist/ssr/ssr-collector-ref.js +0 -29
  169. package/dist/ssr/ssr-collector-ref.js.map +0 -1
  170. package/dist/states/index.d.ts +0 -49
  171. package/dist/states/index.js +0 -170
  172. package/dist/states/index.js.map +0 -1
  173. package/dist/static/tastyStatic.d.ts +0 -46
  174. package/dist/static/tastyStatic.js +0 -30
  175. package/dist/static/tastyStatic.js.map +0 -1
  176. package/dist/static/types.d.ts +0 -49
  177. package/dist/static/types.js +0 -24
  178. package/dist/static/types.js.map +0 -1
  179. package/dist/styles/border.d.ts +0 -25
  180. package/dist/styles/border.js +0 -120
  181. package/dist/styles/border.js.map +0 -1
  182. package/dist/styles/color.d.ts +0 -14
  183. package/dist/styles/color.js +0 -26
  184. package/dist/styles/color.js.map +0 -1
  185. package/dist/styles/const.js +0 -17
  186. package/dist/styles/const.js.map +0 -1
  187. package/dist/styles/createStyle.js +0 -79
  188. package/dist/styles/createStyle.js.map +0 -1
  189. package/dist/styles/dimension.js +0 -109
  190. package/dist/styles/dimension.js.map +0 -1
  191. package/dist/styles/directional.js +0 -133
  192. package/dist/styles/directional.js.map +0 -1
  193. package/dist/styles/display.d.ts +0 -30
  194. package/dist/styles/display.js +0 -73
  195. package/dist/styles/display.js.map +0 -1
  196. package/dist/styles/fade.d.ts +0 -15
  197. package/dist/styles/fade.js +0 -62
  198. package/dist/styles/fade.js.map +0 -1
  199. package/dist/styles/fill.d.ts +0 -42
  200. package/dist/styles/fill.js +0 -51
  201. package/dist/styles/fill.js.map +0 -1
  202. package/dist/styles/flow.d.ts +0 -16
  203. package/dist/styles/flow.js +0 -12
  204. package/dist/styles/flow.js.map +0 -1
  205. package/dist/styles/gap.d.ts +0 -31
  206. package/dist/styles/gap.js +0 -38
  207. package/dist/styles/gap.js.map +0 -1
  208. package/dist/styles/height.d.ts +0 -17
  209. package/dist/styles/height.js +0 -19
  210. package/dist/styles/height.js.map +0 -1
  211. package/dist/styles/index.d.ts +0 -1
  212. package/dist/styles/index.js +0 -8
  213. package/dist/styles/index.js.map +0 -1
  214. package/dist/styles/inset.d.ts +0 -24
  215. package/dist/styles/inset.js +0 -34
  216. package/dist/styles/inset.js.map +0 -1
  217. package/dist/styles/list.d.ts +0 -16
  218. package/dist/styles/list.js +0 -100
  219. package/dist/styles/list.js.map +0 -1
  220. package/dist/styles/margin.d.ts +0 -24
  221. package/dist/styles/margin.js +0 -32
  222. package/dist/styles/margin.js.map +0 -1
  223. package/dist/styles/outline.d.ts +0 -29
  224. package/dist/styles/outline.js +0 -55
  225. package/dist/styles/outline.js.map +0 -1
  226. package/dist/styles/padding.d.ts +0 -24
  227. package/dist/styles/padding.js +0 -32
  228. package/dist/styles/padding.js.map +0 -1
  229. package/dist/styles/placement.d.ts +0 -37
  230. package/dist/styles/placement.js +0 -74
  231. package/dist/styles/placement.js.map +0 -1
  232. package/dist/styles/predefined.d.ts +0 -71
  233. package/dist/styles/predefined.js +0 -237
  234. package/dist/styles/predefined.js.map +0 -1
  235. package/dist/styles/preset.d.ts +0 -52
  236. package/dist/styles/preset.js +0 -127
  237. package/dist/styles/preset.js.map +0 -1
  238. package/dist/styles/radius.d.ts +0 -12
  239. package/dist/styles/radius.js +0 -83
  240. package/dist/styles/radius.js.map +0 -1
  241. package/dist/styles/scrollMargin.d.ts +0 -24
  242. package/dist/styles/scrollMargin.js +0 -32
  243. package/dist/styles/scrollMargin.js.map +0 -1
  244. package/dist/styles/scrollbar.d.ts +0 -25
  245. package/dist/styles/scrollbar.js +0 -51
  246. package/dist/styles/scrollbar.js.map +0 -1
  247. package/dist/styles/shadow.d.ts +0 -14
  248. package/dist/styles/shadow.js +0 -25
  249. package/dist/styles/shadow.js.map +0 -1
  250. package/dist/styles/shared.js +0 -17
  251. package/dist/styles/shared.js.map +0 -1
  252. package/dist/styles/transition.d.ts +0 -14
  253. package/dist/styles/transition.js +0 -159
  254. package/dist/styles/transition.js.map +0 -1
  255. package/dist/styles/width.d.ts +0 -17
  256. package/dist/styles/width.js +0 -19
  257. package/dist/styles/width.js.map +0 -1
  258. package/dist/tasty.d.ts +0 -134
  259. package/dist/tasty.js +0 -248
  260. package/dist/tasty.js.map +0 -1
  261. package/dist/types.d.ts +0 -184
  262. package/dist/utils/cache-wrapper.js +0 -21
  263. package/dist/utils/cache-wrapper.js.map +0 -1
  264. package/dist/utils/case-converter.js +0 -8
  265. package/dist/utils/case-converter.js.map +0 -1
  266. package/dist/utils/color-math.d.ts +0 -46
  267. package/dist/utils/color-math.js +0 -749
  268. package/dist/utils/color-math.js.map +0 -1
  269. package/dist/utils/color-space.d.ts +0 -5
  270. package/dist/utils/color-space.js +0 -228
  271. package/dist/utils/color-space.js.map +0 -1
  272. package/dist/utils/colors.d.ts +0 -5
  273. package/dist/utils/colors.js +0 -10
  274. package/dist/utils/colors.js.map +0 -1
  275. package/dist/utils/css-types.d.ts +0 -7
  276. package/dist/utils/deps-equal.js +0 -15
  277. package/dist/utils/deps-equal.js.map +0 -1
  278. package/dist/utils/dotize.d.ts +0 -26
  279. package/dist/utils/dotize.js +0 -122
  280. package/dist/utils/dotize.js.map +0 -1
  281. package/dist/utils/filter-base-props.d.ts +0 -15
  282. package/dist/utils/filter-base-props.js +0 -45
  283. package/dist/utils/filter-base-props.js.map +0 -1
  284. package/dist/utils/get-display-name.d.ts +0 -7
  285. package/dist/utils/get-display-name.js +0 -10
  286. package/dist/utils/get-display-name.js.map +0 -1
  287. package/dist/utils/has-keys.js +0 -13
  288. package/dist/utils/has-keys.js.map +0 -1
  289. package/dist/utils/hash.js +0 -14
  290. package/dist/utils/hash.js.map +0 -1
  291. package/dist/utils/is-dev-env.js +0 -19
  292. package/dist/utils/is-dev-env.js.map +0 -1
  293. package/dist/utils/is-valid-element-type.js +0 -15
  294. package/dist/utils/is-valid-element-type.js.map +0 -1
  295. package/dist/utils/merge-styles.js.map +0 -1
  296. package/dist/utils/mod-attrs.d.ts +0 -6
  297. package/dist/utils/mod-attrs.js +0 -20
  298. package/dist/utils/mod-attrs.js.map +0 -1
  299. package/dist/utils/process-tokens.d.ts +0 -17
  300. package/dist/utils/process-tokens.js +0 -83
  301. package/dist/utils/process-tokens.js.map +0 -1
  302. package/dist/utils/resolve-recipes.d.ts +0 -17
  303. package/dist/utils/resolve-recipes.js.map +0 -1
  304. package/dist/utils/selector-transform.js +0 -32
  305. package/dist/utils/selector-transform.js.map +0 -1
  306. package/dist/utils/string.js +0 -8
  307. package/dist/utils/string.js.map +0 -1
  308. package/dist/utils/styles.d.ts +0 -99
  309. package/dist/utils/styles.js +0 -220
  310. package/dist/utils/styles.js.map +0 -1
  311. package/dist/utils/typography.d.ts +0 -58
  312. package/dist/utils/typography.js +0 -51
  313. package/dist/utils/typography.js.map +0 -1
  314. package/dist/utils/warnings.d.ts +0 -16
  315. package/dist/utils/warnings.js +0 -16
  316. package/dist/utils/warnings.js.map +0 -1
  317. package/dist/zero/css-writer.d.ts +0 -45
  318. package/dist/zero/css-writer.js +0 -73
  319. package/dist/zero/css-writer.js.map +0 -1
  320. package/dist/zero/extractor.d.ts +0 -24
  321. package/dist/zero/extractor.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"simplify.js","names":[],"sources":["../../src/pipeline/simplify.ts"],"sourcesContent":["/**\n * Condition Simplification Engine\n *\n * Simplifies condition trees by applying boolean algebra rules,\n * detecting contradictions, merging ranges, and deduplicating terms.\n *\n * This is critical for:\n * 1. Detecting invalid combinations (A & !A → FALSE)\n * 2. Reducing CSS output size\n * 3. Producing cleaner selectors\n */\n\nimport { Lru } from '../parser/lru';\n\nimport type {\n ConditionNode,\n ContainerCondition,\n MediaCondition,\n ModifierCondition,\n NumericBound,\n} from './conditions';\nimport {\n and,\n falseCondition,\n getConditionUniqueId,\n not,\n or,\n trueCondition,\n} from './conditions';\n\n// ============================================================================\n// Caching\n// ============================================================================\n\nconst simplifyCache = new Lru<string, ConditionNode>(5000);\n\n// ============================================================================\n// Main Simplify Function\n// ============================================================================\n\n/**\n * Simplify a condition tree aggressively.\n *\n * This applies all possible simplification rules:\n * - Boolean algebra (identity, annihilator, idempotent, absorption)\n * - Contradiction detection (A & !A → FALSE)\n * - Tautology detection (A | !A → TRUE)\n * - Range intersection for numeric queries\n * - Attribute value conflict detection\n * - Deduplication and sorting\n */\nexport function simplifyCondition(node: ConditionNode): ConditionNode {\n // Check cache\n const key = getConditionUniqueId(node);\n const cached = simplifyCache.get(key);\n if (cached) {\n return cached;\n }\n\n const result = simplifyInner(node);\n\n // Cache result\n simplifyCache.set(key, result);\n\n return result;\n}\n\n/**\n * Clear the simplify cache (for testing)\n */\nexport function clearSimplifyCache(): void {\n simplifyCache.clear();\n}\n\n// ============================================================================\n// Inner Simplification\n// ============================================================================\n\nfunction simplifyInner(node: ConditionNode): ConditionNode {\n // Base cases\n if (node.kind === 'true' || node.kind === 'false') {\n return node;\n }\n\n // State conditions - return as-is (they're already leaf nodes)\n if (node.kind === 'state') {\n return node;\n }\n\n // Compound conditions - recursively simplify\n if (node.kind === 'compound') {\n // First, recursively simplify all children\n const simplifiedChildren = node.children.map((c) => simplifyInner(c));\n\n // Then apply compound-specific simplifications\n if (node.operator === 'AND') {\n return simplifyAnd(simplifiedChildren);\n } else {\n return simplifyOr(simplifiedChildren);\n }\n }\n\n return node;\n}\n\n// ============================================================================\n// AND Simplification\n// ============================================================================\n\nfunction simplifyAnd(children: ConditionNode[]): ConditionNode {\n let terms: ConditionNode[] = [];\n\n // Flatten nested ANDs and handle TRUE/FALSE\n for (const child of children) {\n if (child.kind === 'false') {\n // AND with FALSE → FALSE\n return falseCondition();\n }\n if (child.kind === 'true') {\n // AND with TRUE → skip (identity)\n continue;\n }\n if (child.kind === 'compound' && child.operator === 'AND') {\n // Flatten nested AND\n terms.push(...child.children);\n } else {\n terms.push(child);\n }\n }\n\n // Empty → TRUE\n if (terms.length === 0) {\n return trueCondition();\n }\n\n // Single term → return it\n if (terms.length === 1) {\n return terms[0];\n }\n\n // Check for contradictions\n if (hasContradiction(terms)) {\n return falseCondition();\n }\n\n // Check for range contradictions in media/container queries\n if (hasRangeContradiction(terms)) {\n return falseCondition();\n }\n\n // Check for attribute value conflicts\n if (hasAttributeConflict(terms)) {\n return falseCondition();\n }\n\n // Check for container style query conflicts\n if (hasContainerStyleConflict(terms)) {\n return falseCondition();\n }\n\n // Remove redundant negations implied by positive terms\n // e.g., style(--variant: danger) implies NOT style(--variant: success)\n // and style(--variant: danger) implies style(--variant) (existence)\n terms = removeImpliedNegations(terms);\n\n // Deduplicate (by uniqueId)\n terms = deduplicateTerms(terms);\n\n // Try to merge numeric ranges\n terms = mergeRanges(terms);\n\n // Sort for canonical form\n terms = sortTerms(terms);\n\n // Apply absorption: A & (A | B) → A\n terms = applyAbsorptionAnd(terms);\n\n // Apply consensus/resolution: (A | B) & (A | !B) → A\n terms = applyConsensusAnd(terms);\n\n if (terms.length === 0) {\n return trueCondition();\n }\n if (terms.length === 1) {\n return terms[0];\n }\n\n return {\n kind: 'compound',\n operator: 'AND',\n children: terms,\n };\n}\n\n// ============================================================================\n// OR Simplification\n// ============================================================================\n\nfunction simplifyOr(children: ConditionNode[]): ConditionNode {\n let terms: ConditionNode[] = [];\n\n // Flatten nested ORs and handle TRUE/FALSE\n for (const child of children) {\n if (child.kind === 'true') {\n // OR with TRUE → TRUE\n return trueCondition();\n }\n if (child.kind === 'false') {\n // OR with FALSE → skip (identity)\n continue;\n }\n if (child.kind === 'compound' && child.operator === 'OR') {\n // Flatten nested OR\n terms.push(...child.children);\n } else {\n terms.push(child);\n }\n }\n\n // Empty → FALSE\n if (terms.length === 0) {\n return falseCondition();\n }\n\n // Single term → return it\n if (terms.length === 1) {\n return terms[0];\n }\n\n // Check for tautologies (A | !A)\n if (hasTautology(terms)) {\n return trueCondition();\n }\n\n // Deduplicate\n terms = deduplicateTerms(terms);\n\n // Sort for canonical form\n terms = sortTerms(terms);\n\n // Apply absorption: A | (A & B) → A\n terms = applyAbsorptionOr(terms);\n\n // Apply complementary factoring: (A & B) | (A & !B) → A\n terms = applyComplementaryFactoring(terms);\n\n if (terms.length === 0) {\n return falseCondition();\n }\n if (terms.length === 1) {\n return terms[0];\n }\n\n return {\n kind: 'compound',\n operator: 'OR',\n children: terms,\n };\n}\n\n// ============================================================================\n// Contradiction Detection\n// ============================================================================\n\n/**\n * Check if any pair of terms has complementary negation (A and !A).\n * Used for both contradiction detection (in AND) and tautology detection (in OR),\n * since the underlying check is identical: the context determines the semantics.\n */\nfunction hasComplementaryPair(terms: ConditionNode[]): boolean {\n const uniqueIds = new Set<string>();\n\n for (const term of terms) {\n if (term.kind !== 'state') continue;\n\n const id = term.uniqueId;\n const negatedId = term.negated ? id.slice(1) : `!${id}`;\n\n if (uniqueIds.has(negatedId)) {\n return true;\n }\n uniqueIds.add(id);\n }\n\n return false;\n}\n\nconst hasContradiction = hasComplementaryPair;\nconst hasTautology = hasComplementaryPair;\n\n// ============================================================================\n// Range Contradiction Detection\n// ============================================================================\n\n/**\n * Effective bounds computed from conditions (including negated single-bound conditions)\n */\ninterface EffectiveBounds {\n lowerBound: number | null;\n lowerInclusive: boolean;\n upperBound: number | null;\n upperInclusive: boolean;\n}\n\n/**\n * Excluded range from a negated range condition\n */\ninterface ExcludedRange {\n lower: number;\n lowerInclusive: boolean;\n upper: number;\n upperInclusive: boolean;\n}\n\n/**\n * Check for range contradictions in media/container queries\n * e.g., @media(w < 400px) & @media(w > 800px) → FALSE\n *\n * Also handles negated conditions:\n * - Single-bound negations are inverted (not (w < 600px) → w >= 600px)\n * - Range negations create excluded ranges that are checked against positive bounds\n */\nfunction hasRangeContradiction(terms: ConditionNode[]): boolean {\n // Group by dimension, separating positive and negated conditions\n const mediaByDim = new Map<\n string,\n { positive: MediaCondition[]; negated: MediaCondition[] }\n >();\n const containerByDim = new Map<\n string,\n { positive: ContainerCondition[]; negated: ContainerCondition[] }\n >();\n\n for (const term of terms) {\n if (term.kind !== 'state') continue;\n\n if (term.type === 'media' && term.subtype === 'dimension') {\n const key = term.dimension || 'width';\n if (!mediaByDim.has(key)) {\n mediaByDim.set(key, { positive: [], negated: [] });\n }\n const group = mediaByDim.get(key)!;\n if (term.negated) {\n group.negated.push(term);\n } else {\n group.positive.push(term);\n }\n }\n\n if (term.type === 'container' && term.subtype === 'dimension') {\n const key = `${term.containerName || '_'}:${term.dimension || 'width'}`;\n if (!containerByDim.has(key)) {\n containerByDim.set(key, { positive: [], negated: [] });\n }\n const group = containerByDim.get(key)!;\n if (term.negated) {\n group.negated.push(term);\n } else {\n group.positive.push(term);\n }\n }\n }\n\n // Check each dimension group for impossible ranges\n for (const group of mediaByDim.values()) {\n if (rangesAreImpossibleWithNegations(group.positive, group.negated)) {\n return true;\n }\n }\n\n for (const group of containerByDim.values()) {\n if (rangesAreImpossibleWithNegations(group.positive, group.negated)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Check if conditions are impossible, including negated conditions.\n *\n * For negated single-bound conditions:\n * not (w < 600px) → w >= 600px (inverted to lower bound)\n * not (w >= 800px) → w < 800px (inverted to upper bound)\n *\n * For negated range conditions:\n * not (400px <= w < 800px) → excludes [400, 800)\n * If the effective bounds fall entirely within an excluded range, it's impossible.\n */\nfunction rangesAreImpossibleWithNegations(\n positive: (MediaCondition | ContainerCondition)[],\n negated: (MediaCondition | ContainerCondition)[],\n): boolean {\n // Start with bounds from positive conditions\n const bounds = computeEffectiveBounds(positive);\n\n // Apply inverted bounds from single-bound negated conditions\n // and collect excluded ranges from range negated conditions\n const excludedRanges: ExcludedRange[] = [];\n\n for (const cond of negated) {\n const hasLower = cond.lowerBound?.valueNumeric != null;\n const hasUpper = cond.upperBound?.valueNumeric != null;\n\n if (hasLower && hasUpper) {\n // Range negation: not (lower <= w < upper) excludes [lower, upper)\n excludedRanges.push({\n lower: cond.lowerBound!.valueNumeric!,\n lowerInclusive: cond.lowerBound!.inclusive,\n upper: cond.upperBound!.valueNumeric!,\n upperInclusive: cond.upperBound!.inclusive,\n });\n } else if (hasUpper) {\n // not (w < upper) → w >= upper (becomes lower bound)\n // not (w <= upper) → w > upper (becomes lower bound, exclusive)\n const value = cond.upperBound!.valueNumeric!;\n const inclusive = !cond.upperBound!.inclusive; // flip inclusivity\n\n if (bounds.lowerBound === null || value > bounds.lowerBound) {\n bounds.lowerBound = value;\n bounds.lowerInclusive = inclusive;\n } else if (value === bounds.lowerBound && !inclusive) {\n bounds.lowerInclusive = false;\n }\n } else if (hasLower) {\n // not (w >= lower) → w < lower (becomes upper bound)\n // not (w > lower) → w <= lower (becomes upper bound, inclusive)\n const value = cond.lowerBound!.valueNumeric!;\n const inclusive = !cond.lowerBound!.inclusive; // flip inclusivity\n\n if (bounds.upperBound === null || value < bounds.upperBound) {\n bounds.upperBound = value;\n bounds.upperInclusive = inclusive;\n } else if (value === bounds.upperBound && !inclusive) {\n bounds.upperInclusive = false;\n }\n }\n }\n\n // Check if effective bounds are impossible on their own\n if (bounds.lowerBound !== null && bounds.upperBound !== null) {\n if (bounds.lowerBound > bounds.upperBound) {\n return true;\n }\n if (\n bounds.lowerBound === bounds.upperBound &&\n (!bounds.lowerInclusive || !bounds.upperInclusive)\n ) {\n return true;\n }\n }\n\n // Check if effective bounds fall entirely within any excluded range\n if (\n bounds.lowerBound !== null &&\n bounds.upperBound !== null &&\n excludedRanges.length > 0\n ) {\n for (const excluded of excludedRanges) {\n if (boundsWithinExcludedRange(bounds, excluded)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\n/**\n * Compute effective bounds from positive (non-negated) conditions\n */\nfunction computeEffectiveBounds(\n conditions: (MediaCondition | ContainerCondition)[],\n): EffectiveBounds {\n let lowerBound: number | null = null;\n let lowerInclusive = false;\n let upperBound: number | null = null;\n let upperInclusive = false;\n\n for (const cond of conditions) {\n if (cond.lowerBound?.valueNumeric != null) {\n const value = cond.lowerBound.valueNumeric;\n const inclusive = cond.lowerBound.inclusive;\n\n if (lowerBound === null || value > lowerBound) {\n lowerBound = value;\n lowerInclusive = inclusive;\n } else if (value === lowerBound && !inclusive) {\n lowerInclusive = false;\n }\n }\n\n if (cond.upperBound?.valueNumeric != null) {\n const value = cond.upperBound.valueNumeric;\n const inclusive = cond.upperBound.inclusive;\n\n if (upperBound === null || value < upperBound) {\n upperBound = value;\n upperInclusive = inclusive;\n } else if (value === upperBound && !inclusive) {\n upperInclusive = false;\n }\n }\n }\n\n return { lowerBound, lowerInclusive, upperBound, upperInclusive };\n}\n\n/**\n * Check if effective bounds fall entirely within an excluded range.\n *\n * For example:\n * Effective: [400, 800)\n * Excluded: [400, 800)\n * → bounds fall entirely within excluded range → impossible\n */\nfunction boundsWithinExcludedRange(\n bounds: EffectiveBounds,\n excluded: ExcludedRange,\n): boolean {\n if (bounds.lowerBound === null || bounds.upperBound === null) {\n return false;\n }\n\n // Check if bounds.lower >= excluded.lower\n let lowerOk = false;\n if (bounds.lowerBound > excluded.lower) {\n lowerOk = true;\n } else if (bounds.lowerBound === excluded.lower) {\n // If excluded includes lower, and bounds includes or excludes lower, it's within\n // If excluded excludes lower, bounds must also exclude it to be within\n lowerOk = excluded.lowerInclusive || !bounds.lowerInclusive;\n }\n\n // Check if bounds.upper <= excluded.upper\n let upperOk = false;\n if (bounds.upperBound < excluded.upper) {\n upperOk = true;\n } else if (bounds.upperBound === excluded.upper) {\n // If excluded includes upper, and bounds includes or excludes upper, it's within\n // If excluded excludes upper, bounds must also exclude it to be within\n upperOk = excluded.upperInclusive || !bounds.upperInclusive;\n }\n\n return lowerOk && upperOk;\n}\n\n// ============================================================================\n// Attribute Conflict Detection\n// ============================================================================\n\n/**\n * Check for attribute value conflicts\n * e.g., [data-theme=\"dark\"] & [data-theme=\"light\"] → FALSE\n * e.g., [data-theme=\"dark\"] & ![data-theme] → FALSE\n */\n/**\n * Generic value-conflict checker for grouped conditions.\n *\n * Groups terms by a key, splits into positive/negated, then checks:\n * 1. Multiple distinct positive values → conflict\n * 2. Positive value + negated existence (value === undefined) → conflict\n * 3. Positive value + negated same value → conflict\n */\nfunction hasGroupedValueConflict<T extends { negated: boolean }>(\n terms: ConditionNode[],\n match: (term: ConditionNode) => T | null,\n groupKey: (term: T) => string,\n getValue: (term: T) => string | undefined,\n): boolean {\n const groups = new Map<string, { positive: T[]; negated: T[] }>();\n\n for (const term of terms) {\n const matched = match(term);\n if (!matched) continue;\n\n const key = groupKey(matched);\n let group = groups.get(key);\n if (!group) {\n group = { positive: [], negated: [] };\n groups.set(key, group);\n }\n\n if (matched.negated) {\n group.negated.push(matched);\n } else {\n group.positive.push(matched);\n }\n }\n\n for (const [, group] of groups) {\n const positiveValues = group.positive\n .map(getValue)\n .filter((v) => v !== undefined);\n if (new Set(positiveValues).size > 1) return true;\n\n const hasPositiveValue = positiveValues.length > 0;\n const hasNegatedExistence = group.negated.some(\n (t) => getValue(t) === undefined,\n );\n if (hasPositiveValue && hasNegatedExistence) return true;\n\n for (const pos of group.positive) {\n const posVal = getValue(pos);\n if (posVal !== undefined) {\n for (const neg of group.negated) {\n if (getValue(neg) === posVal) return true;\n }\n }\n }\n }\n\n return false;\n}\n\nfunction hasAttributeConflict(terms: ConditionNode[]): boolean {\n return hasGroupedValueConflict<ModifierCondition>(\n terms,\n (t) => (t.kind === 'state' && t.type === 'modifier' ? t : null),\n (t) => t.attribute,\n (t) => t.value,\n );\n}\n\nfunction hasContainerStyleConflict(terms: ConditionNode[]): boolean {\n return hasGroupedValueConflict<ContainerCondition>(\n terms,\n (t) =>\n t.kind === 'state' && t.type === 'container' && t.subtype === 'style'\n ? t\n : null,\n (t) => `${t.containerName || '_'}:${t.property}`,\n (t) => t.propertyValue,\n );\n}\n\n// ============================================================================\n// Implied Negation Removal\n// ============================================================================\n\n/**\n * Remove negations that are implied by positive terms.\n *\n * Key optimizations:\n * 1. style(--variant: danger) implies NOT style(--variant: success)\n * → If we have style(--variant: danger) & not style(--variant: success),\n * the negation is redundant and can be removed.\n *\n * 2. [data-theme=\"dark\"] implies NOT [data-theme=\"light\"]\n * → Same logic for attribute selectors.\n *\n * This produces cleaner CSS:\n * Before: @container style(--variant: danger) and (not style(--variant: success))\n * After: @container style(--variant: danger)\n */\n/**\n * Collect positive values from terms and build a \"is this negation implied?\" check.\n *\n * A negation is implied (redundant) when a positive term for the same group\n * already pins a specific value, making \"NOT other-value\" obvious.\n * e.g. style(--variant: danger) implies NOT style(--variant: success).\n */\nfunction buildImpliedNegationCheck(\n terms: ConditionNode[],\n): (term: ConditionNode) => boolean {\n const positiveValues = new Map<string, string>();\n\n for (const term of terms) {\n if (term.kind !== 'state' || term.negated) continue;\n\n if (term.type === 'container' && term.subtype === 'style') {\n if (term.propertyValue !== undefined) {\n positiveValues.set(\n `c:${term.containerName || '_'}:${term.property}`,\n term.propertyValue,\n );\n }\n } else if (term.type === 'modifier' && term.value !== undefined) {\n positiveValues.set(`m:${term.attribute}`, term.value);\n }\n }\n\n return (term: ConditionNode): boolean => {\n if (term.kind !== 'state' || !term.negated) return false;\n\n if (term.type === 'container' && term.subtype === 'style') {\n if (term.propertyValue === undefined) return false;\n const pos = positiveValues.get(\n `c:${term.containerName || '_'}:${term.property}`,\n );\n return pos !== undefined && term.propertyValue !== pos;\n }\n\n if (term.type === 'modifier' && term.value !== undefined) {\n const pos = positiveValues.get(`m:${term.attribute}`);\n return pos !== undefined && term.value !== pos;\n }\n\n return false;\n };\n}\n\nfunction removeImpliedNegations(terms: ConditionNode[]): ConditionNode[] {\n const isImplied = buildImpliedNegationCheck(terms);\n return terms.filter((t) => !isImplied(t));\n}\n\n// ============================================================================\n// Deduplication\n// ============================================================================\n\nfunction deduplicateTerms(terms: ConditionNode[]): ConditionNode[] {\n const seen = new Set<string>();\n const result: ConditionNode[] = [];\n\n for (const term of terms) {\n const id = getConditionUniqueId(term);\n if (!seen.has(id)) {\n seen.add(id);\n result.push(term);\n }\n }\n\n return result;\n}\n\n// ============================================================================\n// Range Merging\n// ============================================================================\n\n/**\n * Merge compatible range conditions\n * e.g., @media(w >= 400px) & @media(w <= 800px) → @media(400px <= w <= 800px)\n */\nfunction mergeRanges(terms: ConditionNode[]): ConditionNode[] {\n // Group media conditions by dimension\n const mediaByDim = new Map<\n string,\n { conditions: MediaCondition[]; indices: number[] }\n >();\n const containerByDim = new Map<\n string,\n { conditions: ContainerCondition[]; indices: number[] }\n >();\n\n terms.forEach((term, index) => {\n if (term.kind !== 'state') return;\n\n if (\n term.type === 'media' &&\n term.subtype === 'dimension' &&\n !term.negated\n ) {\n const key = term.dimension || 'width';\n if (!mediaByDim.has(key)) {\n mediaByDim.set(key, { conditions: [], indices: [] });\n }\n const group = mediaByDim.get(key)!;\n group.conditions.push(term);\n group.indices.push(index);\n }\n\n if (\n term.type === 'container' &&\n term.subtype === 'dimension' &&\n !term.negated\n ) {\n const key = `${term.containerName || '_'}:${term.dimension || 'width'}`;\n if (!containerByDim.has(key)) {\n containerByDim.set(key, { conditions: [], indices: [] });\n }\n const group = containerByDim.get(key)!;\n group.conditions.push(term);\n group.indices.push(index);\n }\n });\n\n // Track indices to remove\n const indicesToRemove = new Set<number>();\n const mergedTerms: ConditionNode[] = [];\n\n // Merge media conditions\n for (const [_dim, group] of mediaByDim) {\n if (group.conditions.length > 1) {\n const merged = mergeMediaRanges(group.conditions);\n if (merged) {\n group.indices.forEach((i) => indicesToRemove.add(i));\n mergedTerms.push(merged);\n }\n }\n }\n\n // Merge container conditions\n for (const [, group] of containerByDim) {\n if (group.conditions.length > 1) {\n const merged = mergeContainerRanges(group.conditions);\n if (merged) {\n group.indices.forEach((i) => indicesToRemove.add(i));\n mergedTerms.push(merged);\n }\n }\n }\n\n // Build result\n const result: ConditionNode[] = [];\n terms.forEach((term, index) => {\n if (!indicesToRemove.has(index)) {\n result.push(term);\n }\n });\n result.push(...mergedTerms);\n\n return result;\n}\n\n/**\n * Tighten bounds by picking the most restrictive lower and upper bounds\n * from a set of conditions that have lowerBound/upperBound fields.\n */\nfunction tightenBounds(\n conditions: { lowerBound?: NumericBound; upperBound?: NumericBound }[],\n): { lowerBound?: NumericBound; upperBound?: NumericBound } {\n let lowerBound: NumericBound | undefined;\n let upperBound: NumericBound | undefined;\n\n for (const cond of conditions) {\n if (cond.lowerBound) {\n if (\n !lowerBound ||\n (cond.lowerBound.valueNumeric ?? -Infinity) >\n (lowerBound.valueNumeric ?? -Infinity)\n ) {\n lowerBound = cond.lowerBound;\n }\n }\n if (cond.upperBound) {\n if (\n !upperBound ||\n (cond.upperBound.valueNumeric ?? Infinity) <\n (upperBound.valueNumeric ?? Infinity)\n ) {\n upperBound = cond.upperBound;\n }\n }\n }\n\n return { lowerBound, upperBound };\n}\n\nfunction appendBoundsToUniqueId(\n parts: string[],\n lowerBound?: NumericBound,\n upperBound?: NumericBound,\n): void {\n if (lowerBound) {\n parts.push(lowerBound.inclusive ? '>=' : '>');\n parts.push(lowerBound.value);\n }\n if (upperBound) {\n parts.push(upperBound.inclusive ? '<=' : '<');\n parts.push(upperBound.value);\n }\n}\n\nfunction mergeDimensionRanges<T extends MediaCondition | ContainerCondition>(\n conditions: T[],\n idPrefix: string[],\n): T | null {\n if (conditions.length === 0) return null;\n\n const { lowerBound, upperBound } = tightenBounds(conditions);\n const base = conditions[0];\n\n const parts = [...idPrefix];\n appendBoundsToUniqueId(parts, lowerBound, upperBound);\n\n return {\n ...base,\n negated: false,\n raw: buildMergedRaw(base.dimension || 'width', lowerBound, upperBound),\n uniqueId: parts.join(':'),\n lowerBound,\n upperBound,\n };\n}\n\nfunction mergeMediaRanges(conditions: MediaCondition[]): MediaCondition | null {\n const dim = conditions[0]?.dimension ?? 'width';\n return mergeDimensionRanges(conditions, ['media', 'dim', dim]);\n}\n\nfunction mergeContainerRanges(\n conditions: ContainerCondition[],\n): ContainerCondition | null {\n const base = conditions[0];\n if (!base) return null;\n const name = base.containerName || '_';\n const dim = base.dimension ?? 'width';\n return mergeDimensionRanges(conditions, ['container', 'dim', name, dim]);\n}\n\nfunction buildMergedRaw(\n dimension: string,\n lowerBound?: NumericBound,\n upperBound?: NumericBound,\n): string {\n if (lowerBound && upperBound) {\n const lowerOp = lowerBound.inclusive ? '<=' : '<';\n const upperOp = upperBound.inclusive ? '<=' : '<';\n return `@media(${lowerBound.value} ${lowerOp} ${dimension} ${upperOp} ${upperBound.value})`;\n } else if (upperBound) {\n const op = upperBound.inclusive ? '<=' : '<';\n return `@media(${dimension} ${op} ${upperBound.value})`;\n } else if (lowerBound) {\n const op = lowerBound.inclusive ? '>=' : '>';\n return `@media(${dimension} ${op} ${lowerBound.value})`;\n }\n return '@media()';\n}\n\n// ============================================================================\n// Complementary Factoring\n// ============================================================================\n\n/**\n * Apply complementary factoring: (A & B) | (A & !B) → A\n *\n * Finds pairs of AND compounds that share all children except one,\n * where the differing child is complementary (X vs !X).\n * Replaces the pair with the common terms.\n *\n * This is applied iteratively until no more reductions are possible,\n * since factoring can expose further simplification opportunities.\n */\nfunction applyComplementaryFactoring(terms: ConditionNode[]): ConditionNode[] {\n let changed = true;\n\n while (changed) {\n changed = false;\n\n for (let i = 0; i < terms.length; i++) {\n const a = terms[i];\n if (a.kind !== 'compound' || a.operator !== 'AND') continue;\n\n for (let j = i + 1; j < terms.length; j++) {\n const b = terms[j];\n if (b.kind !== 'compound' || b.operator !== 'AND') continue;\n\n const factored = tryFactorPair(a.children, b.children);\n if (factored) {\n const replacement = simplifyInner(factored);\n terms = [\n ...terms.slice(0, i),\n ...terms.slice(i + 1, j),\n ...terms.slice(j + 1),\n replacement,\n ];\n changed = true;\n break;\n }\n }\n\n if (changed) break;\n }\n }\n\n return terms;\n}\n\n/**\n * Try to factor two AND children lists.\n *\n * Extracts the common children (by uniqueId). If the remaining\n * (non-common) parts of each side OR to TRUE, the common part alone\n * is sufficient: `(common & restA) | (common & restB) → common`\n * when `restA | restB → TRUE`.\n *\n * Also handles the simpler case where exactly one child is\n * complementary: `[A, B, C]` and `[A, !B, C]` → `AND(A, C)`.\n */\nfunction tryFactorPair(\n aChildren: ConditionNode[],\n bChildren: ConditionNode[],\n): ConditionNode | null {\n const aIds = aChildren.map((c) => getConditionUniqueId(c));\n const bIds = bChildren.map((c) => getConditionUniqueId(c));\n\n const bIdSet = new Set(bIds);\n const aIdSet = new Set(aIds);\n\n // Extract common children (present in both by uniqueId)\n const commonIndicesA: number[] = [];\n const restIndicesA: number[] = [];\n for (let i = 0; i < aIds.length; i++) {\n if (bIdSet.has(aIds[i])) {\n commonIndicesA.push(i);\n } else {\n restIndicesA.push(i);\n }\n }\n\n const restIndicesB: number[] = [];\n for (let i = 0; i < bIds.length; i++) {\n if (!aIdSet.has(bIds[i])) {\n restIndicesB.push(i);\n }\n }\n\n // Must have at least one common child and differing parts on both sides\n if (commonIndicesA.length === 0) return null;\n if (restIndicesA.length === 0 && restIndicesB.length === 0) return null;\n\n // Build the \"rest\" conditions for each side\n const restA =\n restIndicesA.length === 0\n ? trueCondition()\n : restIndicesA.length === 1\n ? aChildren[restIndicesA[0]]\n : and(...restIndicesA.map((i) => aChildren[i]));\n\n const restB =\n restIndicesB.length === 0\n ? trueCondition()\n : restIndicesB.length === 1\n ? bChildren[restIndicesB[0]]\n : and(...restIndicesB.map((i) => bChildren[i]));\n\n // Check if restA | restB simplifies to TRUE\n const combined = simplifyInner({\n kind: 'compound',\n operator: 'OR',\n children: [restA, restB],\n });\n\n if (combined.kind !== 'true') {\n // Direct complement check for compound conditions.\n // hasTautology only detects leaf-level A/!A pairs, so compound\n // complements like @hc / !@hc (which expand via De Morgan into\n // structurally different trees) are missed. Compare the simplified\n // negation of one rest against the simplified other rest instead.\n const simplifiedRestB = simplifyInner(restB);\n const negRestA = simplifyInner(not(restA));\n\n if (\n getConditionUniqueId(negRestA) !== getConditionUniqueId(simplifiedRestB)\n ) {\n return null;\n }\n }\n\n // restA | restB = TRUE → (common & restA) | (common & restB) = common\n const common = commonIndicesA.map((i) => aChildren[i]);\n\n if (common.length === 0) {\n return trueCondition();\n }\n if (common.length === 1) {\n return common[0];\n }\n return and(...common);\n}\n\n// ============================================================================\n// Sorting\n// ============================================================================\n\nfunction sortTerms(terms: ConditionNode[]): ConditionNode[] {\n const withIds = terms.map((t) => [getConditionUniqueId(t), t] as const);\n withIds.sort((a, b) => a[0].localeCompare(b[0]));\n return withIds.map(([, t]) => t);\n}\n\n// ============================================================================\n// Absorption\n// ============================================================================\n\n/**\n * Apply the absorption law: removes compound terms that are absorbed by\n * another term already present (simple or compound).\n *\n * For AND context: A & (A | B) → A (absorbs OR compounds)\n * For OR context: A | (A & B) → A (absorbs AND compounds)\n *\n * After flattening, a compound A = OR(X, Y) becomes [X, Y, ...] in the\n * outer OR. A child AND(A, B) = AND(OR(X, Y), B) still references the\n * original un-flattened compound. We reconstruct possible compound\n * absorbers from the flattened terms so absorption works across nesting.\n */\nfunction applyAbsorption(\n terms: ConditionNode[],\n absorbedOperator: 'OR' | 'AND',\n): ConditionNode[] {\n // Collect IDs of ALL top-level terms as potential absorbers.\n const absorberIds = new Set<string>();\n for (const term of terms) {\n absorberIds.add(getConditionUniqueId(term));\n }\n\n // Reconstruct compound absorbers: if all children of a compound node\n // (found inside an absorbable term) are themselves absorbers, then\n // that compound is also an absorber. This handles the case where\n // A = OR(X, Y) was flattened into [X, Y, ...] at the top level,\n // but appears un-flattened as a child of AND(A, B).\n let changed = true;\n while (changed) {\n changed = false;\n for (const term of terms) {\n if (term.kind !== 'compound' || term.operator !== absorbedOperator) {\n continue;\n }\n for (const child of term.children) {\n if (child.kind !== 'compound') continue;\n const childId = getConditionUniqueId(child);\n if (absorberIds.has(childId)) continue;\n if (\n child.children.every((c) => absorberIds.has(getConditionUniqueId(c)))\n ) {\n absorberIds.add(childId);\n changed = true;\n }\n }\n }\n }\n\n return terms.filter((term) => {\n if (term.kind === 'compound' && term.operator === absorbedOperator) {\n for (const child of term.children) {\n if (absorberIds.has(getConditionUniqueId(child))) {\n return false;\n }\n }\n }\n return true;\n });\n}\n\nfunction applyAbsorptionAnd(terms: ConditionNode[]): ConditionNode[] {\n return applyAbsorption(terms, 'OR');\n}\n\nfunction applyAbsorptionOr(terms: ConditionNode[]): ConditionNode[] {\n return applyAbsorption(terms, 'AND');\n}\n\n// ============================================================================\n// Consensus / Resolution (AND dual of complementary factoring)\n// ============================================================================\n\n/**\n * Apply the consensus/resolution rule for AND:\n * (A | B) & (A | !B) → A\n *\n * This is the dual of complementary factoring in OR context:\n * (A & B) | (A & !B) → A\n *\n * Extracts common children from two OR terms. If the remaining\n * parts of each side AND to FALSE, the common part alone is\n * sufficient.\n */\nfunction applyConsensusAnd(terms: ConditionNode[]): ConditionNode[] {\n let changed = true;\n\n while (changed) {\n changed = false;\n\n for (let i = 0; i < terms.length; i++) {\n const a = terms[i];\n if (a.kind !== 'compound' || a.operator !== 'OR') continue;\n\n for (let j = i + 1; j < terms.length; j++) {\n const b = terms[j];\n if (b.kind !== 'compound' || b.operator !== 'OR') continue;\n\n const resolved = tryResolvePair(a.children, b.children);\n if (resolved) {\n const replacement = simplifyInner(resolved);\n terms = [\n ...terms.slice(0, i),\n ...terms.slice(i + 1, j),\n ...terms.slice(j + 1),\n replacement,\n ];\n changed = true;\n break;\n }\n }\n\n if (changed) break;\n }\n }\n\n return terms;\n}\n\n/**\n * Try to resolve two OR children lists.\n *\n * Extracts common children (by uniqueId). If the remaining\n * (non-common) parts AND to FALSE, the common part alone\n * is sufficient: `(common | restA) & (common | restB) → common`\n * when `restA & restB → FALSE`.\n */\nfunction tryResolvePair(\n aChildren: ConditionNode[],\n bChildren: ConditionNode[],\n): ConditionNode | null {\n const aIds = aChildren.map((c) => getConditionUniqueId(c));\n const bIds = bChildren.map((c) => getConditionUniqueId(c));\n\n const bIdSet = new Set(bIds);\n const aIdSet = new Set(aIds);\n\n const commonIndicesA: number[] = [];\n const restIndicesA: number[] = [];\n for (let i = 0; i < aIds.length; i++) {\n if (bIdSet.has(aIds[i])) {\n commonIndicesA.push(i);\n } else {\n restIndicesA.push(i);\n }\n }\n\n const restIndicesB: number[] = [];\n for (let i = 0; i < bIds.length; i++) {\n if (!aIdSet.has(bIds[i])) {\n restIndicesB.push(i);\n }\n }\n\n if (commonIndicesA.length === 0) return null;\n if (restIndicesA.length === 0 && restIndicesB.length === 0) return null;\n\n const restA =\n restIndicesA.length === 0\n ? falseCondition()\n : restIndicesA.length === 1\n ? aChildren[restIndicesA[0]]\n : or(...restIndicesA.map((i) => aChildren[i]));\n\n const restB =\n restIndicesB.length === 0\n ? falseCondition()\n : restIndicesB.length === 1\n ? bChildren[restIndicesB[0]]\n : or(...restIndicesB.map((i) => bChildren[i]));\n\n // Check if restA & restB simplifies to FALSE\n const combined = simplifyInner({\n kind: 'compound',\n operator: 'AND',\n children: [restA, restB],\n });\n\n if (combined.kind !== 'false') {\n // Direct complement check for compound conditions\n const simplifiedRestB = simplifyInner(restB);\n const negRestA = simplifyInner(not(restA));\n\n if (\n getConditionUniqueId(negRestA) !== getConditionUniqueId(simplifiedRestB)\n ) {\n return null;\n }\n }\n\n // restA & restB = FALSE → (common | restA) & (common | restB) = common\n const common = commonIndicesA.map((i) => aChildren[i]);\n\n if (common.length === 0) {\n return falseCondition();\n }\n if (common.length === 1) {\n return common[0];\n }\n return or(...common);\n}\n"],"mappings":";;;;;;;;;;;;;;AAkCA,MAAM,gBAAgB,IAAI,IAA2B,IAAK;;;;;;;;;;;;AAiB1D,SAAgB,kBAAkB,MAAoC;CAEpE,MAAM,MAAM,qBAAqB,KAAK;CACtC,MAAM,SAAS,cAAc,IAAI,IAAI;AACrC,KAAI,OACF,QAAO;CAGT,MAAM,SAAS,cAAc,KAAK;AAGlC,eAAc,IAAI,KAAK,OAAO;AAE9B,QAAO;;AAcT,SAAS,cAAc,MAAoC;AAEzD,KAAI,KAAK,SAAS,UAAU,KAAK,SAAS,QACxC,QAAO;AAIT,KAAI,KAAK,SAAS,QAChB,QAAO;AAIT,KAAI,KAAK,SAAS,YAAY;EAE5B,MAAM,qBAAqB,KAAK,SAAS,KAAK,MAAM,cAAc,EAAE,CAAC;AAGrE,MAAI,KAAK,aAAa,MACpB,QAAO,YAAY,mBAAmB;MAEtC,QAAO,WAAW,mBAAmB;;AAIzC,QAAO;;AAOT,SAAS,YAAY,UAA0C;CAC7D,IAAI,QAAyB,EAAE;AAG/B,MAAK,MAAM,SAAS,UAAU;AAC5B,MAAI,MAAM,SAAS,QAEjB,QAAO,gBAAgB;AAEzB,MAAI,MAAM,SAAS,OAEjB;AAEF,MAAI,MAAM,SAAS,cAAc,MAAM,aAAa,MAElD,OAAM,KAAK,GAAG,MAAM,SAAS;MAE7B,OAAM,KAAK,MAAM;;AAKrB,KAAI,MAAM,WAAW,EACnB,QAAO,eAAe;AAIxB,KAAI,MAAM,WAAW,EACnB,QAAO,MAAM;AAIf,KAAI,iBAAiB,MAAM,CACzB,QAAO,gBAAgB;AAIzB,KAAI,sBAAsB,MAAM,CAC9B,QAAO,gBAAgB;AAIzB,KAAI,qBAAqB,MAAM,CAC7B,QAAO,gBAAgB;AAIzB,KAAI,0BAA0B,MAAM,CAClC,QAAO,gBAAgB;AAMzB,SAAQ,uBAAuB,MAAM;AAGrC,SAAQ,iBAAiB,MAAM;AAG/B,SAAQ,YAAY,MAAM;AAG1B,SAAQ,UAAU,MAAM;AAGxB,SAAQ,mBAAmB,MAAM;AAGjC,SAAQ,kBAAkB,MAAM;AAEhC,KAAI,MAAM,WAAW,EACnB,QAAO,eAAe;AAExB,KAAI,MAAM,WAAW,EACnB,QAAO,MAAM;AAGf,QAAO;EACL,MAAM;EACN,UAAU;EACV,UAAU;EACX;;AAOH,SAAS,WAAW,UAA0C;CAC5D,IAAI,QAAyB,EAAE;AAG/B,MAAK,MAAM,SAAS,UAAU;AAC5B,MAAI,MAAM,SAAS,OAEjB,QAAO,eAAe;AAExB,MAAI,MAAM,SAAS,QAEjB;AAEF,MAAI,MAAM,SAAS,cAAc,MAAM,aAAa,KAElD,OAAM,KAAK,GAAG,MAAM,SAAS;MAE7B,OAAM,KAAK,MAAM;;AAKrB,KAAI,MAAM,WAAW,EACnB,QAAO,gBAAgB;AAIzB,KAAI,MAAM,WAAW,EACnB,QAAO,MAAM;AAIf,KAAI,aAAa,MAAM,CACrB,QAAO,eAAe;AAIxB,SAAQ,iBAAiB,MAAM;AAG/B,SAAQ,UAAU,MAAM;AAGxB,SAAQ,kBAAkB,MAAM;AAGhC,SAAQ,4BAA4B,MAAM;AAE1C,KAAI,MAAM,WAAW,EACnB,QAAO,gBAAgB;AAEzB,KAAI,MAAM,WAAW,EACnB,QAAO,MAAM;AAGf,QAAO;EACL,MAAM;EACN,UAAU;EACV,UAAU;EACX;;;;;;;AAYH,SAAS,qBAAqB,OAAiC;CAC7D,MAAM,4BAAY,IAAI,KAAa;AAEnC,MAAK,MAAM,QAAQ,OAAO;AACxB,MAAI,KAAK,SAAS,QAAS;EAE3B,MAAM,KAAK,KAAK;EAChB,MAAM,YAAY,KAAK,UAAU,GAAG,MAAM,EAAE,GAAG,IAAI;AAEnD,MAAI,UAAU,IAAI,UAAU,CAC1B,QAAO;AAET,YAAU,IAAI,GAAG;;AAGnB,QAAO;;AAGT,MAAM,mBAAmB;AACzB,MAAM,eAAe;;;;;;;;;AAkCrB,SAAS,sBAAsB,OAAiC;CAE9D,MAAM,6BAAa,IAAI,KAGpB;CACH,MAAM,iCAAiB,IAAI,KAGxB;AAEH,MAAK,MAAM,QAAQ,OAAO;AACxB,MAAI,KAAK,SAAS,QAAS;AAE3B,MAAI,KAAK,SAAS,WAAW,KAAK,YAAY,aAAa;GACzD,MAAM,MAAM,KAAK,aAAa;AAC9B,OAAI,CAAC,WAAW,IAAI,IAAI,CACtB,YAAW,IAAI,KAAK;IAAE,UAAU,EAAE;IAAE,SAAS,EAAE;IAAE,CAAC;GAEpD,MAAM,QAAQ,WAAW,IAAI,IAAI;AACjC,OAAI,KAAK,QACP,OAAM,QAAQ,KAAK,KAAK;OAExB,OAAM,SAAS,KAAK,KAAK;;AAI7B,MAAI,KAAK,SAAS,eAAe,KAAK,YAAY,aAAa;GAC7D,MAAM,MAAM,GAAG,KAAK,iBAAiB,IAAI,GAAG,KAAK,aAAa;AAC9D,OAAI,CAAC,eAAe,IAAI,IAAI,CAC1B,gBAAe,IAAI,KAAK;IAAE,UAAU,EAAE;IAAE,SAAS,EAAE;IAAE,CAAC;GAExD,MAAM,QAAQ,eAAe,IAAI,IAAI;AACrC,OAAI,KAAK,QACP,OAAM,QAAQ,KAAK,KAAK;OAExB,OAAM,SAAS,KAAK,KAAK;;;AAM/B,MAAK,MAAM,SAAS,WAAW,QAAQ,CACrC,KAAI,iCAAiC,MAAM,UAAU,MAAM,QAAQ,CACjE,QAAO;AAIX,MAAK,MAAM,SAAS,eAAe,QAAQ,CACzC,KAAI,iCAAiC,MAAM,UAAU,MAAM,QAAQ,CACjE,QAAO;AAIX,QAAO;;;;;;;;;;;;;AAcT,SAAS,iCACP,UACA,SACS;CAET,MAAM,SAAS,uBAAuB,SAAS;CAI/C,MAAM,iBAAkC,EAAE;AAE1C,MAAK,MAAM,QAAQ,SAAS;EAC1B,MAAM,WAAW,KAAK,YAAY,gBAAgB;EAClD,MAAM,WAAW,KAAK,YAAY,gBAAgB;AAElD,MAAI,YAAY,SAEd,gBAAe,KAAK;GAClB,OAAO,KAAK,WAAY;GACxB,gBAAgB,KAAK,WAAY;GACjC,OAAO,KAAK,WAAY;GACxB,gBAAgB,KAAK,WAAY;GAClC,CAAC;WACO,UAAU;GAGnB,MAAM,QAAQ,KAAK,WAAY;GAC/B,MAAM,YAAY,CAAC,KAAK,WAAY;AAEpC,OAAI,OAAO,eAAe,QAAQ,QAAQ,OAAO,YAAY;AAC3D,WAAO,aAAa;AACpB,WAAO,iBAAiB;cACf,UAAU,OAAO,cAAc,CAAC,UACzC,QAAO,iBAAiB;aAEjB,UAAU;GAGnB,MAAM,QAAQ,KAAK,WAAY;GAC/B,MAAM,YAAY,CAAC,KAAK,WAAY;AAEpC,OAAI,OAAO,eAAe,QAAQ,QAAQ,OAAO,YAAY;AAC3D,WAAO,aAAa;AACpB,WAAO,iBAAiB;cACf,UAAU,OAAO,cAAc,CAAC,UACzC,QAAO,iBAAiB;;;AAM9B,KAAI,OAAO,eAAe,QAAQ,OAAO,eAAe,MAAM;AAC5D,MAAI,OAAO,aAAa,OAAO,WAC7B,QAAO;AAET,MACE,OAAO,eAAe,OAAO,eAC5B,CAAC,OAAO,kBAAkB,CAAC,OAAO,gBAEnC,QAAO;;AAKX,KACE,OAAO,eAAe,QACtB,OAAO,eAAe,QACtB,eAAe,SAAS;OAEnB,MAAM,YAAY,eACrB,KAAI,0BAA0B,QAAQ,SAAS,CAC7C,QAAO;;AAKb,QAAO;;;;;AAMT,SAAS,uBACP,YACiB;CACjB,IAAI,aAA4B;CAChC,IAAI,iBAAiB;CACrB,IAAI,aAA4B;CAChC,IAAI,iBAAiB;AAErB,MAAK,MAAM,QAAQ,YAAY;AAC7B,MAAI,KAAK,YAAY,gBAAgB,MAAM;GACzC,MAAM,QAAQ,KAAK,WAAW;GAC9B,MAAM,YAAY,KAAK,WAAW;AAElC,OAAI,eAAe,QAAQ,QAAQ,YAAY;AAC7C,iBAAa;AACb,qBAAiB;cACR,UAAU,cAAc,CAAC,UAClC,kBAAiB;;AAIrB,MAAI,KAAK,YAAY,gBAAgB,MAAM;GACzC,MAAM,QAAQ,KAAK,WAAW;GAC9B,MAAM,YAAY,KAAK,WAAW;AAElC,OAAI,eAAe,QAAQ,QAAQ,YAAY;AAC7C,iBAAa;AACb,qBAAiB;cACR,UAAU,cAAc,CAAC,UAClC,kBAAiB;;;AAKvB,QAAO;EAAE;EAAY;EAAgB;EAAY;EAAgB;;;;;;;;;;AAWnE,SAAS,0BACP,QACA,UACS;AACT,KAAI,OAAO,eAAe,QAAQ,OAAO,eAAe,KACtD,QAAO;CAIT,IAAI,UAAU;AACd,KAAI,OAAO,aAAa,SAAS,MAC/B,WAAU;UACD,OAAO,eAAe,SAAS,MAGxC,WAAU,SAAS,kBAAkB,CAAC,OAAO;CAI/C,IAAI,UAAU;AACd,KAAI,OAAO,aAAa,SAAS,MAC/B,WAAU;UACD,OAAO,eAAe,SAAS,MAGxC,WAAU,SAAS,kBAAkB,CAAC,OAAO;AAG/C,QAAO,WAAW;;;;;;;;;;;;;;;AAoBpB,SAAS,wBACP,OACA,OACA,UACA,UACS;CACT,MAAM,yBAAS,IAAI,KAA8C;AAEjE,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,QAAS;EAEd,MAAM,MAAM,SAAS,QAAQ;EAC7B,IAAI,QAAQ,OAAO,IAAI,IAAI;AAC3B,MAAI,CAAC,OAAO;AACV,WAAQ;IAAE,UAAU,EAAE;IAAE,SAAS,EAAE;IAAE;AACrC,UAAO,IAAI,KAAK,MAAM;;AAGxB,MAAI,QAAQ,QACV,OAAM,QAAQ,KAAK,QAAQ;MAE3B,OAAM,SAAS,KAAK,QAAQ;;AAIhC,MAAK,MAAM,GAAG,UAAU,QAAQ;EAC9B,MAAM,iBAAiB,MAAM,SAC1B,IAAI,SAAS,CACb,QAAQ,MAAM,MAAM,KAAA,EAAU;AACjC,MAAI,IAAI,IAAI,eAAe,CAAC,OAAO,EAAG,QAAO;EAE7C,MAAM,mBAAmB,eAAe,SAAS;EACjD,MAAM,sBAAsB,MAAM,QAAQ,MACvC,MAAM,SAAS,EAAE,KAAK,KAAA,EACxB;AACD,MAAI,oBAAoB,oBAAqB,QAAO;AAEpD,OAAK,MAAM,OAAO,MAAM,UAAU;GAChC,MAAM,SAAS,SAAS,IAAI;AAC5B,OAAI,WAAW,KAAA;SACR,MAAM,OAAO,MAAM,QACtB,KAAI,SAAS,IAAI,KAAK,OAAQ,QAAO;;;;AAM7C,QAAO;;AAGT,SAAS,qBAAqB,OAAiC;AAC7D,QAAO,wBACL,QACC,MAAO,EAAE,SAAS,WAAW,EAAE,SAAS,aAAa,IAAI,OACzD,MAAM,EAAE,YACR,MAAM,EAAE,MACV;;AAGH,SAAS,0BAA0B,OAAiC;AAClE,QAAO,wBACL,QACC,MACC,EAAE,SAAS,WAAW,EAAE,SAAS,eAAe,EAAE,YAAY,UAC1D,IACA,OACL,MAAM,GAAG,EAAE,iBAAiB,IAAI,GAAG,EAAE,aACrC,MAAM,EAAE,cACV;;;;;;;;;;;;;;;;;;;;;;;;AA6BH,SAAS,0BACP,OACkC;CAClC,MAAM,iCAAiB,IAAI,KAAqB;AAEhD,MAAK,MAAM,QAAQ,OAAO;AACxB,MAAI,KAAK,SAAS,WAAW,KAAK,QAAS;AAE3C,MAAI,KAAK,SAAS,eAAe,KAAK,YAAY;OAC5C,KAAK,kBAAkB,KAAA,EACzB,gBAAe,IACb,KAAK,KAAK,iBAAiB,IAAI,GAAG,KAAK,YACvC,KAAK,cACN;aAEM,KAAK,SAAS,cAAc,KAAK,UAAU,KAAA,EACpD,gBAAe,IAAI,KAAK,KAAK,aAAa,KAAK,MAAM;;AAIzD,SAAQ,SAAiC;AACvC,MAAI,KAAK,SAAS,WAAW,CAAC,KAAK,QAAS,QAAO;AAEnD,MAAI,KAAK,SAAS,eAAe,KAAK,YAAY,SAAS;AACzD,OAAI,KAAK,kBAAkB,KAAA,EAAW,QAAO;GAC7C,MAAM,MAAM,eAAe,IACzB,KAAK,KAAK,iBAAiB,IAAI,GAAG,KAAK,WACxC;AACD,UAAO,QAAQ,KAAA,KAAa,KAAK,kBAAkB;;AAGrD,MAAI,KAAK,SAAS,cAAc,KAAK,UAAU,KAAA,GAAW;GACxD,MAAM,MAAM,eAAe,IAAI,KAAK,KAAK,YAAY;AACrD,UAAO,QAAQ,KAAA,KAAa,KAAK,UAAU;;AAG7C,SAAO;;;AAIX,SAAS,uBAAuB,OAAyC;CACvE,MAAM,YAAY,0BAA0B,MAAM;AAClD,QAAO,MAAM,QAAQ,MAAM,CAAC,UAAU,EAAE,CAAC;;AAO3C,SAAS,iBAAiB,OAAyC;CACjE,MAAM,uBAAO,IAAI,KAAa;CAC9B,MAAM,SAA0B,EAAE;AAElC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,KAAK,qBAAqB,KAAK;AACrC,MAAI,CAAC,KAAK,IAAI,GAAG,EAAE;AACjB,QAAK,IAAI,GAAG;AACZ,UAAO,KAAK,KAAK;;;AAIrB,QAAO;;;;;;AAWT,SAAS,YAAY,OAAyC;CAE5D,MAAM,6BAAa,IAAI,KAGpB;CACH,MAAM,iCAAiB,IAAI,KAGxB;AAEH,OAAM,SAAS,MAAM,UAAU;AAC7B,MAAI,KAAK,SAAS,QAAS;AAE3B,MACE,KAAK,SAAS,WACd,KAAK,YAAY,eACjB,CAAC,KAAK,SACN;GACA,MAAM,MAAM,KAAK,aAAa;AAC9B,OAAI,CAAC,WAAW,IAAI,IAAI,CACtB,YAAW,IAAI,KAAK;IAAE,YAAY,EAAE;IAAE,SAAS,EAAE;IAAE,CAAC;GAEtD,MAAM,QAAQ,WAAW,IAAI,IAAI;AACjC,SAAM,WAAW,KAAK,KAAK;AAC3B,SAAM,QAAQ,KAAK,MAAM;;AAG3B,MACE,KAAK,SAAS,eACd,KAAK,YAAY,eACjB,CAAC,KAAK,SACN;GACA,MAAM,MAAM,GAAG,KAAK,iBAAiB,IAAI,GAAG,KAAK,aAAa;AAC9D,OAAI,CAAC,eAAe,IAAI,IAAI,CAC1B,gBAAe,IAAI,KAAK;IAAE,YAAY,EAAE;IAAE,SAAS,EAAE;IAAE,CAAC;GAE1D,MAAM,QAAQ,eAAe,IAAI,IAAI;AACrC,SAAM,WAAW,KAAK,KAAK;AAC3B,SAAM,QAAQ,KAAK,MAAM;;GAE3B;CAGF,MAAM,kCAAkB,IAAI,KAAa;CACzC,MAAM,cAA+B,EAAE;AAGvC,MAAK,MAAM,CAAC,MAAM,UAAU,WAC1B,KAAI,MAAM,WAAW,SAAS,GAAG;EAC/B,MAAM,SAAS,iBAAiB,MAAM,WAAW;AACjD,MAAI,QAAQ;AACV,SAAM,QAAQ,SAAS,MAAM,gBAAgB,IAAI,EAAE,CAAC;AACpD,eAAY,KAAK,OAAO;;;AAM9B,MAAK,MAAM,GAAG,UAAU,eACtB,KAAI,MAAM,WAAW,SAAS,GAAG;EAC/B,MAAM,SAAS,qBAAqB,MAAM,WAAW;AACrD,MAAI,QAAQ;AACV,SAAM,QAAQ,SAAS,MAAM,gBAAgB,IAAI,EAAE,CAAC;AACpD,eAAY,KAAK,OAAO;;;CAM9B,MAAM,SAA0B,EAAE;AAClC,OAAM,SAAS,MAAM,UAAU;AAC7B,MAAI,CAAC,gBAAgB,IAAI,MAAM,CAC7B,QAAO,KAAK,KAAK;GAEnB;AACF,QAAO,KAAK,GAAG,YAAY;AAE3B,QAAO;;;;;;AAOT,SAAS,cACP,YAC0D;CAC1D,IAAI;CACJ,IAAI;AAEJ,MAAK,MAAM,QAAQ,YAAY;AAC7B,MAAI,KAAK;OAEL,CAAC,eACA,KAAK,WAAW,gBAAgB,cAC9B,WAAW,gBAAgB,WAE9B,cAAa,KAAK;;AAGtB,MAAI,KAAK;OAEL,CAAC,eACA,KAAK,WAAW,gBAAgB,aAC9B,WAAW,gBAAgB,UAE9B,cAAa,KAAK;;;AAKxB,QAAO;EAAE;EAAY;EAAY;;AAGnC,SAAS,uBACP,OACA,YACA,YACM;AACN,KAAI,YAAY;AACd,QAAM,KAAK,WAAW,YAAY,OAAO,IAAI;AAC7C,QAAM,KAAK,WAAW,MAAM;;AAE9B,KAAI,YAAY;AACd,QAAM,KAAK,WAAW,YAAY,OAAO,IAAI;AAC7C,QAAM,KAAK,WAAW,MAAM;;;AAIhC,SAAS,qBACP,YACA,UACU;AACV,KAAI,WAAW,WAAW,EAAG,QAAO;CAEpC,MAAM,EAAE,YAAY,eAAe,cAAc,WAAW;CAC5D,MAAM,OAAO,WAAW;CAExB,MAAM,QAAQ,CAAC,GAAG,SAAS;AAC3B,wBAAuB,OAAO,YAAY,WAAW;AAErD,QAAO;EACL,GAAG;EACH,SAAS;EACT,KAAK,eAAe,KAAK,aAAa,SAAS,YAAY,WAAW;EACtE,UAAU,MAAM,KAAK,IAAI;EACzB;EACA;EACD;;AAGH,SAAS,iBAAiB,YAAqD;AAE7E,QAAO,qBAAqB,YAAY;EAAC;EAAS;EADtC,WAAW,IAAI,aAAa;EACqB,CAAC;;AAGhE,SAAS,qBACP,YAC2B;CAC3B,MAAM,OAAO,WAAW;AACxB,KAAI,CAAC,KAAM,QAAO;AAGlB,QAAO,qBAAqB,YAAY;EAAC;EAAa;EAFzC,KAAK,iBAAiB;EACvB,KAAK,aAAa;EACyC,CAAC;;AAG1E,SAAS,eACP,WACA,YACA,YACQ;AACR,KAAI,cAAc,YAAY;EAC5B,MAAM,UAAU,WAAW,YAAY,OAAO;EAC9C,MAAM,UAAU,WAAW,YAAY,OAAO;AAC9C,SAAO,UAAU,WAAW,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,WAAW,MAAM;YAChF,WAET,QAAO,UAAU,UAAU,GADhB,WAAW,YAAY,OAAO,IACR,GAAG,WAAW,MAAM;UAC5C,WAET,QAAO,UAAU,UAAU,GADhB,WAAW,YAAY,OAAO,IACR,GAAG,WAAW,MAAM;AAEvD,QAAO;;;;;;;;;;;;AAiBT,SAAS,4BAA4B,OAAyC;CAC5E,IAAI,UAAU;AAEd,QAAO,SAAS;AACd,YAAU;AAEV,OAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;GACrC,MAAM,IAAI,MAAM;AAChB,OAAI,EAAE,SAAS,cAAc,EAAE,aAAa,MAAO;AAEnD,QAAK,IAAI,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;IACzC,MAAM,IAAI,MAAM;AAChB,QAAI,EAAE,SAAS,cAAc,EAAE,aAAa,MAAO;IAEnD,MAAM,WAAW,cAAc,EAAE,UAAU,EAAE,SAAS;AACtD,QAAI,UAAU;KACZ,MAAM,cAAc,cAAc,SAAS;AAC3C,aAAQ;MACN,GAAG,MAAM,MAAM,GAAG,EAAE;MACpB,GAAG,MAAM,MAAM,IAAI,GAAG,EAAE;MACxB,GAAG,MAAM,MAAM,IAAI,EAAE;MACrB;MACD;AACD,eAAU;AACV;;;AAIJ,OAAI,QAAS;;;AAIjB,QAAO;;;;;;;;;;;;;AAcT,SAAS,cACP,WACA,WACsB;CACtB,MAAM,OAAO,UAAU,KAAK,MAAM,qBAAqB,EAAE,CAAC;CAC1D,MAAM,OAAO,UAAU,KAAK,MAAM,qBAAqB,EAAE,CAAC;CAE1D,MAAM,SAAS,IAAI,IAAI,KAAK;CAC5B,MAAM,SAAS,IAAI,IAAI,KAAK;CAG5B,MAAM,iBAA2B,EAAE;CACnC,MAAM,eAAyB,EAAE;AACjC,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,IAC/B,KAAI,OAAO,IAAI,KAAK,GAAG,CACrB,gBAAe,KAAK,EAAE;KAEtB,cAAa,KAAK,EAAE;CAIxB,MAAM,eAAyB,EAAE;AACjC,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,IAC/B,KAAI,CAAC,OAAO,IAAI,KAAK,GAAG,CACtB,cAAa,KAAK,EAAE;AAKxB,KAAI,eAAe,WAAW,EAAG,QAAO;AACxC,KAAI,aAAa,WAAW,KAAK,aAAa,WAAW,EAAG,QAAO;CAGnE,MAAM,QACJ,aAAa,WAAW,IACpB,eAAe,GACf,aAAa,WAAW,IACtB,UAAU,aAAa,MACvB,IAAI,GAAG,aAAa,KAAK,MAAM,UAAU,GAAG,CAAC;CAErD,MAAM,QACJ,aAAa,WAAW,IACpB,eAAe,GACf,aAAa,WAAW,IACtB,UAAU,aAAa,MACvB,IAAI,GAAG,aAAa,KAAK,MAAM,UAAU,GAAG,CAAC;AASrD,KANiB,cAAc;EAC7B,MAAM;EACN,UAAU;EACV,UAAU,CAAC,OAAO,MAAM;EACzB,CAAC,CAEW,SAAS,QAAQ;EAM5B,MAAM,kBAAkB,cAAc,MAAM;AAG5C,MACE,qBAHe,cAAc,IAAI,MAAM,CAAC,CAGV,KAAK,qBAAqB,gBAAgB,CAExE,QAAO;;CAKX,MAAM,SAAS,eAAe,KAAK,MAAM,UAAU,GAAG;AAEtD,KAAI,OAAO,WAAW,EACpB,QAAO,eAAe;AAExB,KAAI,OAAO,WAAW,EACpB,QAAO,OAAO;AAEhB,QAAO,IAAI,GAAG,OAAO;;AAOvB,SAAS,UAAU,OAAyC;CAC1D,MAAM,UAAU,MAAM,KAAK,MAAM,CAAC,qBAAqB,EAAE,EAAE,EAAE,CAAU;AACvE,SAAQ,MAAM,GAAG,MAAM,EAAE,GAAG,cAAc,EAAE,GAAG,CAAC;AAChD,QAAO,QAAQ,KAAK,GAAG,OAAO,EAAE;;;;;;;;;;;;;;AAmBlC,SAAS,gBACP,OACA,kBACiB;CAEjB,MAAM,8BAAc,IAAI,KAAa;AACrC,MAAK,MAAM,QAAQ,MACjB,aAAY,IAAI,qBAAqB,KAAK,CAAC;CAQ7C,IAAI,UAAU;AACd,QAAO,SAAS;AACd,YAAU;AACV,OAAK,MAAM,QAAQ,OAAO;AACxB,OAAI,KAAK,SAAS,cAAc,KAAK,aAAa,iBAChD;AAEF,QAAK,MAAM,SAAS,KAAK,UAAU;AACjC,QAAI,MAAM,SAAS,WAAY;IAC/B,MAAM,UAAU,qBAAqB,MAAM;AAC3C,QAAI,YAAY,IAAI,QAAQ,CAAE;AAC9B,QACE,MAAM,SAAS,OAAO,MAAM,YAAY,IAAI,qBAAqB,EAAE,CAAC,CAAC,EACrE;AACA,iBAAY,IAAI,QAAQ;AACxB,eAAU;;;;;AAMlB,QAAO,MAAM,QAAQ,SAAS;AAC5B,MAAI,KAAK,SAAS,cAAc,KAAK,aAAa;QAC3C,MAAM,SAAS,KAAK,SACvB,KAAI,YAAY,IAAI,qBAAqB,MAAM,CAAC,CAC9C,QAAO;;AAIb,SAAO;GACP;;AAGJ,SAAS,mBAAmB,OAAyC;AACnE,QAAO,gBAAgB,OAAO,KAAK;;AAGrC,SAAS,kBAAkB,OAAyC;AAClE,QAAO,gBAAgB,OAAO,MAAM;;;;;;;;;;;;;AAkBtC,SAAS,kBAAkB,OAAyC;CAClE,IAAI,UAAU;AAEd,QAAO,SAAS;AACd,YAAU;AAEV,OAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;GACrC,MAAM,IAAI,MAAM;AAChB,OAAI,EAAE,SAAS,cAAc,EAAE,aAAa,KAAM;AAElD,QAAK,IAAI,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;IACzC,MAAM,IAAI,MAAM;AAChB,QAAI,EAAE,SAAS,cAAc,EAAE,aAAa,KAAM;IAElD,MAAM,WAAW,eAAe,EAAE,UAAU,EAAE,SAAS;AACvD,QAAI,UAAU;KACZ,MAAM,cAAc,cAAc,SAAS;AAC3C,aAAQ;MACN,GAAG,MAAM,MAAM,GAAG,EAAE;MACpB,GAAG,MAAM,MAAM,IAAI,GAAG,EAAE;MACxB,GAAG,MAAM,MAAM,IAAI,EAAE;MACrB;MACD;AACD,eAAU;AACV;;;AAIJ,OAAI,QAAS;;;AAIjB,QAAO;;;;;;;;;;AAWT,SAAS,eACP,WACA,WACsB;CACtB,MAAM,OAAO,UAAU,KAAK,MAAM,qBAAqB,EAAE,CAAC;CAC1D,MAAM,OAAO,UAAU,KAAK,MAAM,qBAAqB,EAAE,CAAC;CAE1D,MAAM,SAAS,IAAI,IAAI,KAAK;CAC5B,MAAM,SAAS,IAAI,IAAI,KAAK;CAE5B,MAAM,iBAA2B,EAAE;CACnC,MAAM,eAAyB,EAAE;AACjC,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,IAC/B,KAAI,OAAO,IAAI,KAAK,GAAG,CACrB,gBAAe,KAAK,EAAE;KAEtB,cAAa,KAAK,EAAE;CAIxB,MAAM,eAAyB,EAAE;AACjC,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,IAC/B,KAAI,CAAC,OAAO,IAAI,KAAK,GAAG,CACtB,cAAa,KAAK,EAAE;AAIxB,KAAI,eAAe,WAAW,EAAG,QAAO;AACxC,KAAI,aAAa,WAAW,KAAK,aAAa,WAAW,EAAG,QAAO;CAEnE,MAAM,QACJ,aAAa,WAAW,IACpB,gBAAgB,GAChB,aAAa,WAAW,IACtB,UAAU,aAAa,MACvB,GAAG,GAAG,aAAa,KAAK,MAAM,UAAU,GAAG,CAAC;CAEpD,MAAM,QACJ,aAAa,WAAW,IACpB,gBAAgB,GAChB,aAAa,WAAW,IACtB,UAAU,aAAa,MACvB,GAAG,GAAG,aAAa,KAAK,MAAM,UAAU,GAAG,CAAC;AASpD,KANiB,cAAc;EAC7B,MAAM;EACN,UAAU;EACV,UAAU,CAAC,OAAO,MAAM;EACzB,CAAC,CAEW,SAAS,SAAS;EAE7B,MAAM,kBAAkB,cAAc,MAAM;AAG5C,MACE,qBAHe,cAAc,IAAI,MAAM,CAAC,CAGV,KAAK,qBAAqB,gBAAgB,CAExE,QAAO;;CAKX,MAAM,SAAS,eAAe,KAAK,MAAM,UAAU,GAAG;AAEtD,KAAI,OAAO,WAAW,EACpB,QAAO,gBAAgB;AAEzB,KAAI,OAAO,WAAW,EACpB,QAAO,OAAO;AAEhB,QAAO,GAAG,GAAG,OAAO"}
@@ -1,18 +0,0 @@
1
- //#region src/pipeline/warnings.ts
2
- const defaultWarningHandler = (warning) => {
3
- console.warn(`[Tasty] ${warning.message}`);
4
- };
5
- let warningHandler = defaultWarningHandler;
6
- /**
7
- * Emit a structured pipeline warning via the configured handler.
8
- */
9
- function emitWarning(code, message) {
10
- warningHandler({
11
- code,
12
- message
13
- });
14
- }
15
- //#endregion
16
- export { emitWarning };
17
-
18
- //# sourceMappingURL=warnings.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"warnings.js","names":[],"sources":["../../src/pipeline/warnings.ts"],"sourcesContent":["/**\n * Structured warning system for the pipeline.\n *\n * Provides typed warning codes and a configurable handler so consumers\n * can programmatically intercept, suppress, or reroute warnings.\n */\n\nexport type TastyWarningCode = 'INVALID_SELECTOR_AFFIX' | 'XOR_CHAIN_TOO_LONG';\n\nexport interface TastyWarning {\n code: TastyWarningCode;\n message: string;\n}\n\nexport type TastyWarningHandler = (warning: TastyWarning) => void;\n\nconst defaultWarningHandler: TastyWarningHandler = (warning) => {\n console.warn(`[Tasty] ${warning.message}`);\n};\n\nlet warningHandler: TastyWarningHandler = defaultWarningHandler;\n\n/**\n * Set a custom warning handler for pipeline warnings.\n * Returns a function that restores the previous handler.\n */\nexport function setWarningHandler(handler: TastyWarningHandler): () => void {\n const previous = warningHandler;\n warningHandler = handler;\n return () => {\n warningHandler = previous;\n };\n}\n\n/**\n * Emit a structured pipeline warning via the configured handler.\n */\nexport function emitWarning(code: TastyWarningCode, message: string): void {\n warningHandler({ code, message });\n}\n"],"mappings":";AAgBA,MAAM,yBAA8C,YAAY;AAC9D,SAAQ,KAAK,WAAW,QAAQ,UAAU;;AAG5C,IAAI,iBAAsC;;;;AAiB1C,SAAgB,YAAY,MAAwB,SAAuB;AACzE,gBAAe;EAAE;EAAM;EAAS,CAAC"}
@@ -1,2 +0,0 @@
1
- import { TastyPlugin, TastyPluginFactory } from "./types.js";
2
- import { okhslFunc, okhslPlugin } from "./okhsl-plugin.js";
@@ -1,35 +0,0 @@
1
- import { StyleDetails } from "../parser/types.js";
2
- import { TastyPluginFactory } from "./types.js";
3
-
4
- //#region src/plugins/okhsl-plugin.d.ts
5
- /**
6
- * The okhsl function handler for tasty parser.
7
- * Receives parsed style groups and returns an RGB color string.
8
- */
9
- declare const okhslFunc: (groups: StyleDetails[]) => string;
10
- /**
11
- * OKHSL Plugin for Tasty.
12
- *
13
- * Adds support for the `okhsl()` color function in tasty styles.
14
- *
15
- * @example
16
- * ```ts
17
- * import { configure } from '@tenphi/tasty';
18
- * import { okhslPlugin } from '@tenphi/tasty';
19
- *
20
- * configure({
21
- * plugins: [okhslPlugin()],
22
- * });
23
- *
24
- * // Now you can use okhsl in styles:
25
- * const Box = tasty({
26
- * styles: {
27
- * fill: 'okhsl(240 50% 50%)',
28
- * },
29
- * });
30
- * ```
31
- */
32
- declare const okhslPlugin: TastyPluginFactory;
33
- //#endregion
34
- export { okhslFunc, okhslPlugin };
35
- //# sourceMappingURL=okhsl-plugin.d.ts.map
@@ -1,97 +0,0 @@
1
- import { Lru } from "../parser/lru.js";
2
- import { okhslToSrgb } from "../utils/color-math.js";
3
- //#region src/plugins/okhsl-plugin.ts
4
- /**
5
- * OKHSL Plugin for Tasty
6
- *
7
- * Converts OKHSL color syntax to RGB notation.
8
- * Supports angle units: deg, turn, rad, or unitless (degrees).
9
- *
10
- * Examples:
11
- * okhsl(240.5 50% 50%)
12
- * okhsl(240.5deg 50% 50%)
13
- * okhsl(0.25turn 50% 50%)
14
- * okhsl(1.57rad 50% 50%)
15
- */
16
- const conversionCache = new Lru(500);
17
- const clamp = (value, min, max) => Math.max(Math.min(value, max), min);
18
- /**
19
- * Parse an angle value with optional unit.
20
- * Supports: deg, turn, rad, or unitless (treated as degrees).
21
- */
22
- const parseAngle = (value) => {
23
- const match = value.match(/^([+-]?\d*\.?\d+)(deg|turn|rad)?$/);
24
- if (!match) return 0;
25
- const num = parseFloat(match[1]);
26
- switch (match[2]) {
27
- case "turn": return num * 360;
28
- case "rad": return num * 180 / Math.PI;
29
- default: return num;
30
- }
31
- };
32
- /**
33
- * Parse a percentage value (e.g., "50%") to a 0-1 range.
34
- */
35
- const parsePercentage = (value) => {
36
- const match = value.match(/^([+-]?\d*\.?\d+)%?$/);
37
- if (!match) return 0;
38
- const num = parseFloat(match[1]);
39
- return value.includes("%") ? num / 100 : num;
40
- };
41
- /**
42
- * The okhsl function handler for tasty parser.
43
- * Receives parsed style groups and returns an RGB color string.
44
- */
45
- const okhslFunc = (groups) => {
46
- if (groups.length === 0 || groups[0].all.length < 3) {
47
- console.warn("[okhsl] Expected 3 values (H S L), got:", groups);
48
- return "rgb(0% 0% 0%)";
49
- }
50
- const group = groups[0];
51
- const tokens = group.all;
52
- const alpha = group.parts.length > 1 && group.parts[1].all.length > 0 ? group.parts[1].output : void 0;
53
- const cacheKey = tokens.slice(0, 3).join(" ") + (alpha ? ` / ${alpha}` : "");
54
- const cached = conversionCache.get(cacheKey);
55
- if (cached) return cached;
56
- const h = parseAngle(tokens[0]);
57
- const s = parsePercentage(tokens[1]);
58
- const l = parsePercentage(tokens[2]);
59
- const [r, g, b] = okhslToSrgb(h, clamp(s, 0, 1), clamp(l, 0, 1));
60
- const format = (n) => {
61
- const pct = n * 100;
62
- return parseFloat(pct.toFixed(1)).toString() + "%";
63
- };
64
- const result = alpha ? `rgb(${format(r)} ${format(g)} ${format(b)} / ${alpha})` : `rgb(${format(r)} ${format(g)} ${format(b)})`;
65
- conversionCache.set(cacheKey, result);
66
- return result;
67
- };
68
- /**
69
- * OKHSL Plugin for Tasty.
70
- *
71
- * Adds support for the `okhsl()` color function in tasty styles.
72
- *
73
- * @example
74
- * ```ts
75
- * import { configure } from '@tenphi/tasty';
76
- * import { okhslPlugin } from '@tenphi/tasty';
77
- *
78
- * configure({
79
- * plugins: [okhslPlugin()],
80
- * });
81
- *
82
- * // Now you can use okhsl in styles:
83
- * const Box = tasty({
84
- * styles: {
85
- * fill: 'okhsl(240 50% 50%)',
86
- * },
87
- * });
88
- * ```
89
- */
90
- const okhslPlugin = () => ({
91
- name: "okhsl",
92
- funcs: { okhsl: okhslFunc }
93
- });
94
- //#endregion
95
- export { okhslFunc, okhslPlugin };
96
-
97
- //# sourceMappingURL=okhsl-plugin.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"okhsl-plugin.js","names":[],"sources":["../../src/plugins/okhsl-plugin.ts"],"sourcesContent":["/**\n * OKHSL Plugin for Tasty\n *\n * Converts OKHSL color syntax to RGB notation.\n * Supports angle units: deg, turn, rad, or unitless (degrees).\n *\n * Examples:\n * okhsl(240.5 50% 50%)\n * okhsl(240.5deg 50% 50%)\n * okhsl(0.25turn 50% 50%)\n * okhsl(1.57rad 50% 50%)\n */\n\nimport { Lru } from '../parser/lru';\nimport { okhslToSrgb } from '../utils/color-math';\n\nimport type { StyleDetails } from '../parser/types';\nimport type { TastyPlugin, TastyPluginFactory } from './types';\n\nconst conversionCache = new Lru<string, string>(500);\n\nconst clamp = (value: number, min: number, max: number): number =>\n Math.max(Math.min(value, max), min);\n\n/**\n * Parse an angle value with optional unit.\n * Supports: deg, turn, rad, or unitless (treated as degrees).\n */\nconst parseAngle = (value: string): number => {\n const match = value.match(/^([+-]?\\d*\\.?\\d+)(deg|turn|rad)?$/);\n if (!match) return 0;\n\n const num = parseFloat(match[1]);\n const unit = match[2];\n\n switch (unit) {\n case 'turn':\n return num * 360;\n case 'rad':\n return (num * 180) / Math.PI;\n case 'deg':\n default:\n return num;\n }\n};\n\n/**\n * Parse a percentage value (e.g., \"50%\") to a 0-1 range.\n */\nconst parsePercentage = (value: string): number => {\n const match = value.match(/^([+-]?\\d*\\.?\\d+)%?$/);\n if (!match) return 0;\n\n const num = parseFloat(match[1]);\n return value.includes('%') ? num / 100 : num;\n};\n\n/**\n * The okhsl function handler for tasty parser.\n * Receives parsed style groups and returns an RGB color string.\n */\nconst okhslFunc = (groups: StyleDetails[]): string => {\n if (groups.length === 0 || groups[0].all.length < 3) {\n console.warn('[okhsl] Expected 3 values (H S L), got:', groups);\n return 'rgb(0% 0% 0%)';\n }\n\n const group = groups[0];\n const tokens = group.all;\n\n const alpha =\n group.parts.length > 1 && group.parts[1].all.length > 0\n ? group.parts[1].output\n : undefined;\n\n const cacheKey = tokens.slice(0, 3).join(' ') + (alpha ? ` / ${alpha}` : '');\n const cached = conversionCache.get(cacheKey);\n if (cached) return cached;\n\n const h = parseAngle(tokens[0]);\n const s = parsePercentage(tokens[1]);\n const l = parsePercentage(tokens[2]);\n\n const [r, g, b] = okhslToSrgb(h, clamp(s, 0, 1), clamp(l, 0, 1));\n\n const format = (n: number): string => {\n const pct = n * 100;\n return parseFloat(pct.toFixed(1)).toString() + '%';\n };\n\n const result = alpha\n ? `rgb(${format(r)} ${format(g)} ${format(b)} / ${alpha})`\n : `rgb(${format(r)} ${format(g)} ${format(b)})`;\n\n conversionCache.set(cacheKey, result);\n return result;\n};\n\n/**\n * OKHSL Plugin for Tasty.\n *\n * Adds support for the `okhsl()` color function in tasty styles.\n *\n * @example\n * ```ts\n * import { configure } from '@tenphi/tasty';\n * import { okhslPlugin } from '@tenphi/tasty';\n *\n * configure({\n * plugins: [okhslPlugin()],\n * });\n *\n * // Now you can use okhsl in styles:\n * const Box = tasty({\n * styles: {\n * fill: 'okhsl(240 50% 50%)',\n * },\n * });\n * ```\n */\nexport const okhslPlugin: TastyPluginFactory = (): TastyPlugin => ({\n name: 'okhsl',\n funcs: {\n okhsl: okhslFunc,\n },\n});\n\nexport { okhslFunc };\n"],"mappings":";;;;;;;;;;;;;;;AAmBA,MAAM,kBAAkB,IAAI,IAAoB,IAAI;AAEpD,MAAM,SAAS,OAAe,KAAa,QACzC,KAAK,IAAI,KAAK,IAAI,OAAO,IAAI,EAAE,IAAI;;;;;AAMrC,MAAM,cAAc,UAA0B;CAC5C,MAAM,QAAQ,MAAM,MAAM,oCAAoC;AAC9D,KAAI,CAAC,MAAO,QAAO;CAEnB,MAAM,MAAM,WAAW,MAAM,GAAG;AAGhC,SAFa,MAAM,IAEnB;EACE,KAAK,OACH,QAAO,MAAM;EACf,KAAK,MACH,QAAQ,MAAM,MAAO,KAAK;EAE5B,QACE,QAAO;;;;;;AAOb,MAAM,mBAAmB,UAA0B;CACjD,MAAM,QAAQ,MAAM,MAAM,uBAAuB;AACjD,KAAI,CAAC,MAAO,QAAO;CAEnB,MAAM,MAAM,WAAW,MAAM,GAAG;AAChC,QAAO,MAAM,SAAS,IAAI,GAAG,MAAM,MAAM;;;;;;AAO3C,MAAM,aAAa,WAAmC;AACpD,KAAI,OAAO,WAAW,KAAK,OAAO,GAAG,IAAI,SAAS,GAAG;AACnD,UAAQ,KAAK,2CAA2C,OAAO;AAC/D,SAAO;;CAGT,MAAM,QAAQ,OAAO;CACrB,MAAM,SAAS,MAAM;CAErB,MAAM,QACJ,MAAM,MAAM,SAAS,KAAK,MAAM,MAAM,GAAG,IAAI,SAAS,IAClD,MAAM,MAAM,GAAG,SACf,KAAA;CAEN,MAAM,WAAW,OAAO,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,IAAI,QAAQ,MAAM,UAAU;CACzE,MAAM,SAAS,gBAAgB,IAAI,SAAS;AAC5C,KAAI,OAAQ,QAAO;CAEnB,MAAM,IAAI,WAAW,OAAO,GAAG;CAC/B,MAAM,IAAI,gBAAgB,OAAO,GAAG;CACpC,MAAM,IAAI,gBAAgB,OAAO,GAAG;CAEpC,MAAM,CAAC,GAAG,GAAG,KAAK,YAAY,GAAG,MAAM,GAAG,GAAG,EAAE,EAAE,MAAM,GAAG,GAAG,EAAE,CAAC;CAEhE,MAAM,UAAU,MAAsB;EACpC,MAAM,MAAM,IAAI;AAChB,SAAO,WAAW,IAAI,QAAQ,EAAE,CAAC,CAAC,UAAU,GAAG;;CAGjD,MAAM,SAAS,QACX,OAAO,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,KAAK,MAAM,KACtD,OAAO,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC;AAE/C,iBAAgB,IAAI,UAAU,OAAO;AACrC,QAAO;;;;;;;;;;;;;;;;;;;;;;;;AAyBT,MAAa,qBAAsD;CACjE,MAAM;CACN,OAAO,EACL,OAAO,WACR;CACF"}
@@ -1,87 +0,0 @@
1
- import { StyleDetails, UnitHandler } from "../parser/types.js";
2
- import { StyleHandlerDefinition } from "../utils/styles.js";
3
- import { ConfigTokens, RecipeStyles, Styles } from "../styles/types.js";
4
- import { TypographyPreset } from "../utils/typography.js";
5
-
6
- //#region src/plugins/types.d.ts
7
- /**
8
- * A tasty plugin that extends the style system with custom functions, units, states, or handlers.
9
- */
10
- interface TastyPlugin {
11
- /** Unique name for the plugin (used for debugging and conflict detection) */
12
- name: string;
13
- /** Custom functions that transform parsed style groups into CSS values */
14
- funcs?: Record<string, (groups: StyleDetails[]) => string>;
15
- /** Custom units that transform numeric values (e.g., `2x` → `calc(2 * var(--gap))`) */
16
- units?: Record<string, string | UnitHandler>;
17
- /** Custom state aliases (e.g., `'@mobile': '@media(w < 768px)'`) */
18
- states?: Record<string, string>;
19
- /**
20
- * Custom style handlers that transform style properties into CSS declarations.
21
- * Handlers replace built-in handlers for the same style name.
22
- * @example
23
- * ```ts
24
- * handlers: {
25
- * // Simple handler - lookup style inferred from key
26
- * fill: ({ fill }) => fill ? { 'background-color': fill } : undefined,
27
- * // Multi-property handler
28
- * spacing: [['gap', 'padding'], ({ gap, padding }) => ({ ... })],
29
- * }
30
- * ```
31
- */
32
- handlers?: Record<string, StyleHandlerDefinition>;
33
- /**
34
- * Design tokens injected as CSS custom properties on `:root`.
35
- * Values are parsed through the Tasty DSL. Supports state maps.
36
- * - `$name` → `--name` CSS custom property
37
- * - `#name` → `--name-color` and `--name-color-{colorSpace}`
38
- */
39
- tokens?: ConfigTokens;
40
- /** Predefined tokens replaced during style parsing (`$name` or `#name`) */
41
- replaceTokens?: Record<`$${string}` | `#${string}`, string | number>;
42
- /**
43
- * Predefined style recipes -- named style bundles that can be applied via `recipe` style property.
44
- * Recipe values are flat tasty styles (no sub-element keys).
45
- * @example
46
- * ```ts
47
- * recipes: {
48
- * card: { padding: '4x', fill: '#surface', radius: '1r', border: true },
49
- * elevated: { shadow: '2x 2x 4x #shadow' },
50
- * }
51
- * ```
52
- */
53
- recipes?: Record<string, RecipeStyles>;
54
- /**
55
- * Typography presets — shorthand for `generateTypographyTokens()`.
56
- * Generated tokens are merged under explicit `tokens` (tokens win on conflict).
57
- */
58
- presets?: Record<string, TypographyPreset>;
59
- /**
60
- * Global Tasty styles keyed by CSS selector.
61
- * Supports the full Tasty style syntax.
62
- */
63
- globalStyles?: Record<string, Styles>;
64
- }
65
- /**
66
- * A factory function that creates a TastyPlugin.
67
- * Can optionally accept configuration options.
68
- *
69
- * @example
70
- * ```ts
71
- * // Plugin without options
72
- * const okhslPlugin: TastyPluginFactory = () => ({
73
- * name: 'okhsl',
74
- * funcs: { okhsl: okhslFunc },
75
- * });
76
- *
77
- * // Plugin with options
78
- * const debugPlugin: TastyPluginFactory<{ verbose: boolean }> = (options) => ({
79
- * name: 'debug',
80
- * funcs: { debug: createDebugFunc(options.verbose) },
81
- * });
82
- * ```
83
- */
84
- type TastyPluginFactory<TOptions = void> = TOptions extends void ? () => TastyPlugin : (options: TOptions) => TastyPlugin;
85
- //#endregion
86
- export { TastyPlugin, TastyPluginFactory };
87
- //# sourceMappingURL=types.d.ts.map
@@ -1,222 +0,0 @@
1
- import { RE_NUMBER, RE_RAW_UNIT } from "../parser/const.js";
2
- import "../utils/color-space.js";
3
- //#region src/properties/index.ts
4
- const PROPERTIES_KEY = "@properties";
5
- /**
6
- * Valid CSS custom property name pattern (after the -- prefix).
7
- * Must start with a letter or underscore, followed by letters, digits, hyphens, or underscores.
8
- */
9
- const VALID_PROPERTY_NAME_PATTERN = /^[a-z_][a-z0-9-_]*$/i;
10
- /**
11
- * Validate a CSS custom property name (the part after --).
12
- * Returns true if the name is valid for use as a CSS custom property.
13
- */
14
- function isValidPropertyName(name) {
15
- return VALID_PROPERTY_NAME_PATTERN.test(name);
16
- }
17
- /**
18
- * Check if styles object has local @properties definition.
19
- * Fast path: single property lookup.
20
- */
21
- function hasLocalProperties(styles) {
22
- return PROPERTIES_KEY in styles;
23
- }
24
- /**
25
- * Extract local @properties from styles object.
26
- * Returns null if no local properties (fast path).
27
- */
28
- function extractLocalProperties(styles) {
29
- const properties = styles[PROPERTIES_KEY];
30
- if (!properties || typeof properties !== "object") return null;
31
- return properties;
32
- }
33
- /**
34
- * Parse a property token name and return the CSS property name and whether it's a color.
35
- * Supports tasty token syntax and validates the property name.
36
- *
37
- * Token formats:
38
- * - `$name` → { cssName: '--name', isColor: false }
39
- * - `#name` → { cssName: '--name-color', isColor: true }
40
- * - `--name` → { cssName: '--name', isColor: false } (legacy, auto-detect color by suffix)
41
- * - `name` → { cssName: '--name', isColor: false } (legacy)
42
- *
43
- * @param token - The property token to parse
44
- * @returns Parsed result with cssName, isColor, isValid, and optional error
45
- */
46
- function parsePropertyToken(token) {
47
- if (!token || typeof token !== "string") return {
48
- cssName: "",
49
- isColor: false,
50
- isValid: false,
51
- error: "Property token must be a non-empty string"
52
- };
53
- let name;
54
- let isColor;
55
- if (token.startsWith("$")) {
56
- name = token.slice(1);
57
- isColor = false;
58
- } else if (token.startsWith("#")) {
59
- name = token.slice(1);
60
- isColor = true;
61
- } else if (token.startsWith("--")) {
62
- name = token.slice(2);
63
- isColor = token.endsWith("-color");
64
- } else {
65
- name = token;
66
- isColor = token.endsWith("-color");
67
- }
68
- if (!name) return {
69
- cssName: "",
70
- isColor,
71
- isValid: false,
72
- error: "Property name cannot be empty"
73
- };
74
- if (!isValidPropertyName(name)) return {
75
- cssName: "",
76
- isColor,
77
- isValid: false,
78
- error: `Invalid property name "${name}". Must start with a letter or underscore, followed by letters, digits, hyphens, or underscores.`
79
- };
80
- let cssName;
81
- if (token.startsWith("#")) cssName = `--${name}-color`;
82
- else cssName = `--${name}`;
83
- return {
84
- cssName,
85
- isColor,
86
- isValid: true
87
- };
88
- }
89
- /**
90
- * Normalize a property definition to a consistent string representation.
91
- * Used for comparing definitions to detect type conflicts.
92
- *
93
- * Only `syntax` and `inherits` are compared — `initialValue` is intentionally
94
- * excluded because different components may set different defaults for the
95
- * same typed property (e.g. auto-inferred `0px` vs explicit `6px`).
96
- *
97
- * Keys are sorted alphabetically to ensure consistent comparison:
98
- * { inherits: true, syntax: '<color>' } === { syntax: '<color>', inherits: true }
99
- */
100
- function normalizePropertyDefinition(def) {
101
- const normalized = {};
102
- if (def.inherits !== void 0) normalized.inherits = def.inherits;
103
- if (def.syntax !== void 0) normalized.syntax = def.syntax;
104
- return JSON.stringify(normalized);
105
- }
106
- /**
107
- * Get the effective property definition for a token.
108
- * For color tokens (#name), auto-sets syntax to '<color>' and defaults initialValue to 'transparent'.
109
- *
110
- * @param token - Property token ($name, #name, --name, or plain name)
111
- * @param userDefinition - User-provided definition options
112
- * @returns Effective definition with cssName, definition, isValid, and optional error
113
- */
114
- function getEffectiveDefinition(token, userDefinition) {
115
- const parsed = parsePropertyToken(token);
116
- if (!parsed.isValid) return {
117
- cssName: "",
118
- definition: userDefinition,
119
- isColor: false,
120
- isValid: false,
121
- error: parsed.error
122
- };
123
- if (parsed.isColor) return {
124
- cssName: parsed.cssName,
125
- definition: {
126
- syntax: "<color>",
127
- inherits: userDefinition.inherits,
128
- initialValue: userDefinition.initialValue ?? "transparent"
129
- },
130
- isColor: true,
131
- isValid: true
132
- };
133
- return {
134
- cssName: parsed.cssName,
135
- definition: userDefinition,
136
- isColor: false,
137
- isValid: true
138
- };
139
- }
140
- const UNIT_TO_SYNTAX = {};
141
- const LENGTH_UNITS = [
142
- "px",
143
- "em",
144
- "rem",
145
- "vw",
146
- "vh",
147
- "vmin",
148
- "vmax",
149
- "ch",
150
- "ex",
151
- "cap",
152
- "ic",
153
- "lh",
154
- "rlh",
155
- "svw",
156
- "svh",
157
- "lvw",
158
- "lvh",
159
- "dvw",
160
- "dvh",
161
- "cqw",
162
- "cqh",
163
- "cqi",
164
- "cqb",
165
- "cqmin",
166
- "cqmax"
167
- ];
168
- const ANGLE_UNITS = [
169
- "deg",
170
- "rad",
171
- "grad",
172
- "turn"
173
- ];
174
- const TIME_UNITS = ["ms", "s"];
175
- for (const u of LENGTH_UNITS) UNIT_TO_SYNTAX[u] = {
176
- syntax: "<length-percentage>",
177
- initialValue: "0px"
178
- };
179
- UNIT_TO_SYNTAX["%"] = {
180
- syntax: "<length-percentage>",
181
- initialValue: "0px"
182
- };
183
- for (const u of ANGLE_UNITS) UNIT_TO_SYNTAX[u] = {
184
- syntax: "<angle>",
185
- initialValue: "0deg"
186
- };
187
- for (const u of TIME_UNITS) UNIT_TO_SYNTAX[u] = {
188
- syntax: "<time>",
189
- initialValue: "0s"
190
- };
191
- /**
192
- * Infer CSS @property syntax from a concrete value.
193
- * Detects numeric types: \<number\>, \<length-percentage\>, \<angle\>, \<time\>.
194
- * Length and percentage values both map to \<length-percentage\> for maximum flexibility.
195
- * Color properties are handled separately via the #name token convention
196
- * (--name-color gets \<color\> syntax automatically in getEffectiveDefinition).
197
- *
198
- * @param value - The CSS value to infer from (e.g. '10px', '1', '45deg')
199
- * @returns Inferred syntax and initial value, or null if not inferable
200
- */
201
- function inferSyntaxFromValue(value) {
202
- if (!value || typeof value !== "string") return null;
203
- const trimmed = value.trim();
204
- if (!trimmed) return null;
205
- if (RE_NUMBER.test(trimmed)) {
206
- if (parseFloat(trimmed) === 0) return null;
207
- return {
208
- syntax: "<number>",
209
- initialValue: "0"
210
- };
211
- }
212
- const unitMatch = trimmed.match(RE_RAW_UNIT);
213
- if (unitMatch) {
214
- const mapping = UNIT_TO_SYNTAX[unitMatch[2]];
215
- if (mapping) return mapping;
216
- }
217
- return null;
218
- }
219
- //#endregion
220
- export { extractLocalProperties, getEffectiveDefinition, hasLocalProperties, inferSyntaxFromValue, normalizePropertyDefinition, parsePropertyToken };
221
-
222
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/properties/index.ts"],"sourcesContent":["/**\n * Properties Utilities\n *\n * Utilities for extracting and processing CSS @property definitions in styles.\n * Unlike keyframes, properties are permanent once registered and don't need cleanup.\n *\n * Property names use tasty token syntax:\n * - `$name` for regular properties → `--name`\n * - `#name` for color properties → `--name-color` (auto-sets syntax: '<color>')\n */\n\nimport type { PropertyDefinition } from '../injector/types';\nimport { RE_NUMBER, RE_RAW_UNIT } from '../parser/const';\nimport type { Styles } from '../styles/types';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst PROPERTIES_KEY = '@properties';\n\n/**\n * Valid CSS custom property name pattern (after the -- prefix).\n * Must start with a letter or underscore, followed by letters, digits, hyphens, or underscores.\n */\nconst VALID_PROPERTY_NAME_PATTERN = /^[a-z_][a-z0-9-_]*$/i;\n\n// ============================================================================\n// Validation Functions\n// ============================================================================\n\n/**\n * Validate a CSS custom property name (the part after --).\n * Returns true if the name is valid for use as a CSS custom property.\n */\nexport function isValidPropertyName(name: string): boolean {\n return VALID_PROPERTY_NAME_PATTERN.test(name);\n}\n\n/**\n * Result of parsing a property token.\n */\nexport interface ParsedPropertyToken {\n /** The CSS custom property name (e.g., '--my-prop') */\n cssName: string;\n /** Whether this is a color property */\n isColor: boolean;\n /** Whether the token was valid */\n isValid: boolean;\n /** Error message if invalid */\n error?: string;\n}\n\n// ============================================================================\n// Extraction Functions\n// ============================================================================\n\n/**\n * Check if styles object has local @properties definition.\n * Fast path: single property lookup.\n */\nexport function hasLocalProperties(styles: Styles): boolean {\n return PROPERTIES_KEY in styles;\n}\n\n/**\n * Extract local @properties from styles object.\n * Returns null if no local properties (fast path).\n */\nexport function extractLocalProperties(\n styles: Styles,\n): Record<string, PropertyDefinition> | null {\n const properties = styles[PROPERTIES_KEY];\n if (!properties || typeof properties !== 'object') {\n return null;\n }\n return properties as Record<string, PropertyDefinition>;\n}\n\n// ============================================================================\n// Token Parsing Functions\n// ============================================================================\n\n/**\n * Parse a property token name and return the CSS property name and whether it's a color.\n * Supports tasty token syntax and validates the property name.\n *\n * Token formats:\n * - `$name` → { cssName: '--name', isColor: false }\n * - `#name` → { cssName: '--name-color', isColor: true }\n * - `--name` → { cssName: '--name', isColor: false } (legacy, auto-detect color by suffix)\n * - `name` → { cssName: '--name', isColor: false } (legacy)\n *\n * @param token - The property token to parse\n * @returns Parsed result with cssName, isColor, isValid, and optional error\n */\nexport function parsePropertyToken(token: string): ParsedPropertyToken {\n if (!token || typeof token !== 'string') {\n return {\n cssName: '',\n isColor: false,\n isValid: false,\n error: 'Property token must be a non-empty string',\n };\n }\n\n let name: string;\n let isColor: boolean;\n\n if (token.startsWith('$')) {\n // Regular property token: $name → --name\n name = token.slice(1);\n isColor = false;\n } else if (token.startsWith('#')) {\n // Color property token: #name → --name-color\n name = token.slice(1);\n isColor = true;\n } else if (token.startsWith('--')) {\n // Legacy format with -- prefix\n name = token.slice(2);\n isColor = token.endsWith('-color');\n } else {\n // Legacy format without prefix\n name = token;\n isColor = token.endsWith('-color');\n }\n\n // Validate the name\n if (!name) {\n return {\n cssName: '',\n isColor,\n isValid: false,\n error: 'Property name cannot be empty',\n };\n }\n\n if (!isValidPropertyName(name)) {\n return {\n cssName: '',\n isColor,\n isValid: false,\n error: `Invalid property name \"${name}\". Must start with a letter or underscore, followed by letters, digits, hyphens, or underscores.`,\n };\n }\n\n // Build the CSS custom property name\n // For #name tokens, we add -color suffix\n // For legacy formats (--name-color or name-color), the name already includes -color\n let cssName: string;\n if (token.startsWith('#')) {\n // Color token: #name → --name-color\n cssName = `--${name}-color`;\n } else {\n // All other formats: just add -- prefix\n cssName = `--${name}`;\n }\n\n return {\n cssName,\n isColor,\n isValid: true,\n };\n}\n\n// ============================================================================\n// Normalization Functions\n// ============================================================================\n\n/**\n * Normalize a property name to the CSS custom property format.\n *\n * @deprecated Use parsePropertyToken instead for proper token handling\n */\nexport function normalizePropertyName(name: string): string {\n const result = parsePropertyToken(name);\n return result.isValid ? result.cssName : `--${name}`;\n}\n\n/**\n * Normalize a property definition to a consistent string representation.\n * Used for comparing definitions to detect type conflicts.\n *\n * Only `syntax` and `inherits` are compared — `initialValue` is intentionally\n * excluded because different components may set different defaults for the\n * same typed property (e.g. auto-inferred `0px` vs explicit `6px`).\n *\n * Keys are sorted alphabetically to ensure consistent comparison:\n * { inherits: true, syntax: '<color>' } === { syntax: '<color>', inherits: true }\n */\nexport function normalizePropertyDefinition(def: PropertyDefinition): string {\n const normalized: Record<string, unknown> = {};\n\n if (def.inherits !== undefined) {\n normalized.inherits = def.inherits;\n }\n if (def.syntax !== undefined) {\n normalized.syntax = def.syntax;\n }\n\n return JSON.stringify(normalized);\n}\n\n/**\n * Result of getEffectiveDefinition.\n */\nexport interface EffectiveDefinitionResult {\n /** The CSS custom property name */\n cssName: string;\n /** The effective property definition */\n definition: PropertyDefinition;\n /** Whether this is a color property */\n isColor: boolean;\n /** Whether the token was valid */\n isValid: boolean;\n /** Error message if invalid */\n error?: string;\n}\n\n/**\n * Get the effective property definition for a token.\n * For color tokens (#name), auto-sets syntax to '<color>' and defaults initialValue to 'transparent'.\n *\n * @param token - Property token ($name, #name, --name, or plain name)\n * @param userDefinition - User-provided definition options\n * @returns Effective definition with cssName, definition, isValid, and optional error\n */\nexport function getEffectiveDefinition(\n token: string,\n userDefinition: PropertyDefinition,\n): EffectiveDefinitionResult {\n const parsed = parsePropertyToken(token);\n\n if (!parsed.isValid) {\n return {\n cssName: '',\n definition: userDefinition,\n isColor: false,\n isValid: false,\n error: parsed.error,\n };\n }\n\n if (parsed.isColor) {\n // Color properties have fixed syntax and default initialValue\n return {\n cssName: parsed.cssName,\n definition: {\n syntax: '<color>', // Always '<color>' for color tokens, cannot be overridden\n inherits: userDefinition.inherits, // Allow inherits to be customized\n initialValue: userDefinition.initialValue ?? 'transparent', // Default to transparent\n },\n isColor: true,\n isValid: true,\n };\n }\n\n // Regular properties use the definition as-is\n return {\n cssName: parsed.cssName,\n definition: userDefinition,\n isColor: false,\n isValid: true,\n };\n}\n\n// ============================================================================\n// Color Utilities\n// ============================================================================\n\nexport { colorInitialValueToComponents } from '../utils/color-space';\n\n// ============================================================================\n// Value Type Inference\n// ============================================================================\n\n/**\n * Result of inferring a CSS @property syntax from a value.\n */\nexport interface InferredSyntax {\n syntax: string;\n initialValue: string;\n}\n\nconst UNIT_TO_SYNTAX: Record<string, InferredSyntax> = {};\n\nconst LENGTH_UNITS = [\n 'px',\n 'em',\n 'rem',\n 'vw',\n 'vh',\n 'vmin',\n 'vmax',\n 'ch',\n 'ex',\n 'cap',\n 'ic',\n 'lh',\n 'rlh',\n 'svw',\n 'svh',\n 'lvw',\n 'lvh',\n 'dvw',\n 'dvh',\n 'cqw',\n 'cqh',\n 'cqi',\n 'cqb',\n 'cqmin',\n 'cqmax',\n];\n\nconst ANGLE_UNITS = ['deg', 'rad', 'grad', 'turn'];\nconst TIME_UNITS = ['ms', 's'];\n\nfor (const u of LENGTH_UNITS) {\n UNIT_TO_SYNTAX[u] = { syntax: '<length-percentage>', initialValue: '0px' };\n}\nUNIT_TO_SYNTAX['%'] = { syntax: '<length-percentage>', initialValue: '0px' };\nfor (const u of ANGLE_UNITS) {\n UNIT_TO_SYNTAX[u] = { syntax: '<angle>', initialValue: '0deg' };\n}\nfor (const u of TIME_UNITS) {\n UNIT_TO_SYNTAX[u] = { syntax: '<time>', initialValue: '0s' };\n}\n\n/**\n * Infer CSS @property syntax from a concrete value.\n * Detects numeric types: \\<number\\>, \\<length-percentage\\>, \\<angle\\>, \\<time\\>.\n * Length and percentage values both map to \\<length-percentage\\> for maximum flexibility.\n * Color properties are handled separately via the #name token convention\n * (--name-color gets \\<color\\> syntax automatically in getEffectiveDefinition).\n *\n * @param value - The CSS value to infer from (e.g. '10px', '1', '45deg')\n * @returns Inferred syntax and initial value, or null if not inferable\n */\nexport function inferSyntaxFromValue(value: string): InferredSyntax | null {\n if (!value || typeof value !== 'string') return null;\n\n const trimmed = value.trim();\n if (!trimmed) return null;\n\n if (RE_NUMBER.test(trimmed)) {\n // Bare zero is ambiguous (could be <length>, <angle>, <percentage>, etc.)\n if (parseFloat(trimmed) === 0) return null;\n return { syntax: '<number>', initialValue: '0' };\n }\n\n const unitMatch = trimmed.match(RE_RAW_UNIT);\n if (unitMatch) {\n const unit = unitMatch[2];\n const mapping = UNIT_TO_SYNTAX[unit];\n if (mapping) return mapping;\n }\n\n return null;\n}\n"],"mappings":"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"}