@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,835 @@
1
+ /**
2
+ * Fact builders — factory functions that compute the content-addressed ID
3
+ * from each fact's identity attributes. Use these everywhere instead of hand-
4
+ * rolling fact objects so the identity contract stays consistent.
5
+ *
6
+ * The "identity" of a fact is the subset of attributes that determines its
7
+ * ID. Other attributes (e.g., `because`, `description`) can change without
8
+ * changing the ID — same logical fact, refined payload.
9
+ */
10
+
11
+ import type { GovernanceSeverity } from "../governance.js";
12
+ import { factId } from "./ids.js";
13
+ import type {
14
+ A11yNameRequiredFact,
15
+ CanonicalCandidateFact,
16
+ CanonicalMappingFact,
17
+ CanonicalReplacementFact,
18
+ ClassNameDynamicFact,
19
+ ClassNameDynamicReason,
20
+ ClassNameLiteralFact,
21
+ ClassNameOrigin,
22
+ ComponentCapabilityFact,
23
+ ComponentId,
24
+ ComponentMetadataFact,
25
+ FactLocation,
26
+ GovernanceRuleConfigFact,
27
+ JsxComponentPreferredFact,
28
+ JsxImportPathPreferredFact,
29
+ JsxInlineStyleForbiddenRawFact,
30
+ ContractTokenFact,
31
+ JsxUnknownPropsForbiddenFact,
32
+ PropMetadataFact,
33
+ PropValueAvoidedFact,
34
+ PropValueForbiddenFact,
35
+ ScaleFact,
36
+ ScaleValueFact,
37
+ StructuralConfidenceFact,
38
+ StyleCssVarsMustBeDefinedFact,
39
+ StyleDeclarationFact,
40
+ StyleFontSizeScaleFact,
41
+ StylePropertyScaleFact,
42
+ StyleRawColorForbiddenFact,
43
+ StyleRawDimensionForbiddenFact,
44
+ StyleUnsupportedFact,
45
+ UnsupportedStyleReason,
46
+ TailwindPaletteAllowFact,
47
+ TailwindPaletteDenyFact,
48
+ TailwindUnknownClassEnabledFact,
49
+ TailwindClassFact,
50
+ TailwindModifier,
51
+ TailwindResolvedValue,
52
+ TailwindTokenResolvedFact,
53
+ TailwindValue,
54
+ ThemeDeclarationFact,
55
+ TokenDefinitionFact,
56
+ UsageComponentFact,
57
+ UsageImportFact,
58
+ UsageInlineStyleFact,
59
+ UsageNodeFact,
60
+ UsagePropResolvedFact,
61
+ UsageTextChildFact,
62
+ FactId,
63
+ } from "./types.js";
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Component facts
67
+ // ---------------------------------------------------------------------------
68
+
69
+ export function makeComponentMetadataFact(input: {
70
+ componentId: ComponentId;
71
+ name: string;
72
+ description?: string;
73
+ category?: string;
74
+ filePath?: string;
75
+ }): ComponentMetadataFact {
76
+ return {
77
+ id: factId("component", { componentId: input.componentId }),
78
+ kind: "component",
79
+ componentId: input.componentId,
80
+ name: input.name,
81
+ description: input.description,
82
+ category: input.category,
83
+ filePath: input.filePath,
84
+ };
85
+ }
86
+
87
+ export function makeComponentCapabilityFact(input: {
88
+ componentId: ComponentId;
89
+ capability: string;
90
+ }): ComponentCapabilityFact {
91
+ return {
92
+ id: factId("component_capability", {
93
+ componentId: input.componentId,
94
+ capability: input.capability,
95
+ }),
96
+ kind: "component_capability",
97
+ componentId: input.componentId,
98
+ capability: input.capability,
99
+ };
100
+ }
101
+
102
+ // ---------------------------------------------------------------------------
103
+ // Classifier facts
104
+ // ---------------------------------------------------------------------------
105
+
106
+ export function makeCanonicalCandidateFact(input: {
107
+ componentId: ComponentId;
108
+ canonical: string;
109
+ confidence: number;
110
+ band: CanonicalCandidateFact["band"];
111
+ location?: FactLocation;
112
+ classifierVersion: string;
113
+ vocabVersion: string;
114
+ evidence: unknown[];
115
+ userConfirmed?: boolean;
116
+ }): CanonicalCandidateFact {
117
+ return {
118
+ id: factId("canonical_candidate", {
119
+ componentId: input.componentId,
120
+ canonical: input.canonical,
121
+ classifierVersion: input.classifierVersion,
122
+ vocabVersion: input.vocabVersion,
123
+ }),
124
+ kind: "canonical_candidate",
125
+ componentId: input.componentId,
126
+ canonical: input.canonical,
127
+ confidence: input.confidence,
128
+ band: input.band,
129
+ location: input.location,
130
+ classifierVersion: input.classifierVersion,
131
+ vocabVersion: input.vocabVersion,
132
+ evidence: input.evidence,
133
+ userConfirmed: input.userConfirmed,
134
+ };
135
+ }
136
+
137
+ export function makeCanonicalMappingFact(input: {
138
+ componentId: ComponentId;
139
+ canonical: string;
140
+ status: CanonicalMappingFact["status"];
141
+ confidence: number;
142
+ location?: FactLocation;
143
+ }): CanonicalMappingFact {
144
+ return {
145
+ id: factId("canonical_mapping", {
146
+ componentId: input.componentId,
147
+ canonical: input.canonical,
148
+ }),
149
+ kind: "canonical_mapping",
150
+ componentId: input.componentId,
151
+ canonical: input.canonical,
152
+ status: input.status,
153
+ confidence: input.confidence,
154
+ location: input.location,
155
+ };
156
+ }
157
+
158
+ export function makeStructuralConfidenceFact(input: {
159
+ componentId: ComponentId;
160
+ canonical: string;
161
+ confidence: number;
162
+ signalCount: number;
163
+ location?: FactLocation;
164
+ }): StructuralConfidenceFact {
165
+ return {
166
+ id: factId("structural_confidence", {
167
+ componentId: input.componentId,
168
+ canonical: input.canonical,
169
+ }),
170
+ kind: "structural_confidence",
171
+ componentId: input.componentId,
172
+ canonical: input.canonical,
173
+ confidence: input.confidence,
174
+ signalCount: input.signalCount,
175
+ location: input.location,
176
+ };
177
+ }
178
+
179
+ export function makeCanonicalReplacementFact(input: {
180
+ componentId: ComponentId;
181
+ canonical: string;
182
+ importPath?: string;
183
+ componentName?: string;
184
+ confidence: number;
185
+ location?: FactLocation;
186
+ }): CanonicalReplacementFact {
187
+ return {
188
+ id: factId("canonical_replacement", {
189
+ componentId: input.componentId,
190
+ canonical: input.canonical,
191
+ }),
192
+ kind: "canonical_replacement",
193
+ componentId: input.componentId,
194
+ canonical: input.canonical,
195
+ importPath: input.importPath,
196
+ componentName: input.componentName,
197
+ confidence: input.confidence,
198
+ location: input.location,
199
+ };
200
+ }
201
+
202
+ export function makePropMetadataFact(input: {
203
+ componentId: ComponentId;
204
+ prop: string;
205
+ type: string;
206
+ values?: string[];
207
+ required: boolean;
208
+ default?: unknown;
209
+ description?: string;
210
+ }): PropMetadataFact {
211
+ return {
212
+ id: factId("prop_metadata", {
213
+ componentId: input.componentId,
214
+ prop: input.prop,
215
+ }),
216
+ kind: "prop_metadata",
217
+ componentId: input.componentId,
218
+ prop: input.prop,
219
+ type: input.type,
220
+ values: input.values,
221
+ required: input.required,
222
+ default: input.default,
223
+ description: input.description,
224
+ };
225
+ }
226
+
227
+ // ---------------------------------------------------------------------------
228
+ // Component-scoped policy facts
229
+ // ---------------------------------------------------------------------------
230
+
231
+ export function makePropValueAvoidedFact(input: {
232
+ componentId: ComponentId;
233
+ prop: string;
234
+ value: unknown;
235
+ because: string;
236
+ suggest?: string;
237
+ severity: GovernanceSeverity;
238
+ }): PropValueAvoidedFact {
239
+ return {
240
+ id: factId("prop_value_avoided", {
241
+ componentId: input.componentId,
242
+ prop: input.prop,
243
+ value: input.value,
244
+ }),
245
+ kind: "prop_value_avoided",
246
+ componentId: input.componentId,
247
+ prop: input.prop,
248
+ value: input.value,
249
+ because: input.because,
250
+ suggest: input.suggest,
251
+ severity: input.severity,
252
+ };
253
+ }
254
+
255
+ export function makePropValueForbiddenFact(input: {
256
+ componentId: ComponentId;
257
+ prop: string;
258
+ value: unknown;
259
+ pathPattern?: string;
260
+ because: string;
261
+ replaceWith?: unknown;
262
+ severity: GovernanceSeverity;
263
+ }): PropValueForbiddenFact {
264
+ return {
265
+ id: factId("prop_value_forbidden", {
266
+ componentId: input.componentId,
267
+ prop: input.prop,
268
+ value: input.value,
269
+ pathPattern: input.pathPattern,
270
+ }),
271
+ kind: "prop_value_forbidden",
272
+ componentId: input.componentId,
273
+ prop: input.prop,
274
+ value: input.value,
275
+ pathPattern: input.pathPattern,
276
+ because: input.because,
277
+ replaceWith: input.replaceWith,
278
+ severity: input.severity,
279
+ };
280
+ }
281
+
282
+ export function makeA11yNameRequiredFact(input: {
283
+ componentId: ComponentId;
284
+ because: string;
285
+ severity: GovernanceSeverity;
286
+ }): A11yNameRequiredFact {
287
+ return {
288
+ id: factId("a11y_name_required", { componentId: input.componentId }),
289
+ kind: "a11y_name_required",
290
+ componentId: input.componentId,
291
+ because: input.because,
292
+ severity: input.severity,
293
+ };
294
+ }
295
+
296
+ // ---------------------------------------------------------------------------
297
+ // Global policy facts
298
+ // ---------------------------------------------------------------------------
299
+
300
+ export function makeScaleFact(input: {
301
+ name: string;
302
+ unit: "px" | "rem";
303
+ rootFontSizePx?: number;
304
+ emBasePx?: number;
305
+ }): ScaleFact {
306
+ return {
307
+ id: factId("scale", { name: input.name }),
308
+ kind: "scale",
309
+ name: input.name,
310
+ unit: input.unit,
311
+ ...(input.rootFontSizePx === undefined ? {} : { rootFontSizePx: input.rootFontSizePx }),
312
+ ...(input.emBasePx === undefined ? {} : { emBasePx: input.emBasePx }),
313
+ };
314
+ }
315
+
316
+ export function makeScaleValueFact(input: { scale: string; value: number }): ScaleValueFact {
317
+ return {
318
+ id: factId("scale_value", { scale: input.scale, value: input.value }),
319
+ kind: "scale_value",
320
+ scale: input.scale,
321
+ value: input.value,
322
+ };
323
+ }
324
+
325
+ export function makeStyleRawColorForbiddenFact(input: {
326
+ except: string[];
327
+ prefer: "token" | "css-variable";
328
+ severity: GovernanceSeverity;
329
+ }): StyleRawColorForbiddenFact {
330
+ return {
331
+ id: factId("style_raw_color_forbidden", {}),
332
+ kind: "style_raw_color_forbidden",
333
+ except: [...input.except],
334
+ prefer: input.prefer,
335
+ severity: input.severity,
336
+ };
337
+ }
338
+
339
+ export function makeStyleRawDimensionForbiddenFact(input: {
340
+ appliesTo: string[];
341
+ prefer: "token" | "css-variable";
342
+ severity: GovernanceSeverity;
343
+ }): StyleRawDimensionForbiddenFact {
344
+ return {
345
+ id: factId("style_raw_dimension_forbidden", {}),
346
+ kind: "style_raw_dimension_forbidden",
347
+ appliesTo: [...input.appliesTo],
348
+ prefer: input.prefer,
349
+ severity: input.severity,
350
+ };
351
+ }
352
+
353
+ export function makeStylePropertyScaleFact(input: {
354
+ property: string;
355
+ scale: string;
356
+ severity: GovernanceSeverity;
357
+ }): StylePropertyScaleFact {
358
+ return {
359
+ id: factId("style_property_scale", { property: input.property }),
360
+ kind: "style_property_scale",
361
+ property: input.property,
362
+ scale: input.scale,
363
+ severity: input.severity,
364
+ };
365
+ }
366
+
367
+ export function makeStyleFontSizeScaleFact(input: {
368
+ scale: string;
369
+ severity: GovernanceSeverity;
370
+ }): StyleFontSizeScaleFact {
371
+ return {
372
+ id: factId("style_font_size_scale", {}),
373
+ kind: "style_font_size_scale",
374
+ scale: input.scale,
375
+ severity: input.severity,
376
+ };
377
+ }
378
+
379
+ export function makeStyleCssVarsMustBeDefinedFact(input: {
380
+ severity: GovernanceSeverity;
381
+ }): StyleCssVarsMustBeDefinedFact {
382
+ return {
383
+ id: factId("style_css_vars_must_be_defined", {}),
384
+ kind: "style_css_vars_must_be_defined",
385
+ severity: input.severity,
386
+ };
387
+ }
388
+
389
+ export function makeContractTokenFact(input: { name: string }): ContractTokenFact {
390
+ const name = input.name.startsWith("--") ? input.name : `--${input.name}`;
391
+ return {
392
+ id: factId("contract_token", { name }),
393
+ kind: "contract_token",
394
+ name,
395
+ };
396
+ }
397
+
398
+ export function makeJsxUnknownPropsForbiddenFact(input: {
399
+ severity: GovernanceSeverity;
400
+ }): JsxUnknownPropsForbiddenFact {
401
+ return {
402
+ id: factId("jsx_unknown_props_forbidden", {}),
403
+ kind: "jsx_unknown_props_forbidden",
404
+ severity: input.severity,
405
+ };
406
+ }
407
+
408
+ export function makeJsxInlineStyleForbiddenRawFact(input: {
409
+ property: string;
410
+ severity: GovernanceSeverity;
411
+ }): JsxInlineStyleForbiddenRawFact {
412
+ return {
413
+ id: factId("jsx_inline_style_forbidden_raw", { property: input.property }),
414
+ kind: "jsx_inline_style_forbidden_raw",
415
+ property: input.property,
416
+ severity: input.severity,
417
+ };
418
+ }
419
+
420
+ export function makeJsxImportPathPreferredFact(input: {
421
+ from: string;
422
+ to: string;
423
+ imported?: string;
424
+ because?: string;
425
+ severity: GovernanceSeverity;
426
+ }): JsxImportPathPreferredFact {
427
+ return {
428
+ id: factId("jsx_import_path_preferred", {
429
+ from: input.from,
430
+ to: input.to,
431
+ imported: input.imported,
432
+ }),
433
+ kind: "jsx_import_path_preferred",
434
+ from: input.from,
435
+ to: input.to,
436
+ imported: input.imported,
437
+ because: input.because,
438
+ severity: input.severity,
439
+ };
440
+ }
441
+
442
+ export function makeJsxComponentPreferredFact(input: {
443
+ from: ComponentId;
444
+ to: ComponentId;
445
+ because?: string;
446
+ severity: GovernanceSeverity;
447
+ }): JsxComponentPreferredFact {
448
+ return {
449
+ id: factId("jsx_component_preferred", {
450
+ from: input.from,
451
+ to: input.to,
452
+ }),
453
+ kind: "jsx_component_preferred",
454
+ from: input.from,
455
+ to: input.to,
456
+ because: input.because,
457
+ severity: input.severity,
458
+ };
459
+ }
460
+
461
+ export function makeTokenDefinitionFact(input: {
462
+ name: string;
463
+ value: string;
464
+ category?: TokenDefinitionFact["category"];
465
+ referenceFormat?: TokenDefinitionFact["referenceFormat"];
466
+ }): TokenDefinitionFact {
467
+ return {
468
+ id: factId("token_definition", { name: input.name }),
469
+ kind: "token_definition",
470
+ name: input.name,
471
+ value: input.value,
472
+ category: input.category,
473
+ referenceFormat: input.referenceFormat,
474
+ };
475
+ }
476
+
477
+ export function makeTailwindPaletteAllowFact(input: {
478
+ patterns: string[];
479
+ severity: GovernanceSeverity;
480
+ }): TailwindPaletteAllowFact {
481
+ return {
482
+ id: factId("tailwind_palette_allow", {}),
483
+ kind: "tailwind_palette_allow",
484
+ patterns: [...input.patterns],
485
+ severity: input.severity,
486
+ };
487
+ }
488
+
489
+ export function makeTailwindPaletteDenyFact(input: {
490
+ patterns: string[];
491
+ severity: GovernanceSeverity;
492
+ }): TailwindPaletteDenyFact {
493
+ return {
494
+ id: factId("tailwind_palette_deny", {}),
495
+ kind: "tailwind_palette_deny",
496
+ patterns: [...input.patterns],
497
+ severity: input.severity,
498
+ };
499
+ }
500
+
501
+ export function makeTailwindUnknownClassEnabledFact(input: {
502
+ severity: GovernanceSeverity;
503
+ }): TailwindUnknownClassEnabledFact {
504
+ return {
505
+ id: factId("tailwind_unknown_class_enabled", {}),
506
+ kind: "tailwind_unknown_class_enabled",
507
+ severity: input.severity,
508
+ };
509
+ }
510
+
511
+ export function makeGovernanceRuleConfigFact(input: {
512
+ ruleId: string;
513
+ enabled: boolean;
514
+ severity?: GovernanceSeverity;
515
+ options?: Record<string, unknown>;
516
+ }): GovernanceRuleConfigFact {
517
+ return {
518
+ id: factId("governance_rule_config", { ruleId: input.ruleId }),
519
+ kind: "governance_rule_config",
520
+ ruleId: input.ruleId,
521
+ enabled: input.enabled,
522
+ severity: input.severity,
523
+ options: input.options,
524
+ };
525
+ }
526
+
527
+ // ---------------------------------------------------------------------------
528
+ // Usage facts
529
+ // ---------------------------------------------------------------------------
530
+
531
+ export function makeUsageNodeFact(input: {
532
+ file: string;
533
+ nodePath: string;
534
+ element: string;
535
+ role?: string;
536
+ interactive?: boolean;
537
+ location: FactLocation;
538
+ }): UsageNodeFact {
539
+ return {
540
+ // Identity is structural (file + nodePath + element) only — the normalized
541
+ // semantics (role/interactive) are evidence carried on the fact, never part
542
+ // of its ID, so adding them never perturbs existing fact identities.
543
+ id: factId("usage_node", {
544
+ file: input.file,
545
+ nodePath: input.nodePath,
546
+ element: input.element,
547
+ }),
548
+ kind: "usage_node",
549
+ file: input.file,
550
+ nodePath: input.nodePath,
551
+ element: input.element,
552
+ ...(input.role !== undefined ? { role: input.role } : {}),
553
+ ...(input.interactive ? { interactive: true } : {}),
554
+ location: input.location,
555
+ };
556
+ }
557
+
558
+ export function makeUsageComponentFact(input: {
559
+ nodeId: FactId;
560
+ componentId: ComponentId;
561
+ }): UsageComponentFact {
562
+ return {
563
+ id: factId("usage_component", {
564
+ nodeId: input.nodeId,
565
+ componentId: input.componentId,
566
+ }),
567
+ kind: "usage_component",
568
+ nodeId: input.nodeId,
569
+ componentId: input.componentId,
570
+ };
571
+ }
572
+
573
+ export function makeUsageImportFact(input: {
574
+ file: string;
575
+ local: string;
576
+ imported: string;
577
+ source: string;
578
+ namespace?: boolean;
579
+ location: FactLocation;
580
+ }): UsageImportFact {
581
+ return {
582
+ id: factId("usage_import", {
583
+ file: input.file,
584
+ local: input.local,
585
+ imported: input.imported,
586
+ source: input.source,
587
+ }),
588
+ kind: "usage_import",
589
+ file: input.file,
590
+ local: input.local,
591
+ imported: input.imported,
592
+ source: input.source,
593
+ namespace: input.namespace,
594
+ location: input.location,
595
+ };
596
+ }
597
+
598
+ export function makeUsagePropResolvedFact(input: {
599
+ nodeId: FactId;
600
+ prop: string;
601
+ resolution: "static" | "dynamic" | "spread" | "jsx";
602
+ value?: unknown;
603
+ location?: FactLocation;
604
+ }): UsagePropResolvedFact {
605
+ return {
606
+ id: factId("usage_prop_resolved", { nodeId: input.nodeId, prop: input.prop }),
607
+ kind: "usage_prop_resolved",
608
+ nodeId: input.nodeId,
609
+ prop: input.prop,
610
+ resolution: input.resolution,
611
+ value: input.value,
612
+ location: input.location,
613
+ };
614
+ }
615
+
616
+ export function makeUsageInlineStyleFact(input: {
617
+ nodeId: FactId;
618
+ property: string;
619
+ valueKind: "static" | "number" | "css-variable";
620
+ value: string;
621
+ }): UsageInlineStyleFact {
622
+ return {
623
+ id: factId("usage_inline_style", {
624
+ nodeId: input.nodeId,
625
+ property: input.property,
626
+ }),
627
+ kind: "usage_inline_style",
628
+ nodeId: input.nodeId,
629
+ property: input.property,
630
+ valueKind: input.valueKind,
631
+ value: input.value,
632
+ };
633
+ }
634
+
635
+ export function makeUsageTextChildFact(input: {
636
+ nodeId: FactId;
637
+ text: string;
638
+ index: number;
639
+ }): UsageTextChildFact {
640
+ return {
641
+ id: factId("usage_text_child", { nodeId: input.nodeId, index: input.index }),
642
+ kind: "usage_text_child",
643
+ nodeId: input.nodeId,
644
+ text: input.text,
645
+ index: input.index,
646
+ };
647
+ }
648
+
649
+ export function makeClassNameLiteralFact(input: {
650
+ file: string;
651
+ nodeId: FactId;
652
+ attr: string;
653
+ classes: string[];
654
+ origin: ClassNameOrigin;
655
+ originPath: string;
656
+ location: FactLocation;
657
+ }): ClassNameLiteralFact {
658
+ return {
659
+ id: factId("classname_literal", {
660
+ nodeId: input.nodeId,
661
+ attr: input.attr,
662
+ origin: input.origin,
663
+ originPath: input.originPath,
664
+ }),
665
+ kind: "classname_literal",
666
+ file: input.file,
667
+ nodeId: input.nodeId,
668
+ attr: input.attr,
669
+ classes: [...input.classes],
670
+ origin: input.origin,
671
+ originPath: input.originPath,
672
+ location: input.location,
673
+ };
674
+ }
675
+
676
+ export function makeClassNameDynamicFact(input: {
677
+ file: string;
678
+ nodeId: FactId;
679
+ attr: string;
680
+ reason: ClassNameDynamicReason;
681
+ snippet: string;
682
+ originPath: string;
683
+ location: FactLocation;
684
+ }): ClassNameDynamicFact {
685
+ return {
686
+ id: factId("classname_dynamic", {
687
+ nodeId: input.nodeId,
688
+ attr: input.attr,
689
+ reason: input.reason,
690
+ originPath: input.originPath,
691
+ }),
692
+ kind: "classname_dynamic",
693
+ file: input.file,
694
+ nodeId: input.nodeId,
695
+ attr: input.attr,
696
+ reason: input.reason,
697
+ snippet: input.snippet,
698
+ originPath: input.originPath,
699
+ location: input.location,
700
+ };
701
+ }
702
+
703
+ export function makeTailwindClassFact(input: {
704
+ file: string;
705
+ nodeId: FactId;
706
+ raw: string;
707
+ originPath: string;
708
+ prefix: string | null;
709
+ modifiers: TailwindModifier[];
710
+ important: boolean;
711
+ negative: boolean;
712
+ utility: string;
713
+ value: TailwindValue;
714
+ location: FactLocation;
715
+ }): TailwindClassFact {
716
+ return {
717
+ id: factId("tailwind_class", {
718
+ nodeId: input.nodeId,
719
+ originPath: input.originPath,
720
+ }),
721
+ kind: "tailwind_class",
722
+ file: input.file,
723
+ nodeId: input.nodeId,
724
+ raw: input.raw,
725
+ originPath: input.originPath,
726
+ prefix: input.prefix,
727
+ modifiers: input.modifiers.map((m) => ({ ...m })),
728
+ important: input.important,
729
+ negative: input.negative,
730
+ utility: input.utility,
731
+ value: { ...input.value },
732
+ location: input.location,
733
+ };
734
+ }
735
+
736
+ export function makeThemeDeclarationFact(input: {
737
+ file: string;
738
+ name: string;
739
+ value: string;
740
+ declarationPath: string;
741
+ inline: boolean;
742
+ flavor: ThemeDeclarationFact["flavor"];
743
+ location: FactLocation;
744
+ }): ThemeDeclarationFact {
745
+ return {
746
+ id: factId("theme_declaration", {
747
+ file: input.file,
748
+ name: input.name,
749
+ declarationPath: input.declarationPath,
750
+ }),
751
+ kind: "theme_declaration",
752
+ file: input.file,
753
+ name: input.name,
754
+ value: input.value,
755
+ declarationPath: input.declarationPath,
756
+ inline: input.inline,
757
+ flavor: input.flavor,
758
+ location: input.location,
759
+ };
760
+ }
761
+
762
+ export function makeTailwindTokenResolvedFact(input: {
763
+ utility: string;
764
+ token: string;
765
+ resolved: TailwindResolvedValue;
766
+ }): TailwindTokenResolvedFact {
767
+ return {
768
+ id: factId("tailwind_token_resolved", {
769
+ utility: input.utility,
770
+ token: input.token,
771
+ }),
772
+ kind: "tailwind_token_resolved",
773
+ utility: input.utility,
774
+ token: input.token,
775
+ resolved: { ...input.resolved },
776
+ };
777
+ }
778
+
779
+ export function makeStyleDeclarationFact(input: {
780
+ file: string;
781
+ selector: string;
782
+ declarationPath: string;
783
+ property: string;
784
+ value: string;
785
+ location: FactLocation;
786
+ declaredTokenSource?: boolean;
787
+ }): StyleDeclarationFact {
788
+ return {
789
+ id: factId("style_declaration", {
790
+ file: input.file,
791
+ selector: input.selector,
792
+ declarationPath: input.declarationPath,
793
+ property: input.property,
794
+ }),
795
+ kind: "style_declaration",
796
+ file: input.file,
797
+ selector: input.selector,
798
+ declarationPath: input.declarationPath,
799
+ property: input.property,
800
+ value: input.value,
801
+ location: input.location,
802
+ ...(input.declaredTokenSource ? { declaredTokenSource: true } : {}),
803
+ };
804
+ }
805
+
806
+ export function makeStyleUnsupportedFact(input: {
807
+ file: string;
808
+ source: "css-in-js";
809
+ syntax: string;
810
+ selector: string;
811
+ declarationPath: string;
812
+ property?: string;
813
+ reason: UnsupportedStyleReason;
814
+ location: FactLocation;
815
+ }): StyleUnsupportedFact {
816
+ return {
817
+ id: factId("style_unsupported", {
818
+ file: input.file,
819
+ source: input.source,
820
+ syntax: input.syntax,
821
+ selector: input.selector,
822
+ declarationPath: input.declarationPath,
823
+ property: input.property ?? "",
824
+ }),
825
+ kind: "style_unsupported",
826
+ file: input.file,
827
+ source: input.source,
828
+ syntax: input.syntax,
829
+ selector: input.selector,
830
+ declarationPath: input.declarationPath,
831
+ property: input.property,
832
+ reason: input.reason,
833
+ location: input.location,
834
+ };
835
+ }