@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
package/src/schema.ts ADDED
@@ -0,0 +1,427 @@
1
+ import { z } from "zod";
2
+ import { portableRepoPathError } from "./config-paths.js";
3
+ import { componentGovernanceRecordSchema, governanceConfigSchema } from "./governance.js";
4
+ import { compositionAuthoringEntrySchema } from "./rules/composition-pattern-schema.js";
5
+
6
+ /**
7
+ * Zod schemas for runtime validation of fragment definitions
8
+ */
9
+
10
+ // Figma property mapping schemas
11
+ const figmaStringMappingSchema = z.object({
12
+ __type: z.literal("figma-string"),
13
+ figmaProperty: z.string().min(1),
14
+ });
15
+
16
+ const figmaBooleanMappingSchema = z.object({
17
+ __type: z.literal("figma-boolean"),
18
+ figmaProperty: z.string().min(1),
19
+ valueMapping: z.object({ true: z.unknown(), false: z.unknown() }).optional(),
20
+ });
21
+
22
+ const figmaEnumMappingSchema = z.object({
23
+ __type: z.literal("figma-enum"),
24
+ figmaProperty: z.string().min(1),
25
+ valueMapping: z.record(z.unknown()),
26
+ });
27
+
28
+ const figmaInstanceMappingSchema = z.object({
29
+ __type: z.literal("figma-instance"),
30
+ figmaProperty: z.string().min(1),
31
+ });
32
+
33
+ const figmaChildrenMappingSchema = z.object({
34
+ __type: z.literal("figma-children"),
35
+ layers: z.array(z.string().min(1)),
36
+ });
37
+
38
+ const figmaTextContentMappingSchema = z.object({
39
+ __type: z.literal("figma-text-content"),
40
+ layer: z.string().min(1),
41
+ });
42
+
43
+ export const figmaPropMappingSchema = z.discriminatedUnion("__type", [
44
+ figmaStringMappingSchema,
45
+ figmaBooleanMappingSchema,
46
+ figmaEnumMappingSchema,
47
+ figmaInstanceMappingSchema,
48
+ figmaChildrenMappingSchema,
49
+ figmaTextContentMappingSchema,
50
+ ]);
51
+
52
+ export const fragmentMetaSchema = z.object({
53
+ name: z.string().min(1),
54
+ description: z.string().min(1),
55
+ category: z.string().min(1),
56
+ tags: z.array(z.string()).optional(),
57
+ status: z.enum(["stable", "beta", "deprecated", "experimental"]).optional(),
58
+ since: z.string().optional(),
59
+ dependencies: z
60
+ .array(
61
+ z.object({
62
+ name: z.string().min(1),
63
+ version: z.string().min(1),
64
+ reason: z.string().optional(),
65
+ })
66
+ )
67
+ .optional(),
68
+ figma: z.string().url().optional(),
69
+ figmaProps: z.record(figmaPropMappingSchema).optional(),
70
+ });
71
+
72
+ export const fragmentUsageSchema = z.object({
73
+ when: z.array(z.string()),
74
+ whenNot: z.array(z.string()),
75
+ guidelines: z.array(z.string()).optional(),
76
+ accessibility: z.array(z.string()).optional(),
77
+ });
78
+
79
+ export const propTypeSchema: z.ZodType<string> = z.enum([
80
+ "string",
81
+ "number",
82
+ "boolean",
83
+ "enum",
84
+ "function",
85
+ "node",
86
+ "element",
87
+ "object",
88
+ "array",
89
+ "union",
90
+ "custom",
91
+ ]);
92
+
93
+ export const propDefinitionSchema = z.object({
94
+ type: propTypeSchema,
95
+ values: z.array(z.string()).readonly().optional(),
96
+ default: z.unknown().optional(),
97
+ description: z.string().optional(),
98
+ required: z.boolean().optional(),
99
+ constraints: z.array(z.string()).optional(),
100
+ typeDetails: z.record(z.unknown()).optional(),
101
+ });
102
+
103
+ export const relationshipTypeSchema = z.enum([
104
+ "alternative",
105
+ "sibling",
106
+ "parent",
107
+ "child",
108
+ "composition",
109
+ "complementary",
110
+ "used-by",
111
+ ]);
112
+
113
+ export const componentRelationSchema = z.object({
114
+ component: z.string().min(1),
115
+ relationship: relationshipTypeSchema,
116
+ note: z.string().min(1),
117
+ });
118
+
119
+ export const fragmentVariantSchema = z.object({
120
+ name: z.string().min(1),
121
+ description: z.string().min(1),
122
+ render: z.custom<(...args: unknown[]) => unknown>(
123
+ (value) => typeof value === "function",
124
+ "Expected a render function"
125
+ ),
126
+ code: z.string().optional(),
127
+ figma: z.string().url().optional(),
128
+ });
129
+
130
+ /**
131
+ * Schema for banned patterns in codebase
132
+ */
133
+ export const fragmentBanSchema = z.object({
134
+ pattern: z.string().min(1),
135
+ message: z.string().min(1),
136
+ });
137
+
138
+ /**
139
+ * Schema for agent-optimized contract metadata
140
+ */
141
+ export const fragmentContractSchema = z.object({
142
+ propsSummary: z.array(z.string()).optional(),
143
+ a11yRules: z.array(z.string()).optional(),
144
+ bans: z.array(fragmentBanSchema).optional(),
145
+ scenarioTags: z.array(z.string()).optional(),
146
+ performanceBudget: z.number().positive().optional(),
147
+ compoundChildren: z
148
+ .record(
149
+ z.object({
150
+ required: z.boolean().optional(),
151
+ accepts: z.array(z.string()).optional(),
152
+ description: z.string().optional(),
153
+ })
154
+ )
155
+ .optional(),
156
+ canonicalUsage: z.array(z.string()).optional(),
157
+ // Region-scoped composition constraints (brief 04 §10). Authors write the
158
+ // `{ inRegion, rule }` sugar; `lowerCompositionContract` lowers it to the
159
+ // `CompositionPattern[]` the composition/* rules read from policy.
160
+ composition: z.array(compositionAuthoringEntrySchema).optional(),
161
+ });
162
+
163
+ /**
164
+ * Schema for provenance tracking of generated fragments
165
+ */
166
+ export const fragmentGeneratedSchema = z.object({
167
+ source: z.enum(["storybook", "manual", "ai"]),
168
+ sourceFile: z.string().optional(),
169
+ confidence: z.number().min(0).max(1).optional(),
170
+ timestamp: z.string().datetime().optional(),
171
+ });
172
+
173
+ /**
174
+ * Schema for AI-specific metadata for playground context generation
175
+ */
176
+ export const aiMetadataSchema = z.object({
177
+ compositionPattern: z.enum(["compound", "simple", "controlled", "wrapper"]).optional(),
178
+ subComponents: z.array(z.string()).optional(),
179
+ requiredChildren: z.array(z.string()).optional(),
180
+ commonPatterns: z.array(z.string()).optional(),
181
+ });
182
+
183
+ export const observedUsagePropSchema = z.object({
184
+ name: z.string().min(1),
185
+ kind: z.enum(["static", "dynamic", "spread", "jsx", "boolean", "null"]),
186
+ value: z.union([z.string(), z.number(), z.boolean(), z.null()]).optional(),
187
+ });
188
+
189
+ export const observedComponentUsageSchema = z.object({
190
+ file: z.string().min(1),
191
+ line: z.number().int().positive(),
192
+ column: z.number().int().nonnegative(),
193
+ props: z.array(observedUsagePropSchema),
194
+ parentElement: z.string().optional(),
195
+ conditional: z.boolean().optional(),
196
+ });
197
+
198
+ /**
199
+ * Schema for block definitions
200
+ */
201
+ export const blockDefinitionSchema = z.object({
202
+ name: z.string().min(1),
203
+ description: z.string().min(1),
204
+ category: z.string().min(1),
205
+ components: z.array(z.string().min(1)).min(1),
206
+ code: z.string().min(1),
207
+ tags: z.array(z.string()).optional(),
208
+ });
209
+
210
+ export const fragmentDefinitionSchema = z.object({
211
+ component: z.any(), // Allow any component type (function, class, forwardRef, etc.)
212
+ meta: fragmentMetaSchema,
213
+ usage: fragmentUsageSchema,
214
+ props: z.record(propDefinitionSchema),
215
+ relations: z.array(componentRelationSchema).optional(),
216
+ variants: z.array(fragmentVariantSchema), // Allow empty variants array
217
+ contract: fragmentContractSchema.optional(),
218
+ ai: aiMetadataSchema.optional(),
219
+ usages: z.array(observedComponentUsageSchema).optional(),
220
+ _generated: fragmentGeneratedSchema.optional(),
221
+ });
222
+
223
+ const repoRelativePathSchema = z
224
+ .string()
225
+ .min(1)
226
+ .superRefine((path, ctx) => {
227
+ const error = portableRepoPathError(path);
228
+ if (error) {
229
+ ctx.addIssue({
230
+ code: z.ZodIssueCode.custom,
231
+ message: error,
232
+ });
233
+ }
234
+ });
235
+
236
+ const repoRelativePathsSchema = z.array(repoRelativePathSchema).min(1);
237
+
238
+ const tokenSourceSchema = z.object({
239
+ path: repoRelativePathSchema,
240
+ format: z.enum(["auto", "css", "scss", "dtcg", "tailwind"]).optional(),
241
+ });
242
+
243
+ const tokenConfigSchema = z
244
+ .object({
245
+ include: repoRelativePathsSchema.optional(),
246
+ sources: z.array(tokenSourceSchema).min(1).optional(),
247
+ exclude: z.array(repoRelativePathSchema).optional(),
248
+ themeSelectors: z.record(z.string()).optional(),
249
+ enabled: z.boolean().optional(),
250
+ format: z.enum(["auto", "css", "scss", "dtcg", "tailwind"]).optional(),
251
+ namespace: z.string().min(1).optional(),
252
+ })
253
+ .passthrough()
254
+ .superRefine((tokens, ctx) => {
255
+ if (!tokens.include?.length && !tokens.sources?.length) {
256
+ ctx.addIssue({
257
+ code: z.ZodIssueCode.custom,
258
+ message: "Add tokens.include or tokens.sources",
259
+ path: ["sources"],
260
+ });
261
+ }
262
+ });
263
+
264
+ const topologySchema = z.object({
265
+ version: z.number().int().positive(),
266
+ base: z.enum(["app", "repo"]).optional(),
267
+ areas: z.array(
268
+ z.object({
269
+ id: z.string().min(1),
270
+ name: z.string().min(1),
271
+ criticality: z.enum(["low", "medium", "high", "revenue", "regulated"]),
272
+ owners: z.array(z.string()),
273
+ files: repoRelativePathsSchema,
274
+ routes: z.array(z.string().min(1)).optional(),
275
+ priority: z.number().optional(),
276
+ })
277
+ ),
278
+ });
279
+
280
+ /**
281
+ * Config schema - validates required fields, passes through optional config objects.
282
+ * Type definitions are in types.ts - schema just ensures basic structure.
283
+ */
284
+ export const fragmentsConfigSchema = z.object({
285
+ app: z
286
+ .object({
287
+ path: repoRelativePathSchema.optional(),
288
+ include: repoRelativePathsSchema.optional(),
289
+ exclude: z.array(repoRelativePathSchema).optional(),
290
+ })
291
+ .optional(),
292
+ designSystem: z
293
+ .object({
294
+ path: repoRelativePathSchema.optional(),
295
+ packageName: z.string().min(1).optional(),
296
+ components: repoRelativePathsSchema.optional(),
297
+ })
298
+ .optional(),
299
+ include: repoRelativePathsSchema.optional(),
300
+ exclude: z.array(repoRelativePathSchema).optional(),
301
+ components: repoRelativePathsSchema.optional(),
302
+ outFile: repoRelativePathSchema.optional(),
303
+ framework: z.enum(["react", "vue", "svelte"]).optional(),
304
+ figmaFile: z.string().url().optional(),
305
+ figmaToken: z.string().optional(),
306
+ screenshots: z.object({}).passthrough().optional(),
307
+ service: z.object({}).passthrough().optional(),
308
+ registry: z.object({}).passthrough().optional(),
309
+ tokens: tokenConfigSchema.optional(),
310
+ snippets: z
311
+ .object({
312
+ mode: z.enum(["warn", "error"]).optional(),
313
+ scope: z.enum(["snippet", "snippet+render"]).optional(),
314
+ requireFullSnippet: z.boolean().optional(),
315
+ allowedExternalModules: z.array(z.string().min(1)).optional(),
316
+ })
317
+ .optional(),
318
+ performance: z
319
+ .union([
320
+ z.enum(["strict", "standard", "relaxed"]),
321
+ z.object({
322
+ preset: z.enum(["strict", "standard", "relaxed"]).optional(),
323
+ budgets: z
324
+ .object({
325
+ bundleSize: z.number().positive().optional(),
326
+ })
327
+ .optional(),
328
+ }),
329
+ ])
330
+ .optional(),
331
+ storybook: z
332
+ .object({
333
+ exclude: z.array(z.string()).optional(),
334
+ include: z.array(z.string()).optional(),
335
+ excludeDeprecated: z.boolean().optional(),
336
+ excludeTests: z.boolean().optional(),
337
+ excludeSvgIcons: z.boolean().optional(),
338
+ excludeSubComponents: z.boolean().optional(),
339
+ })
340
+ .optional(),
341
+ topology: topologySchema.optional(),
342
+ govern: governanceConfigSchema.optional(),
343
+ inspect: z
344
+ .object({
345
+ localCanonical: z
346
+ .record(
347
+ z.string().min(1),
348
+ z.object({
349
+ importPath: z.string().min(1),
350
+ resolves: z
351
+ .array(
352
+ z.object({
353
+ tag: z.string().min(1),
354
+ role: z.string().min(1).optional(),
355
+ inputType: z.string().min(1).optional(),
356
+ })
357
+ )
358
+ .min(1),
359
+ })
360
+ )
361
+ .optional(),
362
+ })
363
+ .optional(),
364
+ recognizedClassHelpers: z.array(z.string().min(1)).optional(),
365
+ });
366
+
367
+ // ---------------------------------------------------------------------------
368
+ // v2 Schemas — alongside v1 (non-breaking)
369
+ // ---------------------------------------------------------------------------
370
+
371
+ /**
372
+ * Schema for v2 composition metadata (promoted from ai)
373
+ * Uses shorter field names since parent field is `composition`.
374
+ */
375
+ export const compositionMetadataSchema = z.object({
376
+ pattern: z.enum(["compound", "simple", "controlled", "wrapper"]).optional(),
377
+ subComponents: z.array(z.string()).optional(),
378
+ requiredChildren: z.array(z.string()).optional(),
379
+ commonPatterns: z.array(z.string()).optional(),
380
+ });
381
+
382
+ /**
383
+ * Schema for v2 extended provenance tracking
384
+ */
385
+ export const fragmentProvenanceSchema = z.object({
386
+ source: z.enum(["storybook", "manual", "ai", "scan"]),
387
+ sourceFile: z.string().optional(),
388
+ confidence: z.number().min(0).max(1).optional(),
389
+ timestamp: z.string().datetime().optional(),
390
+ autoFields: z.array(z.string()).optional(),
391
+ humanFields: z.array(z.string()).optional(),
392
+ });
393
+
394
+ /**
395
+ * Schema for v2 fragment definitions.
396
+ * Accepts the v2 field names (guidance, examples, composition, _provenance).
397
+ */
398
+ export const fragmentDefinitionV2Schema = z.object({
399
+ component: z.any(),
400
+ meta: fragmentMetaSchema,
401
+ guidance: fragmentUsageSchema,
402
+ props: z.record(propDefinitionSchema),
403
+ relations: z.array(componentRelationSchema).optional(),
404
+ examples: z.array(fragmentVariantSchema),
405
+ composition: compositionMetadataSchema.optional(),
406
+ contract: fragmentContractSchema.optional(),
407
+ _provenance: fragmentProvenanceSchema.optional(),
408
+ });
409
+
410
+ export const governedFragmentDefinitionSchema = z.object({
411
+ component: z.any(),
412
+ meta: fragmentMetaSchema,
413
+ guidance: fragmentUsageSchema,
414
+ props: z.record(propDefinitionSchema).optional(),
415
+ relations: z.array(componentRelationSchema).optional(),
416
+ examples: z.array(fragmentVariantSchema).optional(),
417
+ composition: compositionMetadataSchema.optional(),
418
+ contract: fragmentContractSchema.optional(),
419
+ _provenance: fragmentProvenanceSchema.optional(),
420
+ govern: z.function().optional(),
421
+ governance: z.array(componentGovernanceRecordSchema).optional(),
422
+ });
423
+
424
+ /**
425
+ * @deprecated Use blockDefinitionSchema instead
426
+ */
427
+ export const recipeDefinitionSchema = blockDefinitionSchema;
@@ -0,0 +1,275 @@
1
+ import { z } from "zod";
2
+
3
+ import {
4
+ legacySeverityLevelSchema,
5
+ severityFromLevel,
6
+ severityLevel,
7
+ severityLevelSchema,
8
+ severitySchema,
9
+ } from "../severity.js";
10
+ import type { LegacySeverityLevel, Severity } from "../severity.js";
11
+
12
+ export { legacySeverityLevelSchema, severityLevelSchema, severitySchema };
13
+ export type { LegacySeverityLevel, Severity, SeverityLevel } from "../severity.js";
14
+
15
+ export const factLocationSchema = z.object({
16
+ file: z.string(),
17
+ line: z.number().int().nonnegative(),
18
+ column: z.number().int().nonnegative(),
19
+ endLine: z.number().int().nonnegative().optional(),
20
+ endColumn: z.number().int().nonnegative().optional(),
21
+ });
22
+ export type FactLocation = z.infer<typeof factLocationSchema>;
23
+
24
+ export const findingReplacePropValueFixSchema = z.object({
25
+ kind: z.literal("replacePropValue"),
26
+ title: z.string(),
27
+ prop: z.string(),
28
+ value: z.unknown(),
29
+ deterministic: z.boolean().optional(),
30
+ });
31
+
32
+ export const findingReplaceStyleValueFixSchema = z.object({
33
+ kind: z.literal("replaceStyleValue"),
34
+ title: z.string(),
35
+ property: z.string(),
36
+ value: z.string(),
37
+ deterministic: z.boolean().optional(),
38
+ });
39
+
40
+ export const findingReplaceImportFixSchema = z.object({
41
+ kind: z.literal("replaceImport"),
42
+ title: z.string(),
43
+ from: z.string(),
44
+ to: z.string(),
45
+ deterministic: z.boolean().optional(),
46
+ });
47
+
48
+ export const findingReplaceComponentFixSchema = z.object({
49
+ kind: z.literal("replaceComponent"),
50
+ title: z.string(),
51
+ from: z.string(),
52
+ to: z.string(),
53
+ deterministic: z.boolean().optional(),
54
+ });
55
+
56
+ export const findingReplaceClassTokenFixSchema = z.object({
57
+ kind: z.literal("replaceClassToken"),
58
+ title: z.string(),
59
+ from: z.string(),
60
+ to: z.string().nullable(),
61
+ utility: z.string(),
62
+ deterministic: z.boolean().optional(),
63
+ });
64
+
65
+ export const findingFixSchema = z.discriminatedUnion("kind", [
66
+ findingReplacePropValueFixSchema,
67
+ findingReplaceStyleValueFixSchema,
68
+ findingReplaceImportFixSchema,
69
+ findingReplaceComponentFixSchema,
70
+ findingReplaceClassTokenFixSchema,
71
+ ]);
72
+ export type FindingFix = z.infer<typeof findingFixSchema>;
73
+ export type FindingReplacePropValueFix = z.infer<typeof findingReplacePropValueFixSchema>;
74
+ export type FindingReplaceStyleValueFix = z.infer<typeof findingReplaceStyleValueFixSchema>;
75
+ export type FindingReplaceImportFix = z.infer<typeof findingReplaceImportFixSchema>;
76
+ export type FindingReplaceComponentFix = z.infer<typeof findingReplaceComponentFixSchema>;
77
+ export type FindingReplaceClassTokenFix = z.infer<typeof findingReplaceClassTokenFixSchema>;
78
+
79
+ export interface FactEvidence {
80
+ factId: string;
81
+ fact: {
82
+ kind: string;
83
+ location?: FactLocation;
84
+ };
85
+ }
86
+
87
+ export const factEvidenceSchema: z.ZodType<FactEvidence> = z.object({
88
+ factId: z.string(),
89
+ fact: z
90
+ .object({
91
+ kind: z.string(),
92
+ location: factLocationSchema.optional(),
93
+ })
94
+ .passthrough(),
95
+ });
96
+
97
+ export const findingSchema = z.object({
98
+ ruleId: z.string(),
99
+ ruleVersion: z.string(),
100
+ severity: severitySchema,
101
+ level: severityLevelSchema,
102
+ code: z
103
+ .string()
104
+ .regex(/^FUI\d{4}$/)
105
+ .optional(),
106
+ helpUrl: z.string().url().optional(),
107
+ message: z.string(),
108
+ fingerprint: z.string(),
109
+ location: factLocationSchema,
110
+ evidence: z.array(factEvidenceSchema).min(1),
111
+ fix: findingFixSchema.optional(),
112
+ attributes: z.record(z.unknown()).optional(),
113
+ /**
114
+ * Product area this finding's file resolved to (topology layer). Optional —
115
+ * absent when the repo has no `topology` config. See `topology/resolve-area`.
116
+ */
117
+ area: z
118
+ .object({
119
+ areaId: z.string(),
120
+ areaName: z.string(),
121
+ criticality: z.enum(["low", "medium", "high", "revenue", "regulated"]),
122
+ owners: z.array(z.string()),
123
+ matchedGlob: z.string(),
124
+ })
125
+ .optional(),
126
+ });
127
+ export type Finding = z.infer<typeof findingSchema>;
128
+
129
+ export const violationSchema = z.object({
130
+ nodeId: z.string(),
131
+ nodeType: z.string(),
132
+ rule: z.string(),
133
+ severity: severitySchema,
134
+ level: severityLevelSchema.optional(),
135
+ code: z
136
+ .string()
137
+ .regex(/^FUI\d{4}$/)
138
+ .optional(),
139
+ helpUrl: z.string().url().optional(),
140
+ message: z.string(),
141
+ path: z.string().optional(),
142
+ suggestion: z.string().optional(),
143
+ prop: z.string().optional(),
144
+ filePath: z.string().optional(),
145
+ line: z.number().int().nonnegative().optional(),
146
+ column: z.number().int().nonnegative().optional(),
147
+ rawValue: z.string().optional(),
148
+ extra: z.record(z.unknown()).optional(),
149
+ });
150
+ export type Violation = z.infer<typeof violationSchema>;
151
+
152
+ export const fixSchema = z.object({
153
+ nodeId: z.string(),
154
+ prop: z.string().optional(),
155
+ action: z.enum(["remove", "replace", "add"]),
156
+ value: z.unknown().optional(),
157
+ description: z.string(),
158
+ });
159
+ export type Fix = z.infer<typeof fixSchema>;
160
+
161
+ export const validatorResultSchema = z.object({
162
+ validator: z.string(),
163
+ severity: severitySchema,
164
+ passed: z.boolean(),
165
+ violations: z.array(violationSchema),
166
+ findings: z.array(findingSchema).optional(),
167
+ suggestions: z.array(fixSchema).optional(),
168
+ });
169
+ export type ValidatorResult = z.infer<typeof validatorResultSchema>;
170
+
171
+ export const governanceVerdictMetadataSchema = z.object({
172
+ runner: z.string(),
173
+ policyVersion: z.string().optional(),
174
+ duration: z.number().nonnegative(),
175
+ nodeCount: z.number().int().nonnegative(),
176
+ componentTypes: z.array(z.string()),
177
+ });
178
+ export type GovernanceVerdictMetadata = z.infer<typeof governanceVerdictMetadataSchema>;
179
+
180
+ export const governanceVerdictSchema = z.object({
181
+ verdict: z.enum(["pass", "warn", "fail"]).optional(),
182
+ passed: z.boolean(),
183
+ score: z.number(),
184
+ results: z.array(validatorResultSchema),
185
+ metadata: governanceVerdictMetadataSchema,
186
+ });
187
+ export type GovernanceVerdict = z.infer<typeof governanceVerdictSchema>;
188
+
189
+ export const suppressionDirectiveSchema = z.object({
190
+ ruleId: z.string().optional(),
191
+ reason: z.string().min(1),
192
+ expiresAt: z.string().datetime().optional(),
193
+ expiresOn: z
194
+ .string()
195
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
196
+ .optional(),
197
+ scope: z.enum(["line", "block", "file"]).default("line"),
198
+ code: z
199
+ .string()
200
+ .regex(/^FUI\d{4}$/)
201
+ .optional(),
202
+ });
203
+ export type SuppressionDirective = z.infer<typeof suppressionDirectiveSchema>;
204
+
205
+ export const agentPlanSchema = z.object({
206
+ tool: z.enum([
207
+ "design_system/list_primitives",
208
+ "design_system/conform",
209
+ "design_system/prove_compliant",
210
+ ]),
211
+ args: z.record(z.unknown()),
212
+ confidence: z.number().min(0).max(1),
213
+ });
214
+
215
+ export const agentFindingSchema = z.object({
216
+ code: z
217
+ .string()
218
+ .regex(/^FUI\d{4}$/)
219
+ .optional(),
220
+ ruleId: z.string(),
221
+ filePath: z.string(),
222
+ line: z.number().int().nonnegative(),
223
+ col: z.number().int().nonnegative(),
224
+ severity: severitySchema,
225
+ level: severityLevelSchema,
226
+ message: z.string(),
227
+ evidence: z.array(factEvidenceSchema),
228
+ plan: agentPlanSchema,
229
+ });
230
+
231
+ export const agentFormatSchema = z.object({
232
+ schemaVersion: z.literal("typestyle.agent.v1"),
233
+ passed: z.boolean(),
234
+ score: z.number(),
235
+ summary: z.string(),
236
+ fix_first: z.array(agentFindingSchema),
237
+ remaining: z.array(agentFindingSchema),
238
+ suppression_template: z.object({
239
+ directive: z.string(),
240
+ requiredFields: z.array(z.string()),
241
+ example: z.string(),
242
+ }),
243
+ budget: z.object({
244
+ iteration: z.number().int().nonnegative(),
245
+ maxIterations: z.number().int().positive(),
246
+ scoreDelta: z.number(),
247
+ }),
248
+ findings: z.array(findingSchema).optional(),
249
+ suppressions: z.array(suppressionDirectiveSchema).optional(),
250
+ });
251
+ export type AgentFormat = z.infer<typeof agentFormatSchema>;
252
+
253
+ export function normalizeSeverity(severity: Severity | LegacySeverityLevel): Severity {
254
+ return severitySchema.safeParse(severity).success
255
+ ? (severity as Severity)
256
+ : severityFromLevel(legacySeverityLevelSchema.parse(severity));
257
+ }
258
+
259
+ export function normalizeFinding(
260
+ input: Omit<Finding, "level"> & { level?: Finding["level"] }
261
+ ): Finding {
262
+ return {
263
+ ...input,
264
+ level: input.level ?? severityLevel(input.severity),
265
+ };
266
+ }
267
+
268
+ export function normalizeViolation(
269
+ input: Omit<Violation, "level"> & { level?: Violation["level"] }
270
+ ): Violation {
271
+ return {
272
+ ...input,
273
+ level: input.level ?? severityLevel(input.severity),
274
+ };
275
+ }