{"version":3,"file":"useHydrationMetrics.mjs","sources":["../../../../src/lib/hydration/hooks/useHydrationMetrics.ts"],"sourcesContent":["/**\n * @file useHydrationMetrics Hook\n * @description Hook for accessing and monitoring hydration performance metrics.\n *\n * This hook provides access to aggregated hydration metrics including:\n * - Hydration counts and rates\n * - Timing statistics (average, p95, total)\n * - Interaction replay statistics\n * - Real-time metric updates\n *\n * @module hydration/hooks/useHydrationMetrics\n *\n * @example\n * ```tsx\n * function HydrationDashboard() {\n *   const metrics = useHydrationMetrics();\n *\n *   return (\n *     <div className=\"metrics-dashboard\">\n *       <MetricCard\n *         label=\"Hydration Progress\"\n *         value={`${metrics.hydratedCount}/${metrics.totalBoundaries}`}\n *         progress={metrics.hydrationProgress}\n *       />\n *       <MetricCard\n *         label=\"Average Duration\"\n *         value={`${metrics.averageHydrationDuration.toFixed(0)}ms`}\n *       />\n *       <MetricCard\n *         label=\"P95 Duration\"\n *         value={`${metrics.p95HydrationDuration.toFixed(0)}ms`}\n *       />\n *       <MetricCard\n *         label=\"Time to Full Hydration\"\n *         value={metrics.timeToFullHydration\n *           ? `${metrics.timeToFullHydration.toFixed(0)}ms`\n *           : 'In progress...'}\n *       />\n *     </div>\n *   );\n * }\n * ```\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { useOptionalHydrationContext } from '../HydrationProvider';\nimport { getHydrationScheduler } from '../hydration-scheduler';\n\nimport type { HydrationMetricsSnapshot } from '../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Extended metrics with computed values.\n */\nexport interface UseHydrationMetricsReturn extends HydrationMetricsSnapshot {\n  /**\n   * Hydration progress as a percentage (0-100).\n   */\n  readonly hydrationProgress: number;\n\n  /**\n   * Estimated time remaining for full hydration in milliseconds.\n   * null if not enough data for estimation.\n   */\n  readonly estimatedTimeRemaining: number | null;\n\n  /**\n   * Current hydration rate (boundaries per second).\n   */\n  readonly hydrationRate: number;\n\n  /**\n   * Whether all boundaries have been hydrated.\n   */\n  readonly isFullyHydrated: boolean;\n\n  /**\n   * Whether above-the-fold content has been hydrated.\n   */\n  readonly isAboveFoldHydrated: boolean;\n\n  /**\n   * Interaction replay success rate (0-1).\n   */\n  readonly replaySuccessRate: number;\n\n  /**\n   * History of hydration durations for charting.\n   */\n  readonly durationHistory: readonly number[];\n\n  /**\n   * Manually refresh the metrics.\n   */\n  readonly refresh: () => void;\n}\n\n/**\n * Options for the useHydrationMetrics hook.\n */\nexport interface UseHydrationMetricsOptions {\n  /**\n   * Polling interval for metrics updates in milliseconds.\n   * Set to 0 to disable polling.\n   *\n   * @default 1000\n   */\n  readonly pollInterval?: number;\n\n  /**\n   * Maximum number of duration entries to keep in history.\n   *\n   * @default 50\n   */\n  readonly historySize?: number;\n\n  /**\n   * Whether to enable real-time updates via events.\n   *\n   * @default true\n   */\n  readonly realtime?: boolean;\n}\n\n// ============================================================================\n// Default Values\n// ============================================================================\n\n/**\n * Default metrics snapshot.\n */\nconst DEFAULT_METRICS: HydrationMetricsSnapshot = {\n  totalBoundaries: 0,\n  hydratedCount: 0,\n  pendingCount: 0,\n  failedCount: 0,\n  averageHydrationDuration: 0,\n  p95HydrationDuration: 0,\n  totalReplayedInteractions: 0,\n  timeToFullHydration: null,\n  timeToAboveFoldHydration: null,\n  queueSize: 0,\n  timestamp: Date.now(),\n};\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Calculates the hydration rate (boundaries per second).\n */\nfunction calculateHydrationRate(\n  currentCount: number,\n  previousCount: number,\n  intervalMs: number\n): number {\n  if (intervalMs <= 0) return 0;\n  const countDiff = currentCount - previousCount;\n  return (countDiff / intervalMs) * 1000;\n}\n\n/**\n * Estimates time remaining based on current rate.\n */\nfunction estimateTimeRemaining(\n  pendingCount: number,\n  rate: number\n): number | null {\n  if (rate <= 0 || pendingCount <= 0) return null;\n  return (pendingCount / rate) * 1000;\n}\n\n// ============================================================================\n// Hook Implementation\n// ============================================================================\n\n/**\n * Hook for accessing and monitoring hydration performance metrics.\n *\n * Provides real-time access to hydration metrics with computed values\n * for progress tracking, rate estimation, and performance analysis.\n *\n * @param options - Hook options\n * @returns Extended hydration metrics\n *\n * @example\n * ```tsx\n * // Basic metrics display\n * function MetricsDisplay() {\n *   const {\n *     hydrationProgress,\n *     averageHydrationDuration,\n *     isFullyHydrated,\n *   } = useHydrationMetrics();\n *\n *   return (\n *     <div>\n *       <progress value={hydrationProgress} max={100} />\n *       <span>{hydrationProgress.toFixed(0)}% hydrated</span>\n *       <span>Avg: {averageHydrationDuration.toFixed(0)}ms</span>\n *       {isFullyHydrated && <span>All components interactive!</span>}\n *     </div>\n *   );\n * }\n *\n * // Performance monitoring integration\n * function PerformanceMonitor() {\n *   const metrics = useHydrationMetrics({ pollInterval: 500 });\n *\n *   useEffect(() => {\n *     // Report to analytics when fully hydrated\n *     if (metrics.isFullyHydrated && metrics.timeToFullHydration) {\n *       analytics.track('full_hydration', {\n *         duration: metrics.timeToFullHydration,\n *         average: metrics.averageHydrationDuration,\n *         p95: metrics.p95HydrationDuration,\n *         totalBoundaries: metrics.totalBoundaries,\n *       });\n *     }\n *   }, [metrics.isFullyHydrated, metrics]);\n *\n *   return null;\n * }\n *\n * // Duration history chart\n * function DurationChart() {\n *   const { durationHistory, averageHydrationDuration } = useHydrationMetrics({\n *     historySize: 100,\n *   });\n *\n *   return (\n *     <LineChart\n *       data={durationHistory}\n *       average={averageHydrationDuration}\n *       label=\"Hydration Duration (ms)\"\n *     />\n *   );\n * }\n * ```\n */\nexport function useHydrationMetrics(\n  options: UseHydrationMetricsOptions = {}\n): UseHydrationMetricsReturn {\n  const { pollInterval = 1000, historySize = 50, realtime = true } = options;\n\n  // Get context (optional)\n  const context = useOptionalHydrationContext();\n\n  // ==========================================================================\n  // State\n  // ==========================================================================\n\n  const [metrics, setMetrics] = useState<HydrationMetricsSnapshot>(DEFAULT_METRICS);\n  const [durationHistory, setDurationHistory] = useState<number[]>([]);\n  const [hydrationRate, setHydrationRate] = useState(0);\n\n  // ==========================================================================\n  // Refs\n  // ==========================================================================\n\n  const previousCountRef = useRef(0);\n  const previousTimestampRef = useRef(0);\n\n  // Initialize timestamp ref on first mount\n  useEffect(() => {\n    if (previousTimestampRef.current === 0) {\n      previousTimestampRef.current = Date.now();\n    }\n  }, []);\n\n  // ==========================================================================\n  // Metrics Fetcher\n  // ==========================================================================\n\n  const fetchMetrics = useCallback(() => {\n    let newMetrics: HydrationMetricsSnapshot | undefined;\n\n    if (context) {\n      newMetrics = context.getMetrics();\n    } else {\n      try {\n        const scheduler = getHydrationScheduler();\n        newMetrics = scheduler.getMetrics();\n      } catch {\n        // Scheduler not available\n        return;\n      }\n    }\n\n    if (newMetrics !== null && newMetrics !== undefined) {\n      // Calculate hydration rate\n      const now = Date.now();\n      const intervalMs = now - previousTimestampRef.current;\n      const rate = calculateHydrationRate(\n        newMetrics.hydratedCount,\n        previousCountRef.current,\n        intervalMs\n      );\n\n      setHydrationRate(rate);\n      previousCountRef.current = newMetrics.hydratedCount;\n      previousTimestampRef.current = now;\n\n      setMetrics(newMetrics);\n    }\n  }, [context]);\n\n  // ==========================================================================\n  // Event Subscription\n  // ==========================================================================\n\n  useEffect(() => {\n    if (!realtime) {\n      return;\n    }\n\n    try {\n      const scheduler = getHydrationScheduler();\n\n      return scheduler.on('hydration:complete', (event) => {\n        // Update duration history\n        const payload = event.payload as { duration: number } | undefined;\n        if (payload?.duration !== null && payload?.duration !== undefined) {\n          setDurationHistory((prev) => {\n            const newHistory = [...prev, payload.duration];\n            // Keep only the last historySize entries\n            return newHistory.slice(-historySize);\n          });\n        }\n\n        // Refresh metrics\n        fetchMetrics();\n      });\n    } catch {\n      // Scheduler not available\n      return;\n    }\n  }, [realtime, historySize, fetchMetrics]);\n\n  // ==========================================================================\n  // Polling\n  // ==========================================================================\n\n  useEffect(() => {\n    // Initial fetch\n    fetchMetrics();\n\n    // Setup polling if enabled\n    if (pollInterval > 0) {\n      const intervalId = setInterval(fetchMetrics, pollInterval);\n      return () => clearInterval(intervalId);\n    }\n\n    return;\n  }, [fetchMetrics, pollInterval]);\n\n  // ==========================================================================\n  // Computed Values\n  // ==========================================================================\n\n\n\n\n  return useMemo<UseHydrationMetricsReturn>(() => {\n    const hydrationProgress =\n      metrics.totalBoundaries > 0 ? (metrics.hydratedCount / metrics.totalBoundaries) * 100 : 0;\n\n    const estimatedTimeRemaining = estimateTimeRemaining(metrics.pendingCount, hydrationRate);\n\n    const isFullyHydrated =\n      metrics.totalBoundaries > 0 && metrics.hydratedCount === metrics.totalBoundaries;\n\n    const isAboveFoldHydrated = metrics.timeToAboveFoldHydration !== null;\n\n    // Calculate replay success rate (assuming all replays succeed for now)\n    const replaySuccessRate = metrics.totalReplayedInteractions > 0 ? 1.0 : 0;\n\n    return {\n      ...metrics,\n      hydrationProgress,\n      estimatedTimeRemaining,\n      hydrationRate,\n      isFullyHydrated,\n      isAboveFoldHydrated,\n      replaySuccessRate,\n      durationHistory,\n      refresh: fetchMetrics,\n    };\n  }, [metrics, durationHistory, hydrationRate, fetchMetrics]);\n}\n\n/**\n * Hook for watching a specific metric value.\n *\n * Useful for triggering effects when metrics cross thresholds.\n *\n * @param selector - Function to select the metric value\n * @param options - Hook options\n * @returns The selected metric value\n *\n * @example\n * ```tsx\n * function ProgressWatcher() {\n *   const progress = useHydrationMetricValue((m) => m.hydrationProgress);\n *\n *   useEffect(() => {\n *     if (progress >= 50) {\n *       console.log('Half hydrated!');\n *     }\n *     if (progress >= 100) {\n *       console.log('Fully hydrated!');\n *     }\n *   }, [progress]);\n *\n *   return <span>Progress: {progress.toFixed(0)}%</span>;\n * }\n * ```\n */\nexport function useHydrationMetricValue<T>(\n  selector: (metrics: UseHydrationMetricsReturn) => T,\n  options?: UseHydrationMetricsOptions\n): T {\n  const metrics = useHydrationMetrics(options);\n  return selector(metrics);\n}\n\n/**\n * Hook for getting hydration progress.\n *\n * @returns Hydration progress percentage (0-100)\n *\n * @example\n * ```tsx\n * function ProgressBar() {\n *   const progress = useHydrationProgress();\n *   return <progress value={progress} max={100} />;\n * }\n * ```\n */\nexport function useHydrationProgress(): number {\n  return useHydrationMetricValue((m) => m.hydrationProgress);\n}\n\n/**\n * Hook for checking if hydration is complete.\n *\n * @returns true if all boundaries are hydrated\n *\n * @example\n * ```tsx\n * function LoadingIndicator() {\n *   const isComplete = useIsHydrationComplete();\n *\n *   if (isComplete) {\n *     return null;\n *   }\n *\n *   return <Spinner />;\n * }\n * ```\n */\nexport function useIsHydrationComplete(): boolean {\n  return useHydrationMetricValue((m) => m.isFullyHydrated);\n}\n\n/**\n * Hook for getting time to full hydration.\n *\n * @returns Time to full hydration in ms, or null if not complete\n *\n * @example\n * ```tsx\n * function HydrationTime() {\n *   const time = useTimeToFullHydration();\n *\n *   if (time === null) {\n *     return <span>Hydrating...</span>;\n *   }\n *\n *   return <span>Hydrated in {time.toFixed(0)}ms</span>;\n * }\n * ```\n */\nexport function useTimeToFullHydration(): number | null {\n  return useHydrationMetricValue((m) => m.timeToFullHydration);\n}\n\n// ============================================================================\n// Debug Hook\n// ============================================================================\n\n/**\n * Hook for debugging hydration metrics.\n *\n * Logs metrics to console on each update.\n *\n * @param label - Label for console output\n *\n * @example\n * ```tsx\n * function DebugComponent() {\n *   useHydrationMetricsDebug('HydrationMetrics');\n *   return <div>Check console for metrics</div>;\n * }\n * ```\n */\nexport function useHydrationMetricsDebug(label = 'HydrationMetrics'): void {\n  const metrics = useHydrationMetrics({ pollInterval: 500 });\n\n  useEffect(() => {\n    console.info(`[${label}]`, {\n      progress: `${metrics.hydrationProgress.toFixed(0)}%`,\n      hydrated: `${metrics.hydratedCount}/${metrics.totalBoundaries}`,\n      pending: metrics.pendingCount,\n      failed: metrics.failedCount,\n      avgDuration: `${metrics.averageHydrationDuration.toFixed(0)}ms`,\n      p95Duration: `${metrics.p95HydrationDuration.toFixed(0)}ms`,\n      rate: `${metrics.hydrationRate.toFixed(1)}/s`,\n      queueSize: metrics.queueSize,\n      timeToFull: metrics.timeToFullHydration !== null && metrics.timeToFullHydration !== undefined\n        ? `${metrics.timeToFullHydration.toFixed(0)}ms`\n        : 'pending',\n    });\n  }, [metrics, label]);\n}\n\n// ============================================================================\n// Exports\n// ============================================================================\n\n// Types are already exported inline above\n"],"names":["DEFAULT_METRICS","calculateHydrationRate","currentCount","previousCount","intervalMs","estimateTimeRemaining","pendingCount","rate","useHydrationMetrics","options","pollInterval","historySize","realtime","context","useOptionalHydrationContext","metrics","setMetrics","useState","durationHistory","setDurationHistory","hydrationRate","setHydrationRate","previousCountRef","useRef","previousTimestampRef","useEffect","fetchMetrics","useCallback","newMetrics","getHydrationScheduler","now","event","payload","prev","intervalId","useMemo","hydrationProgress","estimatedTimeRemaining","isFullyHydrated","isAboveFoldHydrated","replaySuccessRate","useHydrationMetricValue","selector","useHydrationProgress","m","useIsHydrationComplete","useTimeToFullHydration","useHydrationMetricsDebug","label"],"mappings":"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"}