{"version":3,"file":"usePredictivePrefetch.mjs","sources":["../../../src/lib/performance/usePredictivePrefetch.tsx"],"sourcesContent":["/**\n * @file Predictive Prefetch React Hook\n * @description React hook for integrating predictive prefetching with react-router.\n * Automatically tracks navigation and triggers predictive prefetching.\n *\n * FEATURE 3: Predictive Navigation Prefetching\n */\n\nimport React, { useEffect, useCallback, useRef, useMemo } from 'react';\nimport { useLocation, useNavigate } from 'react-router-dom';\nimport {\n  getPredictivePrefetchEngine,\n  type PredictivePrefetchConfig,\n  type RoutePrediction,\n  type PrefetchableRoute,\n} from './PredictivePrefetch';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Options for the predictive prefetch hook\n */\nexport interface UsePredictivePrefetchOptions {\n  /** Enable automatic prefetching after navigation */\n  autoPrefetch?: boolean;\n  /** Delay before prefetching predicted routes (ms) */\n  prefetchDelay?: number;\n  /** Enable navigation tracking for learning */\n  enableTracking?: boolean;\n  /** Engine configuration */\n  config?: PredictivePrefetchConfig;\n}\n\n/**\n * Return value from usePredictivePrefetch\n */\nexport interface UsePredictivePrefetchReturn {\n  /** Current route predictions */\n  predictions: RoutePrediction[];\n  /** Manually trigger prefetch for predicted routes */\n  prefetchPredicted: () => Promise<void>;\n  /** Prefetch a specific route */\n  prefetchRoute: (path: string) => Promise<void>;\n  /** Register additional prefetchable routes */\n  registerRoutes: (routes: PrefetchableRoute[]) => void;\n  /** Get analytics about learned patterns */\n  getAnalytics: () => {\n    totalTransitions: number;\n    uniqueRoutes: number;\n    timePatterns: number;\n    topRoutes: Array<{ route: string; visits: number }>;\n  };\n  /** Navigate with optimistic prefetching */\n  navigateWithPrefetch: (to: string) => void;\n  /** Clear all learned patterns */\n  clearPatterns: () => void;\n}\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\n/**\n * Hook for predictive prefetching with react-router integration.\n *\n * Automatically tracks navigation patterns and prefetches likely next routes.\n *\n * @example\n * ```tsx\n * function App() {\n *   const { predictions, navigateWithPrefetch } = usePredictivePrefetch();\n *\n *   return (\n *     <nav>\n *       <button onClick={() => navigateWithPrefetch('/dashboard')}>\n *         Dashboard\n *       </button>\n *       {predictions.length > 0 && (\n *         <span>Predicted next: {predictions[0].route}</span>\n *       )}\n *     </nav>\n *   );\n * }\n * ```\n */\n// eslint-disable-next-line react-refresh/only-export-components -- Hook and component are closely related\nexport function usePredictivePrefetch(\n  options: UsePredictivePrefetchOptions = {}\n): UsePredictivePrefetchReturn {\n  const { autoPrefetch = true, prefetchDelay = 500, enableTracking = true, config } = options;\n\n  const location = useLocation();\n  const navigate = useNavigate();\n  const prevPathRef = useRef<string>('');\n  const prefetchTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n  // Get the singleton engine instance\n  const engine = useMemo(() => getPredictivePrefetchEngine(config), [config]);\n\n  // Track navigation changes\n  useEffect(() => {\n    const currentPath = location.pathname;\n    const prevPath = prevPathRef.current;\n\n    if (enableTracking && prevPath && prevPath !== currentPath) {\n      // Record the navigation for learning\n      engine.recordNavigation(prevPath, currentPath);\n    }\n\n    // Update previous path\n    prevPathRef.current = currentPath;\n\n    // Clear any pending prefetch\n    if (prefetchTimeoutRef.current) {\n      clearTimeout(prefetchTimeoutRef.current);\n    }\n\n    // Trigger prefetch after delay\n    if (autoPrefetch) {\n      prefetchTimeoutRef.current = setTimeout(() => {\n        void engine.prefetchPredictedRoutes(currentPath);\n      }, prefetchDelay);\n    }\n\n    // Cleanup timeout on unmount or path change\n    return () => {\n      if (prefetchTimeoutRef.current) {\n        clearTimeout(prefetchTimeoutRef.current);\n      }\n    };\n  }, [location.pathname, enableTracking, autoPrefetch, prefetchDelay, engine]);\n\n  // Get current predictions\n  const predictions = useMemo(\n    () => engine.getPredictions(location.pathname),\n    [engine, location.pathname]\n  );\n\n  // Manually trigger prefetch\n  const prefetchPredicted = useCallback(async () => {\n    await engine.prefetchPredictedRoutes(location.pathname);\n  }, [engine, location.pathname]);\n\n  // Prefetch a specific route\n  const prefetchRoute = useCallback(\n    async (path: string) => {\n      await engine.prefetchRoute(path);\n    },\n    [engine]\n  );\n\n  // Register additional routes\n  const registerRoutes = useCallback(\n    (routes: PrefetchableRoute[]) => {\n      engine.registerRoutes(routes);\n    },\n    [engine]\n  );\n\n  // Get analytics\n  const getAnalytics = useCallback(() => {\n    return engine.getAnalytics();\n  }, [engine]);\n\n  // Navigate with optimistic prefetching\n  const navigateWithPrefetch = useCallback(\n    (to: string) => {\n      // Start prefetching immediately\n      void engine.prefetchRoute(to);\n      // Navigate\n      void navigate(to);\n    },\n    [engine, navigate]\n  );\n\n  // Clear patterns\n  const clearPatterns = useCallback(() => {\n    engine.clearPatterns();\n  }, [engine]);\n\n  return {\n    predictions,\n    prefetchPredicted,\n    prefetchRoute,\n    registerRoutes,\n    getAnalytics,\n    navigateWithPrefetch,\n    clearPatterns,\n  };\n}\n\n// ============================================================================\n// Convenience Components\n// ============================================================================\n\n/**\n * Props for PredictiveLink component\n */\nexport interface PredictiveLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {\n  /** Target route */\n  to: string;\n  /** Prefetch strategy */\n  prefetchStrategy?: 'hover' | 'viewport' | 'immediate' | 'none';\n  /** Custom loader for code splitting */\n  loader?: () => Promise<unknown>;\n  /** Children */\n  children: React.ReactNode;\n}\n\n/**\n * Get current network quality for adaptive prefetching\n */\nfunction shouldPrefetch(): boolean {\n  const nav = navigator as Navigator & {\n    connection?: { effectiveType?: string; saveData?: boolean };\n  };\n\n  // Don't prefetch on slow connections or data saver mode\n  if (nav.connection?.saveData === true) return false;\n  if (nav.connection?.effectiveType === 'slow-2g') return false;\n  return nav.connection?.effectiveType !== '2g';\n}\n\n/**\n * Link component with predictive prefetching.\n * Prefetches the route on hover, focus, or when entering viewport.\n *\n * @example\n * ```tsx\n * <PredictiveLink to=\"/dashboard\" prefetchStrategy=\"hover\">\n *   Go to Dashboard\n * </PredictiveLink>\n * ```\n */\nexport function PredictiveLink({\n  to,\n  prefetchStrategy = 'hover',\n  loader,\n  children,\n  ...props\n}: PredictiveLinkProps): React.JSX.Element {\n  const navigate = useNavigate();\n  const engine = useMemo(() => getPredictivePrefetchEngine(), []);\n  const elementRef = useRef<HTMLAnchorElement>(null);\n  const hasPrefetchedRef = useRef(false);\n  const hoverTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n  // Trigger prefetch\n  const triggerPrefetch = useCallback(() => {\n    if (hasPrefetchedRef.current || !shouldPrefetch()) return;\n    hasPrefetchedRef.current = true;\n\n    // Prefetch module if provided\n    if (loader) {\n      void loader().catch(() => {});\n    }\n\n    // Prefetch via engine\n    void engine.prefetchRoute(to);\n  }, [to, loader, engine]);\n\n  // Handle click\n  const handleClick = useCallback(\n    (e: React.MouseEvent<HTMLAnchorElement>) => {\n      e.preventDefault();\n      if (props.onClick) {\n        void props.onClick(e);\n      }\n      void navigate(to);\n    },\n    [to, navigate, props]\n  );\n\n  // Hover handlers\n  const handleMouseEnter = useCallback(() => {\n    if (prefetchStrategy === 'hover') {\n      // Small delay to avoid prefetching on accidental hovers\n      hoverTimeoutRef.current = setTimeout(triggerPrefetch, 100);\n    }\n  }, [prefetchStrategy, triggerPrefetch]);\n\n  const handleMouseLeave = useCallback(() => {\n    if (hoverTimeoutRef.current) {\n      clearTimeout(hoverTimeoutRef.current);\n    }\n  }, []);\n\n  // Focus handler\n  const handleFocus = useCallback(() => {\n    if (prefetchStrategy === 'hover') {\n      triggerPrefetch();\n    }\n  }, [prefetchStrategy, triggerPrefetch]);\n\n  // Immediate prefetch\n  useEffect(() => {\n    if (prefetchStrategy === 'immediate') {\n      triggerPrefetch();\n    }\n  }, [prefetchStrategy, triggerPrefetch]);\n\n  // Viewport-based prefetch\n  useEffect(() => {\n    if (prefetchStrategy !== 'viewport' || !elementRef.current) return;\n\n    const observer = new IntersectionObserver(\n      ([entry]) => {\n        if (entry?.isIntersecting === true) {\n          triggerPrefetch();\n          observer.disconnect();\n        }\n      },\n      { rootMargin: '100px' }\n    );\n\n    observer.observe(elementRef.current);\n\n    return () => observer.disconnect();\n  }, [prefetchStrategy, triggerPrefetch]);\n\n  // Cleanup\n  useEffect(() => {\n    return () => {\n      if (hoverTimeoutRef.current) {\n        clearTimeout(hoverTimeoutRef.current);\n      }\n    };\n  }, []);\n\n  return (\n    <a\n      ref={elementRef}\n      href={to}\n      onClick={handleClick}\n      onMouseEnter={handleMouseEnter}\n      onMouseLeave={handleMouseLeave}\n      onFocus={handleFocus}\n      {...props}\n    >\n      {children}\n    </a>\n  );\n}\n"],"names":["usePredictivePrefetch","options","autoPrefetch","prefetchDelay","enableTracking","config","location","useLocation","navigate","useNavigate","prevPathRef","useRef","prefetchTimeoutRef","engine","useMemo","getPredictivePrefetchEngine","useEffect","currentPath","prevPath","predictions","prefetchPredicted","useCallback","prefetchRoute","path","registerRoutes","routes","getAnalytics","navigateWithPrefetch","to","clearPatterns","shouldPrefetch","nav","PredictiveLink","prefetchStrategy","loader","children","props","elementRef","hasPrefetchedRef","hoverTimeoutRef","triggerPrefetch","handleClick","e","handleMouseEnter","handleMouseLeave","handleFocus","observer","entry","jsx"],"mappings":"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"}