{"version":3,"file":"auto-scanner.mjs","sources":["../../../../src/lib/routing/discovery/auto-scanner.ts"],"sourcesContent":["/**\n * @file Automatic Route File Discovery Scanner\n * @description Enterprise-grade automatic route file discovery with glob pattern matching,\n * intelligent caching, and comprehensive file system analysis.\n *\n * @module @/lib/routing/discovery/auto-scanner\n *\n * This module provides:\n * - Glob pattern-based file discovery\n * - Intelligent caching with TTL and invalidation\n * - Multi-root directory scanning\n * - File extension filtering\n * - Ignore pattern support\n * - Detailed scan statistics\n *\n * @example\n * ```typescript\n * import { AutoScanner, createAutoScanner } from '@/lib/routing/discovery/auto-scanner';\n *\n * const scanner = createAutoScanner({\n *   roots: ['src/routes', 'src/features'],\n *   extensions: ['.tsx', '.ts'],\n *   ignore: ['**\\/*.test.ts', '**\\/__tests__/**'],\n * });\n *\n * const result = await scanner.scan();\n * console.log(`Found ${result.files.length} route files`);\n * ```\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * File type classification for discovered files\n */\nexport type RouteFileType =\n  | 'page'\n  | 'layout'\n  | 'loading'\n  | 'error'\n  | 'not-found'\n  | 'template'\n  | 'default'\n  | 'route'\n  | 'middleware';\n\n/**\n * Discovered file metadata\n */\nexport interface DiscoveredFile {\n  /** Absolute file path */\n  readonly absolutePath: string;\n  /** Path relative to the scan root */\n  readonly relativePath: string;\n  /** File name without extension */\n  readonly name: string;\n  /** File extension (e.g., '.tsx') */\n  readonly extension: string;\n  /** Classified file type */\n  readonly fileType: RouteFileType;\n  /** Directory containing the file */\n  readonly directory: string;\n  /** Nesting depth from scan root */\n  readonly depth: number;\n  /** File modification timestamp */\n  readonly modifiedAt: number;\n  /** File size in bytes */\n  readonly size: number;\n}\n\n/**\n * Glob pattern configuration for file matching\n */\nexport interface GlobPattern {\n  /** Pattern string (supports ** and * wildcards) */\n  readonly pattern: string;\n  /** Whether this is an include or exclude pattern */\n  readonly type: 'include' | 'exclude';\n  /** Priority for pattern matching (higher = evaluated first) */\n  readonly priority: number;\n}\n\n/**\n * Auto scanner configuration\n */\nexport interface AutoScannerConfig {\n  /** Root directories to scan */\n  readonly roots: readonly string[];\n  /** File extensions to include */\n  readonly extensions: readonly string[];\n  /** Glob patterns for ignoring files/directories */\n  readonly ignore: readonly string[];\n  /** Additional glob patterns for including specific files */\n  readonly include?: readonly string[];\n  /** Enable deep scanning (follow symlinks) */\n  readonly followSymlinks?: boolean;\n  /** Maximum directory depth to scan */\n  readonly maxDepth?: number;\n  /** Enable caching of scan results */\n  readonly cache?: boolean;\n  /** Cache TTL in milliseconds */\n  readonly cacheTTL?: number;\n  /** Concurrency level for parallel scanning */\n  readonly concurrency?: number;\n  /** Custom file type classifier */\n  readonly classifyFile?: (filename: string) => RouteFileType;\n  /** Feature flag to enable/disable scanner */\n  readonly featureFlag?: string;\n}\n\n/**\n * Scan result with comprehensive metadata\n */\nexport interface ScanResult {\n  /** Discovered files */\n  readonly files: readonly DiscoveredFile[];\n  /** Scan statistics */\n  readonly stats: ScanStatistics;\n  /** Any errors encountered during scanning */\n  readonly errors: readonly ScanError[];\n  /** Whether result came from cache */\n  readonly fromCache: boolean;\n  /** Timestamp when scan completed */\n  readonly scannedAt: number;\n}\n\n/**\n * Scan statistics for monitoring and debugging\n */\nexport interface ScanStatistics {\n  /** Total files discovered */\n  readonly totalFiles: number;\n  /** Total directories scanned */\n  readonly totalDirectories: number;\n  /** Files ignored by patterns */\n  readonly ignoredFiles: number;\n  /** Scan duration in milliseconds */\n  readonly durationMs: number;\n  /** Breakdown by file type */\n  readonly byFileType: Record<RouteFileType, number>;\n  /** Breakdown by extension */\n  readonly byExtension: Record<string, number>;\n  /** Maximum depth reached */\n  readonly maxDepthReached: number;\n  /** Average file size */\n  readonly avgFileSize: number;\n}\n\n/**\n * Error encountered during scanning\n */\nexport interface ScanError {\n  /** Error type */\n  readonly type: 'permission' | 'not-found' | 'symlink' | 'unknown';\n  /** Path that caused the error */\n  readonly path: string;\n  /** Error message */\n  readonly message: string;\n  /** Original error (if available) */\n  readonly originalError?: Error;\n}\n\n/**\n * Cache entry for scan results\n */\ninterface CacheEntry {\n  readonly result: ScanResult;\n  readonly expiresAt: number;\n  readonly configHash: string;\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/**\n * Default scanner configuration\n */\nexport const DEFAULT_SCANNER_CONFIG: AutoScannerConfig = {\n  roots: ['src/routes', 'src/pages', 'src/app'],\n  extensions: ['.tsx', '.ts', '.jsx', '.js'],\n  ignore: [\n    '**/*.test.{ts,tsx,js,jsx}',\n    '**/*.spec.{ts,tsx,js,jsx}',\n    '**/__tests__/**',\n    '**/__mocks__/**',\n    '**/*.stories.{ts,tsx,js,jsx}',\n    '**/*.d.ts',\n    '**/node_modules/**',\n    '**/.git/**',\n  ],\n  followSymlinks: false,\n  maxDepth: 20,\n  cache: true,\n  cacheTTL: 30000, // 30 seconds\n  concurrency: 4,\n};\n\n/**\n * File naming conventions for type classification\n */\nconst FILE_TYPE_PATTERNS: ReadonlyArray<{ pattern: RegExp; type: RouteFileType }> = [\n  { pattern: /^_layout\\.(tsx?|jsx?)$/, type: 'layout' },\n  { pattern: /^layout\\.(tsx?|jsx?)$/, type: 'layout' },\n  { pattern: /^_loading\\.(tsx?|jsx?)$/, type: 'loading' },\n  { pattern: /^loading\\.(tsx?|jsx?)$/, type: 'loading' },\n  { pattern: /^_error\\.(tsx?|jsx?)$/, type: 'error' },\n  { pattern: /^error\\.(tsx?|jsx?)$/, type: 'error' },\n  { pattern: /^_not-found\\.(tsx?|jsx?)$/, type: 'not-found' },\n  { pattern: /^not-found\\.(tsx?|jsx?)$/, type: 'not-found' },\n  { pattern: /^_template\\.(tsx?|jsx?)$/, type: 'template' },\n  { pattern: /^template\\.(tsx?|jsx?)$/, type: 'template' },\n  { pattern: /^_default\\.(tsx?|jsx?)$/, type: 'default' },\n  { pattern: /^default\\.(tsx?|jsx?)$/, type: 'default' },\n  { pattern: /^route\\.(tsx?|jsx?)$/, type: 'route' },\n  { pattern: /^_middleware\\.(tsx?|jsx?)$/, type: 'middleware' },\n  { pattern: /^middleware\\.(tsx?|jsx?)$/, type: 'middleware' },\n];\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\n/**\n * Classify a file based on its name\n *\n * @param filename - The filename to classify\n * @returns The classified file type\n */\nexport function classifyFileType(filename: string): RouteFileType {\n  for (const { pattern, type } of FILE_TYPE_PATTERNS) {\n    if (pattern.test(filename)) {\n      return type;\n    }\n  }\n  return 'page';\n}\n\n/**\n * Convert a glob pattern to a RegExp\n *\n * @param pattern - Glob pattern string\n * @returns Compiled RegExp\n */\nexport function globToRegex(pattern: string): RegExp {\n  const regexStr = pattern\n    // Escape special regex characters (except * and ?)\n    .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n    // Convert ** to match any path\n    .replace(/\\*\\*/g, '{{GLOBSTAR}}')\n    // Convert * to match anything except path separator\n    .replace(/\\*/g, '[^/]*')\n    // Convert ? to match single character\n    .replace(/\\?/g, '.')\n    // Restore globstar\n    .replace(/\\{\\{GLOBSTAR\\}\\}/g, '.*')\n    // Handle brace expansion {a,b,c}\n    .replace(/\\{([^}]+)\\}/g, (_, group: string) => `(${group.split(',').join('|')})`);\n\n  return new RegExp(`^${regexStr}$`);\n}\n\n/**\n * Check if a path matches any of the given patterns\n *\n * @param path - Path to check\n * @param patterns - Patterns to match against\n * @returns True if path matches any pattern\n */\nexport function matchesPatterns(path: string, patterns: readonly string[]): boolean {\n  for (const pattern of patterns) {\n    const regex = globToRegex(pattern);\n    if (regex.test(path)) {\n      return true;\n    }\n  }\n  return false;\n}\n\n/**\n * Generate a hash of the scanner configuration for cache keying\n *\n * @param config - Scanner configuration\n * @returns Configuration hash string\n */\nfunction hashConfig(config: AutoScannerConfig): string {\n  const normalized = {\n    roots: [...config.roots].sort(),\n    extensions: [...config.extensions].sort(),\n    ignore: [...config.ignore].sort(),\n    include: config.include ? [...config.include].sort() : [],\n    maxDepth: config.maxDepth,\n  };\n  return JSON.stringify(normalized);\n}\n\n/**\n * Calculate path depth (number of directory levels)\n *\n * @param path - Path to analyze\n * @returns Depth count\n */\nfunction getPathDepth(path: string): number {\n  return path.split(/[/\\\\]/).filter(Boolean).length;\n}\n\n// =============================================================================\n// AutoScanner Class\n// =============================================================================\n\n/**\n * Automatic route file scanner with caching and parallel scanning support\n *\n * @example\n * ```typescript\n * const scanner = new AutoScanner({\n *   roots: ['src/routes'],\n *   extensions: ['.tsx'],\n * });\n *\n * const result = await scanner.scan();\n * for (const file of result.files) {\n *   console.log(`${file.fileType}: ${file.relativePath}`);\n * }\n * ```\n */\nexport class AutoScanner {\n  private readonly config: AutoScannerConfig;\n  private cache: CacheEntry | null = null;\n  private isScanning = false;\n  private pendingScan: Promise<ScanResult> | null = null;\n\n  /**\n   * Create a new AutoScanner instance\n   *\n   * @param config - Scanner configuration (merged with defaults)\n   */\n  constructor(config: Partial<AutoScannerConfig> = {}) {\n    this.config = {\n      ...DEFAULT_SCANNER_CONFIG,\n      ...config,\n      roots: config.roots ?? DEFAULT_SCANNER_CONFIG.roots,\n      extensions: config.extensions ?? DEFAULT_SCANNER_CONFIG.extensions,\n      ignore: config.ignore ?? DEFAULT_SCANNER_CONFIG.ignore,\n    };\n  }\n\n  /**\n   * Get the current configuration\n   */\n  getConfig(): Readonly<AutoScannerConfig> {\n    return this.config;\n  }\n\n  /**\n   * Perform a file system scan\n   *\n   * @param forceRefresh - Skip cache and perform fresh scan\n   * @returns Scan result with discovered files\n   */\n  async scan(forceRefresh = false): Promise<ScanResult> {\n    // Check cache first\n    if (!forceRefresh && this.config.cache === true && this.cache) {\n      const configHash = hashConfig(this.config);\n      if (this.cache.configHash === configHash && Date.now() < this.cache.expiresAt) {\n        return { ...this.cache.result, fromCache: true };\n      }\n    }\n\n    // Deduplicate concurrent scan requests\n    if (this.isScanning && this.pendingScan) {\n      return this.pendingScan;\n    }\n\n    this.isScanning = true;\n    this.pendingScan = this.performScan();\n\n    try {\n      const result = await this.pendingScan;\n\n      // Update cache\n      if (this.config.cache === true) {\n        this.cache = {\n          result,\n          expiresAt: Date.now() + (this.config.cacheTTL ?? 30000),\n          configHash: hashConfig(this.config),\n        };\n      }\n\n      return result;\n    } finally {\n      this.isScanning = false;\n      this.pendingScan = null;\n    }\n  }\n\n  /**\n   * Clear the scan cache\n   */\n  clearCache(): void {\n    this.cache = null;\n  }\n\n  /**\n   * Check if a file matches the scanner configuration\n   *\n   * @param filePath - File path to check\n   * @returns True if file would be included in scan\n   */\n  matchesFile(filePath: string): boolean {\n    const path = filePath.replace(/\\\\/g, '/');\n\n    // Check extension\n    const ext = `.${  path.split('.').pop()}`;\n    if (!this.config.extensions.includes(ext)) {\n      return false;\n    }\n\n    // Check ignore patterns\n    if (matchesPatterns(path, this.config.ignore)) {\n      return false;\n    }\n\n    // Check include patterns\n    if (this.config.include && this.config.include.length > 0) {\n      if (!matchesPatterns(path, this.config.include)) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n  /**\n   * Get files by type from last scan (requires prior scan)\n   *\n   * @param type - File type to filter by\n   * @returns Files of the specified type, or empty array if no scan performed\n   */\n  getFilesByType(type: RouteFileType): readonly DiscoveredFile[] {\n    if (!this.cache) {\n      return [];\n    }\n    return this.cache.result.files.filter(f => f.fileType === type);\n  }\n\n  /**\n   * Perform the actual scan operation\n   */\n  private async performScan(): Promise<ScanResult> {\n    const startTime = Date.now();\n    const files: DiscoveredFile[] = [];\n    const errors: ScanError[] = [];\n    let totalDirectories = 0;\n    let ignoredFiles = 0;\n    let maxDepthReached = 0;\n    let totalSize = 0;\n    const byFileType: Record<RouteFileType, number> = {\n      page: 0,\n      layout: 0,\n      loading: 0,\n      error: 0,\n      'not-found': 0,\n      template: 0,\n      default: 0,\n      route: 0,\n      middleware: 0,\n    };\n    const byExtension: Record<string, number> = {};\n\n    // Dynamic imports for Node.js fs module (build-time only)\n    const { promises: fs } = await import('fs');\n    const path = await import('path');\n\n    // Scan each root directory\n    for (const root of this.config.roots) {\n      try {\n        await fs.access(root);\n      } catch {\n        // Root doesn't exist, skip\n        continue;\n      }\n\n      const queue: Array<{ dir: string; depth: number }> = [{ dir: root, depth: 0 }];\n      const visited = new Set<string>();\n\n      while (queue.length > 0) {\n        const item = queue.shift();\n        if (!item) continue;\n\n        const { dir, depth } = item;\n\n        // Check max depth\n        if (this.config.maxDepth !== undefined && depth > this.config.maxDepth) {\n          continue;\n        }\n\n        // Skip if already visited (handles symlink cycles)\n        const realPath = await fs.realpath(dir).catch(() => dir);\n        if (visited.has(realPath)) {\n          continue;\n        }\n        visited.add(realPath);\n\n        totalDirectories++;\n        if (depth > maxDepthReached) {\n          maxDepthReached = depth;\n        }\n\n        let entries: Array<{\n          name: string;\n          isFile: () => boolean;\n          isDirectory: () => boolean;\n          isSymbolicLink?: () => boolean;\n        }>;\n        try {\n          const rawEntries = await fs.readdir(dir, { withFileTypes: true });\n          entries = rawEntries as Array<{\n            name: string;\n            isFile: () => boolean;\n            isDirectory: () => boolean;\n            isSymbolicLink?: () => boolean;\n          }>;\n        } catch (err) {\n          errors.push({\n            type: 'permission',\n            path: dir,\n            message: `Cannot read directory: ${(err as Error).message}`,\n            originalError: err as Error,\n          });\n          continue;\n        }\n\n        for (const entry of entries) {\n          const entryName = entry.name;\n          const entryPath = path.join(dir, entryName);\n          const relativePath = path.relative(root, entryPath);\n\n          if (entry.isDirectory()) {\n            // Check ignore patterns for directory\n            if (matchesPatterns(`${relativePath  }/`, this.config.ignore)) {\n              continue;\n            }\n\n            // Handle symlinks\n            if (entry.isSymbolicLink?.() === true && this.config.followSymlinks !== true) {\n              continue;\n            }\n\n            queue.push({ dir: entryPath, depth: depth + 1 });\n          } else if (entry.isFile()) {\n            // Check extension\n            const ext = path.extname(entryName);\n            if (!this.config.extensions.includes(ext)) {\n              ignoredFiles++;\n              continue;\n            }\n\n            // Check ignore patterns\n            if (matchesPatterns(relativePath, this.config.ignore)) {\n              ignoredFiles++;\n              continue;\n            }\n\n            // Check include patterns (if specified)\n            if (this.config.include && this.config.include.length > 0) {\n              if (!matchesPatterns(relativePath, this.config.include)) {\n                ignoredFiles++;\n                continue;\n              }\n            }\n\n            // Get file stats\n            let stats: Awaited<ReturnType<typeof fs.stat>>;\n            try {\n              stats = await fs.stat(entryPath);\n            } catch (err) {\n              errors.push({\n                type: 'permission',\n                path: entryPath,\n                message: `Cannot stat file: ${(err as Error).message}`,\n                originalError: err as Error,\n              });\n              continue;\n            }\n\n            // Classify file type\n            const entryNameStr = entryName.toString();\n            const fileType = this.config.classifyFile\n              ? this.config.classifyFile(entryNameStr)\n              : classifyFileType(entryNameStr);\n\n            const file: DiscoveredFile = {\n              absolutePath: entryPath,\n              relativePath,\n              name: path.basename(entryNameStr, ext),\n              extension: ext,\n              fileType,\n              directory: path.dirname(relativePath),\n              depth: getPathDepth(relativePath),\n              modifiedAt: stats.mtimeMs,\n              size: stats.size,\n            };\n\n            files.push(file);\n            totalSize += stats.size;\n            byFileType[fileType]++;\n            byExtension[ext] = (byExtension[ext] ?? 0) + 1;\n          }\n        }\n      }\n    }\n\n    const durationMs = Date.now() - startTime;\n\n    return {\n      files: Object.freeze(files.sort((a, b) => a.relativePath.localeCompare(b.relativePath))),\n      stats: {\n        totalFiles: files.length,\n        totalDirectories,\n        ignoredFiles,\n        durationMs,\n        byFileType,\n        byExtension,\n        maxDepthReached,\n        avgFileSize: files.length > 0 ? totalSize / files.length : 0,\n      },\n      errors: Object.freeze(errors),\n      fromCache: false,\n      scannedAt: Date.now(),\n    };\n  }\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Create a new AutoScanner instance with configuration\n *\n * @param config - Scanner configuration\n * @returns Configured AutoScanner instance\n *\n * @example\n * ```typescript\n * const scanner = createAutoScanner({\n *   roots: ['src/routes'],\n *   extensions: ['.tsx'],\n *   ignore: ['**\\/*.test.tsx'],\n * });\n * ```\n */\nexport function createAutoScanner(config: Partial<AutoScannerConfig> = {}): AutoScanner {\n  return new AutoScanner(config);\n}\n\n/**\n * Create a scanner with Next.js App Router conventions\n *\n * @param appDir - App directory path (default: 'src/app')\n * @returns Configured scanner for Next.js App Router\n */\nexport function createNextJsAppScanner(appDir = 'src/app'): AutoScanner {\n  return new AutoScanner({\n    roots: [appDir],\n    extensions: ['.tsx', '.ts', '.jsx', '.js'],\n    ignore: [\n      '**/*.test.{ts,tsx,js,jsx}',\n      '**/*.spec.{ts,tsx,js,jsx}',\n      '**/__tests__/**',\n      '**/components/**',\n      '**/hooks/**',\n      '**/utils/**',\n      '**/lib/**',\n      '**/api/**/*.{ts,js}', // API routes handled separately\n    ],\n    classifyFile: (filename) => {\n      if (/^page\\.(tsx?|jsx?)$/.test(filename)) return 'page';\n      if (/^layout\\.(tsx?|jsx?)$/.test(filename)) return 'layout';\n      if (/^loading\\.(tsx?|jsx?)$/.test(filename)) return 'loading';\n      if (/^error\\.(tsx?|jsx?)$/.test(filename)) return 'error';\n      if (/^not-found\\.(tsx?|jsx?)$/.test(filename)) return 'not-found';\n      if (/^template\\.(tsx?|jsx?)$/.test(filename)) return 'template';\n      if (/^default\\.(tsx?|jsx?)$/.test(filename)) return 'default';\n      if (/^route\\.(tsx?|jsx?)$/.test(filename)) return 'route';\n      return 'page';\n    },\n  });\n}\n\n/**\n * Create a scanner with Remix conventions\n *\n * @param routesDir - Routes directory path (default: 'app/routes')\n * @returns Configured scanner for Remix\n */\nexport function createRemixScanner(routesDir = 'app/routes'): AutoScanner {\n  return new AutoScanner({\n    roots: [routesDir],\n    extensions: ['.tsx', '.ts', '.jsx', '.js'],\n    ignore: [\n      '**/*.test.{ts,tsx,js,jsx}',\n      '**/*.spec.{ts,tsx,js,jsx}',\n      '**/__tests__/**',\n    ],\n    classifyFile: (filename) => {\n      // Remix uses file-based routing with specific conventions\n      if (filename.startsWith('_index')) return 'page';\n      if (filename.startsWith('_')) return 'layout';\n      return 'page';\n    },\n  });\n}\n\n// =============================================================================\n// Singleton Instance\n// =============================================================================\n\nlet defaultScanner: AutoScanner | null = null;\n\n/**\n * Get the default AutoScanner instance\n *\n * @returns Default scanner instance\n */\nexport function getAutoScanner(): AutoScanner {\n  defaultScanner ??= new AutoScanner();\n  return defaultScanner;\n}\n\n/**\n * Reset the default scanner (useful for testing)\n */\nexport function resetAutoScanner(): void {\n  defaultScanner = null;\n}\n"],"names":["DEFAULT_SCANNER_CONFIG","FILE_TYPE_PATTERNS","classifyFileType","filename","pattern","type","globToRegex","regexStr","_","group","matchesPatterns","path","patterns","hashConfig","config","normalized","getPathDepth","AutoScanner","forceRefresh","configHash","result","filePath","ext","f","startTime","files","errors","totalDirectories","ignoredFiles","maxDepthReached","totalSize","byFileType","byExtension","fs","root","queue","visited","item","dir","depth","realPath","entries","err","entry","entryName","entryPath","relativePath","stats","entryNameStr","fileType","file","durationMs","b","createAutoScanner","defaultScanner","getAutoScanner","resetAutoScanner"],"mappings":"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"}