{"version":3,"file":"useBuffer.mjs","sources":["../../../../src/lib/hooks/shared/useBuffer.ts"],"sourcesContent":["/**\n * @file useBuffer Hook\n * @description Generic buffering/batching hook for accumulating items and flushing based on size or time\n *\n * This hook is useful for scenarios like:\n * - Batching analytics events before sending\n * - Accumulating performance metrics\n * - Buffering real-time updates before rendering\n * - Debouncing multiple rapid updates into batches\n *\n * @example\n * ```tsx\n * function AnalyticsTracker() {\n *   const { add, flush } = useBuffer({\n *     maxSize: 10,\n *     flushInterval: 5000,\n *     onFlush: (events) => sendToAnalytics(events)\n *   });\n *\n *   const trackEvent = (event) => {\n *     add(event); // Automatically flushes when buffer reaches 10 or after 5s\n *   };\n *\n *   return <button onClick={() => trackEvent({ type: 'click' })}>Click Me</button>;\n * }\n * ```\n */\n\nimport { useRef, useCallback, useEffect } from 'react';\nimport { useLatestRef } from './useLatestRef';\n\n/**\n * Buffer configuration options\n */\nexport interface BufferOptions<T> {\n  /** Maximum buffer size before auto-flush */\n  maxSize?: number;\n\n  /** Time interval for auto-flush (ms) */\n  flushInterval?: number;\n\n  /** Callback when buffer is flushed */\n  onFlush?: (items: T[]) => void | Promise<void>;\n\n  /** Whether to flush on component unmount */\n  flushOnUnmount?: boolean;\n\n  /** Custom flush condition */\n  shouldFlush?: (buffer: T[]) => boolean;\n}\n\n/**\n * Buffer hook return type\n */\nexport interface UseBufferResult<T> {\n  /** Add item to buffer */\n  add: (item: T) => void;\n\n  /** Add multiple items to buffer */\n  addMany: (items: T[]) => void;\n\n  /** Manually flush buffer */\n  flush: () => void;\n\n  /** Clear buffer without flushing */\n  clear: () => void;\n\n  /** Get current buffer size */\n  size: () => number;\n\n  /** Get current buffer contents (read-only) */\n  peek: () => readonly T[];\n}\n\n/**\n * Hook for buffering items with automatic flushing\n *\n * @param options - Buffer configuration\n * @returns Buffer control interface\n */\nexport function useBuffer<T>(options: BufferOptions<T> = {}): UseBufferResult<T> {\n  const {\n    maxSize = 10,\n    flushInterval = 5000,\n    onFlush,\n    flushOnUnmount = true,\n    shouldFlush,\n  } = options;\n\n  const bufferRef = useRef<T[]>([]);\n  const timerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n  const onFlushRef = useLatestRef(onFlush);\n  const shouldFlushRef = useLatestRef(shouldFlush);\n\n  // Flush buffer and call callback\n  const flush = useCallback(() => {\n    if (bufferRef.current.length === 0) return;\n\n    const items = [...bufferRef.current];\n    bufferRef.current = [];\n\n    // Clear timer if exists\n    if (timerRef.current !== undefined && timerRef.current !== null) {\n      clearTimeout(timerRef.current);\n      timerRef.current = undefined;\n    }\n\n    // Call flush callback\n    void onFlushRef.current?.(items);\n  }, [onFlushRef]);\n\n  // Start flush timer\n  const startTimer = useCallback(() => {\n    if (timerRef.current !== undefined && timerRef.current !== null) {\n      clearTimeout(timerRef.current);\n    }\n\n    if (flushInterval > 0) {\n      timerRef.current = setTimeout(flush, flushInterval);\n    }\n  }, [flush, flushInterval]);\n\n  // Check if buffer should be flushed\n  const checkFlush = useCallback(() => {\n    const buffer = bufferRef.current;\n\n    // Check custom condition\n    if (shouldFlushRef.current?.(buffer) === true) {\n      flush();\n      return;\n    }\n\n    // Check size threshold\n    if (maxSize > 0 && buffer.length >= maxSize) {\n      flush();\n    }\n  }, [flush, maxSize, shouldFlushRef]);\n\n  // Add single item\n  const add = useCallback(\n    (item: T) => {\n      bufferRef.current.push(item);\n\n      // Start timer on first item\n      if (bufferRef.current.length === 1) {\n        startTimer();\n      }\n\n      checkFlush();\n    },\n    [checkFlush, startTimer]\n  );\n\n  // Add multiple items\n  const addMany = useCallback(\n    (items: T[]) => {\n      if (items.length === 0) return;\n\n      const wasEmpty = bufferRef.current.length === 0;\n      bufferRef.current.push(...items);\n\n      if (wasEmpty) {\n        startTimer();\n      }\n\n      checkFlush();\n    },\n    [checkFlush, startTimer]\n  );\n\n  // Clear buffer without flushing\n  const clear = useCallback(() => {\n    bufferRef.current = [];\n    if (timerRef.current !== undefined && timerRef.current !== null) {\n      clearTimeout(timerRef.current);\n      timerRef.current = undefined;\n    }\n  }, []);\n\n  // Get buffer size\n  const size = useCallback(() => bufferRef.current.length, []);\n\n  // Peek at buffer contents\n  const peek = useCallback(() => bufferRef.current as readonly T[], []);\n\n  // Cleanup on unmount\n  useEffect(() => {\n    // Capture onFlush reference at effect setup time\n    const capturedOnFlush = onFlushRef.current;\n    return () => {\n      if (timerRef.current !== undefined && timerRef.current !== null) {\n        clearTimeout(timerRef.current);\n      }\n      if (flushOnUnmount && bufferRef.current.length > 0) {\n        void capturedOnFlush?.(bufferRef.current);\n      }\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [flushOnUnmount]);\n\n  return {\n    add,\n    addMany,\n    flush,\n    clear,\n    size,\n    peek,\n  };\n}\n\n/**\n * Hook for buffering with time-based windows\n *\n * Flushes buffer at regular intervals regardless of size. Useful for\n * aggregating time-series data or periodic batch updates.\n *\n * @example\n * ```tsx\n * const { add } = useTimeWindowBuffer({\n *   windowSize: 1000, // 1 second windows\n *   onFlush: (items) => console.log(`${items.length} items in window`)\n * });\n * ```\n */\nexport function useTimeWindowBuffer<T>(\n  options: Omit<BufferOptions<T>, 'maxSize' | 'shouldFlush'> & {\n    windowSize: number;\n  }\n): UseBufferResult<T> {\n  return useBuffer({\n    ...options,\n    maxSize: Infinity,\n    flushInterval: options.windowSize,\n  });\n}\n\n/**\n * Hook for buffering with size-based batches only\n *\n * Flushes only when buffer reaches max size (no time-based flushing).\n * Useful for operations that must happen in specific batch sizes.\n *\n * @example\n * ```tsx\n * const { add } = useBatchBuffer({\n *   batchSize: 100,\n *   onFlush: (batch) => bulkInsert(batch)\n * });\n * ```\n */\nexport function useBatchBuffer<T>(\n  options: Omit<BufferOptions<T>, 'flushInterval' | 'shouldFlush'> & {\n    batchSize: number;\n  }\n): UseBufferResult<T> {\n  return useBuffer({\n    ...options,\n    maxSize: options.batchSize,\n    flushInterval: 0,\n  });\n}\n"],"names":["useBuffer","options","maxSize","flushInterval","onFlush","flushOnUnmount","shouldFlush","bufferRef","useRef","timerRef","onFlushRef","useLatestRef","shouldFlushRef","flush","useCallback","items","startTimer","checkFlush","buffer","add","item","addMany","wasEmpty","clear","size","peek","useEffect","capturedOnFlush","useTimeWindowBuffer","useBatchBuffer"],"mappings":"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"}