@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,293 @@
1
+ /**
2
+ * Compiled fragment types — shared between CLI and MCP packages.
3
+ *
4
+ * These are the JSON-serializable types used in fragments.json and consumed
5
+ * by AI agents, MCP servers, and context generators.
6
+ */
7
+
8
+ import type { ComponentGovernanceRecord } from '../governance.js';
9
+
10
+ /**
11
+ * Component metadata
12
+ */
13
+ export interface FragmentMeta {
14
+ name: string;
15
+ description: string;
16
+ category: string;
17
+ tags?: string[];
18
+ status?: "stable" | "beta" | "deprecated" | "experimental";
19
+ since?: string;
20
+ dependencies?: Array<{ name: string; version: string; reason?: string }>;
21
+ figma?: string;
22
+ figmaProps?: Record<string, unknown>;
23
+ }
24
+
25
+ /**
26
+ * Usage guidelines for AI agents and developers
27
+ */
28
+ export interface FragmentUsage {
29
+ when: string[];
30
+ whenNot: string[];
31
+ guidelines?: string[];
32
+ accessibility?: string[];
33
+ }
34
+
35
+ /**
36
+ * Definition for a single prop
37
+ */
38
+ export interface PropDefinition {
39
+ type: string;
40
+ values?: readonly string[];
41
+ default?: unknown;
42
+ description: string;
43
+ required?: boolean;
44
+ constraints?: string[];
45
+ typeDetails?: Record<string, unknown>;
46
+ controlType?: string;
47
+ controlOptions?: {
48
+ min?: number;
49
+ max?: number;
50
+ step?: number;
51
+ presetColors?: string[];
52
+ };
53
+ }
54
+
55
+ /**
56
+ * Relationship to another component
57
+ */
58
+ export interface ComponentRelation {
59
+ component: string;
60
+ relationship: string;
61
+ note: string;
62
+ }
63
+
64
+ /**
65
+ * Agent-optimized contract metadata
66
+ */
67
+ export interface FragmentContract {
68
+ propsSummary?: string[];
69
+ a11yRules?: string[];
70
+ bans?: Array<{ pattern: string; message: string }>;
71
+ scenarioTags?: string[];
72
+ /** Per-component performance budget override in bytes (gzipped) */
73
+ performanceBudget?: number;
74
+ /** Sub-component slot metadata for compound components */
75
+ compoundChildren?: Record<string, {
76
+ required?: boolean;
77
+ accepts?: string[];
78
+ description?: string;
79
+ }>;
80
+ /** Canonical JSX usage examples showing how to assemble the component */
81
+ canonicalUsage?: string[];
82
+ }
83
+
84
+ /**
85
+ * Provenance tracking for generated fragments
86
+ */
87
+ export interface FragmentGenerated {
88
+ source: "storybook" | "manual" | "ai" | "extracted" | "merged" | "migrated";
89
+ sourceFile?: string;
90
+ /** @deprecated Use provenance.verified instead */
91
+ confidence?: number;
92
+ verified?: boolean;
93
+ timestamp?: string;
94
+ }
95
+
96
+ /**
97
+ * AI-specific metadata
98
+ */
99
+ export interface AIMetadata {
100
+ compositionPattern?: "compound" | "simple" | "controlled" | "wrapper";
101
+ subComponents?: string[];
102
+ requiredChildren?: string[];
103
+ commonPatterns?: string[];
104
+ }
105
+
106
+ /**
107
+ * Performance data for a component (from bundle size measurement)
108
+ */
109
+ /**
110
+ * A single import contributing to bundle size
111
+ */
112
+ export interface ImportEntry {
113
+ /** Resolved file path (relative to project root) */
114
+ path: string;
115
+ /** Bytes contributed to the bundle */
116
+ bytes: number;
117
+ /** Percentage of total bundle size */
118
+ percent: number;
119
+ }
120
+
121
+ export interface PerformanceData {
122
+ /** Gzipped bundle size in bytes */
123
+ bundleSize: number;
124
+ /** Raw (minified, not gzipped) bundle size in bytes */
125
+ rawSize: number;
126
+ /** Complexity classification */
127
+ complexity: 'lightweight' | 'moderate' | 'heavy';
128
+ /** Percentage of budget used (0-100+) */
129
+ budgetPercent: number;
130
+ /** Whether the component exceeds its budget */
131
+ overBudget: boolean;
132
+ /** ISO timestamp when measured */
133
+ measuredAt: string;
134
+ /** Top imports by size (largest first) */
135
+ imports?: ImportEntry[];
136
+ }
137
+
138
+ export interface ObservedUsageProp {
139
+ name: string;
140
+ kind: 'static' | 'dynamic' | 'spread' | 'jsx' | 'boolean' | 'null';
141
+ value?: string | number | boolean | null;
142
+ }
143
+
144
+ export interface ObservedComponentUsage {
145
+ /** Source file path relative to the fragments config root */
146
+ file: string;
147
+ /** 1-indexed source line */
148
+ line: number;
149
+ /** 0-indexed source column */
150
+ column: number;
151
+ props: ObservedUsageProp[];
152
+ parentElement?: string;
153
+ conditional?: boolean;
154
+ }
155
+
156
+ /**
157
+ * Compiled fragment data (JSON-serializable for AI consumption)
158
+ */
159
+ export interface CompiledFragment {
160
+ filePath: string;
161
+ meta: FragmentMeta;
162
+ /** Canonical v2 guidance. `usage` is retained for compatibility. */
163
+ guidance?: FragmentUsage;
164
+ usage: FragmentUsage;
165
+ props: Record<string, PropDefinition>;
166
+ /** Normalized component governance records emitted from *.fragment.ts. */
167
+ governance?: ComponentGovernanceRecord[];
168
+ relations?: ComponentRelation[];
169
+ variants: Array<{
170
+ name: string;
171
+ description: string;
172
+ code?: string;
173
+ figma?: string;
174
+ args?: Record<string, unknown>;
175
+ }>;
176
+ contract?: FragmentContract;
177
+ ai?: AIMetadata;
178
+ /** Framework hint from contract, used by preview adapters and Cloud */
179
+ framework?: string;
180
+ /** Top-level compact prop summaries for agent first-pass */
181
+ propsSummary?: string[];
182
+ /** Source file path relative to config root (for contract-sourced fragments) */
183
+ sourcePath?: string;
184
+ /** Named export for preview adapter resolution */
185
+ exportName?: string;
186
+ /** Clean provenance tracking (V2) */
187
+ provenance?: {
188
+ source: string;
189
+ verified: boolean;
190
+ frameworkSupport?: string;
191
+ sourceHash?: string;
192
+ extractedAt?: string;
193
+ };
194
+ /** Component performance data from bundle size measurement */
195
+ performance?: PerformanceData;
196
+ /** Observed JSX call sites captured during `fragments scan` */
197
+ usages?: ObservedComponentUsage[];
198
+ _generated?: FragmentGenerated;
199
+ }
200
+
201
+ /**
202
+ * Compiled block data (JSON-serializable for AI consumption)
203
+ */
204
+ export interface CompiledBlock {
205
+ filePath: string;
206
+ name: string;
207
+ description: string;
208
+ category: string;
209
+ components: string[];
210
+ code: string;
211
+ tags?: string[];
212
+ }
213
+
214
+ /**
215
+ * A single token entry in the compiled output
216
+ */
217
+ export interface CompiledTokenEntry {
218
+ name: string;
219
+ value?: string;
220
+ description?: string;
221
+ }
222
+
223
+ /**
224
+ * Compiled token data stored in fragments.json
225
+ */
226
+ export interface CompiledTokenData {
227
+ prefix: string;
228
+ total: number;
229
+ categories: Record<string, CompiledTokenEntry[]>;
230
+ }
231
+
232
+ /**
233
+ * Performance summary across all components
234
+ */
235
+ export interface PerformanceSummary {
236
+ /** Preset name used */
237
+ preset: string;
238
+ /** Budget applied in bytes */
239
+ budget: number;
240
+ /** Total components measured */
241
+ total: number;
242
+ /** Number of components over budget */
243
+ overBudget: number;
244
+ /** Distribution by tier */
245
+ tiers: Record<string, number>;
246
+ }
247
+
248
+ /**
249
+ * The compiled fragments.json structure
250
+ */
251
+ export interface CompiledFragmentsFile {
252
+ version: string;
253
+ generatedAt: string;
254
+ /** CLI version that produced this file, used for freshness checks */
255
+ generatorVersion?: string;
256
+ /** Relative source/config inputs used to build this file, used for freshness checks */
257
+ buildInputs?: string[];
258
+ packageName?: string;
259
+ fragments: Record<string, CompiledFragment>;
260
+ blocks?: Record<string, CompiledBlock>;
261
+ tokens?: CompiledTokenData;
262
+ /** Component relationship graph for AI structural queries */
263
+ graph?: import('../graph/types.js').SerializedComponentGraph;
264
+ /** Performance measurement summary */
265
+ performanceSummary?: PerformanceSummary;
266
+ /** @deprecated Use blocks instead */
267
+ recipes?: Record<string, CompiledBlock>;
268
+ }
269
+
270
+ /**
271
+ * Theme identifier
272
+ */
273
+ export type Theme = "light" | "dark";
274
+
275
+ /**
276
+ * Verification result
277
+ */
278
+ export interface VerifyResult {
279
+ verdict: "pass" | "fail" | "error";
280
+ matches: boolean;
281
+ diffPercentage: number;
282
+ screenshot: string;
283
+ baseline: string;
284
+ diffImage?: string;
285
+ notes: string[];
286
+ error?: string;
287
+ timing: {
288
+ renderMs: number;
289
+ captureMs: number;
290
+ diffMs: number;
291
+ totalMs: number;
292
+ };
293
+ }
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Framework-agnostic component contract format.
3
+ *
4
+ * Allows non-React teams to define component metadata via `.contract.json`
5
+ * files instead of `.fragment.tsx`. Maps trivially to CompiledFragment.
6
+ */
7
+
8
+ import { z } from 'zod';
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Component Contract interface
12
+ // ---------------------------------------------------------------------------
13
+
14
+ export interface ComponentContract {
15
+ /** JSON Schema reference for tooling — required for validation */
16
+ $schema: string;
17
+
18
+ /** Component name */
19
+ name: string;
20
+ description: string;
21
+ category: string;
22
+ tags?: string[];
23
+ status?: 'stable' | 'beta' | 'deprecated' | 'experimental';
24
+
25
+ /** Framework hint (for extraction and documentation) */
26
+ framework?: 'react' | 'vue' | 'svelte' | 'web-components' | 'angular';
27
+
28
+ /** Path to the component source file, relative to fragments.config.ts root */
29
+ sourcePath: string;
30
+
31
+ /** Named export from the source file */
32
+ exportName: string;
33
+
34
+ /** Compact one-line-per-prop summaries for agent first-pass (e.g., "variant: primary|secondary (required)") */
35
+ propsSummary: string[];
36
+
37
+ /** Component props schema */
38
+ props: Record<string, {
39
+ type: string;
40
+ values?: string[];
41
+ default?: unknown;
42
+ description: string;
43
+ required?: boolean;
44
+ constraints?: string[];
45
+ }>;
46
+
47
+ /** Usage guidelines */
48
+ usage: {
49
+ when: string[];
50
+ whenNot: string[];
51
+ guidelines?: string[];
52
+ accessibility?: string[];
53
+ };
54
+
55
+ /** Code examples (as strings, not render functions) */
56
+ examples?: Array<{
57
+ name: string;
58
+ description: string;
59
+ code: string;
60
+ args?: Record<string, unknown>;
61
+ }>;
62
+
63
+ /** Relations to other components */
64
+ relations?: Array<{
65
+ component: string;
66
+ relationship: 'alternative' | 'parent' | 'child' | 'sibling' | 'composition' | 'complementary' | 'used-by';
67
+ note: string;
68
+ }>;
69
+
70
+ /** AI generation contract */
71
+ contract?: {
72
+ propsSummary?: string[];
73
+ scenarioTags?: string[];
74
+ a11yRules?: string[];
75
+ bans?: Array<{ pattern: string; message: string }>;
76
+ /** Sub-component slot metadata for compound components */
77
+ compoundChildren?: Record<string, {
78
+ required?: boolean;
79
+ accepts?: string[];
80
+ description?: string;
81
+ }>;
82
+ /** Canonical JSX usage examples */
83
+ canonicalUsage?: string[];
84
+ /** Per-component performance budget override in bytes (gzipped) */
85
+ performanceBudget?: number;
86
+ };
87
+
88
+ /** AI metadata for compound component generation */
89
+ ai?: {
90
+ compositionPattern?: 'compound' | 'simple' | 'controlled' | 'wrapper';
91
+ subComponents?: string[];
92
+ requiredChildren?: string[];
93
+ commonPatterns?: string[];
94
+ };
95
+
96
+ /** Per-component preview configuration */
97
+ preview?: {
98
+ setupModule?: string;
99
+ wrapperModule?: string;
100
+ wrapperExport?: string;
101
+ css?: string[];
102
+ theme?: 'light' | 'dark';
103
+ };
104
+
105
+ /** Provenance tracking — required for verification */
106
+ provenance: {
107
+ source: 'manual' | 'extracted' | 'merged' | 'migrated';
108
+ verified: boolean;
109
+ frameworkSupport?: 'native' | 'manual-only';
110
+ sourceHash?: string;
111
+ extractedAt?: string;
112
+ };
113
+
114
+ /** Design token references used by this component */
115
+ tokens?: string[];
116
+
117
+ /** Figma mappings (same DSL as .fragment.tsx but in JSON form) */
118
+ figma?: {
119
+ nodeUrl?: string;
120
+ propMappings?: Record<string, {
121
+ type: 'string' | 'boolean' | 'enum' | 'instance' | 'children' | 'textContent';
122
+ figmaProperty: string;
123
+ values?: Record<string, string>;
124
+ }>;
125
+ };
126
+ }
127
+
128
+ // ---------------------------------------------------------------------------
129
+ // Zod schema for validation
130
+ // ---------------------------------------------------------------------------
131
+
132
+ const contractPropSchema = z.object({
133
+ type: z.string(),
134
+ values: z.array(z.string()).optional(),
135
+ default: z.unknown().optional(),
136
+ description: z.string(),
137
+ required: z.boolean().optional(),
138
+ constraints: z.array(z.string()).optional(),
139
+ });
140
+
141
+ const contractUsageSchema = z.object({
142
+ when: z.array(z.string()),
143
+ whenNot: z.array(z.string()),
144
+ guidelines: z.array(z.string()).optional(),
145
+ accessibility: z.array(z.string()).optional(),
146
+ });
147
+
148
+ const contractExampleSchema = z.object({
149
+ name: z.string(),
150
+ description: z.string(),
151
+ code: z.string(),
152
+ args: z.record(z.string(), z.unknown()).optional(),
153
+ });
154
+
155
+ const contractRelationSchema = z.object({
156
+ component: z.string(),
157
+ relationship: z.enum(['alternative', 'parent', 'child', 'sibling', 'composition', 'complementary', 'used-by']),
158
+ note: z.string(),
159
+ });
160
+
161
+ const contractContractSchema = z.object({
162
+ propsSummary: z.array(z.string()).optional(),
163
+ scenarioTags: z.array(z.string()).optional(),
164
+ a11yRules: z.array(z.string()).optional(),
165
+ bans: z.array(z.object({
166
+ pattern: z.string(),
167
+ message: z.string(),
168
+ })).optional(),
169
+ compoundChildren: z.record(z.string(), z.object({
170
+ required: z.boolean().optional(),
171
+ accepts: z.array(z.string()).optional(),
172
+ description: z.string().optional(),
173
+ })).optional(),
174
+ canonicalUsage: z.array(z.string()).optional(),
175
+ performanceBudget: z.number().optional(),
176
+ });
177
+
178
+ const contractAiSchema = z.object({
179
+ compositionPattern: z.enum(['compound', 'simple', 'controlled', 'wrapper']).optional(),
180
+ subComponents: z.array(z.string()).optional(),
181
+ requiredChildren: z.array(z.string()).optional(),
182
+ commonPatterns: z.array(z.string()).optional(),
183
+ });
184
+
185
+ const contractPreviewSchema = z.object({
186
+ setupModule: z.string().optional(),
187
+ wrapperModule: z.string().optional(),
188
+ wrapperExport: z.string().optional(),
189
+ css: z.array(z.string()).optional(),
190
+ theme: z.enum(['light', 'dark']).optional(),
191
+ });
192
+
193
+ const contractProvenanceSchema = z.object({
194
+ source: z.enum(['manual', 'extracted', 'merged', 'migrated']),
195
+ verified: z.boolean(),
196
+ frameworkSupport: z.enum(['native', 'manual-only']).optional(),
197
+ sourceHash: z.string().optional(),
198
+ extractedAt: z.string().optional(),
199
+ });
200
+
201
+ const contractFigmaSchema = z.object({
202
+ nodeUrl: z.string().optional(),
203
+ propMappings: z.record(z.string(), z.object({
204
+ type: z.enum(['string', 'boolean', 'enum', 'instance', 'children', 'textContent']),
205
+ figmaProperty: z.string(),
206
+ values: z.record(z.string(), z.string()).optional(),
207
+ })).optional(),
208
+ });
209
+
210
+ export const componentContractSchema = z.object({
211
+ $schema: z.string(),
212
+ name: z.string(),
213
+ description: z.string(),
214
+ category: z.string(),
215
+ tags: z.array(z.string()).optional(),
216
+ status: z.enum(['stable', 'beta', 'deprecated', 'experimental']).optional(),
217
+ framework: z.enum(['react', 'vue', 'svelte', 'web-components', 'angular']).optional(),
218
+ sourcePath: z.string(),
219
+ exportName: z.string(),
220
+ propsSummary: z.array(z.string()),
221
+ props: z.record(z.string(), contractPropSchema),
222
+ usage: contractUsageSchema,
223
+ examples: z.array(contractExampleSchema).optional(),
224
+ relations: z.array(contractRelationSchema).optional(),
225
+ contract: contractContractSchema.optional(),
226
+ ai: contractAiSchema.optional(),
227
+ preview: contractPreviewSchema.optional(),
228
+ provenance: contractProvenanceSchema,
229
+ tokens: z.array(z.string()).optional(),
230
+ figma: contractFigmaSchema.optional(),
231
+ });
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Component discovery utilities.
3
+ *
4
+ * Extracted from the docs app component-registry so that any consumer
5
+ * (govern presets, CLI tools, third-party integrations) can discover
6
+ * components from a module's exports without duplicating the logic.
7
+ */
8
+
9
+ /**
10
+ * Check if a value is likely a React component.
11
+ * Functions or objects with $$typeof (forwardRef, memo) qualify.
12
+ */
13
+ export function isReactComponent(value: unknown): boolean {
14
+ if (!value) return false;
15
+
16
+ // Function component (most common case)
17
+ if (typeof value === 'function') {
18
+ return true;
19
+ }
20
+
21
+ // ForwardRef, memo, etc. have $$typeof
22
+ if (typeof value === 'object') {
23
+ const obj = value as Record<string, unknown>;
24
+ if (obj.$$typeof) return true;
25
+ }
26
+
27
+ return false;
28
+ }
29
+
30
+ export interface DiscoverComponentsOptions {
31
+ /** Set of export names to skip */
32
+ exclude?: Set<string>;
33
+ }
34
+
35
+ /**
36
+ * Discover all components and sub-components from a module's exports.
37
+ * Returns a sorted array of component names including compound names (e.g. 'Card.Header').
38
+ *
39
+ * @param moduleExports - The exports object from a component library (e.g. `import * as UI from '@usefragments/ui'`)
40
+ * @param options - Optional configuration
41
+ */
42
+ export function discoverComponents(
43
+ moduleExports: Record<string, unknown>,
44
+ options?: DiscoverComponentsOptions,
45
+ ): string[] {
46
+ const exclude = options?.exclude;
47
+ const names: string[] = [];
48
+
49
+ for (const [exportName, exportValue] of Object.entries(moduleExports)) {
50
+ // Skip excluded exports
51
+ if (exclude?.has(exportName)) continue;
52
+
53
+ // Skip non-components (types, constants, etc.)
54
+ if (!isReactComponent(exportValue)) continue;
55
+
56
+ // Skip lowercase exports (likely utilities)
57
+ if (exportName[0] !== exportName[0].toUpperCase()) continue;
58
+
59
+ // Add the main component
60
+ names.push(exportName);
61
+
62
+ // Check for sub-components by looking at ALL properties of the component
63
+ const componentObj = exportValue as Record<string, unknown>;
64
+
65
+ for (const key of Object.keys(componentObj)) {
66
+ // Skip internal properties and non-component properties
67
+ if (key.startsWith('_') || key.startsWith('$')) continue;
68
+ if (key === 'displayName' || key === 'propTypes' || key === 'defaultProps') continue;
69
+ // Skip 'render' which is a forwardRef internal, and 'Tabbed' which is a special variant
70
+ if (key === 'render' || key === 'Tabbed') continue;
71
+
72
+ const subComponent = componentObj[key];
73
+ if (isReactComponent(subComponent)) {
74
+ names.push(`${exportName}.${key}`);
75
+ }
76
+ }
77
+ }
78
+
79
+ return names.sort();
80
+ }