@fragments-sdk/cli 0.7.16 → 0.8.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.
- package/dist/bin.js +227 -53
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-QLTLLQBI.js → chunk-2JIKCJX3.js} +312 -24
- package/dist/chunk-2JIKCJX3.js.map +1 -0
- package/dist/{chunk-57OW43NL.js → chunk-CJEGT3WD.js} +2 -2
- package/dist/{chunk-7CRC46HV.js → chunk-GOVI6COW.js} +13 -3
- package/dist/chunk-GOVI6COW.js.map +1 -0
- package/dist/{chunk-WLXFE6XW.js → chunk-NGIMCIK2.js} +60 -2
- package/dist/chunk-NGIMCIK2.js.map +1 -0
- package/dist/{chunk-M42XIHPV.js → chunk-WI6SLMSO.js} +2 -2
- package/dist/core/index.d.ts +110 -3
- package/dist/core/index.js +12 -2
- package/dist/{defineFragment-BI9KoPrs.d.ts → defineFragment-D0UTve-I.d.ts} +9 -0
- package/dist/{generate-ICIPKCKV.js → generate-35OIMW4Y.js} +4 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -4
- package/dist/{init-DIZ6UNBL.js → init-KFYN37ZY.js} +4 -4
- package/dist/mcp-bin.js +67 -3
- package/dist/mcp-bin.js.map +1 -1
- package/dist/{scan-X3DI2X5G.js → scan-65RH3QMM.js} +5 -5
- package/dist/{service-JEWWTSKI.js → service-A5GIGGGK.js} +3 -3
- package/dist/{static-viewer-JIWCYKVK.js → static-viewer-NSODM5VX.js} +3 -3
- package/dist/{test-36UELXTE.js → test-RPWZAYSJ.js} +3 -3
- package/dist/{tokens-K2AGUUOJ.js → tokens-NIXSZRX7.js} +4 -4
- package/dist/{viewer-QKIAPTPG.js → viewer-HZK4BSDK.js} +43 -12
- package/dist/viewer-HZK4BSDK.js.map +1 -0
- package/package.json +3 -3
- package/src/bin.ts +32 -0
- package/src/build.ts +47 -0
- package/src/commands/perf.ts +249 -0
- package/src/core/bundle-measurer.ts +421 -0
- package/src/core/index.ts +16 -0
- package/src/core/performance-presets.ts +142 -0
- package/src/core/schema.ts +10 -0
- package/src/core/types.ts +6 -0
- package/src/mcp/server.ts +77 -0
- package/src/theme/__tests__/component-contrast.test.ts +210 -157
- package/src/viewer/components/BottomPanel.tsx +8 -0
- package/src/viewer/components/PerformancePanel.tsx +301 -0
- package/src/viewer/hooks/useAppState.ts +1 -1
- package/src/viewer/vite-plugin.ts +36 -0
- package/dist/chunk-7CRC46HV.js.map +0 -1
- package/dist/chunk-QLTLLQBI.js.map +0 -1
- package/dist/chunk-WLXFE6XW.js.map +0 -1
- package/dist/viewer-QKIAPTPG.js.map +0 -1
- /package/dist/{chunk-57OW43NL.js.map → chunk-CJEGT3WD.js.map} +0 -0
- /package/dist/{chunk-M42XIHPV.js.map → chunk-WI6SLMSO.js.map} +0 -0
- /package/dist/{generate-ICIPKCKV.js.map → generate-35OIMW4Y.js.map} +0 -0
- /package/dist/{init-DIZ6UNBL.js.map → init-KFYN37ZY.js.map} +0 -0
- /package/dist/{scan-X3DI2X5G.js.map → scan-65RH3QMM.js.map} +0 -0
- /package/dist/{service-JEWWTSKI.js.map → service-A5GIGGGK.js.map} +0 -0
- /package/dist/{static-viewer-JIWCYKVK.js.map → static-viewer-NSODM5VX.js.map} +0 -0
- /package/dist/{test-36UELXTE.js.map → test-RPWZAYSJ.js.map} +0 -0
- /package/dist/{tokens-K2AGUUOJ.js.map → tokens-NIXSZRX7.js.map} +0 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance Panel — bundle size visualization in the viewer.
|
|
3
|
+
*
|
|
4
|
+
* Fetches performance data from /fragments/perf-data (served by the
|
|
5
|
+
* Vite dev server from fragments.json) and displays:
|
|
6
|
+
* - Gzipped and raw bundle size
|
|
7
|
+
* - Complexity tier badge
|
|
8
|
+
* - Budget bar with percentage
|
|
9
|
+
* - Over-budget alert when applicable
|
|
10
|
+
* - Empty state when no data (prompts `fragments perf`)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { useState, useEffect } from 'react';
|
|
14
|
+
import { Card, Badge, Text, Stack, EmptyState, Alert, Collapsible } from '@fragments-sdk/ui';
|
|
15
|
+
import { BRAND } from '../../core/index.js';
|
|
16
|
+
|
|
17
|
+
interface ImportEntry {
|
|
18
|
+
path: string;
|
|
19
|
+
bytes: number;
|
|
20
|
+
percent: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface PerformanceData {
|
|
24
|
+
bundleSize: number;
|
|
25
|
+
rawSize: number;
|
|
26
|
+
complexity: 'lightweight' | 'moderate' | 'heavy';
|
|
27
|
+
budgetPercent: number;
|
|
28
|
+
overBudget: boolean;
|
|
29
|
+
measuredAt: string;
|
|
30
|
+
imports?: ImportEntry[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface PerfDataResponse {
|
|
34
|
+
summary: {
|
|
35
|
+
preset: string;
|
|
36
|
+
budget: number;
|
|
37
|
+
total: number;
|
|
38
|
+
overBudget: number;
|
|
39
|
+
tiers: Record<string, number>;
|
|
40
|
+
} | null;
|
|
41
|
+
components: Record<string, PerformanceData>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface PerformancePanelProps {
|
|
45
|
+
componentName: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function formatBytes(bytes: number): string {
|
|
49
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
50
|
+
const kb = bytes / 1024;
|
|
51
|
+
return kb < 10 ? `${kb.toFixed(1)} KB` : `${Math.round(kb)} KB`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function tierVariant(tier: string): 'success' | 'warning' | 'danger' {
|
|
55
|
+
switch (tier) {
|
|
56
|
+
case 'lightweight': return 'success';
|
|
57
|
+
case 'moderate': return 'warning';
|
|
58
|
+
case 'heavy': return 'danger';
|
|
59
|
+
default: return 'warning';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function BudgetBar({ percent }: { percent: number }) {
|
|
64
|
+
const capped = Math.min(percent, 100);
|
|
65
|
+
const isOver = percent > 100;
|
|
66
|
+
const color = isOver ? 'var(--fui-color-danger, #e53e3e)'
|
|
67
|
+
: percent > 80 ? 'var(--fui-color-warning, #dd6b20)'
|
|
68
|
+
: 'var(--fui-color-success, #38a169)';
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<div style={{ width: '100%' }}>
|
|
72
|
+
<div style={{
|
|
73
|
+
display: 'flex',
|
|
74
|
+
justifyContent: 'space-between',
|
|
75
|
+
marginBottom: '4px',
|
|
76
|
+
}}>
|
|
77
|
+
<Text size="sm" color="secondary">Budget usage</Text>
|
|
78
|
+
<Text size="sm" weight="semibold" style={{ color }}>
|
|
79
|
+
{percent}%
|
|
80
|
+
</Text>
|
|
81
|
+
</div>
|
|
82
|
+
<div style={{
|
|
83
|
+
width: '100%',
|
|
84
|
+
height: '8px',
|
|
85
|
+
borderRadius: '4px',
|
|
86
|
+
backgroundColor: 'var(--fui-color-surface-2, #e2e8f0)',
|
|
87
|
+
overflow: 'hidden',
|
|
88
|
+
}}>
|
|
89
|
+
<div style={{
|
|
90
|
+
width: `${capped}%`,
|
|
91
|
+
height: '100%',
|
|
92
|
+
borderRadius: '4px',
|
|
93
|
+
backgroundColor: color,
|
|
94
|
+
transition: 'width 0.3s ease',
|
|
95
|
+
}} />
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Labels are already clean from the backend (e.g., "react-markdown", "Markdown (self)") */
|
|
102
|
+
|
|
103
|
+
function ImportBreakdown({ imports, rawSize }: { imports: ImportEntry[]; rawSize: number }) {
|
|
104
|
+
return (
|
|
105
|
+
<Collapsible>
|
|
106
|
+
<Collapsible.Trigger style={{ cursor: 'pointer' }}>
|
|
107
|
+
<Text size="sm" weight="semibold">
|
|
108
|
+
Import breakdown ({imports.length} file{imports.length !== 1 ? 's' : ''})
|
|
109
|
+
</Text>
|
|
110
|
+
</Collapsible.Trigger>
|
|
111
|
+
<Collapsible.Content>
|
|
112
|
+
<div style={{ marginTop: '8px' }}>
|
|
113
|
+
{imports.map((imp) => {
|
|
114
|
+
const barWidth = Math.max(1, Math.round(imp.percent / 2));
|
|
115
|
+
return (
|
|
116
|
+
<div
|
|
117
|
+
key={imp.path}
|
|
118
|
+
style={{
|
|
119
|
+
display: 'grid',
|
|
120
|
+
gridTemplateColumns: '1fr 70px 45px minmax(20px, 100px)',
|
|
121
|
+
alignItems: 'center',
|
|
122
|
+
gap: '8px',
|
|
123
|
+
padding: '4px 0',
|
|
124
|
+
borderBottom: '1px solid var(--fui-color-border, #2d3748)',
|
|
125
|
+
}}
|
|
126
|
+
>
|
|
127
|
+
<Text
|
|
128
|
+
size="xs"
|
|
129
|
+
color="secondary"
|
|
130
|
+
style={{
|
|
131
|
+
overflow: 'hidden',
|
|
132
|
+
textOverflow: 'ellipsis',
|
|
133
|
+
whiteSpace: 'nowrap',
|
|
134
|
+
fontFamily: 'var(--fui-font-mono, monospace)',
|
|
135
|
+
}}
|
|
136
|
+
title={imp.path}
|
|
137
|
+
>
|
|
138
|
+
{imp.path}
|
|
139
|
+
</Text>
|
|
140
|
+
<Text size="xs" weight="semibold" style={{ textAlign: 'right' }}>
|
|
141
|
+
{formatBytes(imp.bytes)}
|
|
142
|
+
</Text>
|
|
143
|
+
<Text size="xs" color="tertiary" style={{ textAlign: 'right' }}>
|
|
144
|
+
{imp.percent}%
|
|
145
|
+
</Text>
|
|
146
|
+
<div style={{
|
|
147
|
+
height: '6px',
|
|
148
|
+
borderRadius: '3px',
|
|
149
|
+
backgroundColor: 'var(--fui-color-surface-2, #2d3748)',
|
|
150
|
+
overflow: 'hidden',
|
|
151
|
+
}}>
|
|
152
|
+
<div style={{
|
|
153
|
+
width: `${Math.min(barWidth * 2, 100)}%`,
|
|
154
|
+
height: '100%',
|
|
155
|
+
borderRadius: '3px',
|
|
156
|
+
backgroundColor: imp.percent > 50
|
|
157
|
+
? 'var(--fui-color-danger, #e53e3e)'
|
|
158
|
+
: imp.percent > 20
|
|
159
|
+
? 'var(--fui-color-warning, #dd6b20)'
|
|
160
|
+
: 'var(--fui-color-accent, #4299e1)',
|
|
161
|
+
}} />
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
);
|
|
165
|
+
})}
|
|
166
|
+
</div>
|
|
167
|
+
</Collapsible.Content>
|
|
168
|
+
</Collapsible>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function PerformancePanel({ componentName }: PerformancePanelProps) {
|
|
173
|
+
const [perfData, setPerfData] = useState<PerformanceData | null>(null);
|
|
174
|
+
const [loading, setLoading] = useState(true);
|
|
175
|
+
const [noData, setNoData] = useState(false);
|
|
176
|
+
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
let cancelled = false;
|
|
179
|
+
|
|
180
|
+
async function fetchPerfData() {
|
|
181
|
+
setLoading(true);
|
|
182
|
+
try {
|
|
183
|
+
const res = await fetch('/fragments/perf-data');
|
|
184
|
+
if (!res.ok) {
|
|
185
|
+
setNoData(true);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const data: PerfDataResponse = await res.json();
|
|
189
|
+
if (!cancelled) {
|
|
190
|
+
const componentPerf = data.components[componentName];
|
|
191
|
+
if (componentPerf) {
|
|
192
|
+
setPerfData(componentPerf);
|
|
193
|
+
setNoData(false);
|
|
194
|
+
} else {
|
|
195
|
+
setPerfData(null);
|
|
196
|
+
setNoData(true);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
} catch {
|
|
200
|
+
if (!cancelled) {
|
|
201
|
+
setNoData(true);
|
|
202
|
+
}
|
|
203
|
+
} finally {
|
|
204
|
+
if (!cancelled) {
|
|
205
|
+
setLoading(false);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
fetchPerfData();
|
|
211
|
+
return () => { cancelled = true; };
|
|
212
|
+
}, [componentName]);
|
|
213
|
+
|
|
214
|
+
if (loading) {
|
|
215
|
+
return (
|
|
216
|
+
<div style={{ padding: '16px' }}>
|
|
217
|
+
<Text size="sm" color="secondary">Loading performance data...</Text>
|
|
218
|
+
</div>
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (noData || !perfData) {
|
|
223
|
+
return (
|
|
224
|
+
<div style={{ padding: '16px' }}>
|
|
225
|
+
<EmptyState>
|
|
226
|
+
<EmptyState.Title>No performance data</EmptyState.Title>
|
|
227
|
+
<EmptyState.Description>
|
|
228
|
+
Run <code>{BRAND.cliCommand} perf</code> to measure bundle sizes, then reload.
|
|
229
|
+
</EmptyState.Description>
|
|
230
|
+
</EmptyState>
|
|
231
|
+
</div>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const { bundleSize, rawSize, complexity, budgetPercent, overBudget, measuredAt } = perfData;
|
|
236
|
+
const measuredDate = new Date(measuredAt).toLocaleString();
|
|
237
|
+
|
|
238
|
+
return (
|
|
239
|
+
<div style={{ padding: '16px' }}>
|
|
240
|
+
<Stack gap="md">
|
|
241
|
+
{overBudget && (
|
|
242
|
+
<Alert variant="danger">
|
|
243
|
+
<strong>{componentName}</strong> exceeds its performance budget ({budgetPercent}% of allowed size).
|
|
244
|
+
Consider code splitting, lazy loading heavy dependencies, or tree-shaking unused exports.
|
|
245
|
+
</Alert>
|
|
246
|
+
)}
|
|
247
|
+
|
|
248
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '12px' }}>
|
|
249
|
+
<Card>
|
|
250
|
+
<Card.Body>
|
|
251
|
+
<Stack gap="xs">
|
|
252
|
+
<Text size="sm" color="secondary">Gzipped</Text>
|
|
253
|
+
<Text size="xl" weight="bold">{formatBytes(bundleSize)}</Text>
|
|
254
|
+
</Stack>
|
|
255
|
+
</Card.Body>
|
|
256
|
+
</Card>
|
|
257
|
+
|
|
258
|
+
<Card>
|
|
259
|
+
<Card.Body>
|
|
260
|
+
<Stack gap="xs">
|
|
261
|
+
<Text size="sm" color="secondary">Raw (minified)</Text>
|
|
262
|
+
<Text size="xl" weight="bold">{formatBytes(rawSize)}</Text>
|
|
263
|
+
</Stack>
|
|
264
|
+
</Card.Body>
|
|
265
|
+
</Card>
|
|
266
|
+
|
|
267
|
+
<Card>
|
|
268
|
+
<Card.Body>
|
|
269
|
+
<Stack gap="xs">
|
|
270
|
+
<Text size="sm" color="secondary">Complexity</Text>
|
|
271
|
+
<div>
|
|
272
|
+
<Badge variant={tierVariant(complexity)} size="lg">
|
|
273
|
+
{complexity}
|
|
274
|
+
</Badge>
|
|
275
|
+
</div>
|
|
276
|
+
</Stack>
|
|
277
|
+
</Card.Body>
|
|
278
|
+
</Card>
|
|
279
|
+
</div>
|
|
280
|
+
|
|
281
|
+
<Card>
|
|
282
|
+
<Card.Body>
|
|
283
|
+
<BudgetBar percent={budgetPercent} />
|
|
284
|
+
</Card.Body>
|
|
285
|
+
</Card>
|
|
286
|
+
|
|
287
|
+
{perfData.imports && perfData.imports.length > 0 && (
|
|
288
|
+
<Card>
|
|
289
|
+
<Card.Body>
|
|
290
|
+
<ImportBreakdown imports={perfData.imports} rawSize={rawSize} />
|
|
291
|
+
</Card.Body>
|
|
292
|
+
</Card>
|
|
293
|
+
)}
|
|
294
|
+
|
|
295
|
+
<Text size="xs" color="tertiary">
|
|
296
|
+
Measured {measuredDate}. CSS excluded (JS-only measurement).
|
|
297
|
+
</Text>
|
|
298
|
+
</Stack>
|
|
299
|
+
</div>
|
|
300
|
+
);
|
|
301
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { useReducer, useCallback, useMemo } from 'react';
|
|
7
7
|
|
|
8
|
-
export type ActivePanel = 'code' | 'styles' | 'accessibility' | 'interactions' | 'actions' | 'graph' | 'contract';
|
|
8
|
+
export type ActivePanel = 'code' | 'styles' | 'accessibility' | 'interactions' | 'actions' | 'graph' | 'contract' | 'performance';
|
|
9
9
|
|
|
10
10
|
interface AppUIState {
|
|
11
11
|
activePanel: ActivePanel;
|
|
@@ -1412,6 +1412,42 @@ export function fragmentsPlugin(options: FragmentsPluginOptions): Plugin[] {
|
|
|
1412
1412
|
return;
|
|
1413
1413
|
}
|
|
1414
1414
|
|
|
1415
|
+
// Handle /fragments/perf-data — serve performance data from fragments.json
|
|
1416
|
+
if (req.url === "/fragments/perf-data" && req.method === "GET") {
|
|
1417
|
+
try {
|
|
1418
|
+
const { readFile: readFileAsync } = await import("node:fs/promises");
|
|
1419
|
+
const { resolve: resolvePath, join: joinPath } = await import("node:path");
|
|
1420
|
+
const outFilePath = resolvePath(
|
|
1421
|
+
projectRoot,
|
|
1422
|
+
config.outFile ?? BRAND.outFile
|
|
1423
|
+
);
|
|
1424
|
+
const raw = await readFileAsync(outFilePath, "utf-8");
|
|
1425
|
+
const parsed = JSON.parse(raw);
|
|
1426
|
+
|
|
1427
|
+
// Extract per-component performance + summary
|
|
1428
|
+
const components: Record<string, unknown> = {};
|
|
1429
|
+
for (const [name, frag] of Object.entries(
|
|
1430
|
+
parsed.fragments as Record<string, any>
|
|
1431
|
+
)) {
|
|
1432
|
+
if (frag.performance) {
|
|
1433
|
+
components[name] = frag.performance;
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
res.setHeader("Content-Type", "application/json");
|
|
1438
|
+
res.end(
|
|
1439
|
+
JSON.stringify({
|
|
1440
|
+
summary: parsed.performanceSummary ?? null,
|
|
1441
|
+
components,
|
|
1442
|
+
})
|
|
1443
|
+
);
|
|
1444
|
+
} catch {
|
|
1445
|
+
res.setHeader("Content-Type", "application/json");
|
|
1446
|
+
res.end(JSON.stringify({ summary: null, components: {} }));
|
|
1447
|
+
}
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1415
1451
|
// Handle /fragments/preview/ - isolated iframe for component previews
|
|
1416
1452
|
if (req.url?.startsWith("/fragments/preview")) {
|
|
1417
1453
|
// Redirect to trailing slash
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/schema.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * Zod schemas for runtime validation of fragment definitions\n */\n\n// Figma property mapping schemas\nconst figmaStringMappingSchema = z.object({\n __type: z.literal('figma-string'),\n figmaProperty: z.string().min(1),\n});\n\nconst figmaBooleanMappingSchema = z.object({\n __type: z.literal('figma-boolean'),\n figmaProperty: z.string().min(1),\n valueMapping: z.object({ true: z.unknown(), false: z.unknown() }).optional(),\n});\n\nconst figmaEnumMappingSchema = z.object({\n __type: z.literal('figma-enum'),\n figmaProperty: z.string().min(1),\n valueMapping: z.record(z.unknown()),\n});\n\nconst figmaInstanceMappingSchema = z.object({\n __type: z.literal('figma-instance'),\n figmaProperty: z.string().min(1),\n});\n\nconst figmaChildrenMappingSchema = z.object({\n __type: z.literal('figma-children'),\n layers: z.array(z.string().min(1)),\n});\n\nconst figmaTextContentMappingSchema = z.object({\n __type: z.literal('figma-text-content'),\n layer: z.string().min(1),\n});\n\nexport const figmaPropMappingSchema = z.discriminatedUnion('__type', [\n figmaStringMappingSchema,\n figmaBooleanMappingSchema,\n figmaEnumMappingSchema,\n figmaInstanceMappingSchema,\n figmaChildrenMappingSchema,\n figmaTextContentMappingSchema,\n]);\n\nexport const fragmentMetaSchema = z.object({\n name: z.string().min(1),\n description: z.string().min(1),\n category: z.string().min(1),\n tags: z.array(z.string()).optional(),\n status: z.enum(['stable', 'beta', 'deprecated', 'experimental']).optional(),\n since: z.string().optional(),\n dependencies: z.array(z.object({\n name: z.string().min(1),\n version: z.string().min(1),\n reason: z.string().optional(),\n })).optional(),\n figma: z.string().url().optional(),\n figmaProps: z.record(figmaPropMappingSchema).optional(),\n});\n\nexport const fragmentUsageSchema = z.object({\n when: z.array(z.string()).min(1),\n whenNot: z.array(z.string()).min(1),\n guidelines: z.array(z.string()).optional(),\n accessibility: z.array(z.string()).optional(),\n});\n\nexport const propTypeSchema: z.ZodType<string> = z.enum([\n 'string',\n 'number',\n 'boolean',\n 'enum',\n 'function',\n 'node',\n 'element',\n 'object',\n 'array',\n 'union',\n 'custom',\n]);\n\nexport const propDefinitionSchema = z.object({\n type: propTypeSchema,\n values: z.array(z.string()).readonly().optional(),\n default: z.unknown().optional(),\n description: z.string().optional(),\n required: z.boolean().optional(),\n constraints: z.array(z.string()).optional(),\n typeDetails: z.record(z.unknown()).optional(),\n});\n\nexport const relationshipTypeSchema = z.enum([\n 'alternative',\n 'sibling',\n 'parent',\n 'child',\n 'composition',\n 'complementary',\n 'used-by',\n]);\n\nexport const componentRelationSchema = z.object({\n component: z.string().min(1),\n relationship: relationshipTypeSchema,\n note: z.string().min(1),\n});\n\nexport const fragmentVariantSchema = z.object({\n name: z.string().min(1),\n description: z.string().min(1),\n render: z.function().returns(z.unknown()),\n code: z.string().optional(),\n figma: z.string().url().optional(),\n});\n\n/**\n * Schema for banned patterns in codebase\n */\nexport const fragmentBanSchema = z.object({\n pattern: z.string().min(1),\n message: z.string().min(1),\n});\n\n/**\n * Schema for agent-optimized contract metadata\n */\nexport const fragmentContractSchema = z.object({\n propsSummary: z.array(z.string()).optional(),\n a11yRules: z.array(z.string()).optional(),\n bans: z.array(fragmentBanSchema).optional(),\n scenarioTags: z.array(z.string()).optional(),\n});\n\n/**\n * Schema for provenance tracking of generated fragments\n */\nexport const fragmentGeneratedSchema = z.object({\n source: z.enum(['storybook', 'manual', 'ai']),\n sourceFile: z.string().optional(),\n confidence: z.number().min(0).max(1).optional(),\n timestamp: z.string().datetime().optional(),\n});\n\n/**\n * Schema for AI-specific metadata for playground context generation\n */\nexport const aiMetadataSchema = z.object({\n compositionPattern: z.enum(['compound', 'simple', 'controlled', 'wrapper']).optional(),\n subComponents: z.array(z.string()).optional(),\n requiredChildren: z.array(z.string()).optional(),\n commonPatterns: z.array(z.string()).optional(),\n});\n\n/**\n * Schema for block definitions\n */\nexport const blockDefinitionSchema = z.object({\n name: z.string().min(1),\n description: z.string().min(1),\n category: z.string().min(1),\n components: z.array(z.string().min(1)).min(1),\n code: z.string().min(1),\n tags: z.array(z.string()).optional(),\n});\n\nexport const fragmentDefinitionSchema = z.object({\n component: z.any(), // Allow any component type (function, class, forwardRef, etc.)\n meta: fragmentMetaSchema,\n usage: fragmentUsageSchema,\n props: z.record(propDefinitionSchema),\n relations: z.array(componentRelationSchema).optional(),\n variants: z.array(fragmentVariantSchema), // Allow empty variants array\n contract: fragmentContractSchema.optional(),\n ai: aiMetadataSchema.optional(),\n _generated: fragmentGeneratedSchema.optional(),\n});\n\n/**\n * Config schema - validates required fields, passes through optional config objects.\n * Type definitions are in types.ts - schema just ensures basic structure.\n */\nexport const fragmentsConfigSchema = z.object({\n include: z.array(z.string()).min(1),\n exclude: z.array(z.string()).optional(),\n components: z.array(z.string()).optional(),\n outFile: z.string().optional(),\n framework: z.enum(['react', 'vue', 'svelte']).optional(),\n figmaFile: z.string().url().optional(),\n figmaToken: z.string().optional(),\n screenshots: z.object({}).passthrough().optional(),\n service: z.object({}).passthrough().optional(),\n registry: z.object({}).passthrough().optional(),\n tokens: z.object({\n include: z.array(z.string()).min(1),\n }).passthrough().optional(),\n snippets: z.object({\n mode: z.enum(['warn', 'error']).optional(),\n scope: z.enum(['snippet', 'snippet+render']).optional(),\n requireFullSnippet: z.boolean().optional(),\n allowedExternalModules: z.array(z.string().min(1)).optional(),\n }).optional(),\n});\n\n/**\n * @deprecated Use blockDefinitionSchema instead\n */\nexport const recipeDefinitionSchema = blockDefinitionSchema;\n"],"mappings":"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","names":[]}
|