/** * Framework-agnostic component contract format. * * Allows non-React teams to define component metadata via `.contract.json` * files instead of `.fragment.tsx`. Maps trivially to CompiledFragment. */ import { z } from 'zod'; // --------------------------------------------------------------------------- // Component Contract interface // --------------------------------------------------------------------------- export interface ComponentContract { /** JSON Schema reference for tooling — required for validation */ $schema: string; /** Component name */ name: string; description: string; category: string; tags?: string[]; status?: 'stable' | 'beta' | 'deprecated' | 'experimental'; /** Framework hint (for extraction and documentation) */ framework?: 'react' | 'vue' | 'svelte' | 'web-components' | 'angular'; /** Path to the component source file, relative to fragments.config.ts root */ sourcePath: string; /** Named export from the source file */ exportName: string; /** Compact one-line-per-prop summaries for agent first-pass (e.g., "variant: primary|secondary (required)") */ propsSummary: string[]; /** Component props schema */ props: Record; /** Usage guidelines */ usage: { when: string[]; whenNot: string[]; guidelines?: string[]; accessibility?: string[]; }; /** Code examples (as strings, not render functions) */ examples?: Array<{ name: string; description: string; code: string; args?: Record; }>; /** Relations to other components */ relations?: Array<{ component: string; relationship: 'alternative' | 'parent' | 'child' | 'sibling' | 'composition' | 'complementary' | 'used-by'; note: string; }>; /** AI generation contract */ contract?: { propsSummary?: string[]; scenarioTags?: string[]; a11yRules?: string[]; bans?: Array<{ pattern: string; message: string }>; /** Sub-component slot metadata for compound components */ compoundChildren?: Record; /** Canonical JSX usage examples */ canonicalUsage?: string[]; /** Per-component performance budget override in bytes (gzipped) */ performanceBudget?: number; }; /** AI metadata for compound component generation */ ai?: { compositionPattern?: 'compound' | 'simple' | 'controlled' | 'wrapper'; subComponents?: string[]; requiredChildren?: string[]; commonPatterns?: string[]; }; /** Per-component preview configuration */ preview?: { setupModule?: string; wrapperModule?: string; wrapperExport?: string; css?: string[]; theme?: 'light' | 'dark'; }; /** Provenance tracking — required for verification */ provenance: { source: 'manual' | 'extracted' | 'merged' | 'migrated'; verified: boolean; frameworkSupport?: 'native' | 'manual-only'; sourceHash?: string; extractedAt?: string; }; /** Design token references used by this component */ tokens?: string[]; /** Figma mappings (same DSL as .fragment.tsx but in JSON form) */ figma?: { nodeUrl?: string; propMappings?: Record; }>; }; } // --------------------------------------------------------------------------- // Zod schema for validation // --------------------------------------------------------------------------- const contractPropSchema = z.object({ type: z.string(), values: z.array(z.string()).optional(), default: z.unknown().optional(), description: z.string(), required: z.boolean().optional(), constraints: z.array(z.string()).optional(), }); const contractUsageSchema = z.object({ when: z.array(z.string()), whenNot: z.array(z.string()), guidelines: z.array(z.string()).optional(), accessibility: z.array(z.string()).optional(), }); const contractExampleSchema = z.object({ name: z.string(), description: z.string(), code: z.string(), args: z.record(z.string(), z.unknown()).optional(), }); const contractRelationSchema = z.object({ component: z.string(), relationship: z.enum(['alternative', 'parent', 'child', 'sibling', 'composition', 'complementary', 'used-by']), note: z.string(), }); const contractContractSchema = z.object({ propsSummary: z.array(z.string()).optional(), scenarioTags: z.array(z.string()).optional(), a11yRules: z.array(z.string()).optional(), bans: z.array(z.object({ pattern: z.string(), message: z.string(), })).optional(), compoundChildren: z.record(z.string(), z.object({ required: z.boolean().optional(), accepts: z.array(z.string()).optional(), description: z.string().optional(), })).optional(), canonicalUsage: z.array(z.string()).optional(), performanceBudget: z.number().optional(), }); const contractAiSchema = z.object({ compositionPattern: z.enum(['compound', 'simple', 'controlled', 'wrapper']).optional(), subComponents: z.array(z.string()).optional(), requiredChildren: z.array(z.string()).optional(), commonPatterns: z.array(z.string()).optional(), }); const contractPreviewSchema = z.object({ setupModule: z.string().optional(), wrapperModule: z.string().optional(), wrapperExport: z.string().optional(), css: z.array(z.string()).optional(), theme: z.enum(['light', 'dark']).optional(), }); const contractProvenanceSchema = z.object({ source: z.enum(['manual', 'extracted', 'merged', 'migrated']), verified: z.boolean(), frameworkSupport: z.enum(['native', 'manual-only']).optional(), sourceHash: z.string().optional(), extractedAt: z.string().optional(), }); const contractFigmaSchema = z.object({ nodeUrl: z.string().optional(), propMappings: z.record(z.string(), z.object({ type: z.enum(['string', 'boolean', 'enum', 'instance', 'children', 'textContent']), figmaProperty: z.string(), values: z.record(z.string(), z.string()).optional(), })).optional(), }); export const componentContractSchema = z.object({ $schema: z.string(), name: z.string(), description: z.string(), category: z.string(), tags: z.array(z.string()).optional(), status: z.enum(['stable', 'beta', 'deprecated', 'experimental']).optional(), framework: z.enum(['react', 'vue', 'svelte', 'web-components', 'angular']).optional(), sourcePath: z.string(), exportName: z.string(), propsSummary: z.array(z.string()), props: z.record(z.string(), contractPropSchema), usage: contractUsageSchema, examples: z.array(contractExampleSchema).optional(), relations: z.array(contractRelationSchema).optional(), contract: contractContractSchema.optional(), ai: contractAiSchema.optional(), preview: contractPreviewSchema.optional(), provenance: contractProvenanceSchema, tokens: z.array(z.string()).optional(), figma: contractFigmaSchema.optional(), });