{"version":3,"file":"conflict-detector.mjs","sources":["../../../src/lib/routing/conflict-detector.ts"],"sourcesContent":["/**\n * @file Route Conflict Detection\n * @description Build-time detection of conflicting, ambiguous, and shadowed routes\n *\n * This module wraps the core conflict detection utilities to work with\n * the DiscoveredRoute type from the routing system.\n */\n\nimport type { DiscoveredRoute, RouteConflict } from './types';\nimport {\n  detectConflicts as coreDetectConflicts,\n  findNestedDynamicConflicts as coreFindNestedDynamicConflicts,\n  findDeepNestingWarnings as coreFindDeepNestingWarnings,\n  findIndexLayoutConflicts as coreFindIndexLayoutConflicts,\n  calculateRouteSpecificity as coreCalculateRouteSpecificity,\n  sortBySpecificity as coreSortBySpecificity,\n  validateRoutes as coreValidateRoutes,\n  areRoutesValid as coreAreRoutesValid,\n  type RouteForConflictDetection,\n  type ConflictDetectionResult as CoreConflictDetectionResult,\n} from './core';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Result of conflict detection analysis\n */\nexport interface ConflictDetectionResult {\n  /** Whether any errors were detected */\n  readonly hasErrors: boolean;\n  /** Whether any warnings were detected */\n  readonly hasWarnings: boolean;\n  /** List of detected conflicts */\n  readonly conflicts: readonly RouteConflict[];\n  /** Human-readable report */\n  readonly report: string;\n}\n\n// =============================================================================\n// Adapter: Convert DiscoveredRoute to RouteForConflictDetection\n// =============================================================================\n\n/**\n * Convert a DiscoveredRoute to the format expected by core conflict detection\n */\nfunction toConflictRoute(route: DiscoveredRoute): RouteForConflictDetection {\n  return {\n    urlPath: route.urlPath,\n    filePath: route.filePath,\n    segments: route.segments,\n    isLayout: route.isLayout,\n    isIndex: route.isIndex,\n    depth: route.depth,\n  };\n}\n\n/**\n * Convert core conflict result to local type\n */\nfunction fromCoreResult(result: CoreConflictDetectionResult): ConflictDetectionResult {\n  return {\n    hasErrors: result.hasErrors,\n    hasWarnings: result.hasWarnings,\n    conflicts: result.conflicts as readonly RouteConflict[],\n    report: result.report,\n  };\n}\n\n// =============================================================================\n// Conflict Detection (delegating to core)\n// =============================================================================\n\n/**\n * Detect all route conflicts in a set of discovered routes\n * @see core/conflict-detector.ts for implementation details\n */\nexport function detectRouteConflicts(routes: readonly DiscoveredRoute[]): ConflictDetectionResult {\n  const conflictRoutes = routes.map(toConflictRoute);\n  const result = coreDetectConflicts(conflictRoutes);\n  return fromCoreResult(result);\n}\n\n// =============================================================================\n// Validation (delegating to core)\n// =============================================================================\n\n/**\n * Validate routes and throw on errors\n * @see core/conflict-detector.ts for implementation details\n *\n * @throws Error if route conflicts are detected\n */\nexport function validateRoutes(routes: readonly DiscoveredRoute[]): void {\n  const conflictRoutes = routes.map(toConflictRoute);\n  coreValidateRoutes(conflictRoutes);\n}\n\n/**\n * Check if routes are valid (without throwing)\n * @see core/conflict-detector.ts for implementation details\n */\nexport function areRoutesValid(routes: readonly DiscoveredRoute[]): boolean {\n  const conflictRoutes = routes.map(toConflictRoute);\n  return coreAreRoutesValid(conflictRoutes);\n}\n\n// =============================================================================\n// Route Ordering (delegating to core)\n// =============================================================================\n\n/**\n * Calculate route specificity for proper ordering\n * @see core/conflict-detector.ts for implementation details\n *\n * Higher specificity = more specific route = should be matched first\n */\nexport function calculateRouteSpecificity(route: DiscoveredRoute): number {\n  return coreCalculateRouteSpecificity(toConflictRoute(route));\n}\n\n/**\n * Sort routes by specificity (most specific first)\n * @see core/conflict-detector.ts for implementation details\n */\nexport function sortRoutesBySpecificity(routes: readonly DiscoveredRoute[]): DiscoveredRoute[] {\n  // Sort using core, then map back to original routes\n  const routesArray = [...routes];\n  const conflictRoutes = routesArray.map((r, i) => ({ ...toConflictRoute(r), _originalIndex: i }));\n  const sorted = coreSortBySpecificity(conflictRoutes);\n  return sorted.map((cr) => {\n    const idx = (cr as { _originalIndex: number })._originalIndex;\n    const route = routesArray[idx];\n    if (!route) throw new Error(`Invalid route index: ${idx}`);\n    return route;\n  });\n}\n\n// =============================================================================\n// Advanced Conflict Detection (delegating to core)\n// =============================================================================\n\n/**\n * Detect nested dynamic route conflicts\n * @see core/conflict-detector.ts for implementation details\n */\nexport function findNestedDynamicConflicts(routes: readonly DiscoveredRoute[]): RouteConflict[] {\n  const conflictRoutes = routes.map(toConflictRoute);\n  return coreFindNestedDynamicConflicts(conflictRoutes) as RouteConflict[];\n}\n\n/**\n * Detect deep nesting issues (warning)\n * @see core/conflict-detector.ts for implementation details\n */\nexport function findDeepNestingWarnings(\n  routes: readonly DiscoveredRoute[],\n  maxDepth: number = 5\n): RouteConflict[] {\n  const conflictRoutes = routes.map(toConflictRoute);\n  return coreFindDeepNestingWarnings(conflictRoutes, maxDepth) as RouteConflict[];\n}\n\n/**\n * Detect index route conflicts\n * @see core/conflict-detector.ts for implementation details\n */\nexport function findIndexLayoutConflicts(routes: readonly DiscoveredRoute[]): RouteConflict[] {\n  const conflictRoutes = routes.map(toConflictRoute);\n  return coreFindIndexLayoutConflicts(conflictRoutes) as RouteConflict[];\n}\n\n// =============================================================================\n// Auto-Fix Suggestions\n// =============================================================================\n\nimport type { RouteFixSuggestion } from './types';\n\n/**\n * Generate fix suggestions for detected conflicts\n */\nexport function generateFixSuggestions(conflicts: readonly RouteConflict[]): RouteFixSuggestion[] {\n  const suggestions: RouteFixSuggestion[] = [];\n\n  for (const conflict of conflicts) {\n    switch (conflict.type) {\n      case 'exact':\n        suggestions.push(...generateDuplicateFixes(conflict));\n        break;\n      case 'ambiguous':\n        suggestions.push(...generateAmbiguousFixes(conflict));\n        break;\n      case 'shadow':\n        suggestions.push(...generateShadowFixes(conflict));\n        break;\n    }\n  }\n\n  return suggestions;\n}\n\n/**\n * Generate fixes for duplicate route conflicts\n */\nfunction generateDuplicateFixes(conflict: RouteConflict): RouteFixSuggestion[] {\n  const suggestions: RouteFixSuggestion[] = [];\n\n  if (conflict.files.length >= 2 && conflict.files[0] != null) {\n    // Suggest keeping the first file and removing duplicates\n    for (let i = 1; i < conflict.files.length; i++) {\n      const file = conflict.files[i];\n      if (file != null) {\n        suggestions.push({\n          description: `Remove duplicate route file: ${file}`,\n          oldValue: file,\n          newValue: '',\n          autoFixable: false, // Deletion requires manual confirmation\n        });\n      }\n    }\n\n    // Suggest consolidating into one file\n    suggestions.push({\n      description: `Consolidate duplicate routes into ${conflict.files[0]}`,\n      oldValue: conflict.files.slice(1).join(', '),\n      newValue: conflict.files[0],\n      autoFixable: false,\n    });\n  }\n\n  return suggestions;\n}\n\n/**\n * Generate fixes for ambiguous route conflicts\n */\nfunction generateAmbiguousFixes(conflict: RouteConflict): RouteFixSuggestion[] {\n  const suggestions: RouteFixSuggestion[] = [];\n\n  if (conflict.files.length >= 2) {\n    // Extract param names from files\n    const paramPatterns = conflict.files\n      .map((file) => {\n        const match = file.match(/\\[([^\\]]+)\\]/);\n        return match ? match[1] : null;\n      })\n      .filter(Boolean);\n\n    if (paramPatterns.length > 0) {\n      const [preferredParam] = paramPatterns;\n\n      suggestions.push({\n        description: `Standardize parameter names to use \"${preferredParam}\" across all conflicting routes`,\n        oldValue: paramPatterns.slice(1).join(', '),\n        newValue: preferredParam ?? '',\n        autoFixable: true,\n      });\n    }\n  }\n\n  return suggestions;\n}\n\n/**\n * Generate fixes for shadow conflicts\n */\nfunction generateShadowFixes(conflict: RouteConflict): RouteFixSuggestion[] {\n  const suggestions: RouteFixSuggestion[] = [];\n\n  if (conflict.files.length >= 2) {\n    const staticFile = conflict.files.find((f) => !f.includes('['));\n    const dynamicFile = conflict.files.find((f) => f.includes('['));\n\n    if (staticFile != null && dynamicFile != null) {\n      // Suggest renaming the static route to avoid shadowing\n      const staticNameMatch = staticFile\n        .split('/')\n        .pop()\n        ?.replace(/\\.(tsx?|jsx?)$/, '');\n      const staticName = staticNameMatch ?? '';\n      const newName = `_${staticName}`;\n\n      suggestions.push({\n        description: `Rename static route file to use underscore prefix for explicit ordering`,\n        oldValue: staticFile,\n        newValue: staticFile.replace(staticName, newName),\n        autoFixable: true,\n      });\n\n      // Suggest adding route priority\n      suggestions.push({\n        description: `Add explicit route ordering by ensuring static routes are defined before dynamic routes`,\n        oldValue: `${staticFile} after ${dynamicFile}`,\n        newValue: `${staticFile} before ${dynamicFile}`,\n        autoFixable: false,\n      });\n    }\n  }\n\n  return suggestions;\n}\n\n// =============================================================================\n// Comprehensive Conflict Detection (delegating to core)\n// =============================================================================\n\n/**\n * Run all conflict detection patterns\n * @see core/conflict-detector.ts for implementation details\n */\nexport function detectAllConflicts(\n  routes: readonly DiscoveredRoute[],\n  options: {\n    maxNestingDepth?: number;\n    checkNestedDynamic?: boolean;\n    checkDeepNesting?: boolean;\n    checkIndexLayouts?: boolean;\n  } = {}\n): ConflictDetectionResult {\n  const conflictRoutes = routes.map(toConflictRoute);\n  const result = coreDetectConflicts(conflictRoutes, options);\n  return fromCoreResult(result);\n}\n"],"names":["toConflictRoute","route","fromCoreResult","result","detectRouteConflicts","routes","conflictRoutes","coreDetectConflicts","sortRoutesBySpecificity","routesArray","r","i","coreSortBySpecificity","cr","idx","generateFixSuggestions","conflicts","suggestions","conflict","generateDuplicateFixes","generateAmbiguousFixes","generateShadowFixes","file","paramPatterns","match","preferredParam","staticFile","f","dynamicFile","staticName","newName","detectAllConflicts","options"],"mappings":"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"}