@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,463 @@
1
+ import type { Severity } from "../severity.js";
2
+ export type { Severity } from "../severity.js";
3
+
4
+ export const EXPLAIN_URL_BASE = "https://usefragments.com/errors/";
5
+
6
+ export type CodeCategory =
7
+ | "canonical-usage"
8
+ | "tokens"
9
+ | "a11y"
10
+ | "states"
11
+ | "composition"
12
+ | "props"
13
+ | "performance"
14
+ | "safety"
15
+ | "system";
16
+
17
+ export type CodeLifecycle = "experimental" | "stable" | "deprecated";
18
+
19
+ export interface FuiCode {
20
+ code: string;
21
+ ruleId: string;
22
+ category: CodeCategory;
23
+ defaultSeverity: Severity;
24
+ title: string;
25
+ explainUrl: string;
26
+ lifecycle: CodeLifecycle;
27
+ replacedBy?: string;
28
+ fixAvailable: boolean;
29
+ evidenceRequired: boolean;
30
+ }
31
+
32
+ function code(entry: Omit<FuiCode, "explainUrl"> & { explainUrl?: never }): FuiCode {
33
+ return {
34
+ ...entry,
35
+ explainUrl: `${EXPLAIN_URL_BASE}${entry.code}`,
36
+ };
37
+ }
38
+
39
+ export const CODES = [
40
+ code({
41
+ code: "FUI1001",
42
+ ruleId: "components/non-canonical",
43
+ category: "canonical-usage",
44
+ defaultSeverity: "serious",
45
+ title: "Component should use the canonical primitive",
46
+ lifecycle: "experimental",
47
+ fixAvailable: true,
48
+ evidenceRequired: true,
49
+ }),
50
+ code({
51
+ code: "FUI1002",
52
+ ruleId: "components/preferred-component",
53
+ category: "canonical-usage",
54
+ defaultSeverity: "serious",
55
+ title: "Preferred component should be used",
56
+ lifecycle: "experimental",
57
+ fixAvailable: true,
58
+ evidenceRequired: true,
59
+ }),
60
+ code({
61
+ code: "FUI1003",
62
+ ruleId: "imports/preferred-path",
63
+ category: "canonical-usage",
64
+ defaultSeverity: "moderate",
65
+ title: "Import should use the preferred path",
66
+ lifecycle: "experimental",
67
+ fixAvailable: true,
68
+ evidenceRequired: true,
69
+ }),
70
+ code({
71
+ code: "FUI1004",
72
+ ruleId: "components/prefer-library",
73
+ category: "canonical-usage",
74
+ defaultSeverity: "moderate",
75
+ title: "Library component should be preferred",
76
+ lifecycle: "experimental",
77
+ fixAvailable: true,
78
+ evidenceRequired: true,
79
+ }),
80
+ code({
81
+ code: "FUI1005",
82
+ ruleId: "components/allow",
83
+ category: "canonical-usage",
84
+ defaultSeverity: "serious",
85
+ title: "Component is not in the allowed set",
86
+ lifecycle: "experimental",
87
+ fixAvailable: false,
88
+ evidenceRequired: true,
89
+ }),
90
+ code({
91
+ code: "FUI1006",
92
+ ruleId: "components/deny",
93
+ category: "canonical-usage",
94
+ defaultSeverity: "serious",
95
+ title: "Component is denied by policy",
96
+ lifecycle: "experimental",
97
+ fixAvailable: false,
98
+ evidenceRequired: true,
99
+ }),
100
+ code({
101
+ code: "FUI2001",
102
+ ruleId: "tokens/require-design-tokens",
103
+ category: "tokens",
104
+ defaultSeverity: "serious",
105
+ title: "Design token is required",
106
+ lifecycle: "experimental",
107
+ fixAvailable: true,
108
+ evidenceRequired: true,
109
+ }),
110
+ code({
111
+ code: "FUI2002",
112
+ ruleId: "tokens/allowed-prefixes",
113
+ category: "tokens",
114
+ defaultSeverity: "moderate",
115
+ title: "Token prefix is not allowed",
116
+ lifecycle: "experimental",
117
+ fixAvailable: true,
118
+ evidenceRequired: true,
119
+ }),
120
+ code({
121
+ code: "FUI2003",
122
+ ruleId: "tokens/require-dual-fallback",
123
+ category: "tokens",
124
+ defaultSeverity: "serious",
125
+ title: "Token fallback is required",
126
+ lifecycle: "experimental",
127
+ fixAvailable: true,
128
+ evidenceRequired: true,
129
+ }),
130
+ code({
131
+ code: "FUI2004",
132
+ ruleId: "styles/no-raw-dimensions",
133
+ category: "tokens",
134
+ defaultSeverity: "serious",
135
+ title: "Raw dimension should use a token",
136
+ lifecycle: "experimental",
137
+ fixAvailable: true,
138
+ evidenceRequired: true,
139
+ }),
140
+ code({
141
+ code: "FUI2005",
142
+ ruleId: "styles/no-raw-color",
143
+ category: "tokens",
144
+ defaultSeverity: "serious",
145
+ title: "Raw color should use a token",
146
+ lifecycle: "experimental",
147
+ fixAvailable: true,
148
+ evidenceRequired: true,
149
+ }),
150
+ code({
151
+ code: "FUI2006",
152
+ ruleId: "styles/no-raw-spacing",
153
+ category: "tokens",
154
+ defaultSeverity: "serious",
155
+ title: "Raw spacing should use the scale",
156
+ lifecycle: "experimental",
157
+ fixAvailable: true,
158
+ evidenceRequired: true,
159
+ }),
160
+ code({
161
+ code: "FUI2007",
162
+ ruleId: "tailwind/arbitrary-color",
163
+ category: "tokens",
164
+ defaultSeverity: "moderate",
165
+ title: "Tailwind arbitrary color should use a token",
166
+ lifecycle: "experimental",
167
+ fixAvailable: true,
168
+ evidenceRequired: true,
169
+ }),
170
+ code({
171
+ code: "FUI2008",
172
+ ruleId: "tailwind/arbitrary-spacing",
173
+ category: "tokens",
174
+ defaultSeverity: "moderate",
175
+ title: "Tailwind arbitrary spacing should use the scale",
176
+ lifecycle: "experimental",
177
+ fixAvailable: true,
178
+ evidenceRequired: true,
179
+ }),
180
+ code({
181
+ code: "FUI2009",
182
+ ruleId: "tailwind/forbidden-palette",
183
+ category: "tokens",
184
+ defaultSeverity: "moderate",
185
+ title: "Tailwind palette is forbidden",
186
+ lifecycle: "experimental",
187
+ fixAvailable: false,
188
+ evidenceRequired: true,
189
+ }),
190
+ code({
191
+ code: "FUI2010",
192
+ ruleId: "tailwind/raw-color-via-token",
193
+ category: "tokens",
194
+ defaultSeverity: "moderate",
195
+ title: "Tailwind color should use a resolved token",
196
+ lifecycle: "experimental",
197
+ fixAvailable: true,
198
+ evidenceRequired: true,
199
+ }),
200
+ code({
201
+ code: "FUI2011",
202
+ ruleId: "tailwind/off-scale-spacing-token",
203
+ category: "tokens",
204
+ defaultSeverity: "moderate",
205
+ title: "Tailwind spacing token is off scale",
206
+ lifecycle: "experimental",
207
+ fixAvailable: true,
208
+ evidenceRequired: true,
209
+ }),
210
+ code({
211
+ code: "FUI2012",
212
+ ruleId: "tailwind/unknown-class",
213
+ category: "tokens",
214
+ defaultSeverity: "moderate",
215
+ title: "Tailwind class is unknown",
216
+ lifecycle: "experimental",
217
+ fixAvailable: false,
218
+ evidenceRequired: true,
219
+ }),
220
+ code({
221
+ code: "FUI2013",
222
+ ruleId: "brand/enforce-seeds",
223
+ category: "tokens",
224
+ defaultSeverity: "serious",
225
+ title: "Brand seed token is required",
226
+ lifecycle: "experimental",
227
+ fixAvailable: true,
228
+ evidenceRequired: true,
229
+ }),
230
+ code({
231
+ code: "FUI2014",
232
+ ruleId: "theme/no-theme-coupled-literal",
233
+ category: "tokens",
234
+ defaultSeverity: "moderate",
235
+ title: "Theme-coupled literal should use a semantic token",
236
+ lifecycle: "experimental",
237
+ fixAvailable: false,
238
+ evidenceRequired: true,
239
+ }),
240
+ code({
241
+ code: "FUI2015",
242
+ ruleId: "tokens/css-vars-must-be-defined",
243
+ category: "tokens",
244
+ defaultSeverity: "moderate",
245
+ title: "CSS variable is not in the contract vocabulary",
246
+ lifecycle: "experimental",
247
+ fixAvailable: false,
248
+ evidenceRequired: true,
249
+ }),
250
+ code({
251
+ code: "FUI3001",
252
+ ruleId: "a11y/required-accessible-name",
253
+ category: "a11y",
254
+ defaultSeverity: "serious",
255
+ title: "Accessible name is required",
256
+ lifecycle: "experimental",
257
+ fixAvailable: false,
258
+ evidenceRequired: true,
259
+ }),
260
+ code({
261
+ code: "FUI3002",
262
+ ruleId: "a11y/standard",
263
+ category: "a11y",
264
+ defaultSeverity: "serious",
265
+ title: "Accessibility standard failed",
266
+ lifecycle: "experimental",
267
+ fixAvailable: false,
268
+ evidenceRequired: true,
269
+ }),
270
+ code({
271
+ code: "FUI5001",
272
+ ruleId: "components/required-children",
273
+ category: "composition",
274
+ defaultSeverity: "serious",
275
+ title: "Required child component is missing",
276
+ lifecycle: "experimental",
277
+ fixAvailable: false,
278
+ evidenceRequired: true,
279
+ }),
280
+ code({
281
+ code: "FUI5002",
282
+ ruleId: "components/canonical-usage",
283
+ category: "composition",
284
+ defaultSeverity: "serious",
285
+ title: "Component should use canonical composition",
286
+ lifecycle: "deprecated",
287
+ replacedBy: "FUI1001",
288
+ fixAvailable: true,
289
+ evidenceRequired: true,
290
+ }),
291
+ code({
292
+ code: "FUI5003",
293
+ ruleId: "composition/cardinality",
294
+ category: "composition",
295
+ defaultSeverity: "moderate",
296
+ title: "Too many of a component in one region",
297
+ lifecycle: "experimental",
298
+ fixAvailable: false,
299
+ evidenceRequired: true,
300
+ }),
301
+ code({
302
+ code: "FUI5004",
303
+ ruleId: "composition/co-occurrence",
304
+ category: "composition",
305
+ defaultSeverity: "moderate",
306
+ title: "Required companion component is missing from the region",
307
+ lifecycle: "experimental",
308
+ fixAvailable: false,
309
+ evidenceRequired: true,
310
+ }),
311
+ code({
312
+ code: "FUI6001",
313
+ ruleId: "props/unknown",
314
+ category: "props",
315
+ defaultSeverity: "moderate",
316
+ title: "Prop is unknown",
317
+ lifecycle: "experimental",
318
+ fixAvailable: false,
319
+ evidenceRequired: true,
320
+ }),
321
+ code({
322
+ code: "FUI6002",
323
+ ruleId: "props/invalid-value",
324
+ category: "props",
325
+ defaultSeverity: "serious",
326
+ title: "Prop value is invalid",
327
+ lifecycle: "experimental",
328
+ fixAvailable: true,
329
+ evidenceRequired: true,
330
+ }),
331
+ code({
332
+ code: "FUI6003",
333
+ ruleId: "props/banned-pattern",
334
+ category: "props",
335
+ defaultSeverity: "serious",
336
+ title: "Prop pattern is banned",
337
+ lifecycle: "experimental",
338
+ fixAvailable: false,
339
+ evidenceRequired: true,
340
+ }),
341
+ code({
342
+ code: "FUI6004",
343
+ ruleId: "components/forbidden-prop-value",
344
+ category: "props",
345
+ defaultSeverity: "serious",
346
+ title: "Prop value is forbidden",
347
+ lifecycle: "experimental",
348
+ fixAvailable: true,
349
+ evidenceRequired: true,
350
+ }),
351
+ code({
352
+ code: "FUI6005",
353
+ ruleId: "components/unknown-prop",
354
+ category: "props",
355
+ defaultSeverity: "moderate",
356
+ title: "Component prop is unknown",
357
+ lifecycle: "experimental",
358
+ fixAvailable: false,
359
+ evidenceRequired: true,
360
+ }),
361
+ code({
362
+ code: "FUI8001",
363
+ ruleId: "safety/block-event-handlers",
364
+ category: "safety",
365
+ defaultSeverity: "serious",
366
+ title: "Event handler prop is blocked",
367
+ lifecycle: "experimental",
368
+ fixAvailable: true,
369
+ evidenceRequired: true,
370
+ }),
371
+ code({
372
+ code: "FUI8002",
373
+ ruleId: "safety/block-dangerous-props",
374
+ category: "safety",
375
+ defaultSeverity: "critical",
376
+ title: "Dangerous prop is blocked",
377
+ lifecycle: "experimental",
378
+ fixAvailable: true,
379
+ evidenceRequired: true,
380
+ }),
381
+ code({
382
+ code: "FUI8003",
383
+ ruleId: "safety/block-controlled-props",
384
+ category: "safety",
385
+ defaultSeverity: "serious",
386
+ title: "Controlled prop is blocked",
387
+ lifecycle: "experimental",
388
+ fixAvailable: true,
389
+ evidenceRequired: true,
390
+ }),
391
+ code({
392
+ code: "FUI8004",
393
+ ruleId: "safety/block-function-props",
394
+ category: "safety",
395
+ defaultSeverity: "serious",
396
+ title: "Function prop is blocked",
397
+ lifecycle: "experimental",
398
+ fixAvailable: true,
399
+ evidenceRequired: true,
400
+ }),
401
+ code({
402
+ code: "FUI8005",
403
+ ruleId: "safety/block-internal-props",
404
+ category: "safety",
405
+ defaultSeverity: "critical",
406
+ title: "Internal prop is blocked",
407
+ lifecycle: "experimental",
408
+ fixAvailable: true,
409
+ evidenceRequired: true,
410
+ }),
411
+ code({
412
+ code: "FUI8006",
413
+ ruleId: "safety/sanitize-hrefs",
414
+ category: "safety",
415
+ defaultSeverity: "critical",
416
+ title: "Href must be sanitized",
417
+ lifecycle: "experimental",
418
+ fixAvailable: true,
419
+ evidenceRequired: true,
420
+ }),
421
+ code({
422
+ code: "FUI9001",
423
+ ruleId: "unused-suppression",
424
+ category: "system",
425
+ defaultSeverity: "minor",
426
+ title: "Suppression is unused",
427
+ lifecycle: "experimental",
428
+ fixAvailable: true,
429
+ evidenceRequired: true,
430
+ }),
431
+ code({
432
+ code: "FUI9002",
433
+ ruleId: "expired-suppression",
434
+ category: "system",
435
+ defaultSeverity: "serious",
436
+ title: "Suppression is expired",
437
+ lifecycle: "experimental",
438
+ fixAvailable: true,
439
+ evidenceRequired: true,
440
+ }),
441
+ code({
442
+ code: "FUI9003",
443
+ ruleId: "missing-expiry",
444
+ category: "system",
445
+ defaultSeverity: "minor",
446
+ title: "Suppression expiry is missing",
447
+ lifecycle: "experimental",
448
+ fixAvailable: true,
449
+ evidenceRequired: true,
450
+ }),
451
+ ] as const satisfies readonly FuiCode[];
452
+
453
+ export const byCode: ReadonlyMap<string, FuiCode> = new Map(
454
+ CODES.map((entry) => [entry.code, entry])
455
+ );
456
+
457
+ export const byRuleId: ReadonlyMap<string, FuiCode> = new Map(
458
+ CODES.map((entry) => [entry.ruleId, entry])
459
+ );
460
+
461
+ export function explainUrlForCode(code: string): string {
462
+ return `${EXPLAIN_URL_BASE}${code}`;
463
+ }
@@ -0,0 +1,7 @@
1
+ export { CODES, EXPLAIN_URL_BASE, byCode, byRuleId, explainUrlForCode } from "./codes.js";
2
+ export type { CodeCategory, CodeLifecycle, FuiCode, Severity } from "./codes.js";
3
+ export {
4
+ bridgeSourceViolation,
5
+ bridgeSourceViolations,
6
+ type BridgeViolationInput,
7
+ } from "./source-finding-bridge.js";
@@ -0,0 +1,113 @@
1
+ import {
2
+ canonicalJson,
3
+ hash64Hex,
4
+ type FactLocation,
5
+ } from "../facts/index.js";
6
+ import {
7
+ normalizeFinding,
8
+ normalizeViolation,
9
+ type Finding,
10
+ type Violation,
11
+ } from "../schemas/index.js";
12
+ import { byRuleId } from "./codes.js";
13
+
14
+ export interface BridgeViolationInput {
15
+ violation: Violation;
16
+ canonicalRuleId?: string;
17
+ validator?: string;
18
+ attributes?: Record<string, unknown>;
19
+ }
20
+
21
+ export function bridgeSourceViolation(input: BridgeViolationInput): {
22
+ violation: Violation;
23
+ finding: Finding;
24
+ } {
25
+ const ruleId = input.canonicalRuleId ?? input.violation.rule;
26
+ const code = byRuleId.get(ruleId);
27
+ const violation = normalizeViolation({
28
+ ...input.violation,
29
+ code: input.violation.code ?? code?.code,
30
+ helpUrl: input.violation.helpUrl ?? code?.explainUrl,
31
+ });
32
+ const location = locationFromViolation(violation);
33
+ const fingerprint = hash64Hex(
34
+ canonicalJson({
35
+ ruleId,
36
+ legacyRule: violation.rule,
37
+ nodeId: violation.nodeId,
38
+ nodeType: violation.nodeType,
39
+ prop: violation.prop,
40
+ path: violation.path,
41
+ filePath: violation.filePath,
42
+ line: violation.line,
43
+ column: violation.column,
44
+ rawValue: violation.rawValue,
45
+ message: violation.message,
46
+ }),
47
+ );
48
+
49
+ return {
50
+ violation,
51
+ finding: normalizeFinding({
52
+ ruleId,
53
+ ruleVersion: "1.0.0",
54
+ severity: violation.severity,
55
+ level: violation.level,
56
+ code: code?.code,
57
+ helpUrl: code?.explainUrl,
58
+ message: violation.message,
59
+ fingerprint,
60
+ location,
61
+ evidence: [
62
+ {
63
+ factId: [
64
+ "source",
65
+ input.validator ?? "validator",
66
+ ruleId,
67
+ violation.filePath ?? violation.nodeId,
68
+ violation.line ?? 0,
69
+ violation.column ?? 0,
70
+ violation.prop ?? violation.path ?? "",
71
+ ].join(":"),
72
+ fact: {
73
+ kind: "source_violation",
74
+ location,
75
+ },
76
+ },
77
+ ],
78
+ attributes: {
79
+ source: "source-validator",
80
+ validator: input.validator,
81
+ legacyRule: violation.rule === ruleId ? undefined : violation.rule,
82
+ nodeId: violation.nodeId,
83
+ nodeType: violation.nodeType,
84
+ prop: violation.prop,
85
+ path: violation.path,
86
+ rawValue: violation.rawValue,
87
+ legacySuggestion: violation.suggestion,
88
+ ...input.attributes,
89
+ },
90
+ }),
91
+ };
92
+ }
93
+
94
+ export function bridgeSourceViolations(
95
+ violations: Violation[],
96
+ options: Omit<BridgeViolationInput, "violation"> = {},
97
+ ): { violations: Violation[]; findings: Finding[] } {
98
+ const bridged = violations.map((violation) =>
99
+ bridgeSourceViolation({ ...options, violation }),
100
+ );
101
+ return {
102
+ violations: bridged.map((entry) => entry.violation),
103
+ findings: bridged.map((entry) => entry.finding),
104
+ };
105
+ }
106
+
107
+ function locationFromViolation(violation: Violation): FactLocation {
108
+ return {
109
+ file: violation.filePath ?? "<source>",
110
+ line: violation.line ?? 0,
111
+ column: violation.column ?? 0,
112
+ };
113
+ }