import type { ComponentType, ReactNode, JSX } from "react"; import type { GovernanceConfig } from "./governance.js"; import type { InspectConfig } from "./local-canonical.js"; import type { Topology } from "./topology/resolve-area.js"; /** * A React component that can be used in a fragment definition. * This type is intentionally broad to support various React component patterns * including FC, forwardRef, memo, and class components across different React versions. */ export type FragmentComponent = | ComponentType | ((props: TProps) => ReactNode | JSX.Element | null); /** * Framework-agnostic component reference. * Used in contexts where React is not required (e.g., JSON component contracts). * Accepts a string identifier, a generic function, or a record of metadata. */ export type ComponentRef = string | ((...args: unknown[]) => unknown) | Record; /** * Metadata about the component */ export interface FragmentMeta { /** Component display name */ name: string; /** Brief description of the component's purpose */ description: string; /** Category for organizing components (e.g., "actions", "forms", "layout") */ category: string; /** Optional tags for additional categorization */ tags?: string[]; /** Component status */ status?: "stable" | "beta" | "deprecated" | "experimental"; /** Version when component was introduced */ since?: string; /** External npm packages required by this component (displayed in docs Setup section) */ dependencies?: Array<{ name: string; version: string; reason?: string; }>; /** Figma frame URL for design verification */ figma?: string; /** Figma property mappings (how Figma props map to code props) */ figmaProps?: Record; } /** * Figma property mapping types - describes how a Figma property maps to code */ export type FigmaPropMapping = | FigmaStringMapping | FigmaBooleanMapping | FigmaEnumMapping | FigmaInstanceMapping | FigmaChildrenMapping | FigmaTextContentMapping; /** Maps a Figma text property to a string prop */ export interface FigmaStringMapping { __type: "figma-string"; figmaProperty: string; } /** Maps a Figma boolean property to a boolean prop (with optional value mapping) */ export interface FigmaBooleanMapping { __type: "figma-boolean"; figmaProperty: string; valueMapping?: { true: unknown; false: unknown }; } /** Maps a Figma variant property to an enum prop */ export interface FigmaEnumMapping { __type: "figma-enum"; figmaProperty: string; valueMapping: Record; } /** References a nested Figma component instance */ export interface FigmaInstanceMapping { __type: "figma-instance"; figmaProperty: string; } /** Renders children from Figma layer names */ export interface FigmaChildrenMapping { __type: "figma-children"; layers: string[]; } /** Extracts text content from a Figma text layer */ export interface FigmaTextContentMapping { __type: "figma-text-content"; layer: string; } /** * Usage guidelines for AI agents and developers */ export interface FragmentUsage { /** When to use this component */ when: string[]; /** When NOT to use this component (with alternatives) */ whenNot: string[]; /** Additional usage guidelines and best practices */ guidelines?: string[]; /** Accessibility considerations */ accessibility?: string[]; } /** * Prop type definitions */ export type PropType = | { type: "string"; pattern?: string } | { type: "number"; min?: number; max?: number } | { type: "boolean" } | { type: "enum"; values: readonly string[] } | { type: "function"; signature?: string } | { type: "node" } | { type: "element" } | { type: "object"; shape?: Record } | { type: "array"; items?: PropType } | { type: "union"; types: PropType[] } | { type: "custom"; typescript: string }; /** * Storybook control types for UI rendering */ export type ControlType = | "text" | "number" | "range" | "boolean" | "select" | "multi-select" | "radio" | "inline-radio" | "check" | "inline-check" | "object" | "file" | "color" | "date"; /** * Definition for a single prop */ export interface PropDefinition { /** The prop type */ type: PropType["type"]; /** For enum types, the allowed values */ values?: readonly string[]; /** Default value if not provided */ default?: unknown; /** Description of what this prop does */ description: string; /** Whether this prop is required */ required?: boolean; /** Usage constraints for AI agents */ constraints?: string[]; /** Additional type details for complex types */ typeDetails?: Omit; /** Original Storybook control type for UI rendering (e.g., "color", "date", "range") */ controlType?: ControlType; /** Control options (e.g., min/max for range, presetColors for color) */ controlOptions?: { min?: number; max?: number; step?: number; presetColors?: string[]; }; } /** * Relationship types between components */ export type RelationshipType = | "alternative" // Use instead of this component in certain cases | "sibling" // Related component at same level | "parent" // This component should be wrapped by | "child" // This component should contain | "composition" // Used together as compound component | "complementary" // Enhances or works alongside this component | "used-by"; // This component is consumed by another /** * Relationship to another component */ export interface ComponentRelation { /** Name of the related component */ component: string; /** Type of relationship */ relationship: RelationshipType; /** Explanation of the relationship */ note: string; } /** * Loader function type for async data loading before render */ export type VariantLoader = () => Promise>; /** * Play function context passed during interaction testing */ export interface PlayFunctionContext { /** The rendered canvas element containing the story */ canvasElement: HTMLElement; /** Args passed to the story */ args: Record; /** Step function for organizing interactions */ step: (name: string, fn: () => Promise) => Promise; } /** * Play function type for interaction testing */ export type PlayFunction = (context: PlayFunctionContext) => Promise; /** * Options passed to variant render function */ export interface VariantRenderOptions { /** Props/args to override the variant defaults */ args?: Record; /** Data loaded from async loaders */ loadedData?: Record; } /** * A single variant/example of the component */ export interface FragmentVariant { /** Variant name */ name: string; /** Description of when to use this variant */ description: string; /** Render function that returns the component example * @param options - Optional args overrides and loaded data */ render: (options?: VariantRenderOptions) => ReactNode; /** Optional code string for display (auto-generated if not provided) */ code?: string; /** Figma frame URL for this specific variant (overrides meta.figma) */ figma?: string; /** Whether this variant has a Storybook play function (for display purposes) */ hasPlayFunction?: boolean; /** The actual play function for interaction testing */ play?: PlayFunction; /** Storybook story ID for this variant (generated by @storybook/csf toId) */ storyId?: string; /** Optional tags for this variant (inherited from story tags) */ tags?: string[]; /** Async loaders to execute before rendering (from Storybook loaders) */ loaders?: VariantLoader[]; /** The args/props used to render this variant (for code generation) */ args?: Record; } /** * Agent-optimized contract metadata * Provides compact, structured data for AI code generation */ export interface FragmentContract { /** Short prop descriptions for agents (e.g., "variant: primary|secondary (required)") */ propsSummary?: string[]; /** Accessibility rule IDs for lookup in glossary (e.g., "A11Y_BTN_LABEL") */ a11yRules?: string[]; /** Banned patterns in codebase - triggers warnings during code review */ bans?: Array<{ /** Pattern to match (regex string or literal) */ pattern: string; /** Message explaining why this pattern is banned and what to use instead */ message: string; }>; /** Scenario tags for use-case matching (e.g., "form.submit", "navigation.primary") */ scenarioTags?: string[]; /** Per-component performance budget override in bytes (gzipped). Overrides global budget. */ performanceBudget?: number; /** Sub-component slot metadata for compound components (e.g., Card.Header, Dialog.Body) */ compoundChildren?: Record< string, { required?: boolean; accepts?: string[]; description?: string; } >; /** Canonical JSX usage examples showing how to assemble the component */ canonicalUsage?: string[]; } /** * Provenance tracking for generated fragments * Helps distinguish human-authored from machine-generated content */ export interface FragmentGenerated { /** Source of this fragment definition */ source: "storybook" | "manual" | "ai" | "extracted" | "merged" | "migrated"; /** Original source file (e.g., "Button.stories.tsx") */ sourceFile?: string; /** @deprecated Use provenance.verified instead — kept for backwards compatibility */ confidence?: number; /** Whether the fragment has been verified against source */ verified?: boolean; /** ISO timestamp when this was generated */ timestamp?: string; } /** * AI-specific metadata for playground context generation * Provides hints for AI code generation about component composition */ export interface AIMetadata { /** How this component is composed with others */ compositionPattern?: "compound" | "simple" | "controlled" | "wrapper"; /** Sub-component names (without parent prefix, e.g., "Header" not "Card.Header") */ subComponents?: string[]; /** Sub-components that must be present for valid composition */ requiredChildren?: string[]; /** Common usage patterns as JSX strings for AI reference */ commonPatterns?: string[]; } /** * Complete fragment definition */ export interface FragmentDefinition { /** The component being documented */ component: FragmentComponent; /** Component metadata */ meta: FragmentMeta; /** Usage guidelines */ usage: FragmentUsage; /** Props documentation */ props: Record; /** Relationships to other components */ relations?: ComponentRelation[]; /** Component variants/examples */ variants: FragmentVariant[]; /** Agent-optimized contract metadata */ contract?: FragmentContract; /** AI-specific metadata for playground context generation */ ai?: AIMetadata; /** Provenance tracking (for generated fragments) */ _generated?: FragmentGenerated; } // --------------------------------------------------------------------------- // v2 API — Clearer naming, alongside v1 (non-breaking) // --------------------------------------------------------------------------- /** * Usage guidelines for AI agents and developers. * v2 alias for FragmentUsage — clearer naming. * @since 2.0 */ export type FragmentGuidance = FragmentUsage; /** * A single example of the component. * v2 alias for FragmentVariant — research shows "examples" is clearer for AI consumption. * @since 2.0 */ export type FragmentExample = FragmentVariant; /** * Composition metadata — promoted from ai.compositionPattern to first-class field. * Uses shorter field names since the parent field is already `composition`. * @since 2.0 */ export interface CompositionMetadata { /** How this component is composed with others */ pattern?: "compound" | "simple" | "controlled" | "wrapper"; /** Sub-component names (without parent prefix, e.g., "Header" not "Card.Header") */ subComponents?: string[]; /** Sub-components that must be present for valid composition */ requiredChildren?: string[]; /** Common usage patterns as JSX strings for AI reference */ commonPatterns?: string[]; } /** * Extended provenance tracking for generated fragments. * Superset of FragmentGenerated with field-level attribution. * @since 2.0 */ export interface FragmentProvenance { /** Source of this fragment definition */ source: "storybook" | "manual" | "ai" | "scan" | "extracted" | "merged" | "migrated"; /** Original source file (e.g., "Button.stories.tsx") */ sourceFile?: string; /** @deprecated Use verified instead */ confidence?: number; /** Whether the fragment has been verified against source */ verified: boolean; /** Framework support level */ frameworkSupport?: "native" | "manual-only"; /** SHA hash of the source file at extraction time */ sourceHash?: string; /** ISO timestamp when this was generated */ timestamp?: string; /** Fields that were auto-extracted (e.g., ['props', 'composition']) */ autoFields?: string[]; /** Fields that were human-authored (e.g., ['guidance', 'examples']) */ humanFields?: string[]; } /** * v2 fragment definition with clearer field names. * * Changes from v1 FragmentDefinition: * - `usage` → `guidance` (clearer intent) * - `variants` → `examples` (research-backed: examples > parameter lists) * - `ai` → `composition` (promoted to first-class, shorter field names) * - `_generated` → `_provenance` (extended with autoFields/humanFields) * - `contract` remains but is auto-compiled at build time * * @since 2.0 */ export interface FragmentDefinitionV2 { /** The component being documented */ component: FragmentComponent; /** Component metadata */ meta: FragmentMeta; /** Usage guidelines (v2 name for 'usage') */ guidance: FragmentGuidance; /** Props documentation */ props: Record; /** Relationships to other components */ relations?: ComponentRelation[]; /** Component examples (v2 name for 'variants') */ examples: FragmentExample[]; /** Composition metadata (v2 name for 'ai', promoted to first-class) */ composition?: CompositionMetadata; /** Agent-optimized contract — auto-compiled at build time */ contract?: FragmentContract; /** Provenance tracking (v2 name for '_generated', extended) */ _provenance?: FragmentProvenance; } /** * Registry generation options */ export interface RegistryOptions { /** Only include components that have a corresponding .stories.tsx file */ requireStory?: boolean; /** Only include components that are exported (public API) */ publicOnly?: boolean; /** Maximum depth for category inference from directory structure (default: 1) */ categoryDepth?: number; /** Include props in registry (default: false - AI can read TypeScript directly) */ includeProps?: boolean; /** Include full fragment data in registry (default: false - reference fragmentPath instead) */ embedFragments?: boolean; } /** * Design token configuration */ export type RepoRelativePath = string; export interface AppConfig { /** Repo-root-relative app/source directory to scan. Defaults to ".". */ path?: RepoRelativePath; /** Optional source globs relative to app.path. */ include?: string[]; /** Optional source exclusions relative to app.path. */ exclude?: string[]; } export interface DesignSystemConfig { /** Repo-root-relative directory that owns the canonical primitives. */ path?: RepoRelativePath; /** Package name for primitive imports. Inferred from path/package.json when omitted. */ packageName?: string; /** Component source globs relative to designSystem.path. */ components?: string[]; } export type TokenSourceFormat = "auto" | "css" | "scss" | "dtcg" | "tailwind"; export interface TokenSourceConfig { /** Repo-root-relative token file or glob. */ path: RepoRelativePath; /** Explicit token format, or "auto" to infer from extension/content. */ format?: TokenSourceFormat; } export interface TokenConfig { /** * Glob patterns for files to scan for tokens * e.g., ["src/styles/theme.scss", "src/styles/variables.css"] */ include?: string[]; /** Repo-root-relative token source files/globs for monorepos and Cloud setup. */ sources?: TokenSourceConfig[]; /** * Glob patterns to exclude * @example ["node_modules"] */ exclude?: string[]; /** * Map CSS selectors to theme names * @example { ":root": "default", "[data-theme='dark']": "dark" } */ themeSelectors?: Record; /** Enable token comparison in style diffs (default: true) */ enabled?: boolean; /** Token source format detection ('auto' detects from file extension) */ format?: TokenSourceFormat; /** Vendor namespace for Fragments extensions in DTCG files (default: 'com.usefragments') */ namespace?: string; } /** * CI configuration for automated compliance checks */ export interface CIConfig { /** Minimum compliance percentage to pass (default: 80) */ minCompliance?: number; /** Whether to fail on any visual regression */ failOnDiff?: boolean; /** Whether to output JSON format */ jsonOutput?: boolean; } /** * Snippet policy configuration. * Controls snippet/render quality enforcement in `fragments validate`. */ export interface SnippetPolicyConfig { /** Validation mode: warn (non-blocking) or error (blocking). Default: warn */ mode?: "warn" | "error"; /** Validate snippet strings only, or snippet strings + render functions. Default: snippet+render */ scope?: "snippet" | "snippet+render"; /** Require authored snippets to be full, copy-pasteable examples with imports. Default: true */ requireFullSnippet?: boolean; /** Allow these external modules for JSX components in snippets/renders. */ allowedExternalModules?: string[]; } /** * Storybook adapter filtering configuration. * Controls which Storybook stories are included when generating fragments. */ export interface StorybookFilterConfig { /** Glob-style patterns for component names to explicitly exclude */ exclude?: string[]; /** Glob-style patterns for component names to force-include (bypasses all heuristic filters) */ include?: string[]; /** Exclude stories with "Deprecated" in the title (default: true) */ excludeDeprecated?: boolean; /** Exclude test stories (title ending /test(s) or *.test.stories.* files) (default: true) */ excludeTests?: boolean; /** Exclude SVG icon components (names matching Svg[A-Z]*) (default: true) */ excludeSvgIcons?: boolean; /** Exclude sub-components detected by directory structure (default: true) */ excludeSubComponents?: boolean; } /** * Theme seed configuration. * The 5 root values that derive 120+ CSS custom properties. */ export interface ThemeSeeds { /** Primary brand color as hex (e.g., "#6366f1") */ brand?: string; /** Neutral palette name */ neutral?: "stone" | "ice" | "earth" | "sand" | "fire" | "fragments"; /** Spacing density scale */ density?: "compact" | "default" | "relaxed"; /** Border radius style */ radiusStyle?: "sharp" | "subtle" | "default" | "rounded" | "pill"; /** Danger/error color as hex (e.g., "#ef4444") */ danger?: string; } /** * Visual snapshot testing configuration. */ export interface SnapshotConfig { /** Enable visual snapshot tests (default: false) */ enabled: boolean; /** Output directory for snapshot baselines (relative to project root) */ outputDir?: string; /** Viewport width for snapshots (default: 1440) */ viewportWidth?: number; /** Viewport height for snapshots (default: 1100) */ viewportHeight?: number; /** Diff threshold percentage before a snapshot fails (default: 0.1) */ threshold?: number; /** Disable CSS animations during capture (default: true) */ disableAnimations?: boolean; } /** * Config file structure */ export interface FragmentsConfig { /** App/source scan scope. Paths are repo-root-relative. */ app?: AppConfig; /** Canonical primitive component source. Paths are repo-root-relative. */ designSystem?: DesignSystemConfig; /** Glob patterns for finding fragment/fragment files */ include?: string[]; /** Glob patterns to exclude */ exclude?: string[]; /** Glob patterns for finding component files (for coverage validation) */ components?: string[]; /** Output path for compiled output */ outFile?: string; /** Framework adapter to use */ framework?: "react" | "vue" | "svelte"; /** Figma file URL for the design system (used by `fragments link`) */ figmaFile?: string; /** Figma access token (alternative to FIGMA_ACCESS_TOKEN env var) */ figmaToken?: string; /** Screenshot configuration */ screenshots?: ScreenshotConfig; /** Service configuration */ service?: ServiceConfig; /** Registry generation options */ registry?: RegistryOptions; /** Design token discovery and mapping configuration */ tokens?: TokenConfig; /** CI pipeline configuration */ ci?: CIConfig; /** Snippet/render policy validation */ snippets?: SnippetPolicyConfig; /** Performance budgets: preset name or custom config */ performance?: string | { preset?: string; budgets?: { bundleSize?: number } }; /** Storybook adapter filtering configuration */ storybook?: StorybookFilterConfig; /** Theme seed values for the 5-seed theming system */ theme?: ThemeSeeds; /** * Product-area topology — maps file paths to areas (checkout, dashboard, …) * for area-scoped governance. Resolved at scan time; areas are tagged onto * findings and coverage. See `apps/cloud/docs/topology/`. */ topology?: Topology; /** Visual snapshot testing configuration */ snapshots?: SnapshotConfig; /** Preview configuration for component rendering */ preview?: { /** Module that runs before any render (global CSS, MSW, mocks) */ setupModule?: string; /** Module path for the render wrapper component (ThemeProvider, etc.) */ wrapperModule?: string; /** Named export from wrapperModule (e.g., 'ThemeProvider') */ wrapperExport?: string; /** Additional CSS files to inject */ css?: string[]; /** Default theme for previews */ theme?: "light" | "dark"; }; /** Governance policy configuration (replaces standalone govern.config.ts) */ govern?: GovernanceConfig; /** Local Inspect-only configuration. */ inspect?: InspectConfig; /** * Identifier names recognized as className-helper callees during Tailwind * extraction (e.g. `clsx`, `cn`). Supplying this list **replaces** the * default `['clsx', 'cn', 'classnames', 'twMerge', 'tw', 'cva']` — it does * not merge. */ recognizedClassHelpers?: string[]; } /** * Screenshot capture configuration */ export interface ScreenshotConfig { /** Default viewport for captures */ viewport?: Viewport; /** Diff threshold percentage (0-100) */ threshold?: number; /** Additional delay after render before capture (ms) */ delay?: number; /** Output directory for baselines (relative to project root) */ outputDir?: string; /** Themes to capture */ themes?: Theme[]; } /** * Service configuration */ export interface ServiceConfig { /** Browser pool size */ poolSize?: number; /** Idle timeout before shutdown (ms) */ idleTimeout?: number; } /** * Viewport dimensions */ export interface Viewport { width: number; height: number; deviceScaleFactor?: number; } /** * Theme identifier */ export type Theme = "light" | "dark"; /** * Screenshot metadata */ export interface Screenshot { /** PNG image data */ data: Buffer; /** SHA-256 hash of image data (for change detection) */ hash: string; /** Viewport used for capture */ viewport: Viewport; /** When this screenshot was taken */ capturedAt: Date; /** Capture metadata */ metadata: ScreenshotMetadata; } /** * Screenshot metadata */ export interface ScreenshotMetadata { /** Component name */ component: string; /** Variant name */ variant: string; /** Theme used */ theme: Theme; /** Time to render the component (ms) */ renderTimeMs: number; /** Time to capture the screenshot (ms) */ captureTimeMs: number; } /** * Result of comparing two screenshots */ export interface DiffResult { /** Whether images are considered matching (below threshold) */ matches: boolean; /** Percentage of pixels that differ (0-100) */ diffPercentage: number; /** Number of differing pixels */ diffPixelCount: number; /** Total pixels compared */ totalPixels: number; /** PNG image highlighting differences */ diffImage?: Buffer; /** Bounding boxes of changed regions */ changedRegions: BoundingBox[]; /** Time taken to compute diff (ms) */ diffTimeMs: number; } /** * Bounding box for changed region */ export interface BoundingBox { x: number; y: number; width: number; height: number; } /** * Baseline information stored in manifest */ export interface BaselineInfo { /** Component name */ component: string; /** Variant name */ variant: string; /** Theme */ theme: Theme; /** Relative path to image file */ path: string; /** SHA-256 hash */ hash: string; /** Viewport used */ viewport: Viewport; /** When captured */ capturedAt: string; /** File size in bytes */ fileSize: number; } /** * Manifest file structure */ export interface Manifest { /** Schema version */ version: "1.0.0"; /** When manifest was generated */ generatedAt: string; /** Configuration used for capture */ config: { defaultViewport: Viewport; defaultThreshold: number; captureDelay: number; }; /** All baselines indexed by component/variant */ baselines: Record>; } /** * Verification request from AI agents */ export interface VerifyRequest { /** Component name */ component: string; /** Variant name */ variant: string; /** Theme to verify against */ theme?: Theme; /** Override diff threshold */ threshold?: number; } /** * Verification result */ export interface VerifyResult { /** Overall verdict */ verdict: "pass" | "fail" | "error"; /** Whether diff is below threshold */ matches: boolean; /** Percentage of pixels that differ */ diffPercentage: number; /** Current screenshot (base64 PNG) */ screenshot: string; /** Baseline screenshot (base64 PNG) */ baseline: string; /** Diff image if different (base64 PNG) */ diffImage?: string; /** Human-readable notes */ notes: string[]; /** Error message if verdict is "error" */ error?: string; /** Performance metrics */ timing: { renderMs: number; captureMs: number; diffMs: number; totalMs: number; }; } // Compiled types — re-exported from ./compiled-types. export type { CompiledFragment, CompiledBlock, CompiledTokenEntry, CompiledTokenData, ObservedComponentUsage, ObservedUsageProp, CompiledFragmentsFile, } from "./compiled-types/index.js"; import type { CompiledBlock as _CompiledBlock } from "./compiled-types/index.js"; /** * Block definition — a named composition pattern showing how * design system components wire together for a common use case. */ export interface BlockDefinition { name: string; description: string; category: string; components: string[]; code: string; tags?: string[]; } /** * @deprecated Use BlockDefinition instead */ export type RecipeDefinition = BlockDefinition; /** * @deprecated Use CompiledBlock instead */ export type CompiledRecipe = _CompiledBlock;