@usefragments/core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/LICENSE +21 -0
  2. package/dist/chunk-243QYRUF.js +2145 -0
  3. package/dist/chunk-243QYRUF.js.map +1 -0
  4. package/dist/chunk-3LLRNCPX.js +111 -0
  5. package/dist/chunk-3LLRNCPX.js.map +1 -0
  6. package/dist/chunk-EIYNNS77.js +48 -0
  7. package/dist/chunk-EIYNNS77.js.map +1 -0
  8. package/dist/chunk-JNBFJ34I.js +326 -0
  9. package/dist/chunk-JNBFJ34I.js.map +1 -0
  10. package/dist/chunk-R3X7UBHU.js +228 -0
  11. package/dist/chunk-R3X7UBHU.js.map +1 -0
  12. package/dist/chunk-SH4KPIYH.js +393 -0
  13. package/dist/chunk-SH4KPIYH.js.map +1 -0
  14. package/dist/chunk-V4VQB57N.js +56 -0
  15. package/dist/chunk-V4VQB57N.js.map +1 -0
  16. package/dist/chunk-X34IA4LR.js +319 -0
  17. package/dist/chunk-X34IA4LR.js.map +1 -0
  18. package/dist/chunk-YF65VYRY.js +493 -0
  19. package/dist/chunk-YF65VYRY.js.map +1 -0
  20. package/dist/codes/index.d.ts +40 -0
  21. package/dist/codes/index.js +23 -0
  22. package/dist/codes/index.js.map +1 -0
  23. package/dist/compiled-types/index.d.ts +5 -0
  24. package/dist/compiled-types/index.js +1 -0
  25. package/dist/compiled-types/index.js.map +1 -0
  26. package/dist/generate/index.d.ts +47 -0
  27. package/dist/generate/index.js +11 -0
  28. package/dist/generate/index.js.map +1 -0
  29. package/dist/governance-BOa3KyiJ.d.ts +3279 -0
  30. package/dist/graph/index.d.ts +66 -0
  31. package/dist/graph/index.js +17 -0
  32. package/dist/graph/index.js.map +1 -0
  33. package/dist/index.d.ts +11388 -0
  34. package/dist/index.js +8711 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/package-identity.d.ts +42 -0
  37. package/dist/package-identity.js +23 -0
  38. package/dist/package-identity.js.map +1 -0
  39. package/dist/preview/index.d.ts +484 -0
  40. package/dist/preview/index.js +1129 -0
  41. package/dist/preview/index.js.map +1 -0
  42. package/dist/react-types.d.ts +22 -0
  43. package/dist/react-types.js +1 -0
  44. package/dist/react-types.js.map +1 -0
  45. package/dist/registry.d.ts +2367 -0
  46. package/dist/registry.js +62 -0
  47. package/dist/registry.js.map +1 -0
  48. package/dist/schemas/index.d.ts +2584 -0
  49. package/dist/schemas/index.js +55 -0
  50. package/dist/schemas/index.js.map +1 -0
  51. package/dist/severity.d.ts +18 -0
  52. package/dist/severity.js +27 -0
  53. package/dist/severity.js.map +1 -0
  54. package/dist/test-utils.d.ts +14 -0
  55. package/dist/test-utils.js +63 -0
  56. package/dist/test-utils.js.map +1 -0
  57. package/dist/topology/index.d.ts +48 -0
  58. package/dist/topology/index.js +7 -0
  59. package/dist/topology/index.js.map +1 -0
  60. package/dist/types-xJ2xyp_G.d.ts +119 -0
  61. package/package.json +120 -0
  62. package/src/__tests__/contract-parser.test.ts +614 -0
  63. package/src/__tests__/dtcg-generators.test.ts +439 -0
  64. package/src/__tests__/dtcg-parser.test.ts +884 -0
  65. package/src/__tests__/preview-runtime.test.tsx +111 -0
  66. package/src/__tests__/schema.test.ts +31 -0
  67. package/src/__tests__/severity.test.ts +34 -0
  68. package/src/__tests__/style-comparison.test.ts +662 -0
  69. package/src/__tests__/tokens-compat.test.ts +51 -0
  70. package/src/agent-format.test.ts +105 -0
  71. package/src/agent-format.ts +141 -0
  72. package/src/bundle.ts +149 -0
  73. package/src/codes/__tests__/codes.test.ts +73 -0
  74. package/src/codes/codes.ts +463 -0
  75. package/src/codes/index.ts +7 -0
  76. package/src/codes/source-finding-bridge.ts +113 -0
  77. package/src/compiled-types/index.ts +293 -0
  78. package/src/component-contract.ts +231 -0
  79. package/src/component-discovery.ts +80 -0
  80. package/src/composition.test.ts +262 -0
  81. package/src/composition.ts +318 -0
  82. package/src/config-paths.test.ts +78 -0
  83. package/src/config-paths.ts +47 -0
  84. package/src/config.ts +18 -0
  85. package/src/conform-prove.test.ts +274 -0
  86. package/src/conform-prove.ts +326 -0
  87. package/src/conform.ts +160 -0
  88. package/src/constants.ts +114 -0
  89. package/src/context.ts +2 -0
  90. package/src/contract/fixture.ts +110 -0
  91. package/src/contract/hash.test.ts +86 -0
  92. package/src/contract/hash.ts +195 -0
  93. package/src/contract/index.ts +43 -0
  94. package/src/contract/preimage.test.ts +254 -0
  95. package/src/contract/preimage.ts +365 -0
  96. package/src/contract/stamp.test.ts +100 -0
  97. package/src/contract/stamp.ts +105 -0
  98. package/src/contract-parser.ts +141 -0
  99. package/src/defineFragment.test.ts +257 -0
  100. package/src/defineFragment.ts +293 -0
  101. package/src/dtcg-generators.ts +324 -0
  102. package/src/dtcg-parser.ts +1 -0
  103. package/src/dtcg.ts +237 -0
  104. package/src/facts/__snapshots__/facts.test.ts.snap +124 -0
  105. package/src/facts/builders.ts +835 -0
  106. package/src/facts/compile.ts +422 -0
  107. package/src/facts/fact-index.ts +394 -0
  108. package/src/facts/facts.test.ts +494 -0
  109. package/src/facts/ids.ts +125 -0
  110. package/src/facts/index.ts +119 -0
  111. package/src/facts/types.ts +614 -0
  112. package/src/figma.ts +263 -0
  113. package/src/fragment-types.ts +214 -0
  114. package/src/generate/generate.test.ts +594 -0
  115. package/src/generate/index.ts +438 -0
  116. package/src/governance-integrity.test.ts +186 -0
  117. package/src/governance-integrity.ts +282 -0
  118. package/src/governance.test.ts +300 -0
  119. package/src/governance.ts +605 -0
  120. package/src/governance.type-test.ts +75 -0
  121. package/src/graph/engine.ts +446 -0
  122. package/src/graph/graph.test.ts +746 -0
  123. package/src/graph/index.ts +32 -0
  124. package/src/graph/serialization.ts +55 -0
  125. package/src/graph/types.ts +178 -0
  126. package/src/index.ts +891 -0
  127. package/src/local-canonical.ts +102 -0
  128. package/src/mcp-snapshot.ts +222 -0
  129. package/src/package-identity-match.ts +33 -0
  130. package/src/package-identity.generated.ts +233 -0
  131. package/src/package-identity.test.ts +99 -0
  132. package/src/package-identity.ts +157 -0
  133. package/src/performance-presets.ts +142 -0
  134. package/src/preview/comparison-artifact.test.ts +228 -0
  135. package/src/preview/comparison-artifact.ts +181 -0
  136. package/src/preview/comparison-context.ts +93 -0
  137. package/src/preview/comparison-evidence.ts +112 -0
  138. package/src/preview/comparison-format.ts +21 -0
  139. package/src/preview/comparison-presentation.ts +187 -0
  140. package/src/preview/comparison-references.test.ts +430 -0
  141. package/src/preview/comparison-references.ts +72 -0
  142. package/src/preview/comparison-score.ts +59 -0
  143. package/src/preview/comparison-styles.ts +197 -0
  144. package/src/preview/comparison.test.ts +559 -0
  145. package/src/preview/comparison.ts +488 -0
  146. package/src/preview/index.ts +10 -0
  147. package/src/preview/types.ts +309 -0
  148. package/src/preview/validation.test.ts +290 -0
  149. package/src/preview/validation.ts +356 -0
  150. package/src/preview-runtime.tsx +144 -0
  151. package/src/raw-html-canonical.test.ts +41 -0
  152. package/src/raw-html-canonical.ts +179 -0
  153. package/src/react-types.ts +21 -0
  154. package/src/registry-install-plan.ts +344 -0
  155. package/src/registry.test.ts +245 -0
  156. package/src/registry.ts +369 -0
  157. package/src/rules/a11y-required-accessible-name.ts +93 -0
  158. package/src/rules/color-math.test.ts +72 -0
  159. package/src/rules/color-math.ts +192 -0
  160. package/src/rules/components-forbidden-prop-value.ts +108 -0
  161. package/src/rules/components-prefer-library.ts +943 -0
  162. package/src/rules/components-unknown-prop.ts +94 -0
  163. package/src/rules/composition-pattern-schema.ts +125 -0
  164. package/src/rules/composition-pattern.test.ts +320 -0
  165. package/src/rules/composition-pattern.ts +271 -0
  166. package/src/rules/emit-gate.test.ts +42 -0
  167. package/src/rules/emit-gate.ts +60 -0
  168. package/src/rules/finding.ts +54 -0
  169. package/src/rules/index.ts +235 -0
  170. package/src/rules/jsx-preferred-component.ts +68 -0
  171. package/src/rules/jsx-preferred-import-path.ts +67 -0
  172. package/src/rules/presets.ts +57 -0
  173. package/src/rules/props-invalid-value.ts +115 -0
  174. package/src/rules/rules.test.ts +3636 -0
  175. package/src/rules/spacing-resolution.ts +184 -0
  176. package/src/rules/styles-no-raw-color.ts +329 -0
  177. package/src/rules/styles-no-raw-dimensions.ts +357 -0
  178. package/src/rules/styles-no-raw-spacing.ts +236 -0
  179. package/src/rules/styles-no-raw-typography.ts +237 -0
  180. package/src/rules/tailwind-arbitrary-color.test.ts +128 -0
  181. package/src/rules/tailwind-arbitrary-color.ts +93 -0
  182. package/src/rules/tailwind-arbitrary-spacing.test.ts +157 -0
  183. package/src/rules/tailwind-arbitrary-spacing.ts +120 -0
  184. package/src/rules/tailwind-forbidden-palette.test.ts +116 -0
  185. package/src/rules/tailwind-forbidden-palette.ts +107 -0
  186. package/src/rules/tailwind-off-scale-spacing-token.test.ts +100 -0
  187. package/src/rules/tailwind-off-scale-spacing-token.ts +108 -0
  188. package/src/rules/tailwind-raw-color-via-token.test.ts +108 -0
  189. package/src/rules/tailwind-raw-color-via-token.ts +82 -0
  190. package/src/rules/tailwind-rule-utils.ts +106 -0
  191. package/src/rules/tailwind-unknown-class.test.ts +157 -0
  192. package/src/rules/tailwind-unknown-class.ts +121 -0
  193. package/src/rules/taxonomy.test.ts +238 -0
  194. package/src/rules/theme-no-theme-coupled-literal.ts +132 -0
  195. package/src/rules/tiers.ts +111 -0
  196. package/src/rules/tokens-css-vars-must-be-defined.test.ts +244 -0
  197. package/src/rules/tokens-css-vars-must-be-defined.ts +156 -0
  198. package/src/rules/tokens-require-dual-fallback.ts +78 -0
  199. package/src/rules/types.ts +27 -0
  200. package/src/rules/utils.test.ts +110 -0
  201. package/src/rules/utils.ts +599 -0
  202. package/src/schema.ts +427 -0
  203. package/src/schemas/index.ts +275 -0
  204. package/src/schemas/schemas.test.ts +124 -0
  205. package/src/severity.ts +54 -0
  206. package/src/storyAdapter.test.ts +571 -0
  207. package/src/storyAdapter.ts +761 -0
  208. package/src/storyFilters.test.ts +350 -0
  209. package/src/storyFilters.ts +253 -0
  210. package/src/storybook-csf.ts +11 -0
  211. package/src/style-comparison.ts +423 -0
  212. package/src/test-utils.ts +68 -0
  213. package/src/token-parser.ts +2 -0
  214. package/src/token-types.ts +287 -0
  215. package/src/tokens/categories.ts +131 -0
  216. package/src/tokens/color.ts +193 -0
  217. package/src/tokens/design-token-parser.test.ts +159 -0
  218. package/src/tokens/design-token-parser.ts +402 -0
  219. package/src/tokens/dtcg.ts +428 -0
  220. package/src/tokens/index.ts +130 -0
  221. package/src/tokens/lookup.ts +19 -0
  222. package/src/tokens/scss.ts +171 -0
  223. package/src/tokens/tailwind-v4.ts +187 -0
  224. package/src/tokens/types.ts +43 -0
  225. package/src/topology/index.ts +9 -0
  226. package/src/topology/resolve-area.test.ts +174 -0
  227. package/src/topology/resolve-area.ts +215 -0
  228. package/src/types.ts +1057 -0
@@ -0,0 +1,2145 @@
1
+ import {
2
+ normalizeFinding,
3
+ normalizeViolation
4
+ } from "./chunk-R3X7UBHU.js";
5
+ import {
6
+ authorOwnedImport,
7
+ canonicalizeOwnedComponentId,
8
+ canonicalizeOwnedImport,
9
+ ownedImportEquivalents,
10
+ projectV1OwnedComponentId,
11
+ projectV1OwnedImportIdentity
12
+ } from "./chunk-JNBFJ34I.js";
13
+ import {
14
+ portableRepoPathError
15
+ } from "./chunk-EIYNNS77.js";
16
+
17
+ // src/codes/codes.ts
18
+ var EXPLAIN_URL_BASE = "https://usefragments.com/errors/";
19
+ function code(entry) {
20
+ return {
21
+ ...entry,
22
+ explainUrl: `${EXPLAIN_URL_BASE}${entry.code}`
23
+ };
24
+ }
25
+ var CODES = [
26
+ code({
27
+ code: "FUI1001",
28
+ ruleId: "components/non-canonical",
29
+ category: "canonical-usage",
30
+ defaultSeverity: "serious",
31
+ title: "Component should use the canonical primitive",
32
+ lifecycle: "experimental",
33
+ fixAvailable: true,
34
+ evidenceRequired: true
35
+ }),
36
+ code({
37
+ code: "FUI1002",
38
+ ruleId: "components/preferred-component",
39
+ category: "canonical-usage",
40
+ defaultSeverity: "serious",
41
+ title: "Preferred component should be used",
42
+ lifecycle: "experimental",
43
+ fixAvailable: true,
44
+ evidenceRequired: true
45
+ }),
46
+ code({
47
+ code: "FUI1003",
48
+ ruleId: "imports/preferred-path",
49
+ category: "canonical-usage",
50
+ defaultSeverity: "moderate",
51
+ title: "Import should use the preferred path",
52
+ lifecycle: "experimental",
53
+ fixAvailable: true,
54
+ evidenceRequired: true
55
+ }),
56
+ code({
57
+ code: "FUI1004",
58
+ ruleId: "components/prefer-library",
59
+ category: "canonical-usage",
60
+ defaultSeverity: "moderate",
61
+ title: "Library component should be preferred",
62
+ lifecycle: "experimental",
63
+ fixAvailable: true,
64
+ evidenceRequired: true
65
+ }),
66
+ code({
67
+ code: "FUI1005",
68
+ ruleId: "components/allow",
69
+ category: "canonical-usage",
70
+ defaultSeverity: "serious",
71
+ title: "Component is not in the allowed set",
72
+ lifecycle: "experimental",
73
+ fixAvailable: false,
74
+ evidenceRequired: true
75
+ }),
76
+ code({
77
+ code: "FUI1006",
78
+ ruleId: "components/deny",
79
+ category: "canonical-usage",
80
+ defaultSeverity: "serious",
81
+ title: "Component is denied by policy",
82
+ lifecycle: "experimental",
83
+ fixAvailable: false,
84
+ evidenceRequired: true
85
+ }),
86
+ code({
87
+ code: "FUI2001",
88
+ ruleId: "tokens/require-design-tokens",
89
+ category: "tokens",
90
+ defaultSeverity: "serious",
91
+ title: "Design token is required",
92
+ lifecycle: "experimental",
93
+ fixAvailable: true,
94
+ evidenceRequired: true
95
+ }),
96
+ code({
97
+ code: "FUI2002",
98
+ ruleId: "tokens/allowed-prefixes",
99
+ category: "tokens",
100
+ defaultSeverity: "moderate",
101
+ title: "Token prefix is not allowed",
102
+ lifecycle: "experimental",
103
+ fixAvailable: true,
104
+ evidenceRequired: true
105
+ }),
106
+ code({
107
+ code: "FUI2003",
108
+ ruleId: "tokens/require-dual-fallback",
109
+ category: "tokens",
110
+ defaultSeverity: "serious",
111
+ title: "Token fallback is required",
112
+ lifecycle: "experimental",
113
+ fixAvailable: true,
114
+ evidenceRequired: true
115
+ }),
116
+ code({
117
+ code: "FUI2004",
118
+ ruleId: "styles/no-raw-dimensions",
119
+ category: "tokens",
120
+ defaultSeverity: "serious",
121
+ title: "Raw dimension should use a token",
122
+ lifecycle: "experimental",
123
+ fixAvailable: true,
124
+ evidenceRequired: true
125
+ }),
126
+ code({
127
+ code: "FUI2005",
128
+ ruleId: "styles/no-raw-color",
129
+ category: "tokens",
130
+ defaultSeverity: "serious",
131
+ title: "Raw color should use a token",
132
+ lifecycle: "experimental",
133
+ fixAvailable: true,
134
+ evidenceRequired: true
135
+ }),
136
+ code({
137
+ code: "FUI2006",
138
+ ruleId: "styles/no-raw-spacing",
139
+ category: "tokens",
140
+ defaultSeverity: "serious",
141
+ title: "Raw spacing should use the scale",
142
+ lifecycle: "experimental",
143
+ fixAvailable: true,
144
+ evidenceRequired: true
145
+ }),
146
+ code({
147
+ code: "FUI2007",
148
+ ruleId: "tailwind/arbitrary-color",
149
+ category: "tokens",
150
+ defaultSeverity: "moderate",
151
+ title: "Tailwind arbitrary color should use a token",
152
+ lifecycle: "experimental",
153
+ fixAvailable: true,
154
+ evidenceRequired: true
155
+ }),
156
+ code({
157
+ code: "FUI2008",
158
+ ruleId: "tailwind/arbitrary-spacing",
159
+ category: "tokens",
160
+ defaultSeverity: "moderate",
161
+ title: "Tailwind arbitrary spacing should use the scale",
162
+ lifecycle: "experimental",
163
+ fixAvailable: true,
164
+ evidenceRequired: true
165
+ }),
166
+ code({
167
+ code: "FUI2009",
168
+ ruleId: "tailwind/forbidden-palette",
169
+ category: "tokens",
170
+ defaultSeverity: "moderate",
171
+ title: "Tailwind palette is forbidden",
172
+ lifecycle: "experimental",
173
+ fixAvailable: false,
174
+ evidenceRequired: true
175
+ }),
176
+ code({
177
+ code: "FUI2010",
178
+ ruleId: "tailwind/raw-color-via-token",
179
+ category: "tokens",
180
+ defaultSeverity: "moderate",
181
+ title: "Tailwind color should use a resolved token",
182
+ lifecycle: "experimental",
183
+ fixAvailable: true,
184
+ evidenceRequired: true
185
+ }),
186
+ code({
187
+ code: "FUI2011",
188
+ ruleId: "tailwind/off-scale-spacing-token",
189
+ category: "tokens",
190
+ defaultSeverity: "moderate",
191
+ title: "Tailwind spacing token is off scale",
192
+ lifecycle: "experimental",
193
+ fixAvailable: true,
194
+ evidenceRequired: true
195
+ }),
196
+ code({
197
+ code: "FUI2012",
198
+ ruleId: "tailwind/unknown-class",
199
+ category: "tokens",
200
+ defaultSeverity: "moderate",
201
+ title: "Tailwind class is unknown",
202
+ lifecycle: "experimental",
203
+ fixAvailable: false,
204
+ evidenceRequired: true
205
+ }),
206
+ code({
207
+ code: "FUI2013",
208
+ ruleId: "brand/enforce-seeds",
209
+ category: "tokens",
210
+ defaultSeverity: "serious",
211
+ title: "Brand seed token is required",
212
+ lifecycle: "experimental",
213
+ fixAvailable: true,
214
+ evidenceRequired: true
215
+ }),
216
+ code({
217
+ code: "FUI2014",
218
+ ruleId: "theme/no-theme-coupled-literal",
219
+ category: "tokens",
220
+ defaultSeverity: "moderate",
221
+ title: "Theme-coupled literal should use a semantic token",
222
+ lifecycle: "experimental",
223
+ fixAvailable: false,
224
+ evidenceRequired: true
225
+ }),
226
+ code({
227
+ code: "FUI2015",
228
+ ruleId: "tokens/css-vars-must-be-defined",
229
+ category: "tokens",
230
+ defaultSeverity: "moderate",
231
+ title: "CSS variable is not in the contract vocabulary",
232
+ lifecycle: "experimental",
233
+ fixAvailable: false,
234
+ evidenceRequired: true
235
+ }),
236
+ code({
237
+ code: "FUI3001",
238
+ ruleId: "a11y/required-accessible-name",
239
+ category: "a11y",
240
+ defaultSeverity: "serious",
241
+ title: "Accessible name is required",
242
+ lifecycle: "experimental",
243
+ fixAvailable: false,
244
+ evidenceRequired: true
245
+ }),
246
+ code({
247
+ code: "FUI3002",
248
+ ruleId: "a11y/standard",
249
+ category: "a11y",
250
+ defaultSeverity: "serious",
251
+ title: "Accessibility standard failed",
252
+ lifecycle: "experimental",
253
+ fixAvailable: false,
254
+ evidenceRequired: true
255
+ }),
256
+ code({
257
+ code: "FUI5001",
258
+ ruleId: "components/required-children",
259
+ category: "composition",
260
+ defaultSeverity: "serious",
261
+ title: "Required child component is missing",
262
+ lifecycle: "experimental",
263
+ fixAvailable: false,
264
+ evidenceRequired: true
265
+ }),
266
+ code({
267
+ code: "FUI5002",
268
+ ruleId: "components/canonical-usage",
269
+ category: "composition",
270
+ defaultSeverity: "serious",
271
+ title: "Component should use canonical composition",
272
+ lifecycle: "deprecated",
273
+ replacedBy: "FUI1001",
274
+ fixAvailable: true,
275
+ evidenceRequired: true
276
+ }),
277
+ code({
278
+ code: "FUI5003",
279
+ ruleId: "composition/cardinality",
280
+ category: "composition",
281
+ defaultSeverity: "moderate",
282
+ title: "Too many of a component in one region",
283
+ lifecycle: "experimental",
284
+ fixAvailable: false,
285
+ evidenceRequired: true
286
+ }),
287
+ code({
288
+ code: "FUI5004",
289
+ ruleId: "composition/co-occurrence",
290
+ category: "composition",
291
+ defaultSeverity: "moderate",
292
+ title: "Required companion component is missing from the region",
293
+ lifecycle: "experimental",
294
+ fixAvailable: false,
295
+ evidenceRequired: true
296
+ }),
297
+ code({
298
+ code: "FUI6001",
299
+ ruleId: "props/unknown",
300
+ category: "props",
301
+ defaultSeverity: "moderate",
302
+ title: "Prop is unknown",
303
+ lifecycle: "experimental",
304
+ fixAvailable: false,
305
+ evidenceRequired: true
306
+ }),
307
+ code({
308
+ code: "FUI6002",
309
+ ruleId: "props/invalid-value",
310
+ category: "props",
311
+ defaultSeverity: "serious",
312
+ title: "Prop value is invalid",
313
+ lifecycle: "experimental",
314
+ fixAvailable: true,
315
+ evidenceRequired: true
316
+ }),
317
+ code({
318
+ code: "FUI6003",
319
+ ruleId: "props/banned-pattern",
320
+ category: "props",
321
+ defaultSeverity: "serious",
322
+ title: "Prop pattern is banned",
323
+ lifecycle: "experimental",
324
+ fixAvailable: false,
325
+ evidenceRequired: true
326
+ }),
327
+ code({
328
+ code: "FUI6004",
329
+ ruleId: "components/forbidden-prop-value",
330
+ category: "props",
331
+ defaultSeverity: "serious",
332
+ title: "Prop value is forbidden",
333
+ lifecycle: "experimental",
334
+ fixAvailable: true,
335
+ evidenceRequired: true
336
+ }),
337
+ code({
338
+ code: "FUI6005",
339
+ ruleId: "components/unknown-prop",
340
+ category: "props",
341
+ defaultSeverity: "moderate",
342
+ title: "Component prop is unknown",
343
+ lifecycle: "experimental",
344
+ fixAvailable: false,
345
+ evidenceRequired: true
346
+ }),
347
+ code({
348
+ code: "FUI8001",
349
+ ruleId: "safety/block-event-handlers",
350
+ category: "safety",
351
+ defaultSeverity: "serious",
352
+ title: "Event handler prop is blocked",
353
+ lifecycle: "experimental",
354
+ fixAvailable: true,
355
+ evidenceRequired: true
356
+ }),
357
+ code({
358
+ code: "FUI8002",
359
+ ruleId: "safety/block-dangerous-props",
360
+ category: "safety",
361
+ defaultSeverity: "critical",
362
+ title: "Dangerous prop is blocked",
363
+ lifecycle: "experimental",
364
+ fixAvailable: true,
365
+ evidenceRequired: true
366
+ }),
367
+ code({
368
+ code: "FUI8003",
369
+ ruleId: "safety/block-controlled-props",
370
+ category: "safety",
371
+ defaultSeverity: "serious",
372
+ title: "Controlled prop is blocked",
373
+ lifecycle: "experimental",
374
+ fixAvailable: true,
375
+ evidenceRequired: true
376
+ }),
377
+ code({
378
+ code: "FUI8004",
379
+ ruleId: "safety/block-function-props",
380
+ category: "safety",
381
+ defaultSeverity: "serious",
382
+ title: "Function prop is blocked",
383
+ lifecycle: "experimental",
384
+ fixAvailable: true,
385
+ evidenceRequired: true
386
+ }),
387
+ code({
388
+ code: "FUI8005",
389
+ ruleId: "safety/block-internal-props",
390
+ category: "safety",
391
+ defaultSeverity: "critical",
392
+ title: "Internal prop is blocked",
393
+ lifecycle: "experimental",
394
+ fixAvailable: true,
395
+ evidenceRequired: true
396
+ }),
397
+ code({
398
+ code: "FUI8006",
399
+ ruleId: "safety/sanitize-hrefs",
400
+ category: "safety",
401
+ defaultSeverity: "critical",
402
+ title: "Href must be sanitized",
403
+ lifecycle: "experimental",
404
+ fixAvailable: true,
405
+ evidenceRequired: true
406
+ }),
407
+ code({
408
+ code: "FUI9001",
409
+ ruleId: "unused-suppression",
410
+ category: "system",
411
+ defaultSeverity: "minor",
412
+ title: "Suppression is unused",
413
+ lifecycle: "experimental",
414
+ fixAvailable: true,
415
+ evidenceRequired: true
416
+ }),
417
+ code({
418
+ code: "FUI9002",
419
+ ruleId: "expired-suppression",
420
+ category: "system",
421
+ defaultSeverity: "serious",
422
+ title: "Suppression is expired",
423
+ lifecycle: "experimental",
424
+ fixAvailable: true,
425
+ evidenceRequired: true
426
+ }),
427
+ code({
428
+ code: "FUI9003",
429
+ ruleId: "missing-expiry",
430
+ category: "system",
431
+ defaultSeverity: "minor",
432
+ title: "Suppression expiry is missing",
433
+ lifecycle: "experimental",
434
+ fixAvailable: true,
435
+ evidenceRequired: true
436
+ })
437
+ ];
438
+ var byCode = new Map(
439
+ CODES.map((entry) => [entry.code, entry])
440
+ );
441
+ var byRuleId = new Map(
442
+ CODES.map((entry) => [entry.ruleId, entry])
443
+ );
444
+ function explainUrlForCode(code2) {
445
+ return `${EXPLAIN_URL_BASE}${code2}`;
446
+ }
447
+
448
+ // src/facts/ids.ts
449
+ function canonicalJson(value) {
450
+ if (value === void 0) return "null";
451
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
452
+ if (Array.isArray(value)) {
453
+ return `[${value.map(canonicalJson).join(",")}]`;
454
+ }
455
+ const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
456
+ return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(",")}}`;
457
+ }
458
+ var FNV_PRIME = 16777619;
459
+ var FNV_SEED_A = 2166136261;
460
+ var FNV_SEED_B = 2654435761;
461
+ function fnv1a32(input, seed) {
462
+ let hash = seed >>> 0;
463
+ for (let i = 0; i < input.length; i++) {
464
+ const code2 = input.charCodeAt(i);
465
+ hash ^= code2 & 255;
466
+ hash = Math.imul(hash, FNV_PRIME) >>> 0;
467
+ if (code2 > 255) {
468
+ hash ^= code2 >>> 8 & 255;
469
+ hash = Math.imul(hash, FNV_PRIME) >>> 0;
470
+ }
471
+ }
472
+ return hash >>> 0;
473
+ }
474
+ function hash64Hex(input) {
475
+ const a = fnv1a32(input, FNV_SEED_A);
476
+ const b = fnv1a32(input, FNV_SEED_B);
477
+ return a.toString(16).padStart(8, "0") + b.toString(16).padStart(8, "0");
478
+ }
479
+ function componentId(packageName, name) {
480
+ if (!packageName.length || !name.length) {
481
+ throw new Error("componentId requires a non-empty packageName and name");
482
+ }
483
+ return `${authorOwnedImport(packageName)}#${name}`;
484
+ }
485
+ function asComponentId(value) {
486
+ if (!value.includes("#")) {
487
+ throw new Error(`componentId must look like "package#Name", got "${value}"`);
488
+ }
489
+ return value;
490
+ }
491
+ function factId(kind, identity) {
492
+ const payload = canonicalJson({ k: kind, i: projectFactIdentityV1(kind, identity) });
493
+ return `${kind}:${hash64Hex(payload)}`;
494
+ }
495
+ function projectFactIdentityV1(kind, identity) {
496
+ return Object.fromEntries(
497
+ Object.entries(identity).map(([key, value]) => {
498
+ if (key === "componentId" && typeof value === "string") {
499
+ return [key, projectV1OwnedComponentId(value)];
500
+ }
501
+ if (kind === "jsx_component_preferred" && (key === "from" || key === "to") && typeof value === "string") {
502
+ return [key, projectV1OwnedComponentId(value)];
503
+ }
504
+ if (kind === "jsx_import_path_preferred" && (key === "from" || key === "to") && typeof value === "string") {
505
+ return [key, projectV1OwnedImportIdentity(value)];
506
+ }
507
+ if (kind === "usage_import" && key === "source" && typeof value === "string") {
508
+ return [key, projectV1OwnedImportIdentity(value)];
509
+ }
510
+ return [key, value];
511
+ })
512
+ );
513
+ }
514
+
515
+ // src/facts/builders.ts
516
+ function makeComponentMetadataFact(input) {
517
+ return {
518
+ id: factId("component", { componentId: input.componentId }),
519
+ kind: "component",
520
+ componentId: input.componentId,
521
+ name: input.name,
522
+ description: input.description,
523
+ category: input.category,
524
+ filePath: input.filePath
525
+ };
526
+ }
527
+ function makeComponentCapabilityFact(input) {
528
+ return {
529
+ id: factId("component_capability", {
530
+ componentId: input.componentId,
531
+ capability: input.capability
532
+ }),
533
+ kind: "component_capability",
534
+ componentId: input.componentId,
535
+ capability: input.capability
536
+ };
537
+ }
538
+ function makeCanonicalCandidateFact(input) {
539
+ return {
540
+ id: factId("canonical_candidate", {
541
+ componentId: input.componentId,
542
+ canonical: input.canonical,
543
+ classifierVersion: input.classifierVersion,
544
+ vocabVersion: input.vocabVersion
545
+ }),
546
+ kind: "canonical_candidate",
547
+ componentId: input.componentId,
548
+ canonical: input.canonical,
549
+ confidence: input.confidence,
550
+ band: input.band,
551
+ location: input.location,
552
+ classifierVersion: input.classifierVersion,
553
+ vocabVersion: input.vocabVersion,
554
+ evidence: input.evidence,
555
+ userConfirmed: input.userConfirmed
556
+ };
557
+ }
558
+ function makeCanonicalMappingFact(input) {
559
+ return {
560
+ id: factId("canonical_mapping", {
561
+ componentId: input.componentId,
562
+ canonical: input.canonical
563
+ }),
564
+ kind: "canonical_mapping",
565
+ componentId: input.componentId,
566
+ canonical: input.canonical,
567
+ status: input.status,
568
+ confidence: input.confidence,
569
+ location: input.location
570
+ };
571
+ }
572
+ function makeStructuralConfidenceFact(input) {
573
+ return {
574
+ id: factId("structural_confidence", {
575
+ componentId: input.componentId,
576
+ canonical: input.canonical
577
+ }),
578
+ kind: "structural_confidence",
579
+ componentId: input.componentId,
580
+ canonical: input.canonical,
581
+ confidence: input.confidence,
582
+ signalCount: input.signalCount,
583
+ location: input.location
584
+ };
585
+ }
586
+ function makeCanonicalReplacementFact(input) {
587
+ return {
588
+ id: factId("canonical_replacement", {
589
+ componentId: input.componentId,
590
+ canonical: input.canonical
591
+ }),
592
+ kind: "canonical_replacement",
593
+ componentId: input.componentId,
594
+ canonical: input.canonical,
595
+ importPath: input.importPath,
596
+ componentName: input.componentName,
597
+ confidence: input.confidence,
598
+ location: input.location
599
+ };
600
+ }
601
+ function makePropMetadataFact(input) {
602
+ return {
603
+ id: factId("prop_metadata", {
604
+ componentId: input.componentId,
605
+ prop: input.prop
606
+ }),
607
+ kind: "prop_metadata",
608
+ componentId: input.componentId,
609
+ prop: input.prop,
610
+ type: input.type,
611
+ values: input.values,
612
+ required: input.required,
613
+ default: input.default,
614
+ description: input.description
615
+ };
616
+ }
617
+ function makePropValueAvoidedFact(input) {
618
+ return {
619
+ id: factId("prop_value_avoided", {
620
+ componentId: input.componentId,
621
+ prop: input.prop,
622
+ value: input.value
623
+ }),
624
+ kind: "prop_value_avoided",
625
+ componentId: input.componentId,
626
+ prop: input.prop,
627
+ value: input.value,
628
+ because: input.because,
629
+ suggest: input.suggest,
630
+ severity: input.severity
631
+ };
632
+ }
633
+ function makePropValueForbiddenFact(input) {
634
+ return {
635
+ id: factId("prop_value_forbidden", {
636
+ componentId: input.componentId,
637
+ prop: input.prop,
638
+ value: input.value,
639
+ pathPattern: input.pathPattern
640
+ }),
641
+ kind: "prop_value_forbidden",
642
+ componentId: input.componentId,
643
+ prop: input.prop,
644
+ value: input.value,
645
+ pathPattern: input.pathPattern,
646
+ because: input.because,
647
+ replaceWith: input.replaceWith,
648
+ severity: input.severity
649
+ };
650
+ }
651
+ function makeA11yNameRequiredFact(input) {
652
+ return {
653
+ id: factId("a11y_name_required", { componentId: input.componentId }),
654
+ kind: "a11y_name_required",
655
+ componentId: input.componentId,
656
+ because: input.because,
657
+ severity: input.severity
658
+ };
659
+ }
660
+ function makeScaleFact(input) {
661
+ return {
662
+ id: factId("scale", { name: input.name }),
663
+ kind: "scale",
664
+ name: input.name,
665
+ unit: input.unit,
666
+ ...input.rootFontSizePx === void 0 ? {} : { rootFontSizePx: input.rootFontSizePx },
667
+ ...input.emBasePx === void 0 ? {} : { emBasePx: input.emBasePx }
668
+ };
669
+ }
670
+ function makeScaleValueFact(input) {
671
+ return {
672
+ id: factId("scale_value", { scale: input.scale, value: input.value }),
673
+ kind: "scale_value",
674
+ scale: input.scale,
675
+ value: input.value
676
+ };
677
+ }
678
+ function makeStyleRawColorForbiddenFact(input) {
679
+ return {
680
+ id: factId("style_raw_color_forbidden", {}),
681
+ kind: "style_raw_color_forbidden",
682
+ except: [...input.except],
683
+ prefer: input.prefer,
684
+ severity: input.severity
685
+ };
686
+ }
687
+ function makeStyleRawDimensionForbiddenFact(input) {
688
+ return {
689
+ id: factId("style_raw_dimension_forbidden", {}),
690
+ kind: "style_raw_dimension_forbidden",
691
+ appliesTo: [...input.appliesTo],
692
+ prefer: input.prefer,
693
+ severity: input.severity
694
+ };
695
+ }
696
+ function makeStylePropertyScaleFact(input) {
697
+ return {
698
+ id: factId("style_property_scale", { property: input.property }),
699
+ kind: "style_property_scale",
700
+ property: input.property,
701
+ scale: input.scale,
702
+ severity: input.severity
703
+ };
704
+ }
705
+ function makeStyleFontSizeScaleFact(input) {
706
+ return {
707
+ id: factId("style_font_size_scale", {}),
708
+ kind: "style_font_size_scale",
709
+ scale: input.scale,
710
+ severity: input.severity
711
+ };
712
+ }
713
+ function makeStyleCssVarsMustBeDefinedFact(input) {
714
+ return {
715
+ id: factId("style_css_vars_must_be_defined", {}),
716
+ kind: "style_css_vars_must_be_defined",
717
+ severity: input.severity
718
+ };
719
+ }
720
+ function makeContractTokenFact(input) {
721
+ const name = input.name.startsWith("--") ? input.name : `--${input.name}`;
722
+ return {
723
+ id: factId("contract_token", { name }),
724
+ kind: "contract_token",
725
+ name
726
+ };
727
+ }
728
+ function makeJsxUnknownPropsForbiddenFact(input) {
729
+ return {
730
+ id: factId("jsx_unknown_props_forbidden", {}),
731
+ kind: "jsx_unknown_props_forbidden",
732
+ severity: input.severity
733
+ };
734
+ }
735
+ function makeJsxInlineStyleForbiddenRawFact(input) {
736
+ return {
737
+ id: factId("jsx_inline_style_forbidden_raw", { property: input.property }),
738
+ kind: "jsx_inline_style_forbidden_raw",
739
+ property: input.property,
740
+ severity: input.severity
741
+ };
742
+ }
743
+ function makeJsxImportPathPreferredFact(input) {
744
+ return {
745
+ id: factId("jsx_import_path_preferred", {
746
+ from: input.from,
747
+ to: input.to,
748
+ imported: input.imported
749
+ }),
750
+ kind: "jsx_import_path_preferred",
751
+ from: input.from,
752
+ to: input.to,
753
+ imported: input.imported,
754
+ because: input.because,
755
+ severity: input.severity
756
+ };
757
+ }
758
+ function makeJsxComponentPreferredFact(input) {
759
+ return {
760
+ id: factId("jsx_component_preferred", {
761
+ from: input.from,
762
+ to: input.to
763
+ }),
764
+ kind: "jsx_component_preferred",
765
+ from: input.from,
766
+ to: input.to,
767
+ because: input.because,
768
+ severity: input.severity
769
+ };
770
+ }
771
+ function makeTokenDefinitionFact(input) {
772
+ return {
773
+ id: factId("token_definition", { name: input.name }),
774
+ kind: "token_definition",
775
+ name: input.name,
776
+ value: input.value,
777
+ category: input.category,
778
+ referenceFormat: input.referenceFormat
779
+ };
780
+ }
781
+ function makeTailwindPaletteAllowFact(input) {
782
+ return {
783
+ id: factId("tailwind_palette_allow", {}),
784
+ kind: "tailwind_palette_allow",
785
+ patterns: [...input.patterns],
786
+ severity: input.severity
787
+ };
788
+ }
789
+ function makeTailwindPaletteDenyFact(input) {
790
+ return {
791
+ id: factId("tailwind_palette_deny", {}),
792
+ kind: "tailwind_palette_deny",
793
+ patterns: [...input.patterns],
794
+ severity: input.severity
795
+ };
796
+ }
797
+ function makeTailwindUnknownClassEnabledFact(input) {
798
+ return {
799
+ id: factId("tailwind_unknown_class_enabled", {}),
800
+ kind: "tailwind_unknown_class_enabled",
801
+ severity: input.severity
802
+ };
803
+ }
804
+ function makeGovernanceRuleConfigFact(input) {
805
+ return {
806
+ id: factId("governance_rule_config", { ruleId: input.ruleId }),
807
+ kind: "governance_rule_config",
808
+ ruleId: input.ruleId,
809
+ enabled: input.enabled,
810
+ severity: input.severity,
811
+ options: input.options
812
+ };
813
+ }
814
+ function makeUsageNodeFact(input) {
815
+ return {
816
+ // Identity is structural (file + nodePath + element) only — the normalized
817
+ // semantics (role/interactive) are evidence carried on the fact, never part
818
+ // of its ID, so adding them never perturbs existing fact identities.
819
+ id: factId("usage_node", {
820
+ file: input.file,
821
+ nodePath: input.nodePath,
822
+ element: input.element
823
+ }),
824
+ kind: "usage_node",
825
+ file: input.file,
826
+ nodePath: input.nodePath,
827
+ element: input.element,
828
+ ...input.role !== void 0 ? { role: input.role } : {},
829
+ ...input.interactive ? { interactive: true } : {},
830
+ location: input.location
831
+ };
832
+ }
833
+ function makeUsageComponentFact(input) {
834
+ return {
835
+ id: factId("usage_component", {
836
+ nodeId: input.nodeId,
837
+ componentId: input.componentId
838
+ }),
839
+ kind: "usage_component",
840
+ nodeId: input.nodeId,
841
+ componentId: input.componentId
842
+ };
843
+ }
844
+ function makeUsageImportFact(input) {
845
+ return {
846
+ id: factId("usage_import", {
847
+ file: input.file,
848
+ local: input.local,
849
+ imported: input.imported,
850
+ source: input.source
851
+ }),
852
+ kind: "usage_import",
853
+ file: input.file,
854
+ local: input.local,
855
+ imported: input.imported,
856
+ source: input.source,
857
+ namespace: input.namespace,
858
+ location: input.location
859
+ };
860
+ }
861
+ function makeUsagePropResolvedFact(input) {
862
+ return {
863
+ id: factId("usage_prop_resolved", { nodeId: input.nodeId, prop: input.prop }),
864
+ kind: "usage_prop_resolved",
865
+ nodeId: input.nodeId,
866
+ prop: input.prop,
867
+ resolution: input.resolution,
868
+ value: input.value,
869
+ location: input.location
870
+ };
871
+ }
872
+ function makeUsageInlineStyleFact(input) {
873
+ return {
874
+ id: factId("usage_inline_style", {
875
+ nodeId: input.nodeId,
876
+ property: input.property
877
+ }),
878
+ kind: "usage_inline_style",
879
+ nodeId: input.nodeId,
880
+ property: input.property,
881
+ valueKind: input.valueKind,
882
+ value: input.value
883
+ };
884
+ }
885
+ function makeUsageTextChildFact(input) {
886
+ return {
887
+ id: factId("usage_text_child", { nodeId: input.nodeId, index: input.index }),
888
+ kind: "usage_text_child",
889
+ nodeId: input.nodeId,
890
+ text: input.text,
891
+ index: input.index
892
+ };
893
+ }
894
+ function makeClassNameLiteralFact(input) {
895
+ return {
896
+ id: factId("classname_literal", {
897
+ nodeId: input.nodeId,
898
+ attr: input.attr,
899
+ origin: input.origin,
900
+ originPath: input.originPath
901
+ }),
902
+ kind: "classname_literal",
903
+ file: input.file,
904
+ nodeId: input.nodeId,
905
+ attr: input.attr,
906
+ classes: [...input.classes],
907
+ origin: input.origin,
908
+ originPath: input.originPath,
909
+ location: input.location
910
+ };
911
+ }
912
+ function makeClassNameDynamicFact(input) {
913
+ return {
914
+ id: factId("classname_dynamic", {
915
+ nodeId: input.nodeId,
916
+ attr: input.attr,
917
+ reason: input.reason,
918
+ originPath: input.originPath
919
+ }),
920
+ kind: "classname_dynamic",
921
+ file: input.file,
922
+ nodeId: input.nodeId,
923
+ attr: input.attr,
924
+ reason: input.reason,
925
+ snippet: input.snippet,
926
+ originPath: input.originPath,
927
+ location: input.location
928
+ };
929
+ }
930
+ function makeTailwindClassFact(input) {
931
+ return {
932
+ id: factId("tailwind_class", {
933
+ nodeId: input.nodeId,
934
+ originPath: input.originPath
935
+ }),
936
+ kind: "tailwind_class",
937
+ file: input.file,
938
+ nodeId: input.nodeId,
939
+ raw: input.raw,
940
+ originPath: input.originPath,
941
+ prefix: input.prefix,
942
+ modifiers: input.modifiers.map((m) => ({ ...m })),
943
+ important: input.important,
944
+ negative: input.negative,
945
+ utility: input.utility,
946
+ value: { ...input.value },
947
+ location: input.location
948
+ };
949
+ }
950
+ function makeThemeDeclarationFact(input) {
951
+ return {
952
+ id: factId("theme_declaration", {
953
+ file: input.file,
954
+ name: input.name,
955
+ declarationPath: input.declarationPath
956
+ }),
957
+ kind: "theme_declaration",
958
+ file: input.file,
959
+ name: input.name,
960
+ value: input.value,
961
+ declarationPath: input.declarationPath,
962
+ inline: input.inline,
963
+ flavor: input.flavor,
964
+ location: input.location
965
+ };
966
+ }
967
+ function makeTailwindTokenResolvedFact(input) {
968
+ return {
969
+ id: factId("tailwind_token_resolved", {
970
+ utility: input.utility,
971
+ token: input.token
972
+ }),
973
+ kind: "tailwind_token_resolved",
974
+ utility: input.utility,
975
+ token: input.token,
976
+ resolved: { ...input.resolved }
977
+ };
978
+ }
979
+ function makeStyleDeclarationFact(input) {
980
+ return {
981
+ id: factId("style_declaration", {
982
+ file: input.file,
983
+ selector: input.selector,
984
+ declarationPath: input.declarationPath,
985
+ property: input.property
986
+ }),
987
+ kind: "style_declaration",
988
+ file: input.file,
989
+ selector: input.selector,
990
+ declarationPath: input.declarationPath,
991
+ property: input.property,
992
+ value: input.value,
993
+ location: input.location,
994
+ ...input.declaredTokenSource ? { declaredTokenSource: true } : {}
995
+ };
996
+ }
997
+ function makeStyleUnsupportedFact(input) {
998
+ return {
999
+ id: factId("style_unsupported", {
1000
+ file: input.file,
1001
+ source: input.source,
1002
+ syntax: input.syntax,
1003
+ selector: input.selector,
1004
+ declarationPath: input.declarationPath,
1005
+ property: input.property ?? ""
1006
+ }),
1007
+ kind: "style_unsupported",
1008
+ file: input.file,
1009
+ source: input.source,
1010
+ syntax: input.syntax,
1011
+ selector: input.selector,
1012
+ declarationPath: input.declarationPath,
1013
+ property: input.property,
1014
+ reason: input.reason,
1015
+ location: input.location
1016
+ };
1017
+ }
1018
+
1019
+ // src/package-identity-match.ts
1020
+ function ownedImportsEqual(left, right) {
1021
+ if (left === right) return true;
1022
+ if (ownedImportEquivalents(left).includes(right)) return true;
1023
+ return canonicalizeOwnedImport(left) === canonicalizeOwnedImport(right);
1024
+ }
1025
+ function ownedImportMatchesRoot(imported, configuredRoot) {
1026
+ for (const equivalent of ownedImportEquivalents(configuredRoot)) {
1027
+ if (imported === equivalent || imported.startsWith(`${equivalent}/`)) {
1028
+ return true;
1029
+ }
1030
+ }
1031
+ const canonicalImported = canonicalizeOwnedImport(imported);
1032
+ const canonicalRoot = canonicalizeOwnedImport(configuredRoot);
1033
+ return canonicalImported === canonicalRoot || canonicalImported.startsWith(`${canonicalRoot}/`);
1034
+ }
1035
+ function ownedComponentIdsEqual(left, right) {
1036
+ return canonicalizeOwnedComponentId(left) === canonicalizeOwnedComponentId(right);
1037
+ }
1038
+
1039
+ // src/facts/fact-index.ts
1040
+ function hasComponentId(fact) {
1041
+ return "componentId" in fact && typeof fact.componentId === "string";
1042
+ }
1043
+ function logicalComponentId(componentId2) {
1044
+ return canonicalizeOwnedComponentId(componentId2);
1045
+ }
1046
+ function logicalFactForComparison(fact) {
1047
+ if (fact.kind === "jsx_import_path_preferred") {
1048
+ return {
1049
+ ...fact,
1050
+ from: canonicalizeOwnedImport(fact.from),
1051
+ to: canonicalizeOwnedImport(fact.to)
1052
+ };
1053
+ }
1054
+ if (fact.kind === "jsx_component_preferred") {
1055
+ return {
1056
+ ...fact,
1057
+ from: logicalComponentId(fact.from),
1058
+ to: logicalComponentId(fact.to)
1059
+ };
1060
+ }
1061
+ if (fact.kind === "usage_import") {
1062
+ return { ...fact, source: canonicalizeOwnedImport(fact.source) };
1063
+ }
1064
+ if (hasComponentId(fact)) {
1065
+ return { ...fact, componentId: logicalComponentId(fact.componentId) };
1066
+ }
1067
+ return { ...fact };
1068
+ }
1069
+ function uniqueByLogicalIdentity(items, keyOf) {
1070
+ const seen = /* @__PURE__ */ new Set();
1071
+ return items.filter((item) => {
1072
+ const key = keyOf(item);
1073
+ if (seen.has(key)) return false;
1074
+ seen.add(key);
1075
+ return true;
1076
+ });
1077
+ }
1078
+ function matchesGlob(path, pattern) {
1079
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
1080
+ const expanded = escaped.replace(/\*\*/g, "::DOUBLE::").replace(/\*/g, "[^/]*").replace(/::DOUBLE::/g, ".*");
1081
+ return new RegExp(`^${expanded}$`).test(path);
1082
+ }
1083
+ var FactIndex = class {
1084
+ facts = /* @__PURE__ */ new Map();
1085
+ idsByKind = /* @__PURE__ */ new Map();
1086
+ idsByComponent = /* @__PURE__ */ new Map();
1087
+ add(fact) {
1088
+ const existing = this.facts.get(fact.id);
1089
+ if (existing) {
1090
+ if (canonicalJson(logicalFactForComparison(existing)) !== canonicalJson(logicalFactForComparison(fact))) {
1091
+ console.warn(
1092
+ `FactIndex: conflicting facts for id ${fact.id} \u2014 keeping first fact, skipping ${describeFactForConflict(fact)}`
1093
+ );
1094
+ }
1095
+ return;
1096
+ }
1097
+ this.facts.set(fact.id, fact);
1098
+ let byKind = this.idsByKind.get(fact.kind);
1099
+ if (!byKind) {
1100
+ byKind = /* @__PURE__ */ new Set();
1101
+ this.idsByKind.set(fact.kind, byKind);
1102
+ }
1103
+ byKind.add(fact.id);
1104
+ if (hasComponentId(fact)) {
1105
+ const componentId2 = logicalComponentId(fact.componentId);
1106
+ let byComponent = this.idsByComponent.get(componentId2);
1107
+ if (!byComponent) {
1108
+ byComponent = /* @__PURE__ */ new Set();
1109
+ this.idsByComponent.set(componentId2, byComponent);
1110
+ }
1111
+ byComponent.add(fact.id);
1112
+ }
1113
+ }
1114
+ addMany(facts) {
1115
+ for (const fact of facts) this.add(fact);
1116
+ }
1117
+ get(id) {
1118
+ return this.facts.get(id);
1119
+ }
1120
+ has(id) {
1121
+ return this.facts.has(id);
1122
+ }
1123
+ size() {
1124
+ return this.facts.size;
1125
+ }
1126
+ all() {
1127
+ return [...this.facts.values()];
1128
+ }
1129
+ byKind(kind) {
1130
+ const ids = this.idsByKind.get(kind);
1131
+ if (!ids) return [];
1132
+ const out = [];
1133
+ for (const id of ids) {
1134
+ const fact = this.facts.get(id);
1135
+ if (fact && fact.kind === kind) out.push(fact);
1136
+ }
1137
+ return out;
1138
+ }
1139
+ /**
1140
+ * Resolve evidence for a list of fact IDs. Throws if any ID is missing —
1141
+ * findings must never reference facts that aren't in the index.
1142
+ */
1143
+ evidence(ids) {
1144
+ const missing = [];
1145
+ const out = [];
1146
+ for (const id of ids) {
1147
+ const fact = this.facts.get(id);
1148
+ if (!fact) {
1149
+ missing.push(id);
1150
+ continue;
1151
+ }
1152
+ out.push({ factId: id, fact });
1153
+ }
1154
+ if (missing.length) {
1155
+ throw new Error(`FactIndex.evidence: missing facts in index: ${missing.join(", ")}`);
1156
+ }
1157
+ return out;
1158
+ }
1159
+ // ---------------------------------------------------------------------
1160
+ // Component queries
1161
+ // ---------------------------------------------------------------------
1162
+ components = {
1163
+ list: () => uniqueByLogicalIdentity(
1164
+ this.byKind("component"),
1165
+ (fact) => canonicalJson({
1166
+ ...logicalFactForComparison(fact),
1167
+ id: void 0
1168
+ })
1169
+ ),
1170
+ byId: (componentId2) => this.byComponentOfKind(componentId2, "component")[0],
1171
+ propsOf: (componentId2) => this.byComponentOfKind(componentId2, "prop_metadata"),
1172
+ propOf: (componentId2, prop) => this.components.propsOf(componentId2).find((p) => p.prop === prop),
1173
+ capabilitiesOf: (componentId2) => this.byComponentOfKind(componentId2, "component_capability")
1174
+ };
1175
+ // ---------------------------------------------------------------------
1176
+ // Policy queries
1177
+ // ---------------------------------------------------------------------
1178
+ policy = {
1179
+ forbiddenPropValues: (componentId2, opts = {}) => {
1180
+ const all = this.byComponentOfKind(
1181
+ componentId2,
1182
+ "prop_value_forbidden"
1183
+ );
1184
+ return all.filter((fact) => {
1185
+ if (opts.prop !== void 0 && fact.prop !== opts.prop) return false;
1186
+ if (opts.value !== void 0 && fact.value !== opts.value) return false;
1187
+ if (opts.path !== void 0 && fact.pathPattern) {
1188
+ if (!matchesGlob(opts.path, fact.pathPattern)) return false;
1189
+ }
1190
+ return true;
1191
+ });
1192
+ },
1193
+ avoidedPropValues: (componentId2, opts = {}) => {
1194
+ const all = this.byComponentOfKind(componentId2, "prop_value_avoided");
1195
+ return all.filter((fact) => {
1196
+ if (opts.prop !== void 0 && fact.prop !== opts.prop) return false;
1197
+ if (opts.value !== void 0 && fact.value !== opts.value) return false;
1198
+ return true;
1199
+ });
1200
+ },
1201
+ a11yNameRequired: (componentId2) => this.byComponentOfKind(componentId2, "a11y_name_required")[0],
1202
+ scales: () => this.byKind("scale"),
1203
+ scale: (name) => this.byKind("scale").find((s) => s.name === name),
1204
+ scaleValues: (name) => this.byKind("scale_value").filter((v) => v.scale === name),
1205
+ rawColorPolicy: () => this.byKind("style_raw_color_forbidden")[0],
1206
+ rawDimensionPolicy: () => this.byKind("style_raw_dimension_forbidden")[0],
1207
+ propertyScale: (property) => this.byKind("style_property_scale").find((p) => p.property === property),
1208
+ fontSizeScale: () => this.byKind("style_font_size_scale")[0],
1209
+ cssVarsMustBeDefined: () => this.byKind("style_css_vars_must_be_defined")[0],
1210
+ tailwindPaletteAllow: () => this.byKind("tailwind_palette_allow")[0],
1211
+ tailwindPaletteDeny: () => this.byKind("tailwind_palette_deny")[0],
1212
+ tailwindUnknownClassEnabled: () => this.byKind("tailwind_unknown_class_enabled")[0],
1213
+ ruleConfig: (ruleId) => this.byKind("governance_rule_config").find((fact) => fact.ruleId === ruleId),
1214
+ jsxUnknownPropsForbidden: () => this.byKind("jsx_unknown_props_forbidden")[0],
1215
+ jsxInlineStyleForbiddenRaw: (opts = {}) => {
1216
+ const all = this.byKind("jsx_inline_style_forbidden_raw");
1217
+ return opts.property === void 0 ? all : all.filter((f) => f.property === opts.property);
1218
+ },
1219
+ jsxImportPathPreferred: (opts = {}) => {
1220
+ const all = uniqueByLogicalIdentity(
1221
+ this.byKind("jsx_import_path_preferred"),
1222
+ (fact) => canonicalJson({
1223
+ from: canonicalizeOwnedImport(fact.from),
1224
+ to: canonicalizeOwnedImport(fact.to),
1225
+ imported: fact.imported,
1226
+ because: fact.because,
1227
+ severity: fact.severity
1228
+ })
1229
+ );
1230
+ return all.filter((fact) => {
1231
+ if (opts.from !== void 0 && !ownedImportsEqual(fact.from, opts.from)) return false;
1232
+ if (opts.imported !== void 0 && fact.imported !== opts.imported) return false;
1233
+ return true;
1234
+ });
1235
+ },
1236
+ jsxComponentPreferred: (opts = {}) => {
1237
+ const all = uniqueByLogicalIdentity(
1238
+ this.byKind("jsx_component_preferred"),
1239
+ (fact) => canonicalJson({
1240
+ from: canonicalizeOwnedComponentId(fact.from),
1241
+ to: canonicalizeOwnedComponentId(fact.to),
1242
+ because: fact.because,
1243
+ severity: fact.severity
1244
+ })
1245
+ );
1246
+ return opts.from === void 0 ? all : all.filter((fact) => ownedComponentIdsEqual(fact.from, opts.from));
1247
+ }
1248
+ };
1249
+ // ---------------------------------------------------------------------
1250
+ // Token registry queries
1251
+ // ---------------------------------------------------------------------
1252
+ tokens = {
1253
+ list: () => this.byKind("token_definition"),
1254
+ byCategory: (category) => this.byKind("token_definition").filter((t) => t.category === category)
1255
+ };
1256
+ // ---------------------------------------------------------------------
1257
+ // Internals
1258
+ // ---------------------------------------------------------------------
1259
+ byComponentOfKind(componentId2, kind) {
1260
+ const ids = this.idsByComponent.get(logicalComponentId(componentId2));
1261
+ if (!ids) return [];
1262
+ const out = [];
1263
+ const seen = /* @__PURE__ */ new Set();
1264
+ for (const id of ids) {
1265
+ const fact = this.facts.get(id);
1266
+ if (fact && fact.kind === kind) {
1267
+ const logicalFact = hasComponentId(fact) ? {
1268
+ ...fact,
1269
+ id: void 0,
1270
+ componentId: logicalComponentId(fact.componentId)
1271
+ } : { ...fact, id: void 0 };
1272
+ const key = canonicalJson(logicalFact);
1273
+ if (seen.has(key)) continue;
1274
+ seen.add(key);
1275
+ out.push(fact);
1276
+ }
1277
+ }
1278
+ return out;
1279
+ }
1280
+ };
1281
+ function describeFactForConflict(fact) {
1282
+ const parts = [`kind=${fact.kind}`];
1283
+ const maybeLocation = "location" in fact ? fact.location : void 0;
1284
+ if (maybeLocation && typeof maybeLocation === "object") {
1285
+ const loc = maybeLocation;
1286
+ if (typeof loc.file === "string") {
1287
+ const line = typeof loc.line === "number" ? `:${loc.line}` : "";
1288
+ const column = typeof loc.column === "number" ? `:${loc.column}` : "";
1289
+ parts.push(`location=${loc.file}${line}${column}`);
1290
+ }
1291
+ }
1292
+ if ("nodePath" in fact && typeof fact.nodePath === "string") {
1293
+ parts.push(`nodePath=${fact.nodePath}`);
1294
+ }
1295
+ if ("element" in fact && typeof fact.element === "string") {
1296
+ parts.push(`element=${fact.element}`);
1297
+ }
1298
+ return parts.join(" ");
1299
+ }
1300
+
1301
+ // src/governance.ts
1302
+ import { z } from "zod";
1303
+ var governanceSeveritySchema = z.enum(["error", "warn", "info"]);
1304
+ var scaleGovernanceRecordSchema = z.object({
1305
+ kind: z.literal("scale").default("scale"),
1306
+ name: z.string().min(1).optional(),
1307
+ unit: z.enum(["px", "rem"]),
1308
+ values: z.array(z.number()),
1309
+ rootFontSizePx: z.number().positive().optional(),
1310
+ emBasePx: z.number().positive().optional()
1311
+ });
1312
+ var styleRawColorsForbidRecordSchema = z.object({
1313
+ kind: z.literal("style.rawColors.forbid"),
1314
+ except: z.array(z.string()),
1315
+ prefer: z.enum(["token", "css-variable"]),
1316
+ severity: governanceSeveritySchema
1317
+ });
1318
+ var styleRawDimensionsForbidRecordSchema = z.object({
1319
+ kind: z.literal("style.rawDimensions.forbid"),
1320
+ appliesTo: z.array(z.string().min(1)),
1321
+ prefer: z.enum(["token", "css-variable"]),
1322
+ severity: governanceSeveritySchema
1323
+ });
1324
+ var styleRawSpacingMustMatchScaleRecordSchema = z.object({
1325
+ kind: z.literal("style.rawSpacing.mustMatchScale"),
1326
+ scale: z.string().min(1),
1327
+ appliesTo: z.array(z.string().min(1)),
1328
+ severity: governanceSeveritySchema
1329
+ });
1330
+ var styleFontSizeMustMatchScaleRecordSchema = z.object({
1331
+ kind: z.literal("style.fontSize.mustMatchScale"),
1332
+ scale: z.string().min(1),
1333
+ severity: governanceSeveritySchema
1334
+ });
1335
+ var styleCssVarsMustBeDefinedRecordSchema = z.object({
1336
+ kind: z.literal("style.cssVars.mustBeDefined"),
1337
+ severity: governanceSeveritySchema
1338
+ });
1339
+ var globalStyleGovernanceRecordSchema = z.discriminatedUnion("kind", [
1340
+ styleRawColorsForbidRecordSchema,
1341
+ styleRawDimensionsForbidRecordSchema,
1342
+ styleRawSpacingMustMatchScaleRecordSchema,
1343
+ styleFontSizeMustMatchScaleRecordSchema,
1344
+ styleCssVarsMustBeDefinedRecordSchema
1345
+ ]);
1346
+ var jsxUnknownPropsForbidRecordSchema = z.object({
1347
+ kind: z.literal("jsx.unknownProps.forbid"),
1348
+ severity: governanceSeveritySchema
1349
+ });
1350
+ var jsxInlineStyleForbidRawRecordSchema = z.object({
1351
+ kind: z.literal("jsx.inlineStyle.forbidRaw"),
1352
+ properties: z.array(z.string().min(1)),
1353
+ severity: governanceSeveritySchema
1354
+ });
1355
+ var jsxImportPathPreferRecordSchema = z.object({
1356
+ kind: z.literal("jsx.importPath.prefer"),
1357
+ from: z.string().min(1),
1358
+ to: z.string().min(1),
1359
+ imported: z.string().min(1).optional(),
1360
+ because: z.string().optional(),
1361
+ severity: governanceSeveritySchema
1362
+ });
1363
+ var jsxComponentPreferRecordSchema = z.object({
1364
+ kind: z.literal("jsx.component.prefer"),
1365
+ from: z.string().min(1),
1366
+ to: z.string().min(1),
1367
+ because: z.string().optional(),
1368
+ severity: governanceSeveritySchema
1369
+ });
1370
+ var globalJsxGovernanceRecordSchema = z.discriminatedUnion("kind", [
1371
+ jsxUnknownPropsForbidRecordSchema,
1372
+ jsxInlineStyleForbidRawRecordSchema,
1373
+ jsxImportPathPreferRecordSchema,
1374
+ jsxComponentPreferRecordSchema
1375
+ ]);
1376
+ var globalGovernanceRecordSchema = z.discriminatedUnion("kind", [
1377
+ scaleGovernanceRecordSchema,
1378
+ styleRawColorsForbidRecordSchema,
1379
+ styleRawDimensionsForbidRecordSchema,
1380
+ styleRawSpacingMustMatchScaleRecordSchema,
1381
+ styleFontSizeMustMatchScaleRecordSchema,
1382
+ styleCssVarsMustBeDefinedRecordSchema,
1383
+ jsxUnknownPropsForbidRecordSchema,
1384
+ jsxInlineStyleForbidRawRecordSchema,
1385
+ jsxImportPathPreferRecordSchema,
1386
+ jsxComponentPreferRecordSchema
1387
+ ]);
1388
+ var componentCapabilityRecordSchema = z.object({
1389
+ kind: z.literal("capability"),
1390
+ capability: z.string().min(1)
1391
+ });
1392
+ var propValueAvoidRecordSchema = z.object({
1393
+ kind: z.literal("prop.value.avoid"),
1394
+ prop: z.string().min(1),
1395
+ value: z.unknown(),
1396
+ because: z.string(),
1397
+ suggest: z.string().optional(),
1398
+ severity: governanceSeveritySchema
1399
+ });
1400
+ var propValueForbidRecordSchema = z.object({
1401
+ kind: z.literal("prop.value.forbid"),
1402
+ prop: z.string().min(1),
1403
+ value: z.unknown(),
1404
+ when: z.object({ path: z.string().min(1).optional() }).optional(),
1405
+ because: z.string(),
1406
+ fix: z.object({ replaceWith: z.unknown() }).optional(),
1407
+ severity: governanceSeveritySchema
1408
+ });
1409
+ var a11yRequireNameRecordSchema = z.object({
1410
+ kind: z.literal("a11y.requireName"),
1411
+ because: z.string(),
1412
+ severity: governanceSeveritySchema
1413
+ });
1414
+ var componentGovernanceRecordSchema = z.discriminatedUnion("kind", [
1415
+ componentCapabilityRecordSchema,
1416
+ propValueAvoidRecordSchema,
1417
+ propValueForbidRecordSchema,
1418
+ a11yRequireNameRecordSchema
1419
+ ]);
1420
+ var componentGovernanceRecordsSchema = z.array(componentGovernanceRecordSchema);
1421
+ var componentPolicyMatchSchema = z.object({
1422
+ componentKey: z.string().min(1).optional(),
1423
+ componentId: z.string().min(1).optional(),
1424
+ publicRef: z.string().min(1).optional(),
1425
+ importPath: z.string().min(1).optional(),
1426
+ sourcePath: z.string().min(1).optional(),
1427
+ path: z.string().min(1).optional(),
1428
+ exportName: z.string().min(1).optional(),
1429
+ export: z.string().min(1).optional(),
1430
+ name: z.string().min(1).optional(),
1431
+ scope: z.enum(["one", "all"]).optional()
1432
+ }).strict();
1433
+ var componentPolicyRecordSchema = z.object({
1434
+ rules: componentGovernanceRecordsSchema,
1435
+ status: z.enum(["active", "draft", "disabled"]).optional()
1436
+ }).strict();
1437
+ var componentPolicyOverrideSchema = z.object({
1438
+ match: componentPolicyMatchSchema,
1439
+ rules: componentGovernanceRecordsSchema,
1440
+ status: z.enum(["active", "draft", "disabled"]).optional()
1441
+ }).strict();
1442
+ var canonicalSourcePathSchema = z.string().min(1).superRefine((value, ctx) => {
1443
+ const error = portableRepoPathError(value);
1444
+ if (error) {
1445
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: error });
1446
+ }
1447
+ });
1448
+ var canonicalSourceRegistryHashSchema = z.string().regex(/^[0-9a-f]{64}$/);
1449
+ var canonicalSourceSchema = z.discriminatedUnion("kind", [
1450
+ z.object({
1451
+ kind: z.literal("npm"),
1452
+ specifier: z.string().min(1),
1453
+ implementationPath: z.string().min(1).optional(),
1454
+ include: z.array(z.string().min(1)).optional(),
1455
+ exclude: z.array(z.string().min(1)).optional()
1456
+ }),
1457
+ z.object({
1458
+ kind: z.literal("directory"),
1459
+ path: z.string().min(1),
1460
+ include: z.array(z.string().min(1)).optional(),
1461
+ exclude: z.array(z.string().min(1)).optional()
1462
+ }),
1463
+ z.object({
1464
+ kind: z.literal("registry"),
1465
+ registryId: z.string().min(1),
1466
+ registryHash: canonicalSourceRegistryHashSchema.optional(),
1467
+ receiptPath: canonicalSourcePathSchema.default(".fragments/registry-lock.json"),
1468
+ installPath: canonicalSourcePathSchema,
1469
+ importPath: z.string().min(1).optional(),
1470
+ include: z.array(z.string().min(1)).optional(),
1471
+ exclude: z.array(z.string().min(1)).optional(),
1472
+ severity: governanceSeveritySchema.optional()
1473
+ })
1474
+ ]);
1475
+ var governanceConfigSchema = z.object({
1476
+ extends: z.array(z.string()).optional(),
1477
+ severity: governanceSeveritySchema.optional(),
1478
+ rules: z.record(z.string(), z.unknown()).optional(),
1479
+ agents: z.record(
1480
+ z.string(),
1481
+ z.object({
1482
+ rules: z.record(z.string(), z.unknown()).optional()
1483
+ }).passthrough()
1484
+ ).optional(),
1485
+ audit: z.object({}).passthrough().optional(),
1486
+ runners: z.record(z.string(), z.object({}).passthrough()).optional(),
1487
+ canonicalSources: z.array(canonicalSourceSchema).optional(),
1488
+ presets: z.array(z.string()).optional(),
1489
+ scales: z.record(z.string(), scaleGovernanceRecordSchema).optional(),
1490
+ styles: z.array(globalStyleGovernanceRecordSchema).optional(),
1491
+ jsx: z.array(globalJsxGovernanceRecordSchema).optional(),
1492
+ tailwind: z.object({
1493
+ palette: z.object({
1494
+ allow: z.array(z.string().min(1)).optional(),
1495
+ deny: z.array(z.string().min(1)).optional()
1496
+ }).optional()
1497
+ }).passthrough().optional(),
1498
+ agent: z.object({
1499
+ repairOrder: z.array(z.string().min(1)).optional()
1500
+ }).passthrough().optional(),
1501
+ components: z.record(componentPolicyRecordSchema).optional(),
1502
+ overrides: z.array(componentPolicyOverrideSchema).optional(),
1503
+ ci: z.object({
1504
+ failOnWarnings: z.boolean().optional()
1505
+ }).passthrough().optional()
1506
+ }).strict();
1507
+ function createComponentGovernanceBuilder() {
1508
+ return {
1509
+ capability(capability) {
1510
+ return { kind: "capability", capability };
1511
+ },
1512
+ prop(prop) {
1513
+ return {
1514
+ avoid(value, options = {}) {
1515
+ return {
1516
+ kind: "prop.value.avoid",
1517
+ prop,
1518
+ value,
1519
+ because: options.because ?? "",
1520
+ suggest: options.suggest,
1521
+ severity: options.severity ?? "warn"
1522
+ };
1523
+ },
1524
+ forbid(value, options = {}) {
1525
+ return {
1526
+ kind: "prop.value.forbid",
1527
+ prop,
1528
+ value,
1529
+ when: options.when,
1530
+ because: options.because ?? "",
1531
+ fix: options.fix,
1532
+ severity: options.severity ?? "error"
1533
+ };
1534
+ }
1535
+ };
1536
+ },
1537
+ accessibility() {
1538
+ return {
1539
+ requireName(options = {}) {
1540
+ return {
1541
+ kind: "a11y.requireName",
1542
+ because: options.because ?? "",
1543
+ severity: options.severity ?? "error"
1544
+ };
1545
+ }
1546
+ };
1547
+ }
1548
+ };
1549
+ }
1550
+ function resolveComponentGovernance(definition) {
1551
+ const builder = createComponentGovernanceBuilder();
1552
+ const records = definition.govern ? definition.govern(builder) : definition.governance ?? [];
1553
+ return componentGovernanceRecordsSchema.parse(records);
1554
+ }
1555
+ function normalizeGovernanceConfig(config) {
1556
+ if (!config.govern?.scales) {
1557
+ return config;
1558
+ }
1559
+ const scales = Object.fromEntries(
1560
+ Object.entries(config.govern.scales).map(([name, scale]) => {
1561
+ if (!scale || typeof scale !== "object") {
1562
+ return [name, scale];
1563
+ }
1564
+ return [name, { ...scale, name: scale.name ?? name }];
1565
+ })
1566
+ );
1567
+ return {
1568
+ ...config,
1569
+ govern: {
1570
+ ...config.govern,
1571
+ scales
1572
+ }
1573
+ };
1574
+ }
1575
+ var g = {
1576
+ scale: {
1577
+ px(values) {
1578
+ return { kind: "scale", unit: "px", values: [...values] };
1579
+ },
1580
+ rem(values) {
1581
+ return { kind: "scale", unit: "rem", values: [...values] };
1582
+ }
1583
+ },
1584
+ styles: {
1585
+ rawColors() {
1586
+ return {
1587
+ forbid(options = {}) {
1588
+ return {
1589
+ kind: "style.rawColors.forbid",
1590
+ except: options.except ?? [],
1591
+ prefer: options.prefer ?? "token",
1592
+ severity: options.severity ?? "error"
1593
+ };
1594
+ }
1595
+ };
1596
+ },
1597
+ rawSpacing() {
1598
+ return {
1599
+ mustMatchScale(scale, options = {}) {
1600
+ return {
1601
+ kind: "style.rawSpacing.mustMatchScale",
1602
+ scale,
1603
+ appliesTo: options.appliesTo ?? [],
1604
+ severity: options.severity ?? "error"
1605
+ };
1606
+ }
1607
+ };
1608
+ },
1609
+ fontSize() {
1610
+ return {
1611
+ mustMatchScale(scale, options = {}) {
1612
+ return {
1613
+ kind: "style.fontSize.mustMatchScale",
1614
+ scale,
1615
+ severity: options.severity ?? "warn"
1616
+ };
1617
+ }
1618
+ };
1619
+ },
1620
+ cssVars() {
1621
+ return {
1622
+ mustBeDefined(options = {}) {
1623
+ return {
1624
+ kind: "style.cssVars.mustBeDefined",
1625
+ severity: options.severity ?? "error"
1626
+ };
1627
+ }
1628
+ };
1629
+ }
1630
+ },
1631
+ jsx: {
1632
+ unknownProps() {
1633
+ return {
1634
+ forbid(options = {}) {
1635
+ return {
1636
+ kind: "jsx.unknownProps.forbid",
1637
+ severity: options.severity ?? "error"
1638
+ };
1639
+ }
1640
+ };
1641
+ },
1642
+ inlineStyle() {
1643
+ return {
1644
+ forbidRaw(properties, options = {}) {
1645
+ return {
1646
+ kind: "jsx.inlineStyle.forbidRaw",
1647
+ properties: [...properties],
1648
+ severity: options.severity ?? "error"
1649
+ };
1650
+ }
1651
+ };
1652
+ },
1653
+ importPath() {
1654
+ return {
1655
+ prefer(from, to, options = {}) {
1656
+ return {
1657
+ kind: "jsx.importPath.prefer",
1658
+ from,
1659
+ to,
1660
+ imported: options.imported,
1661
+ because: options.because,
1662
+ severity: options.severity ?? "error"
1663
+ };
1664
+ }
1665
+ };
1666
+ },
1667
+ component() {
1668
+ return {
1669
+ prefer(from, to, options = {}) {
1670
+ return {
1671
+ kind: "jsx.component.prefer",
1672
+ from,
1673
+ to,
1674
+ because: options.because,
1675
+ severity: options.severity ?? "error"
1676
+ };
1677
+ }
1678
+ };
1679
+ }
1680
+ },
1681
+ component: createComponentGovernanceBuilder()
1682
+ };
1683
+
1684
+ // src/facts/compile.ts
1685
+ function compileGlobalGovernanceFacts(govern) {
1686
+ if (!govern) return [];
1687
+ const out = [];
1688
+ if (govern.scales) {
1689
+ for (const [name, scale] of Object.entries(govern.scales)) {
1690
+ out.push(
1691
+ makeScaleFact({
1692
+ name,
1693
+ unit: scale.unit,
1694
+ rootFontSizePx: scale.rootFontSizePx,
1695
+ emBasePx: scale.emBasePx
1696
+ })
1697
+ );
1698
+ for (const value of scale.values) {
1699
+ out.push(makeScaleValueFact({ scale: name, value }));
1700
+ }
1701
+ }
1702
+ }
1703
+ if (govern.styles) {
1704
+ for (const style of govern.styles) {
1705
+ switch (style.kind) {
1706
+ case "style.rawColors.forbid":
1707
+ out.push(
1708
+ makeStyleRawColorForbiddenFact({
1709
+ except: style.except,
1710
+ prefer: style.prefer,
1711
+ severity: style.severity
1712
+ })
1713
+ );
1714
+ break;
1715
+ case "style.rawDimensions.forbid":
1716
+ out.push(
1717
+ makeStyleRawDimensionForbiddenFact({
1718
+ appliesTo: style.appliesTo,
1719
+ prefer: style.prefer,
1720
+ severity: style.severity
1721
+ })
1722
+ );
1723
+ break;
1724
+ case "style.rawSpacing.mustMatchScale":
1725
+ for (const property of style.appliesTo) {
1726
+ out.push(
1727
+ makeStylePropertyScaleFact({
1728
+ property,
1729
+ scale: style.scale,
1730
+ severity: style.severity
1731
+ })
1732
+ );
1733
+ }
1734
+ break;
1735
+ case "style.fontSize.mustMatchScale":
1736
+ out.push(makeStyleFontSizeScaleFact({ scale: style.scale, severity: style.severity }));
1737
+ break;
1738
+ case "style.cssVars.mustBeDefined":
1739
+ out.push(makeStyleCssVarsMustBeDefinedFact({ severity: style.severity }));
1740
+ break;
1741
+ }
1742
+ }
1743
+ }
1744
+ if (govern.jsx) {
1745
+ for (const jsx of govern.jsx) {
1746
+ switch (jsx.kind) {
1747
+ case "jsx.unknownProps.forbid":
1748
+ out.push(makeJsxUnknownPropsForbiddenFact({ severity: jsx.severity }));
1749
+ break;
1750
+ case "jsx.inlineStyle.forbidRaw":
1751
+ for (const property of jsx.properties) {
1752
+ out.push(
1753
+ makeJsxInlineStyleForbiddenRawFact({
1754
+ property,
1755
+ severity: jsx.severity
1756
+ })
1757
+ );
1758
+ }
1759
+ break;
1760
+ case "jsx.importPath.prefer":
1761
+ out.push(
1762
+ makeJsxImportPathPreferredFact({
1763
+ from: jsx.from,
1764
+ to: jsx.to,
1765
+ imported: jsx.imported,
1766
+ because: jsx.because,
1767
+ severity: jsx.severity
1768
+ })
1769
+ );
1770
+ break;
1771
+ case "jsx.component.prefer":
1772
+ out.push(
1773
+ makeJsxComponentPreferredFact({
1774
+ from: asComponentId(jsx.from),
1775
+ to: asComponentId(jsx.to),
1776
+ because: jsx.because,
1777
+ severity: jsx.severity
1778
+ })
1779
+ );
1780
+ break;
1781
+ }
1782
+ }
1783
+ }
1784
+ const tailwindPalette = govern.tailwind?.palette;
1785
+ const forbiddenPaletteSeverity = ruleSeverity(
1786
+ govern.rules?.["tailwind/forbidden-palette"],
1787
+ govern.severity ?? "warn"
1788
+ );
1789
+ if (tailwindPalette?.allow?.length) {
1790
+ out.push(
1791
+ makeTailwindPaletteAllowFact({
1792
+ patterns: tailwindPalette.allow,
1793
+ severity: forbiddenPaletteSeverity
1794
+ })
1795
+ );
1796
+ }
1797
+ if (tailwindPalette?.deny?.length) {
1798
+ out.push(
1799
+ makeTailwindPaletteDenyFact({
1800
+ patterns: tailwindPalette.deny,
1801
+ severity: forbiddenPaletteSeverity
1802
+ })
1803
+ );
1804
+ }
1805
+ const unknownClassSeverity = enabledRuleSeverity(
1806
+ govern.rules?.["tailwind/unknown-class"],
1807
+ "info"
1808
+ );
1809
+ if (unknownClassSeverity) {
1810
+ out.push(makeTailwindUnknownClassEnabledFact({ severity: unknownClassSeverity }));
1811
+ }
1812
+ out.push(...compileRuleConfigFacts(govern));
1813
+ return out;
1814
+ }
1815
+ var RULE_FAMILY_MEMBERS = {
1816
+ "tokens/hardcoded-values": [
1817
+ "styles/no-raw-color",
1818
+ "styles/no-raw-spacing",
1819
+ "tokens/require-dual-fallback",
1820
+ "theme/no-theme-coupled-literal"
1821
+ ],
1822
+ "components/usage": [
1823
+ "components/forbidden-prop-value",
1824
+ "components/preferred-component",
1825
+ "components/unknown-prop",
1826
+ "props/invalid-value"
1827
+ ],
1828
+ "a11y/wcag": ["a11y/required-accessible-name"]
1829
+ };
1830
+ function compileRuleConfigFacts(govern) {
1831
+ const rules = govern.rules;
1832
+ if (!rules && !govern.canonicalSources?.length) return [];
1833
+ const configs = /* @__PURE__ */ new Map();
1834
+ for (const [ruleId, value] of Object.entries(rules ?? {})) {
1835
+ const family = RULE_FAMILY_MEMBERS[ruleId];
1836
+ if (!family) continue;
1837
+ const config = ruleConfigFromValue(value, govern.severity ?? "warn");
1838
+ for (const memberRuleId of family) configs.set(memberRuleId, config);
1839
+ }
1840
+ for (const [ruleId, value] of Object.entries(rules ?? {})) {
1841
+ configs.set(ruleId, ruleConfigFromValue(value, govern.severity ?? "warn"));
1842
+ }
1843
+ if (govern.canonicalSources?.length && configs.get("components/prefer-library")?.enabled !== false) {
1844
+ const existing = configs.get("components/prefer-library");
1845
+ configs.set("components/prefer-library", {
1846
+ enabled: true,
1847
+ severity: existing?.severity ?? govern.severity ?? "warn",
1848
+ options: {
1849
+ ...existing?.options,
1850
+ canonicalSources: existing?.options?.canonicalSources ?? govern.canonicalSources
1851
+ }
1852
+ });
1853
+ }
1854
+ return [...configs.entries()].map(
1855
+ ([ruleId, config]) => makeGovernanceRuleConfigFact({
1856
+ ruleId,
1857
+ enabled: config.enabled,
1858
+ severity: config.severity,
1859
+ options: config.options
1860
+ })
1861
+ );
1862
+ }
1863
+ function ruleConfigFromValue(value, fallbackSeverity) {
1864
+ if (value === false) return { enabled: false };
1865
+ if (value === true) return { enabled: true, severity: fallbackSeverity };
1866
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1867
+ return { enabled: true, severity: fallbackSeverity };
1868
+ }
1869
+ const record = value;
1870
+ const options = record.options && typeof record.options === "object" && !Array.isArray(record.options) ? record.options : void 0;
1871
+ return {
1872
+ enabled: record.enabled !== false,
1873
+ severity: parseSeverity(record.severity) ?? fallbackSeverity,
1874
+ options
1875
+ };
1876
+ }
1877
+ function enabledRuleSeverity(value, fallback) {
1878
+ if (value === true) return fallback;
1879
+ if (!value || typeof value !== "object") return null;
1880
+ const record = value;
1881
+ if (record.enabled === false) return null;
1882
+ if (record.enabled !== true) return null;
1883
+ return parseSeverity(record.severity) ?? fallback;
1884
+ }
1885
+ function ruleSeverity(value, fallback) {
1886
+ if (!value || typeof value !== "object") return fallback;
1887
+ return parseSeverity(value.severity) ?? fallback;
1888
+ }
1889
+ function parseSeverity(value) {
1890
+ if (value === "warning") return "warn";
1891
+ return value === "error" || value === "warn" || value === "info" ? value : void 0;
1892
+ }
1893
+ function isResolvedGovernedDefinition(value) {
1894
+ if (!value || typeof value !== "object") return false;
1895
+ return "governance" in value && Array.isArray(value.governance);
1896
+ }
1897
+ function isGovernedDefinition(value) {
1898
+ if (!value || typeof value !== "object") return false;
1899
+ return "govern" in value || "governance" in value;
1900
+ }
1901
+ function toComponentFactSource(input) {
1902
+ if (isResolvedGovernedDefinition(input)) {
1903
+ return {
1904
+ meta: input.meta,
1905
+ guidance: input.guidance,
1906
+ props: input.props ?? {},
1907
+ governance: input.governance
1908
+ };
1909
+ }
1910
+ if (isGovernedDefinition(input)) {
1911
+ return {
1912
+ meta: input.meta,
1913
+ guidance: input.guidance,
1914
+ props: input.props ?? {},
1915
+ governance: resolveComponentGovernance(input)
1916
+ };
1917
+ }
1918
+ return {
1919
+ meta: input.meta,
1920
+ guidance: input.guidance ?? input.usage,
1921
+ props: input.props,
1922
+ governance: input.governance,
1923
+ filePath: input.filePath
1924
+ };
1925
+ }
1926
+ function compileComponentFacts(componentId2, input) {
1927
+ const source = toComponentFactSource(input);
1928
+ const facts = [];
1929
+ facts.push(
1930
+ makeComponentMetadataFact({
1931
+ componentId: componentId2,
1932
+ name: source.meta.name,
1933
+ description: source.meta.description,
1934
+ category: source.meta.category,
1935
+ filePath: source.filePath
1936
+ })
1937
+ );
1938
+ for (const [propName, prop] of Object.entries(source.props ?? {})) {
1939
+ facts.push(
1940
+ makePropMetadataFact({
1941
+ componentId: componentId2,
1942
+ prop: propName,
1943
+ type: prop.type,
1944
+ values: prop.values ? [...prop.values] : void 0,
1945
+ required: prop.required ?? false,
1946
+ default: prop.default,
1947
+ description: prop.description
1948
+ })
1949
+ );
1950
+ }
1951
+ for (const record of source.governance ?? []) {
1952
+ const fact = compileComponentGovernanceRecord(componentId2, record);
1953
+ if (fact) facts.push(fact);
1954
+ }
1955
+ return facts;
1956
+ }
1957
+ function compileComponentGovernanceRecord(componentId2, record) {
1958
+ switch (record.kind) {
1959
+ case "capability":
1960
+ return makeComponentCapabilityFact({ componentId: componentId2, capability: record.capability });
1961
+ case "prop.value.avoid":
1962
+ return makePropValueAvoidedFact({
1963
+ componentId: componentId2,
1964
+ prop: record.prop,
1965
+ value: record.value,
1966
+ because: record.because,
1967
+ suggest: record.suggest,
1968
+ severity: record.severity
1969
+ });
1970
+ case "prop.value.forbid":
1971
+ return makePropValueForbiddenFact({
1972
+ componentId: componentId2,
1973
+ prop: record.prop,
1974
+ value: record.value,
1975
+ pathPattern: record.when?.path,
1976
+ because: record.because,
1977
+ replaceWith: record.fix?.replaceWith,
1978
+ severity: record.severity
1979
+ });
1980
+ case "a11y.requireName":
1981
+ return makeA11yNameRequiredFact({
1982
+ componentId: componentId2,
1983
+ because: record.because,
1984
+ severity: record.severity
1985
+ });
1986
+ }
1987
+ }
1988
+
1989
+ // src/codes/source-finding-bridge.ts
1990
+ function bridgeSourceViolation(input) {
1991
+ const ruleId = input.canonicalRuleId ?? input.violation.rule;
1992
+ const code2 = byRuleId.get(ruleId);
1993
+ const violation = normalizeViolation({
1994
+ ...input.violation,
1995
+ code: input.violation.code ?? code2?.code,
1996
+ helpUrl: input.violation.helpUrl ?? code2?.explainUrl
1997
+ });
1998
+ const location = locationFromViolation(violation);
1999
+ const fingerprint = hash64Hex(
2000
+ canonicalJson({
2001
+ ruleId,
2002
+ legacyRule: violation.rule,
2003
+ nodeId: violation.nodeId,
2004
+ nodeType: violation.nodeType,
2005
+ prop: violation.prop,
2006
+ path: violation.path,
2007
+ filePath: violation.filePath,
2008
+ line: violation.line,
2009
+ column: violation.column,
2010
+ rawValue: violation.rawValue,
2011
+ message: violation.message
2012
+ })
2013
+ );
2014
+ return {
2015
+ violation,
2016
+ finding: normalizeFinding({
2017
+ ruleId,
2018
+ ruleVersion: "1.0.0",
2019
+ severity: violation.severity,
2020
+ level: violation.level,
2021
+ code: code2?.code,
2022
+ helpUrl: code2?.explainUrl,
2023
+ message: violation.message,
2024
+ fingerprint,
2025
+ location,
2026
+ evidence: [
2027
+ {
2028
+ factId: [
2029
+ "source",
2030
+ input.validator ?? "validator",
2031
+ ruleId,
2032
+ violation.filePath ?? violation.nodeId,
2033
+ violation.line ?? 0,
2034
+ violation.column ?? 0,
2035
+ violation.prop ?? violation.path ?? ""
2036
+ ].join(":"),
2037
+ fact: {
2038
+ kind: "source_violation",
2039
+ location
2040
+ }
2041
+ }
2042
+ ],
2043
+ attributes: {
2044
+ source: "source-validator",
2045
+ validator: input.validator,
2046
+ legacyRule: violation.rule === ruleId ? void 0 : violation.rule,
2047
+ nodeId: violation.nodeId,
2048
+ nodeType: violation.nodeType,
2049
+ prop: violation.prop,
2050
+ path: violation.path,
2051
+ rawValue: violation.rawValue,
2052
+ legacySuggestion: violation.suggestion,
2053
+ ...input.attributes
2054
+ }
2055
+ })
2056
+ };
2057
+ }
2058
+ function bridgeSourceViolations(violations, options = {}) {
2059
+ const bridged = violations.map(
2060
+ (violation) => bridgeSourceViolation({ ...options, violation })
2061
+ );
2062
+ return {
2063
+ violations: bridged.map((entry) => entry.violation),
2064
+ findings: bridged.map((entry) => entry.finding)
2065
+ };
2066
+ }
2067
+ function locationFromViolation(violation) {
2068
+ return {
2069
+ file: violation.filePath ?? "<source>",
2070
+ line: violation.line ?? 0,
2071
+ column: violation.column ?? 0
2072
+ };
2073
+ }
2074
+
2075
+ export {
2076
+ EXPLAIN_URL_BASE,
2077
+ CODES,
2078
+ byCode,
2079
+ byRuleId,
2080
+ explainUrlForCode,
2081
+ canonicalJson,
2082
+ hash64Hex,
2083
+ componentId,
2084
+ asComponentId,
2085
+ factId,
2086
+ makeComponentMetadataFact,
2087
+ makeComponentCapabilityFact,
2088
+ makeCanonicalCandidateFact,
2089
+ makeCanonicalMappingFact,
2090
+ makeStructuralConfidenceFact,
2091
+ makeCanonicalReplacementFact,
2092
+ makePropMetadataFact,
2093
+ makePropValueAvoidedFact,
2094
+ makePropValueForbiddenFact,
2095
+ makeA11yNameRequiredFact,
2096
+ makeScaleFact,
2097
+ makeScaleValueFact,
2098
+ makeStyleRawColorForbiddenFact,
2099
+ makeStylePropertyScaleFact,
2100
+ makeStyleFontSizeScaleFact,
2101
+ makeStyleCssVarsMustBeDefinedFact,
2102
+ makeContractTokenFact,
2103
+ makeJsxUnknownPropsForbiddenFact,
2104
+ makeJsxInlineStyleForbiddenRawFact,
2105
+ makeJsxImportPathPreferredFact,
2106
+ makeJsxComponentPreferredFact,
2107
+ makeTokenDefinitionFact,
2108
+ makeTailwindPaletteAllowFact,
2109
+ makeTailwindPaletteDenyFact,
2110
+ makeTailwindUnknownClassEnabledFact,
2111
+ makeUsageNodeFact,
2112
+ makeUsageComponentFact,
2113
+ makeUsageImportFact,
2114
+ makeUsagePropResolvedFact,
2115
+ makeUsageInlineStyleFact,
2116
+ makeUsageTextChildFact,
2117
+ makeClassNameLiteralFact,
2118
+ makeClassNameDynamicFact,
2119
+ makeTailwindClassFact,
2120
+ makeThemeDeclarationFact,
2121
+ makeTailwindTokenResolvedFact,
2122
+ makeStyleDeclarationFact,
2123
+ makeStyleUnsupportedFact,
2124
+ ownedImportsEqual,
2125
+ ownedImportMatchesRoot,
2126
+ ownedComponentIdsEqual,
2127
+ matchesGlob,
2128
+ FactIndex,
2129
+ governanceSeveritySchema,
2130
+ scaleGovernanceRecordSchema,
2131
+ globalStyleGovernanceRecordSchema,
2132
+ globalJsxGovernanceRecordSchema,
2133
+ globalGovernanceRecordSchema,
2134
+ componentGovernanceRecordSchema,
2135
+ componentGovernanceRecordsSchema,
2136
+ governanceConfigSchema,
2137
+ resolveComponentGovernance,
2138
+ normalizeGovernanceConfig,
2139
+ g,
2140
+ compileGlobalGovernanceFacts,
2141
+ compileComponentFacts,
2142
+ bridgeSourceViolation,
2143
+ bridgeSourceViolations
2144
+ };
2145
+ //# sourceMappingURL=chunk-243QYRUF.js.map