@fragments-sdk/cli 0.15.10 → 0.17.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 (88) hide show
  1. package/dist/bin.js +901 -789
  2. package/dist/bin.js.map +1 -1
  3. package/dist/{chunk-6SQPP47U.js → chunk-ANTWP3UG.js} +532 -31
  4. package/dist/chunk-ANTWP3UG.js.map +1 -0
  5. package/dist/{chunk-ONUP6Z4W.js → chunk-B4A4ZEGS.js} +9 -9
  6. package/dist/{chunk-32LIWN2P.js → chunk-FFCI6OVZ.js} +584 -261
  7. package/dist/chunk-FFCI6OVZ.js.map +1 -0
  8. package/dist/{chunk-HQ6A6DTV.js → chunk-HNHE64CR.js} +315 -1089
  9. package/dist/chunk-HNHE64CR.js.map +1 -0
  10. package/dist/{chunk-BJE3425I.js → chunk-MN3B2EE6.js} +2 -2
  11. package/dist/{chunk-QCN35LJU.js → chunk-SAQW37L5.js} +3 -2
  12. package/dist/chunk-SAQW37L5.js.map +1 -0
  13. package/dist/{chunk-2WXKALIG.js → chunk-SNZXGHL2.js} +2 -2
  14. package/dist/{chunk-5JF26E55.js → chunk-VT2J62ND.js} +11 -11
  15. package/dist/{codebase-scanner-MQHUZC2G.js → codebase-scanner-2T5QIDBA.js} +2 -2
  16. package/dist/core/index.js +53 -1
  17. package/dist/{create-EXURTBKK.js → create-D44QD7MV.js} +2 -2
  18. package/dist/{doctor-BDPMYYE6.js → doctor-7B5N4JYU.js} +2 -2
  19. package/dist/{generate-PVOLUAAC.js → generate-T47JZRVU.js} +4 -4
  20. package/dist/govern-scan-X6UEIOSV.js +632 -0
  21. package/dist/govern-scan-X6UEIOSV.js.map +1 -0
  22. package/dist/index.js +7 -8
  23. package/dist/index.js.map +1 -1
  24. package/dist/{init-SSGUSP7Z.js → init-2RGAY4W6.js} +5 -5
  25. package/dist/mcp-bin.js +2 -2
  26. package/dist/scan-A2WJM54L.js +14 -0
  27. package/dist/{scan-generate-VY27PIOX.js → scan-generate-LUSOHT36.js} +4 -4
  28. package/dist/{service-QJGWUIVL.js → service-ROCP7TKG.js} +13 -15
  29. package/dist/{snapshot-WIJMEIFT.js → snapshot-B3SAW74Y.js} +2 -2
  30. package/dist/{static-viewer-7QIBQZRC.js → static-viewer-7L6UEYTJ.js} +3 -3
  31. package/dist/{test-64Z5BKBA.js → test-PQDVDURE.js} +3 -3
  32. package/dist/{token-normalizer-TEPOVBPV.js → token-normalizer-7TFCVDZL.js} +2 -2
  33. package/dist/{tokens-NZWFQIAB.js → tokens-64FG5FDP.js} +8 -9
  34. package/dist/{tokens-NZWFQIAB.js.map → tokens-64FG5FDP.js.map} +1 -1
  35. package/dist/{tokens-generate-5JQSJ27E.js → tokens-generate-CL4LBBQA.js} +2 -2
  36. package/package.json +9 -8
  37. package/src/bin.ts +55 -88
  38. package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.contract.json +1 -1
  39. package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.contract.json +1 -1
  40. package/src/commands/__tests__/context-cloud.test.ts +291 -0
  41. package/src/commands/__tests__/govern-scan.test.ts +185 -0
  42. package/src/commands/__tests__/govern.test.ts +1 -0
  43. package/src/commands/context-cloud.ts +355 -0
  44. package/src/commands/govern-scan-report.ts +170 -0
  45. package/src/commands/govern-scan.ts +282 -135
  46. package/src/commands/govern.ts +0 -157
  47. package/src/mcp/__tests__/server.integration.test.ts +9 -20
  48. package/src/service/enhance/codebase-scanner.ts +3 -2
  49. package/src/service/enhance/types.ts +3 -0
  50. package/dist/chunk-32LIWN2P.js.map +0 -1
  51. package/dist/chunk-6SQPP47U.js.map +0 -1
  52. package/dist/chunk-HQ6A6DTV.js.map +0 -1
  53. package/dist/chunk-MHIBEEW4.js +0 -511
  54. package/dist/chunk-MHIBEEW4.js.map +0 -1
  55. package/dist/chunk-QCN35LJU.js.map +0 -1
  56. package/dist/govern-scan-DW4QUAYD.js +0 -414
  57. package/dist/govern-scan-DW4QUAYD.js.map +0 -1
  58. package/dist/init-cloud-3DNKPWFB.js +0 -304
  59. package/dist/init-cloud-3DNKPWFB.js.map +0 -1
  60. package/dist/node-37AUE74M.js +0 -65
  61. package/dist/push-contracts-WY32TFP6.js +0 -84
  62. package/dist/push-contracts-WY32TFP6.js.map +0 -1
  63. package/dist/scan-PKSYSTRR.js +0 -15
  64. package/dist/static-viewer-7QIBQZRC.js.map +0 -1
  65. package/dist/token-parser-32KOIOFN.js +0 -22
  66. package/dist/token-parser-32KOIOFN.js.map +0 -1
  67. package/dist/tokens-push-HY3KO36V.js +0 -148
  68. package/dist/tokens-push-HY3KO36V.js.map +0 -1
  69. package/src/commands/init-cloud.ts +0 -382
  70. package/src/commands/push-contracts.ts +0 -112
  71. package/src/commands/tokens-push.ts +0 -199
  72. /package/dist/{chunk-ONUP6Z4W.js.map → chunk-B4A4ZEGS.js.map} +0 -0
  73. /package/dist/{chunk-BJE3425I.js.map → chunk-MN3B2EE6.js.map} +0 -0
  74. /package/dist/{chunk-2WXKALIG.js.map → chunk-SNZXGHL2.js.map} +0 -0
  75. /package/dist/{chunk-5JF26E55.js.map → chunk-VT2J62ND.js.map} +0 -0
  76. /package/dist/{codebase-scanner-MQHUZC2G.js.map → codebase-scanner-2T5QIDBA.js.map} +0 -0
  77. /package/dist/{create-EXURTBKK.js.map → create-D44QD7MV.js.map} +0 -0
  78. /package/dist/{doctor-BDPMYYE6.js.map → doctor-7B5N4JYU.js.map} +0 -0
  79. /package/dist/{generate-PVOLUAAC.js.map → generate-T47JZRVU.js.map} +0 -0
  80. /package/dist/{init-SSGUSP7Z.js.map → init-2RGAY4W6.js.map} +0 -0
  81. /package/dist/{node-37AUE74M.js.map → scan-A2WJM54L.js.map} +0 -0
  82. /package/dist/{scan-generate-VY27PIOX.js.map → scan-generate-LUSOHT36.js.map} +0 -0
  83. /package/dist/{scan-PKSYSTRR.js.map → service-ROCP7TKG.js.map} +0 -0
  84. /package/dist/{snapshot-WIJMEIFT.js.map → snapshot-B3SAW74Y.js.map} +0 -0
  85. /package/dist/{service-QJGWUIVL.js.map → static-viewer-7L6UEYTJ.js.map} +0 -0
  86. /package/dist/{test-64Z5BKBA.js.map → test-PQDVDURE.js.map} +0 -0
  87. /package/dist/{token-normalizer-TEPOVBPV.js.map → token-normalizer-7TFCVDZL.js.map} +0 -0
  88. /package/dist/{tokens-generate-5JQSJ27E.js.map → tokens-generate-CL4LBBQA.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/service/enhance/codebase-scanner.ts","../src/service/enhance/aggregator.ts","../src/service/enhance/cache.ts"],"sourcesContent":["/**\n * Full Codebase Scanner\n *\n * Scans entire codebase for component usage patterns with caching and progress.\n */\n\nimport fg from \"fast-glob\";\nimport { readFile } from \"node:fs/promises\";\nimport { relative, resolve, dirname, basename } from \"node:path\";\nimport type {\n ScanOptions,\n ScanProgress,\n UsageAnalysis,\n ComponentImport,\n ComponentUsage,\n FileChanges,\n AnalysisCache,\n} from \"./types.js\";\nimport { scanFile } from \"./scanner.js\";\nimport { aggregateAllUsages } from \"./aggregator.js\";\nimport {\n loadCache,\n saveCache,\n createEmptyCache,\n computeFileHash,\n isFileCached,\n updateCacheFile,\n removeCacheFile,\n getCachedFile,\n detectFileChanges,\n getCacheStats,\n} from \"./cache.js\";\n\n/**\n * Default patterns for files to scan\n */\nconst DEFAULT_INCLUDE = [\n \"**/*.tsx\",\n \"**/*.ts\",\n \"**/*.jsx\",\n \"**/*.js\",\n];\n\n/**\n * Default patterns for files to exclude\n */\nconst DEFAULT_EXCLUDE = [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/.next/**\",\n \"**/coverage/**\",\n \"**/__tests__/**\",\n \"**/*.test.*\",\n \"**/*.spec.*\",\n \"**/*.stories.*\",\n \"**/*.fragment.*\",\n \"**/storybook-static/**\",\n];\n\n/**\n * Scan entire codebase for component usages\n */\nexport async function scanCodebase(\n options: ScanOptions\n): Promise<UsageAnalysis> {\n const {\n rootDir,\n include = DEFAULT_INCLUDE,\n exclude = DEFAULT_EXCLUDE,\n componentNames,\n useCache = true,\n onProgress,\n } = options;\n\n const absoluteRoot = resolve(rootDir);\n\n // Load or create cache\n let cache: AnalysisCache | null = null;\n if (useCache) {\n cache = await loadCache(absoluteRoot);\n }\n if (!cache) {\n cache = createEmptyCache(absoluteRoot);\n }\n\n // Discover all files\n onProgress?.({\n current: 0,\n total: 0,\n currentFile: \"\",\n phase: \"discovering\",\n });\n\n const files = await fg(include, {\n cwd: absoluteRoot,\n ignore: exclude,\n absolute: true,\n onlyFiles: true,\n });\n\n const totalFiles = files.length;\n\n // Track components to find (if specific ones provided)\n const trackedComponents = componentNames\n ? new Set(componentNames)\n : undefined;\n\n // Collect all imports and usages\n const allImports: ComponentImport[] = [];\n const allUsages: ComponentUsage[] = [];\n const errorFiles: string[] = [];\n const componentSources = new Map<string, string>();\n\n // Process files\n for (let i = 0; i < files.length; i++) {\n const filePath = files[i];\n const relativePath = relative(absoluteRoot, filePath);\n\n onProgress?.({\n current: i + 1,\n total: totalFiles,\n currentFile: relativePath,\n phase: \"scanning\",\n });\n\n try {\n // Read file content for hash\n const content = await readFile(filePath, \"utf-8\");\n const fileHash = computeFileHash(content);\n\n // Check cache\n const cachedEntry = getCachedFile(cache, filePath);\n if (cachedEntry && cachedEntry.hash === fileHash) {\n // Use cached results\n allImports.push(...cachedEntry.imports);\n allUsages.push(...cachedEntry.usages);\n\n // Track component sources from imports\n for (const imp of cachedEntry.imports) {\n if (!componentSources.has(imp.componentName)) {\n const sourceFile = resolveImportSource(filePath, imp.source);\n if (sourceFile) {\n componentSources.set(imp.componentName, sourceFile);\n }\n }\n }\n continue;\n }\n\n // Scan file\n const { imports, usages } = await scanFile(filePath, trackedComponents);\n\n // Update cache\n updateCacheFile(cache, filePath, fileHash, imports, usages);\n\n allImports.push(...imports);\n allUsages.push(...usages);\n\n // Track component sources\n for (const imp of imports) {\n if (!componentSources.has(imp.componentName)) {\n const sourceFile = resolveImportSource(filePath, imp.source);\n if (sourceFile) {\n componentSources.set(imp.componentName, sourceFile);\n }\n }\n }\n } catch (error) {\n errorFiles.push(relativePath);\n console.warn(`Error scanning ${relativePath}:`, (error as Error).message);\n }\n }\n\n // Save cache\n if (useCache) {\n await saveCache(absoluteRoot, cache);\n }\n\n // Aggregate results\n onProgress?.({\n current: totalFiles,\n total: totalFiles,\n currentFile: \"\",\n phase: \"aggregating\",\n });\n\n const analysis = aggregateAllUsages(allUsages, allImports, componentSources);\n analysis.rootDir = absoluteRoot;\n analysis.totalFiles = totalFiles;\n analysis.errorFiles = errorFiles;\n\n return analysis;\n}\n\n/**\n * Perform incremental scan for changed files only\n */\nexport async function incrementalScan(\n rootDir: string,\n changes: FileChanges,\n existingCache: AnalysisCache,\n onProgress?: (progress: ScanProgress) => void\n): Promise<{ analysis: UsageAnalysis; cache: AnalysisCache }> {\n const absoluteRoot = resolve(rootDir);\n const cache = { ...existingCache, files: { ...existingCache.files } };\n\n // Remove deleted files from cache\n for (const filePath of changes.deleted) {\n removeCacheFile(cache, filePath);\n }\n\n // Files to scan (added + modified)\n const filesToScan = [...changes.added, ...changes.modified];\n const totalFiles = filesToScan.length;\n\n const allImports: ComponentImport[] = [];\n const allUsages: ComponentUsage[] = [];\n const errorFiles: string[] = [];\n const componentSources = new Map<string, string>();\n\n // First, collect unchanged file data from cache\n for (const filePath of changes.unchanged) {\n const cachedEntry = getCachedFile(cache, filePath);\n if (cachedEntry) {\n allImports.push(...cachedEntry.imports);\n allUsages.push(...cachedEntry.usages);\n\n for (const imp of cachedEntry.imports) {\n if (!componentSources.has(imp.componentName)) {\n const sourceFile = resolveImportSource(filePath, imp.source);\n if (sourceFile) {\n componentSources.set(imp.componentName, sourceFile);\n }\n }\n }\n }\n }\n\n // Scan changed files\n for (let i = 0; i < filesToScan.length; i++) {\n const filePath = filesToScan[i];\n const relativePath = relative(absoluteRoot, filePath);\n\n onProgress?.({\n current: i + 1,\n total: totalFiles,\n currentFile: relativePath,\n phase: \"scanning\",\n });\n\n try {\n const content = await readFile(filePath, \"utf-8\");\n const fileHash = computeFileHash(content);\n\n const { imports, usages } = await scanFile(filePath);\n\n updateCacheFile(cache, filePath, fileHash, imports, usages);\n\n allImports.push(...imports);\n allUsages.push(...usages);\n\n for (const imp of imports) {\n if (!componentSources.has(imp.componentName)) {\n const sourceFile = resolveImportSource(filePath, imp.source);\n if (sourceFile) {\n componentSources.set(imp.componentName, sourceFile);\n }\n }\n }\n } catch (error) {\n errorFiles.push(relativePath);\n }\n }\n\n // Save updated cache\n await saveCache(absoluteRoot, cache);\n\n // Aggregate\n const analysis = aggregateAllUsages(allUsages, allImports, componentSources);\n analysis.rootDir = absoluteRoot;\n analysis.totalFiles = changes.unchanged.length + filesToScan.length;\n analysis.errorFiles = errorFiles;\n\n return { analysis, cache };\n}\n\n/**\n * Resolve import source to absolute file path\n */\nfunction resolveImportSource(\n importingFile: string,\n source: string\n): string | null {\n // Skip node_modules imports\n if (!source.startsWith(\".\") && !source.startsWith(\"/\")) {\n return null;\n }\n\n const importDir = dirname(importingFile);\n\n // Try common extensions\n const extensions = [\"\", \".tsx\", \".ts\", \".jsx\", \".js\", \"/index.tsx\", \"/index.ts\"];\n for (const ext of extensions) {\n const fullPath = resolve(importDir, source + ext);\n // We don't check if file exists here for performance\n // The source path is mainly for reference\n if (ext === \"\" && source.endsWith(\".tsx\")) {\n return fullPath;\n }\n if (ext) {\n return resolve(importDir, source) + ext;\n }\n }\n\n return resolve(importDir, source);\n}\n\n/**\n * Get scan statistics for display\n */\nexport function getScanStats(analysis: UsageAnalysis): {\n totalFiles: number;\n totalComponents: number;\n totalUsages: number;\n topComponents: { name: string; usages: number }[];\n} {\n const totalUsages = Object.values(analysis.components).reduce(\n (sum, c) => sum + c.totalUsages,\n 0\n );\n\n const topComponents = Object.values(analysis.components)\n .map((c) => ({ name: c.name, usages: c.totalUsages }))\n .sort((a, b) => b.usages - a.usages)\n .slice(0, 10);\n\n return {\n totalFiles: analysis.totalFiles,\n totalComponents: analysis.totalComponents,\n totalUsages,\n topComponents,\n };\n}\n\n/**\n * Export for quick check if cache exists and is valid\n */\nexport async function hasCachedAnalysis(rootDir: string): Promise<boolean> {\n const cache = await loadCache(resolve(rootDir));\n if (!cache) return false;\n const stats = getCacheStats(cache);\n return stats.totalFiles > 0;\n}\n\n// Re-export types and utilities\nexport { loadCache, getCacheStats, detectFileChanges } from \"./cache.js\";\n","/**\n * Usage Pattern Aggregator\n *\n * Aggregates component usages into patterns for AI analysis.\n */\n\nimport type {\n ComponentUsage,\n ComponentImport,\n ComponentAnalysis,\n UsagePattern,\n FileContext,\n UsageAnalysis,\n} from \"./types.js\";\n\n/**\n * Aggregate usages for a single component into patterns\n */\nexport function aggregateComponentUsages(\n componentName: string,\n sourceFile: string,\n usages: ComponentUsage[],\n imports: ComponentImport[]\n): ComponentAnalysis {\n // Count unique files\n const uniqueFiles = new Set(usages.map((u) => u.filePath));\n\n // Group usages by prop pattern\n const patternMap = new Map<string, UsagePattern>();\n\n for (const usage of usages) {\n // Create a key from static props (sorted for consistency)\n const propKey = createPropKey(usage.props.static);\n\n const existing = patternMap.get(propKey);\n if (existing) {\n existing.count++;\n if (!existing.files.includes(usage.filePath)) {\n existing.files.push(usage.filePath);\n }\n if (existing.sampleContexts.length < 3) {\n existing.sampleContexts.push(usage.context);\n }\n } else {\n patternMap.set(propKey, {\n props: { ...usage.props.static },\n count: 1,\n files: [usage.filePath],\n sampleContexts: [usage.context],\n });\n }\n }\n\n // Sort patterns by frequency\n const patterns = Array.from(patternMap.values()).sort(\n (a, b) => b.count - a.count\n );\n\n // Classify file contexts\n const contextMap = new Map<FileContext, { count: number; files: string[] }>();\n\n for (const usage of usages) {\n const context = classifyFileContext(usage.filePath, usage.context);\n const existing = contextMap.get(context);\n if (existing) {\n existing.count++;\n if (!existing.files.includes(usage.filePath)) {\n existing.files.push(usage.filePath);\n }\n } else {\n contextMap.set(context, { count: 1, files: [usage.filePath] });\n }\n }\n\n // Convert to sorted array\n const contexts = Array.from(contextMap.entries())\n .map(([type, data]) => ({ type, ...data }))\n .sort((a, b) => b.count - a.count);\n\n return {\n name: componentName,\n sourceFile,\n totalUsages: usages.length,\n uniqueFiles: uniqueFiles.size,\n patterns,\n contexts,\n usages,\n imports,\n };\n}\n\n/**\n * Aggregate all usages across the codebase\n */\nexport function aggregateAllUsages(\n usages: ComponentUsage[],\n imports: ComponentImport[],\n componentSources: Map<string, string>\n): UsageAnalysis {\n // Group usages by component\n const usagesByComponent = new Map<string, ComponentUsage[]>();\n for (const usage of usages) {\n const existing = usagesByComponent.get(usage.componentName) ?? [];\n existing.push(usage);\n usagesByComponent.set(usage.componentName, existing);\n }\n\n // Group imports by component\n const importsByComponent = new Map<string, ComponentImport[]>();\n for (const imp of imports) {\n const existing = importsByComponent.get(imp.componentName) ?? [];\n existing.push(imp);\n importsByComponent.set(imp.componentName, existing);\n }\n\n // Build component analyses\n const components: Record<string, ComponentAnalysis> = {};\n const allComponentNames = new Set([\n ...usagesByComponent.keys(),\n ...importsByComponent.keys(),\n ]);\n\n for (const componentName of allComponentNames) {\n const componentUsages = usagesByComponent.get(componentName) ?? [];\n const componentImports = importsByComponent.get(componentName) ?? [];\n const sourceFile = componentSources.get(componentName) ?? \"unknown\";\n\n components[componentName] = aggregateComponentUsages(\n componentName,\n sourceFile,\n componentUsages,\n componentImports\n );\n }\n\n // Collect unique files\n const allFiles = new Set<string>();\n for (const usage of usages) {\n allFiles.add(usage.filePath);\n }\n for (const imp of imports) {\n allFiles.add(imp.filePath);\n }\n\n return {\n timestamp: new Date().toISOString(),\n rootDir: \"\", // Will be set by caller\n totalFiles: allFiles.size,\n totalComponents: Object.keys(components).length,\n components,\n errorFiles: [], // Will be populated by scanner\n };\n}\n\n/**\n * Create a stable key from props for pattern grouping\n */\nfunction createPropKey(props: Record<string, string | boolean | number>): string {\n const entries = Object.entries(props).sort(([a], [b]) => a.localeCompare(b));\n return entries.map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(\"|\");\n}\n\n/**\n * Classify file context based on path and content\n */\nfunction classifyFileContext(filePath: string, context: string): FileContext {\n const lowerPath = filePath.toLowerCase();\n const lowerContext = context.toLowerCase();\n\n // Check path patterns first\n if (\n lowerPath.includes(\"/modal\") ||\n lowerPath.includes(\"modal.\") ||\n lowerContext.includes(\"<modal\")\n ) {\n return \"modal\";\n }\n\n if (\n lowerPath.includes(\"/dialog\") ||\n lowerPath.includes(\"dialog.\") ||\n lowerContext.includes(\"<dialog\")\n ) {\n return \"dialog\";\n }\n\n if (\n lowerPath.includes(\"/form\") ||\n lowerPath.includes(\"form.\") ||\n lowerContext.includes(\"<form\") ||\n lowerContext.includes(\"onsubmit\")\n ) {\n return \"form\";\n }\n\n if (\n lowerPath.includes(\"/page\") ||\n lowerPath.includes(\"pages/\") ||\n lowerPath.includes(\"/routes/\")\n ) {\n return \"page\";\n }\n\n if (\n lowerPath.includes(\"/layout\") ||\n lowerPath.includes(\"layout.\") ||\n lowerPath.includes(\"/layouts/\")\n ) {\n return \"layout\";\n }\n\n if (\n lowerPath.includes(\"/card\") ||\n lowerPath.includes(\"card.\") ||\n lowerContext.includes(\"<card\")\n ) {\n return \"card\";\n }\n\n if (\n lowerPath.includes(\"/table\") ||\n lowerPath.includes(\"table.\") ||\n lowerContext.includes(\"<table\") ||\n lowerContext.includes(\"<tr\")\n ) {\n return \"table\";\n }\n\n if (\n lowerPath.includes(\"/list\") ||\n lowerPath.includes(\"list.\") ||\n lowerContext.includes(\"<ul\") ||\n lowerContext.includes(\"<ol\") ||\n lowerContext.includes(\"<li\")\n ) {\n return \"list\";\n }\n\n if (\n lowerPath.includes(\"/nav\") ||\n lowerPath.includes(\"navigation\") ||\n lowerContext.includes(\"<nav\")\n ) {\n return \"navigation\";\n }\n\n if (\n lowerPath.includes(\"/header\") ||\n lowerPath.includes(\"header.\") ||\n lowerContext.includes(\"<header\")\n ) {\n return \"header\";\n }\n\n if (\n lowerPath.includes(\"/footer\") ||\n lowerPath.includes(\"footer.\") ||\n lowerContext.includes(\"<footer\")\n ) {\n return \"footer\";\n }\n\n if (\n lowerPath.includes(\"/sidebar\") ||\n lowerPath.includes(\"sidebar.\") ||\n lowerContext.includes(\"<aside\")\n ) {\n return \"sidebar\";\n }\n\n return \"unknown\";\n}\n\n/**\n * Find common prop combinations across usages\n */\nexport function findCommonPropCombinations(\n usages: ComponentUsage[],\n minOccurrences: number = 3\n): { props: string[]; count: number }[] {\n const combinations = new Map<string, number>();\n\n for (const usage of usages) {\n // Get all prop names (static and dynamic)\n const allProps = [\n ...Object.keys(usage.props.static),\n ...usage.props.dynamic,\n ].sort();\n\n // Generate all combinations of 2+ props\n for (let size = 2; size <= Math.min(allProps.length, 4); size++) {\n for (const combo of combinations_k(allProps, size)) {\n const key = combo.join(\",\");\n combinations.set(key, (combinations.get(key) ?? 0) + 1);\n }\n }\n }\n\n // Filter and sort by frequency\n return Array.from(combinations.entries())\n .filter(([, count]) => count >= minOccurrences)\n .map(([key, count]) => ({ props: key.split(\",\"), count }))\n .sort((a, b) => b.count - a.count);\n}\n\n/**\n * Generate k-combinations of an array\n */\nfunction* combinations_k<T>(arr: T[], k: number): Generator<T[]> {\n if (k === 0) {\n yield [];\n return;\n }\n if (arr.length < k) return;\n\n for (let i = 0; i <= arr.length - k; i++) {\n for (const combo of combinations_k(arr.slice(i + 1), k - 1)) {\n yield [arr[i], ...combo];\n }\n }\n}\n\n/**\n * Component relationship with frequency data\n */\nexport interface ComponentRelation {\n /** Related component name */\n component: string;\n /** Type of relationship */\n relationship: \"parent\" | \"sibling\" | \"child\";\n /** How often this relationship occurs */\n frequency: number;\n /** Sample files where this relationship occurs */\n sampleFiles: string[];\n}\n\n/**\n * Infer component relationships from usage patterns\n *\n * Analyzes parent/sibling relationships from actual usage data:\n * - parent: Components that contain this component (from parentElement)\n * - sibling: Components that appear alongside this component in the same parent\n */\nexport function inferRelations(\n componentName: string,\n analysis: ComponentAnalysis,\n allAnalyses: Record<string, ComponentAnalysis>\n): ComponentRelation[] {\n const relations: ComponentRelation[] = [];\n const parentCounts = new Map<string, { count: number; files: string[] }>();\n const siblingCounts = new Map<string, { count: number; files: string[] }>();\n\n // Count parent relationships\n for (const usage of analysis.usages) {\n if (usage.parentElement && usage.parentElement !== \"div\" && usage.parentElement !== \"span\") {\n const parent = usage.parentElement;\n const existing = parentCounts.get(parent);\n if (existing) {\n existing.count++;\n if (!existing.files.includes(usage.filePath)) {\n existing.files.push(usage.filePath);\n }\n } else {\n parentCounts.set(parent, { count: 1, files: [usage.filePath] });\n }\n }\n }\n\n // Infer sibling relationships: other components used in the same files\n const filesWithThisComponent = new Set(analysis.usages.map((u) => u.filePath));\n\n for (const [otherName, otherAnalysis] of Object.entries(allAnalyses)) {\n if (otherName === componentName) continue;\n\n // Count how many files have both components\n let sharedFiles = 0;\n const sharedFileList: string[] = [];\n\n for (const usage of otherAnalysis.usages) {\n if (filesWithThisComponent.has(usage.filePath)) {\n if (!sharedFileList.includes(usage.filePath)) {\n sharedFiles++;\n sharedFileList.push(usage.filePath);\n }\n }\n }\n\n // Only include if they appear together frequently\n if (sharedFiles >= 3) {\n siblingCounts.set(otherName, {\n count: sharedFiles,\n files: sharedFileList.slice(0, 5),\n });\n }\n }\n\n // Convert to relations array (parents)\n for (const [parent, data] of parentCounts) {\n // Only include meaningful parent relationships (count >= 2)\n if (data.count >= 2) {\n relations.push({\n component: parent,\n relationship: \"parent\",\n frequency: data.count,\n sampleFiles: data.files.slice(0, 3),\n });\n }\n }\n\n // Convert to relations array (siblings)\n for (const [sibling, data] of siblingCounts) {\n relations.push({\n component: sibling,\n relationship: \"sibling\",\n frequency: data.count,\n sampleFiles: data.files.slice(0, 3),\n });\n }\n\n // Sort by frequency\n relations.sort((a, b) => b.frequency - a.frequency);\n\n return relations;\n}\n\n/**\n * Infer relations for all components in the analysis\n */\nexport function inferAllRelations(\n analysis: UsageAnalysis\n): Map<string, ComponentRelation[]> {\n const allRelations = new Map<string, ComponentRelation[]>();\n\n for (const [componentName, componentAnalysis] of Object.entries(analysis.components)) {\n const relations = inferRelations(componentName, componentAnalysis, analysis.components);\n if (relations.length > 0) {\n allRelations.set(componentName, relations);\n }\n }\n\n return allRelations;\n}\n\n/**\n * Summarize patterns for AI prompt\n */\nexport function summarizePatternsForPrompt(\n analysis: ComponentAnalysis,\n maxPatterns: number = 10,\n maxContexts: number = 5\n): string {\n const lines: string[] = [];\n\n lines.push(`## ${analysis.name}`);\n lines.push(`Total usages: ${analysis.totalUsages} across ${analysis.uniqueFiles} files`);\n lines.push(\"\");\n\n // Top patterns\n lines.push(\"### Usage Patterns (by frequency)\");\n const topPatterns = analysis.patterns.slice(0, maxPatterns);\n for (const pattern of topPatterns) {\n const propsStr = Object.entries(pattern.props)\n .map(([k, v]) => `${k}=${JSON.stringify(v)}`)\n .join(\", \");\n lines.push(`- ${propsStr || \"(no props)\"}: ${pattern.count} usages in ${pattern.files.length} files`);\n }\n lines.push(\"\");\n\n // File contexts\n lines.push(\"### File Contexts\");\n const topContexts = analysis.contexts.slice(0, maxContexts);\n for (const ctx of topContexts) {\n lines.push(`- ${ctx.type}: ${ctx.count} usages`);\n }\n lines.push(\"\");\n\n // Sample code contexts (one per pattern)\n lines.push(\"### Sample Usages\");\n for (const pattern of topPatterns.slice(0, 3)) {\n if (pattern.sampleContexts[0]) {\n lines.push(\"```tsx\");\n lines.push(pattern.sampleContexts[0].trim());\n lines.push(\"```\");\n lines.push(\"\");\n }\n }\n\n return lines.join(\"\\n\");\n}\n","/**\n * Analysis Cache Layer\n *\n * Caches file analysis results to enable fast incremental updates.\n */\n\nimport { readFile, writeFile, stat } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { createHash } from \"node:crypto\";\nimport { join, dirname } from \"node:path\";\nimport { mkdir } from \"node:fs/promises\";\nimport type {\n AnalysisCache,\n FileCacheEntry,\n FileChanges,\n ComponentImport,\n ComponentUsage,\n CACHE_VERSION,\n} from \"./types.js\";\n\nconst CURRENT_CACHE_VERSION = 1;\nconst CACHE_FILENAME = \"analysis-cache.json\";\n\n/**\n * Get the cache file path for a project\n */\nexport function getCachePath(rootDir: string): string {\n return join(rootDir, \".fragments\", CACHE_FILENAME);\n}\n\n/**\n * Load cache from disk\n * Returns null if cache doesn't exist or is invalid\n */\nexport async function loadCache(rootDir: string): Promise<AnalysisCache | null> {\n const cachePath = getCachePath(rootDir);\n\n if (!existsSync(cachePath)) {\n return null;\n }\n\n try {\n const content = await readFile(cachePath, \"utf-8\");\n const cache = JSON.parse(content) as AnalysisCache;\n\n // Validate version\n if (cache.version !== CURRENT_CACHE_VERSION) {\n console.warn(\n `Cache version mismatch: expected ${CURRENT_CACHE_VERSION}, got ${cache.version}. Invalidating cache.`\n );\n return null;\n }\n\n // Validate root dir matches\n if (cache.rootDir !== rootDir) {\n console.warn(`Cache root mismatch. Invalidating cache.`);\n return null;\n }\n\n return cache;\n } catch (error) {\n console.warn(`Failed to load cache: ${(error as Error).message}`);\n return null;\n }\n}\n\n/**\n * Save cache to disk\n */\nexport async function saveCache(\n rootDir: string,\n cache: AnalysisCache\n): Promise<void> {\n const cachePath = getCachePath(rootDir);\n const cacheDir = dirname(cachePath);\n\n // Ensure .fragments directory exists\n if (!existsSync(cacheDir)) {\n await mkdir(cacheDir, { recursive: true });\n }\n\n cache.updatedAt = new Date().toISOString();\n await writeFile(cachePath, JSON.stringify(cache, null, 2));\n}\n\n/**\n * Create a new empty cache\n */\nexport function createEmptyCache(rootDir: string): AnalysisCache {\n const now = new Date().toISOString();\n return {\n version: CURRENT_CACHE_VERSION,\n createdAt: now,\n updatedAt: now,\n rootDir,\n files: {},\n };\n}\n\n/**\n * Compute content hash for a file\n */\nexport function computeFileHash(content: string): string {\n return createHash(\"md5\").update(content).digest(\"hex\");\n}\n\n/**\n * Compute file hash from disk\n */\nexport async function computeFileHashFromDisk(\n filePath: string\n): Promise<string> {\n const content = await readFile(filePath, \"utf-8\");\n return computeFileHash(content);\n}\n\n/**\n * Check if a file is cached and unchanged\n */\nexport function isFileCached(\n cache: AnalysisCache,\n filePath: string,\n currentHash: string\n): boolean {\n const entry = cache.files[filePath];\n if (!entry) return false;\n return entry.hash === currentHash;\n}\n\n/**\n * Get cached data for a file\n */\nexport function getCachedFile(\n cache: AnalysisCache,\n filePath: string\n): FileCacheEntry | null {\n return cache.files[filePath] ?? null;\n}\n\n/**\n * Update cache with results for a file\n */\nexport function updateCacheFile(\n cache: AnalysisCache,\n filePath: string,\n hash: string,\n imports: ComponentImport[],\n usages: ComponentUsage[]\n): void {\n cache.files[filePath] = {\n hash,\n timestamp: new Date().toISOString(),\n imports,\n usages,\n };\n}\n\n/**\n * Remove a file from cache\n */\nexport function removeCacheFile(cache: AnalysisCache, filePath: string): void {\n delete cache.files[filePath];\n}\n\n/**\n * Invalidate cache entries for specific files\n */\nexport function invalidateCache(\n cache: AnalysisCache,\n filePaths: string[]\n): void {\n for (const filePath of filePaths) {\n delete cache.files[filePath];\n }\n}\n\n/**\n * Detect file changes by comparing current files to cache\n */\nexport async function detectFileChanges(\n cache: AnalysisCache,\n currentFiles: string[],\n getFileHash: (filePath: string) => Promise<string>\n): Promise<FileChanges> {\n const changes: FileChanges = {\n added: [],\n modified: [],\n deleted: [],\n unchanged: [],\n };\n\n const currentFileSet = new Set(currentFiles);\n const cachedFileSet = new Set(Object.keys(cache.files));\n\n // Check for added and modified files\n for (const filePath of currentFiles) {\n const entry = cache.files[filePath];\n\n if (!entry) {\n changes.added.push(filePath);\n } else {\n const currentHash = await getFileHash(filePath);\n if (currentHash !== entry.hash) {\n changes.modified.push(filePath);\n } else {\n changes.unchanged.push(filePath);\n }\n }\n }\n\n // Check for deleted files\n for (const cachedFile of cachedFileSet) {\n if (!currentFileSet.has(cachedFile)) {\n changes.deleted.push(cachedFile);\n }\n }\n\n return changes;\n}\n\n/**\n * Get all cached imports\n */\nexport function getAllCachedImports(cache: AnalysisCache): ComponentImport[] {\n const imports: ComponentImport[] = [];\n for (const entry of Object.values(cache.files)) {\n imports.push(...entry.imports);\n }\n return imports;\n}\n\n/**\n * Get all cached usages\n */\nexport function getAllCachedUsages(cache: AnalysisCache): ComponentUsage[] {\n const usages: ComponentUsage[] = [];\n for (const entry of Object.values(cache.files)) {\n usages.push(...entry.usages);\n }\n return usages;\n}\n\n/**\n * Get cache statistics\n */\nexport function getCacheStats(cache: AnalysisCache): {\n totalFiles: number;\n totalImports: number;\n totalUsages: number;\n cacheAge: string;\n} {\n let totalImports = 0;\n let totalUsages = 0;\n\n for (const entry of Object.values(cache.files)) {\n totalImports += entry.imports.length;\n totalUsages += entry.usages.length;\n }\n\n const ageMs = Date.now() - new Date(cache.updatedAt).getTime();\n const ageMinutes = Math.round(ageMs / 60000);\n const cacheAge =\n ageMinutes < 60\n ? `${ageMinutes} minutes ago`\n : ageMinutes < 1440\n ? `${Math.round(ageMinutes / 60)} hours ago`\n : `${Math.round(ageMinutes / 1440)} days ago`;\n\n return {\n totalFiles: Object.keys(cache.files).length,\n totalImports,\n totalUsages,\n cacheAge,\n };\n}\n"],"mappings":"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","names":["readFile","dirname","readFile","dirname"]}
@@ -1,414 +0,0 @@
1
- import { createRequire as __banner_createRequire } from 'module'; const require = __banner_createRequire(import.meta.url);
2
- import "./chunk-D2CDBRNU.js";
3
- import {
4
- BRAND
5
- } from "./chunk-32LIWN2P.js";
6
-
7
- // src/commands/govern-scan.ts
8
- import pc from "picocolors";
9
- import { resolve, relative } from "path";
10
- import { existsSync } from "fs";
11
- var SCAN_DEFAULT_RULES = {
12
- "safety/block-event-handlers": true,
13
- "safety/block-dangerous-props": true,
14
- "safety/block-controlled-props": true,
15
- "safety/block-function-props": true,
16
- "safety/sanitize-hrefs": true,
17
- "tokens/require-design-tokens": true
18
- };
19
- function detectRootDir(cwd) {
20
- const candidates = ["src", "app", "pages", "components"];
21
- for (const dir of candidates) {
22
- if (existsSync(resolve(cwd, dir))) {
23
- return cwd;
24
- }
25
- }
26
- return cwd;
27
- }
28
- function groupByFile(usages) {
29
- const grouped = /* @__PURE__ */ new Map();
30
- for (const usage of usages) {
31
- const existing = grouped.get(usage.filePath);
32
- if (existing) {
33
- existing.push(usage);
34
- } else {
35
- grouped.set(usage.filePath, [usage]);
36
- }
37
- }
38
- return grouped;
39
- }
40
- async function governScan(options = {}) {
41
- const {
42
- loadPolicy,
43
- createEngine,
44
- buildAdaptersFromConfig,
45
- createCloudAdapter,
46
- formatVerdict,
47
- computeComponentHealth
48
- } = await import("@fragments-sdk/govern");
49
- const { scanCodebase } = await import("./codebase-scanner-MQHUZC2G.js");
50
- const { usagesToSpec } = await import("./converter-7XM3Y6NJ.js");
51
- const format = options.format ?? "summary";
52
- const quiet = options.quiet ?? false;
53
- if (!quiet) {
54
- console.log(pc.cyan(`
55
- ${BRAND.name} Governance Scan
56
- `));
57
- }
58
- const rootDir = resolve(options.dir ?? detectRootDir(process.cwd()));
59
- if (!quiet) {
60
- console.log(pc.dim(` Root: ${rootDir}
61
- `));
62
- }
63
- let policy = await loadPolicy(options.config);
64
- const hasRules = Object.keys(policy.rules).length > 0;
65
- if (!hasRules) {
66
- policy = { ...policy, rules: SCAN_DEFAULT_RULES };
67
- if (!quiet) {
68
- console.log(pc.dim(" No config found \u2014 using scan defaults (safety + tokens)\n"));
69
- }
70
- }
71
- let codeTokens;
72
- if (process.env.FRAGMENTS_API_KEY) {
73
- codeTokens = await extractCodeTokens(rootDir, options.config, quiet);
74
- }
75
- let contractRegistry;
76
- let registryMap;
77
- {
78
- const { readFileSync, existsSync: existsSync2 } = await import("fs");
79
- const fragmentsJsonPath = resolve(rootDir, "fragments.json");
80
- if (existsSync2(fragmentsJsonPath)) {
81
- try {
82
- const raw = readFileSync(fragmentsJsonPath, "utf-8");
83
- const parsed = JSON.parse(raw);
84
- if (parsed.fragments && Array.isArray(parsed.fragments)) {
85
- const map = {};
86
- for (const f of parsed.fragments) {
87
- if (f.meta?.name) {
88
- map[f.meta.name] = f;
89
- }
90
- }
91
- registryMap = map;
92
- if (process.env.FRAGMENTS_API_KEY) {
93
- contractRegistry = JSON.stringify({ fragments: parsed.fragments });
94
- }
95
- if (!quiet) {
96
- console.log(pc.dim(` Contract registry loaded (${parsed.fragments.length} components)
97
- `));
98
- }
99
- }
100
- } catch {
101
- }
102
- }
103
- }
104
- const adapters = buildAdaptersFromConfig(policy.audit);
105
- const hasCloudAdapter = adapters.length > 0 && policy.audit?.cloud;
106
- if (!hasCloudAdapter && process.env.FRAGMENTS_API_KEY) {
107
- adapters.push(createCloudAdapter({ codeTokens, contractRegistry }));
108
- if (!quiet) {
109
- console.log(pc.dim(" Cloud audit enabled (FRAGMENTS_API_KEY detected)\n"));
110
- }
111
- }
112
- const engine = createEngine(
113
- policy,
114
- adapters,
115
- registryMap ? { registry: { fragments: registryMap } } : void 0
116
- );
117
- if (!quiet) {
118
- console.log(pc.dim(" Scanning files...\n"));
119
- }
120
- const analysis = await scanCodebase({
121
- rootDir,
122
- useCache: true,
123
- onProgress: quiet ? void 0 : (progress) => {
124
- if (progress.phase === "scanning") {
125
- process.stdout.write(
126
- `\r ${pc.dim(`[${progress.current}/${progress.total}]`)} ${pc.dim(relative(rootDir, progress.currentFile))}`
127
- );
128
- }
129
- }
130
- });
131
- if (!quiet) {
132
- process.stdout.write("\r" + " ".repeat(80) + "\r");
133
- console.log(
134
- pc.dim(` Scanned ${analysis.totalFiles} files, found ${analysis.totalComponents} component types
135
- `)
136
- );
137
- }
138
- const allUsages = [];
139
- for (const comp of Object.values(analysis.components)) {
140
- allUsages.push(...comp.usages);
141
- }
142
- if (allUsages.length === 0) {
143
- if (!quiet) {
144
- console.log(pc.yellow(" No component usages found.\n"));
145
- }
146
- return { exitCode: 0 };
147
- }
148
- const grouped = groupByFile(allUsages);
149
- let totalFiles = 0;
150
- let passedFiles = 0;
151
- let totalViolations = 0;
152
- const violationCounts = /* @__PURE__ */ new Map();
153
- const allVerdicts = [];
154
- const usageSnapshot = [];
155
- for (const [filePath, usages] of grouped) {
156
- const spec = usagesToSpec(usages, filePath, rootDir);
157
- const relPath = relative(rootDir, filePath);
158
- const verdict = await engine.check(spec, {
159
- runner: "cli",
160
- input: relPath
161
- });
162
- allVerdicts.push(verdict);
163
- usageSnapshot.push({
164
- file: relPath,
165
- components: usages.map((u) => ({
166
- name: u.componentName,
167
- line: u.line,
168
- props: {
169
- static: u.props.static,
170
- dynamic: u.props.dynamic
171
- }
172
- }))
173
- });
174
- totalFiles++;
175
- if (verdict.passed) {
176
- passedFiles++;
177
- } else {
178
- if (!quiet) {
179
- console.log(pc.red(` \u2717 ${relPath}`));
180
- if (format === "summary") {
181
- for (const result of verdict.results) {
182
- for (const v of result.violations) {
183
- const count = violationCounts.get(v.rule) ?? 0;
184
- violationCounts.set(v.rule, count + 1);
185
- totalViolations++;
186
- console.log(
187
- pc.dim(` ${v.severity} `) + pc.yellow(v.rule) + pc.dim(` \u2014 ${v.message}`)
188
- );
189
- if (v.nodeId) {
190
- console.log(pc.dim(` at ${v.nodeId}`));
191
- }
192
- }
193
- }
194
- }
195
- }
196
- }
197
- if (verdict.passed && !quiet && format === "summary") {
198
- console.log(pc.green(` \u2713 ${relPath}`) + pc.dim(` (${usages.length} components, score: ${verdict.score}/100)`));
199
- }
200
- if (format === "json" || format === "sarif") {
201
- const output = formatVerdict(verdict, format);
202
- console.log(output);
203
- }
204
- }
205
- const health = computeComponentHealth(allVerdicts, registryMap ?? {});
206
- if (!quiet && format === "summary") {
207
- console.log(pc.dim("\n \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
208
- console.log(` Files checked: ${totalFiles}`);
209
- console.log(` Passed: ${passedFiles}/${totalFiles}`);
210
- console.log(` Violations: ${totalViolations}`);
211
- if (violationCounts.size > 0) {
212
- console.log(pc.dim("\n Top violations:"));
213
- const sorted = [...violationCounts.entries()].sort((a, b) => b[1] - a[1]);
214
- for (const [rule, count] of sorted.slice(0, 5)) {
215
- console.log(pc.dim(` ${count}\xD7 `) + pc.yellow(rule));
216
- }
217
- }
218
- console.log(pc.dim("\n Component Health:"));
219
- console.log(` Contract coverage: ${health.contractCoverage}% (${health.contractedComponents}/${health.totalComponents})`);
220
- console.log(` Compliance rate: ${health.overallCompliance}%`);
221
- if (health.uncontracted.length > 0) {
222
- console.log(pc.dim(` Uncontracted: ${health.uncontracted.slice(0, 5).join(", ")}${health.uncontracted.length > 5 ? ` (+${health.uncontracted.length - 5} more)` : ""}`));
223
- }
224
- console.log();
225
- if (passedFiles === totalFiles) {
226
- console.log(pc.green(` \u2713 All files passed governance checks
227
- `));
228
- } else {
229
- console.log(
230
- pc.red(` \u2717 ${totalFiles - passedFiles} file(s) failed governance checks
231
- `)
232
- );
233
- }
234
- }
235
- if (process.env.FRAGMENTS_API_KEY && usageSnapshot.length > 0) {
236
- try {
237
- const apiKey = process.env.FRAGMENTS_API_KEY;
238
- const url = process.env.FRAGMENTS_URL ?? "https://app.usefragments.com";
239
- await fetch(`${url}/api/ingest`, {
240
- method: "POST",
241
- headers: {
242
- "Content-Type": "application/json",
243
- Authorization: `Bearer ${apiKey}`
244
- },
245
- body: JSON.stringify({
246
- componentUsage: JSON.stringify(usageSnapshot),
247
- componentHealth: JSON.stringify(health)
248
- })
249
- });
250
- } catch {
251
- }
252
- }
253
- return { exitCode: passedFiles === totalFiles ? 0 : 1 };
254
- }
255
- async function extractCodeTokens(rootDir, configPath, quiet) {
256
- try {
257
- try {
258
- const { loadConfig } = await import("./node-37AUE74M.js");
259
- const { parseTokenFiles } = await import("./service-QJGWUIVL.js");
260
- const { config, configDir } = await loadConfig(configPath);
261
- if (config.tokens?.include?.length) {
262
- const result = await parseTokenFiles(config.tokens, configDir);
263
- if (result.tokens.length > 0) {
264
- const flat = {};
265
- for (const token of result.tokens) {
266
- flat[token.name] = token.resolvedValue;
267
- }
268
- if (!quiet) {
269
- console.log(
270
- pc.dim(` Extracted ${result.tokens.length} code tokens from config
271
- `)
272
- );
273
- }
274
- return JSON.stringify(flat);
275
- }
276
- }
277
- } catch {
278
- }
279
- const {
280
- findTailwindConfig,
281
- loadTailwindConfig
282
- } = await import("./token-normalizer-TEPOVBPV.js");
283
- const tailwindPath = findTailwindConfig(rootDir);
284
- if (tailwindPath) {
285
- const tokens = await loadTailwindConfig(tailwindPath);
286
- if (tokens.length > 0) {
287
- const flat = {};
288
- for (const token of tokens) {
289
- flat[token.name] = token.value;
290
- }
291
- if (!quiet) {
292
- console.log(
293
- pc.dim(` Extracted ${tokens.length} tokens from Tailwind config
294
- `)
295
- );
296
- }
297
- return JSON.stringify(flat);
298
- }
299
- }
300
- } catch (error) {
301
- if (!quiet) {
302
- console.log(
303
- pc.dim(
304
- ` Token extraction skipped: ${error instanceof Error ? error.message : "unknown error"}
305
- `
306
- )
307
- );
308
- }
309
- }
310
- return void 0;
311
- }
312
- async function governWatch(options = {}) {
313
- const {
314
- loadPolicy,
315
- createEngine,
316
- buildAdaptersFromConfig,
317
- createCloudAdapter,
318
- formatVerdict
319
- } = await import("@fragments-sdk/govern");
320
- const { scanFile } = await import("./scanner-4KZNOXAK.js");
321
- const { usagesToSpec } = await import("./converter-7XM3Y6NJ.js");
322
- const quiet = options.quiet ?? false;
323
- const debounceMs = options.debounce ?? 300;
324
- const format = options.format ?? "summary";
325
- console.log(pc.cyan(`
326
- ${BRAND.name} Governance Watch
327
- `));
328
- const { exitCode } = await governScan(options);
329
- if (!quiet) {
330
- console.log(
331
- pc.dim(` Initial scan ${exitCode === 0 ? "passed" : "completed with violations"}
332
- `)
333
- );
334
- }
335
- const rootDir = resolve(options.dir ?? detectRootDir(process.cwd()));
336
- let policy = await loadPolicy(options.config);
337
- if (Object.keys(policy.rules).length === 0) {
338
- policy = { ...policy, rules: SCAN_DEFAULT_RULES };
339
- }
340
- const adapters = buildAdaptersFromConfig(policy.audit);
341
- if (!adapters.some(() => policy.audit?.cloud) && process.env.FRAGMENTS_API_KEY) {
342
- adapters.push(createCloudAdapter());
343
- }
344
- const engine = createEngine(policy, adapters);
345
- console.log(pc.dim(" Watching for changes... (Ctrl+C to stop)\n"));
346
- const chokidar = await import("chokidar");
347
- const watcher = chokidar.watch(
348
- ["**/*.tsx", "**/*.ts", "**/*.jsx", "**/*.js"],
349
- {
350
- cwd: rootDir,
351
- ignoreInitial: true,
352
- ignored: [
353
- "**/node_modules/**",
354
- "**/dist/**",
355
- "**/build/**",
356
- "**/.next/**",
357
- "**/*.test.*",
358
- "**/*.spec.*",
359
- "**/*.stories.*"
360
- ],
361
- awaitWriteFinish: { stabilityThreshold: debounceMs }
362
- }
363
- );
364
- const handleChange = async (changedRelPath) => {
365
- const absolutePath = resolve(rootDir, changedRelPath);
366
- try {
367
- const { usages } = await scanFile(absolutePath);
368
- if (usages.length === 0) {
369
- if (!quiet) {
370
- console.log(pc.dim(` \u25CB ${changedRelPath} \u2014 no component usages`));
371
- }
372
- return;
373
- }
374
- const spec = usagesToSpec(usages, absolutePath, rootDir);
375
- const verdict = await engine.check(spec, {
376
- runner: "cli",
377
- input: changedRelPath
378
- });
379
- if (verdict.passed) {
380
- console.log(
381
- pc.green(` \u2713 ${changedRelPath}`) + pc.dim(` (${usages.length} components, score: ${verdict.score}/100)`)
382
- );
383
- } else {
384
- console.log(pc.red(` \u2717 ${changedRelPath}`));
385
- if (format === "summary") {
386
- for (const result of verdict.results) {
387
- for (const v of result.violations) {
388
- console.log(
389
- pc.dim(` ${v.severity} `) + pc.yellow(v.rule) + pc.dim(` \u2014 ${v.message}`)
390
- );
391
- }
392
- }
393
- } else {
394
- console.log(formatVerdict(verdict, format));
395
- }
396
- }
397
- } catch (error) {
398
- if (!quiet) {
399
- console.log(
400
- pc.dim(` \u26A0 ${changedRelPath} \u2014 `) + pc.yellow(error instanceof Error ? error.message : "parse error")
401
- );
402
- }
403
- }
404
- };
405
- watcher.on("change", handleChange);
406
- watcher.on("add", handleChange);
407
- await new Promise(() => {
408
- });
409
- }
410
- export {
411
- governScan,
412
- governWatch
413
- };
414
- //# sourceMappingURL=govern-scan-DW4QUAYD.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/govern-scan.ts"],"sourcesContent":["/**\n * govern scan / govern watch — Zero-config governance scanning\n *\n * Parses real JSX/TSX files via the existing codebase scanner, converts\n * component usages to UISpec, and runs governance checks per file.\n * Optionally submits results to Fragments Cloud.\n */\n\nimport pc from 'picocolors';\nimport { resolve, relative } from 'node:path';\nimport { existsSync } from 'node:fs';\nimport { BRAND } from '../core/index.js';\nimport type { ComponentUsage } from '../service/enhance/types.js';\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface GovernScanOptions {\n /** Root directory to scan (default: auto-detect) */\n dir?: string;\n /** Path to govern.config.ts */\n config?: string;\n /** Output format */\n format?: 'summary' | 'json' | 'sarif';\n /** Suppress non-error output */\n quiet?: boolean;\n}\n\nexport interface GovernWatchOptions extends GovernScanOptions {\n /** Debounce interval in ms (default: 300) */\n debounce?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Scan defaults — applied when no config file exists\n// ---------------------------------------------------------------------------\n\nconst SCAN_DEFAULT_RULES: Record<string, boolean | object> = {\n 'safety/block-event-handlers': true,\n 'safety/block-dangerous-props': true,\n 'safety/block-controlled-props': true,\n 'safety/block-function-props': true,\n 'safety/sanitize-hrefs': true,\n 'tokens/require-design-tokens': true,\n};\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Auto-detect root directory by looking for common React project dirs\n */\nfunction detectRootDir(cwd: string): string {\n const candidates = ['src', 'app', 'pages', 'components'];\n for (const dir of candidates) {\n if (existsSync(resolve(cwd, dir))) {\n return cwd;\n }\n }\n return cwd;\n}\n\n/**\n * Group component usages by their source file\n */\nfunction groupByFile(usages: ComponentUsage[]): Map<string, ComponentUsage[]> {\n const grouped = new Map<string, ComponentUsage[]>();\n for (const usage of usages) {\n const existing = grouped.get(usage.filePath);\n if (existing) {\n existing.push(usage);\n } else {\n grouped.set(usage.filePath, [usage]);\n }\n }\n return grouped;\n}\n\n// ---------------------------------------------------------------------------\n// governScan\n// ---------------------------------------------------------------------------\n\nexport async function governScan(\n options: GovernScanOptions = {},\n): Promise<{ exitCode: number }> {\n const {\n loadPolicy,\n createEngine,\n buildAdaptersFromConfig,\n createCloudAdapter,\n formatVerdict,\n computeComponentHealth,\n } = await import('@fragments-sdk/govern');\n\n const { scanCodebase } = await import(\n '../service/enhance/codebase-scanner.js'\n );\n const { usagesToSpec } = await import(\n '../service/enhance/converter.js'\n );\n\n const format = options.format ?? 'summary';\n const quiet = options.quiet ?? false;\n\n if (!quiet) {\n console.log(pc.cyan(`\\n${BRAND.name} Governance Scan\\n`));\n }\n\n // 1. Resolve root directory\n const rootDir = resolve(options.dir ?? detectRootDir(process.cwd()));\n if (!quiet) {\n console.log(pc.dim(` Root: ${rootDir}\\n`));\n }\n\n // 2. Load policy — use scan defaults if no config exists\n let policy = await loadPolicy(options.config);\n const hasRules = Object.keys(policy.rules).length > 0;\n\n if (!hasRules) {\n policy = { ...policy, rules: SCAN_DEFAULT_RULES };\n if (!quiet) {\n console.log(pc.dim(' No config found — using scan defaults (safety + tokens)\\n'));\n }\n }\n\n // 3. Extract code tokens for cloud ingest\n let codeTokens: string | undefined;\n if (process.env.FRAGMENTS_API_KEY) {\n codeTokens = await extractCodeTokens(rootDir, options.config, quiet);\n }\n\n // 3b. Load contract registry for governance + cloud ingest (if fragments.json exists)\n let contractRegistry: string | undefined;\n let registryMap: Record<string, unknown> | undefined;\n {\n const { readFileSync, existsSync } = await import('node:fs');\n const fragmentsJsonPath = resolve(rootDir, 'fragments.json');\n if (existsSync(fragmentsJsonPath)) {\n try {\n const raw = readFileSync(fragmentsJsonPath, 'utf-8');\n const parsed = JSON.parse(raw);\n if (parsed.fragments && Array.isArray(parsed.fragments)) {\n // Build name-keyed map for engine registry injection\n const map: Record<string, unknown> = {};\n for (const f of parsed.fragments) {\n if (f.meta?.name) {\n map[f.meta.name] = f;\n }\n }\n registryMap = map;\n\n if (process.env.FRAGMENTS_API_KEY) {\n contractRegistry = JSON.stringify({ fragments: parsed.fragments });\n }\n if (!quiet) {\n console.log(pc.dim(` Contract registry loaded (${parsed.fragments.length} components)\\n`));\n }\n }\n } catch {\n // Invalid fragments.json — skip\n }\n }\n }\n\n // 4. Build adapters. Auto-add cloud if FRAGMENTS_API_KEY is set\n const adapters = buildAdaptersFromConfig(policy.audit);\n const hasCloudAdapter = adapters.length > 0 && policy.audit?.cloud;\n if (!hasCloudAdapter && process.env.FRAGMENTS_API_KEY) {\n adapters.push(createCloudAdapter({ codeTokens, contractRegistry }));\n if (!quiet) {\n console.log(pc.dim(' Cloud audit enabled (FRAGMENTS_API_KEY detected)\\n'));\n }\n }\n\n // 5. Create engine (with registry for contract-aware validators)\n const engine = createEngine(\n policy,\n adapters,\n registryMap\n ? { registry: { fragments: registryMap as Record<string, Record<string, unknown>> } }\n : undefined,\n );\n\n // 6. Scan codebase\n if (!quiet) {\n console.log(pc.dim(' Scanning files...\\n'));\n }\n\n const analysis = await scanCodebase({\n rootDir,\n useCache: true,\n onProgress: quiet\n ? undefined\n : (progress) => {\n if (progress.phase === 'scanning') {\n process.stdout.write(\n `\\r ${pc.dim(`[${progress.current}/${progress.total}]`)} ${pc.dim(relative(rootDir, progress.currentFile))}`,\n );\n }\n },\n });\n\n if (!quiet) {\n // Clear progress line\n process.stdout.write('\\r' + ' '.repeat(80) + '\\r');\n console.log(\n pc.dim(` Scanned ${analysis.totalFiles} files, found ${analysis.totalComponents} component types\\n`),\n );\n }\n\n // 7. Collect all usages across components\n const allUsages: ComponentUsage[] = [];\n for (const comp of Object.values(analysis.components)) {\n allUsages.push(...comp.usages);\n }\n\n if (allUsages.length === 0) {\n if (!quiet) {\n console.log(pc.yellow(' No component usages found.\\n'));\n }\n return { exitCode: 0 };\n }\n\n // 8. Group by file and run checks\n const grouped = groupByFile(allUsages);\n let totalFiles = 0;\n let passedFiles = 0;\n let totalViolations = 0;\n const violationCounts = new Map<string, number>();\n const allVerdicts: Awaited<ReturnType<typeof engine.check>>[] = [];\n\n // Build per-file usage snapshot for cloud\n const usageSnapshot: Array<{\n file: string;\n components: Array<{\n name: string;\n line: number;\n props: { static: Record<string, unknown>; dynamic: string[] };\n }>;\n }> = [];\n\n for (const [filePath, usages] of grouped) {\n const spec = usagesToSpec(usages, filePath, rootDir);\n const relPath = relative(rootDir, filePath);\n\n const verdict = await engine.check(spec, {\n runner: 'cli',\n input: relPath,\n });\n allVerdicts.push(verdict);\n\n // Collect per-file usage snapshot\n usageSnapshot.push({\n file: relPath,\n components: usages.map((u) => ({\n name: u.componentName,\n line: u.line,\n props: {\n static: u.props.static,\n dynamic: u.props.dynamic,\n },\n })),\n });\n\n totalFiles++;\n\n if (verdict.passed) {\n passedFiles++;\n } else {\n if (!quiet) {\n console.log(pc.red(` ✗ ${relPath}`));\n if (format === 'summary') {\n for (const result of verdict.results) {\n for (const v of result.violations) {\n const count = violationCounts.get(v.rule) ?? 0;\n violationCounts.set(v.rule, count + 1);\n totalViolations++;\n console.log(\n pc.dim(` ${v.severity} `) +\n pc.yellow(v.rule) +\n pc.dim(` — ${v.message}`),\n );\n if (v.nodeId) {\n console.log(pc.dim(` at ${v.nodeId}`));\n }\n }\n }\n }\n }\n }\n\n if (verdict.passed && !quiet && format === 'summary') {\n console.log(pc.green(` ✓ ${relPath}`) + pc.dim(` (${usages.length} components, score: ${verdict.score}/100)`));\n }\n\n // JSON/SARIF: print per-file\n if (format === 'json' || format === 'sarif') {\n const output = formatVerdict(verdict, format);\n console.log(output);\n }\n }\n\n // 8b. Compute component health\n const health = computeComponentHealth(allVerdicts, registryMap ?? {});\n\n // 9. Summary\n if (!quiet && format === 'summary') {\n console.log(pc.dim('\\n ─────────────────────────────────────\\n'));\n console.log(` Files checked: ${totalFiles}`);\n console.log(` Passed: ${passedFiles}/${totalFiles}`);\n console.log(` Violations: ${totalViolations}`);\n\n if (violationCounts.size > 0) {\n console.log(pc.dim('\\n Top violations:'));\n const sorted = [...violationCounts.entries()].sort((a, b) => b[1] - a[1]);\n for (const [rule, count] of sorted.slice(0, 5)) {\n console.log(pc.dim(` ${count}× `) + pc.yellow(rule));\n }\n }\n\n // Component health\n console.log(pc.dim('\\n Component Health:'));\n console.log(` Contract coverage: ${health.contractCoverage}% (${health.contractedComponents}/${health.totalComponents})`);\n console.log(` Compliance rate: ${health.overallCompliance}%`);\n\n if (health.uncontracted.length > 0) {\n console.log(pc.dim(` Uncontracted: ${health.uncontracted.slice(0, 5).join(', ')}${health.uncontracted.length > 5 ? ` (+${health.uncontracted.length - 5} more)` : ''}`));\n }\n\n console.log();\n\n if (passedFiles === totalFiles) {\n console.log(pc.green(` ✓ All files passed governance checks\\n`));\n } else {\n console.log(\n pc.red(` ✗ ${totalFiles - passedFiles} file(s) failed governance checks\\n`),\n );\n }\n }\n\n // 10. Push component usage snapshot + health to Cloud (if API key set)\n if (process.env.FRAGMENTS_API_KEY && usageSnapshot.length > 0) {\n try {\n const apiKey = process.env.FRAGMENTS_API_KEY;\n const url = process.env.FRAGMENTS_URL ?? 'https://app.usefragments.com';\n await fetch(`${url}/api/ingest`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify({\n componentUsage: JSON.stringify(usageSnapshot),\n componentHealth: JSON.stringify(health),\n }),\n });\n } catch {\n // Non-critical — don't fail the scan\n }\n }\n\n return { exitCode: passedFiles === totalFiles ? 0 : 1 };\n}\n\n// ---------------------------------------------------------------------------\n// Token extraction for cloud ingest\n// ---------------------------------------------------------------------------\n\n/**\n * Auto-detect and extract code tokens to send alongside governance verdicts.\n * Tries: 1) tokens config from fragments.config.ts, 2) Tailwind config.\n * Returns a flat JSON string of token name → value, or undefined if nothing found.\n */\nasync function extractCodeTokens(\n rootDir: string,\n configPath?: string,\n quiet?: boolean,\n): Promise<string | undefined> {\n try {\n // 1. Try fragments.config.ts tokens config\n try {\n const { loadConfig } = await import('../core/node.js');\n const { parseTokenFiles } = await import('../service/index.js');\n\n const { config, configDir } = await loadConfig(configPath);\n if (config.tokens?.include?.length) {\n const result = await parseTokenFiles(config.tokens, configDir);\n if (result.tokens.length > 0) {\n const flat: Record<string, string> = {};\n for (const token of result.tokens) {\n flat[token.name] = token.resolvedValue;\n }\n if (!quiet) {\n console.log(\n pc.dim(` Extracted ${result.tokens.length} code tokens from config\\n`),\n );\n }\n return JSON.stringify(flat);\n }\n }\n } catch {\n // No config or no tokens section — fall through\n }\n\n // 2. Try Tailwind config\n const {\n findTailwindConfig,\n loadTailwindConfig,\n } = await import('../service/token-normalizer.js');\n\n const tailwindPath = findTailwindConfig(rootDir);\n if (tailwindPath) {\n const tokens = await loadTailwindConfig(tailwindPath);\n if (tokens.length > 0) {\n const flat: Record<string, string> = {};\n for (const token of tokens) {\n flat[token.name] = token.value;\n }\n if (!quiet) {\n console.log(\n pc.dim(` Extracted ${tokens.length} tokens from Tailwind config\\n`),\n );\n }\n return JSON.stringify(flat);\n }\n }\n } catch (error) {\n if (!quiet) {\n console.log(\n pc.dim(\n ` Token extraction skipped: ${error instanceof Error ? error.message : 'unknown error'}\\n`,\n ),\n );\n }\n }\n\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// governWatch\n// ---------------------------------------------------------------------------\n\nexport async function governWatch(\n options: GovernWatchOptions = {},\n): Promise<void> {\n const {\n loadPolicy,\n createEngine,\n buildAdaptersFromConfig,\n createCloudAdapter,\n formatVerdict,\n } = await import('@fragments-sdk/govern');\n\n const { scanFile } = await import('../service/enhance/scanner.js');\n const { usagesToSpec } = await import(\n '../service/enhance/converter.js'\n );\n\n const quiet = options.quiet ?? false;\n const debounceMs = options.debounce ?? 300;\n const format = options.format ?? 'summary';\n\n // 1. Run initial scan\n console.log(pc.cyan(`\\n${BRAND.name} Governance Watch\\n`));\n\n const { exitCode } = await governScan(options);\n if (!quiet) {\n console.log(\n pc.dim(` Initial scan ${exitCode === 0 ? 'passed' : 'completed with violations'}\\n`),\n );\n }\n\n // 2. Set up engine for incremental checks\n const rootDir = resolve(options.dir ?? detectRootDir(process.cwd()));\n let policy = await loadPolicy(options.config);\n if (Object.keys(policy.rules).length === 0) {\n policy = { ...policy, rules: SCAN_DEFAULT_RULES };\n }\n const adapters = buildAdaptersFromConfig(policy.audit);\n if (!adapters.some(() => policy.audit?.cloud) && process.env.FRAGMENTS_API_KEY) {\n adapters.push(createCloudAdapter());\n }\n const engine = createEngine(policy, adapters);\n\n // 3. Watch for changes\n console.log(pc.dim(' Watching for changes... (Ctrl+C to stop)\\n'));\n\n const chokidar = await import('chokidar');\n\n const watcher = chokidar.watch(\n ['**/*.tsx', '**/*.ts', '**/*.jsx', '**/*.js'],\n {\n cwd: rootDir,\n ignoreInitial: true,\n ignored: [\n '**/node_modules/**',\n '**/dist/**',\n '**/build/**',\n '**/.next/**',\n '**/*.test.*',\n '**/*.spec.*',\n '**/*.stories.*',\n ],\n awaitWriteFinish: { stabilityThreshold: debounceMs },\n },\n );\n\n const handleChange = async (changedRelPath: string) => {\n const absolutePath = resolve(rootDir, changedRelPath);\n\n try {\n const { usages } = await scanFile(absolutePath);\n\n if (usages.length === 0) {\n if (!quiet) {\n console.log(pc.dim(` ○ ${changedRelPath} — no component usages`));\n }\n return;\n }\n\n const spec = usagesToSpec(usages, absolutePath, rootDir);\n const verdict = await engine.check(spec, {\n runner: 'cli',\n input: changedRelPath,\n });\n\n if (verdict.passed) {\n console.log(\n pc.green(` ✓ ${changedRelPath}`) +\n pc.dim(` (${usages.length} components, score: ${verdict.score}/100)`),\n );\n } else {\n console.log(pc.red(` ✗ ${changedRelPath}`));\n if (format === 'summary') {\n for (const result of verdict.results) {\n for (const v of result.violations) {\n console.log(\n pc.dim(` ${v.severity} `) +\n pc.yellow(v.rule) +\n pc.dim(` — ${v.message}`),\n );\n }\n }\n } else {\n console.log(formatVerdict(verdict, format));\n }\n }\n } catch (error) {\n if (!quiet) {\n console.log(\n pc.dim(` ⚠ ${changedRelPath} — `) +\n pc.yellow(error instanceof Error ? error.message : 'parse error'),\n );\n }\n }\n };\n\n watcher.on('change', handleChange);\n watcher.on('add', handleChange);\n\n // Keep process alive\n await new Promise(() => {});\n}\n"],"mappings":"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","names":["existsSync"]}