{"version":3,"file":"useAsyncQueuer.cjs","names":["useDefaultPacerOptions","AsyncQueuer","shallow"],"sources":["../../src/async-queuer/useAsyncQueuer.ts"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react'\nimport { AsyncQueuer } from '@tanstack/pacer/async-queuer'\nimport { shallow, useSelector } from '@tanstack/react-store'\nimport { useDefaultPacerOptions } from '../provider/PacerProvider'\nimport type { Store } from '@tanstack/react-store'\nimport type {\n  AsyncQueuerOptions,\n  AsyncQueuerState,\n} from '@tanstack/pacer/async-queuer'\nimport type { FunctionComponent, ReactNode } from 'react'\n\nexport interface ReactAsyncQueuerOptions<\n  TValue,\n  TSelected = {},\n> extends AsyncQueuerOptions<TValue> {\n  /**\n   * Optional callback invoked when the component unmounts. Receives the queuer instance.\n   * When provided, replaces the default cleanup (stop + abort); use it to call flush(), flushAsBatch(), stop(), add logging, etc.\n   */\n  onUnmount?: (queuer: ReactAsyncQueuer<TValue, TSelected>) => void\n}\n\nexport interface ReactAsyncQueuer<TValue, TSelected = {}> extends Omit<\n  AsyncQueuer<TValue>,\n  'store'\n> {\n  /**\n   * A React HOC (Higher Order Component) that allows you to subscribe to the queuer state.\n   *\n   * This is useful for opting into state re-renders for specific parts of the queuer state\n   * deep in your component tree without needing to pass a selector to the hook.\n   *\n   * @example\n   * <queuer.Subscribe selector={(state) => ({ size: state.size, isRunning: state.isRunning })}>\n   *   {({ size, isRunning }) => (\n   *     <div>Queue: {size} items, {isRunning ? 'Processing' : 'Idle'}</div>\n   *   )}\n   * </queuer.Subscribe>\n   */\n  Subscribe: <TSelected>(props: {\n    selector: (state: AsyncQueuerState<TValue>) => TSelected\n    children: ((state: TSelected) => ReactNode) | ReactNode\n  }) => ReturnType<FunctionComponent>\n  /**\n   * Reactive state that will be updated and re-rendered when the queuer state changes\n   *\n   * Use this instead of `queuer.store.state`\n   */\n  readonly state: Readonly<TSelected>\n  /**\n   * @deprecated Use `queuer.state` instead of `queuer.store.state` if you want to read reactive state.\n   * The state on the store object is not reactive, as it has not been wrapped in a `useSelector` hook internally.\n   * Although, you can make the state reactive by using the `useSelector` in your own usage.\n   */\n  readonly store: Store<Readonly<AsyncQueuerState<TValue>>>\n}\n\n/**\n * A lower-level React hook that creates an `AsyncQueuer` instance for managing an async queue of items.\n *\n * Features:\n * - Priority queue support via getPriority option\n * - Configurable concurrency limit\n * - Task success/error/completion callbacks\n * - FIFO (First In First Out) or LIFO (Last In First Out) queue behavior\n * - Pause/resume task processing\n * - Task cancellation\n * - Item expiration to clear stale items from the queue\n *\n * Tasks are processed concurrently up to the configured concurrency limit. When a task completes,\n * the next pending task is processed if below the concurrency limit.\n *\n * Error Handling:\n * - If an `onError` handler is provided, it will be called with the error and queuer instance\n * - If `throwOnError` is true (default when no onError handler is provided), the error will be thrown\n * - If `throwOnError` is false (default when onError handler is provided), the error will be swallowed\n * - Both onError and throwOnError can be used together - the handler will be called before any error is thrown\n * - The error state can be checked using the underlying AsyncQueuer instance\n *\n * ## State Management and Selector\n *\n * The hook uses TanStack Store for reactive state management. You can subscribe to state changes\n * in two ways:\n *\n * **1. Using `queuer.Subscribe` HOC (Recommended for component tree subscriptions)**\n *\n * Use the `Subscribe` HOC to subscribe to state changes deep in your component tree without\n * needing to pass a selector to the hook. This is ideal when you want to subscribe to state\n * in child components.\n *\n * **2. Using the `selector` parameter (For hook-level subscriptions)**\n *\n * The `selector` parameter allows you to specify which state changes will trigger a re-render\n * at the hook level, optimizing performance by preventing unnecessary re-renders when irrelevant\n * state changes occur.\n *\n * **By default, there will be no reactive state subscriptions** and you must opt-in to state\n * tracking by providing a selector function or using the `Subscribe` HOC. This prevents unnecessary\n * re-renders and gives you full control over when your component updates.\n *\n * Available state properties:\n * - `activeItems`: Items currently being processed by the queuer\n * - `errorCount`: Number of task executions that have resulted in errors\n * - `expirationCount`: Number of items that have been removed due to expiration\n * - `isEmpty`: Whether the queuer has no items to process\n * - `isFull`: Whether the queuer has reached its maximum capacity\n * - `isIdle`: Whether the queuer is not currently processing any items\n * - `isRunning`: Whether the queuer is active and will process items automatically\n * - `items`: Array of items currently waiting to be processed\n * - `itemTimestamps`: Timestamps when items were added for expiration tracking\n * - `lastResult`: The result from the most recent task execution\n * - `pendingTick`: Whether the queuer has a pending timeout for processing the next item\n * - `rejectionCount`: Number of items that have been rejected from being added\n * - `settledCount`: Number of task executions that have completed (success or error)\n * - `size`: Number of items currently in the queue\n * - `status`: Current processing status ('idle' | 'running' | 'stopped')\n * - `successCount`: Number of task executions that have completed successfully\n *\n * ## Unmount behavior\n *\n * By default, the hook stops the queuer and aborts any in-flight task executions when the component unmounts.\n * Abort only cancels underlying operations (e.g. fetch) when the abort signal from `getAbortSignal()` is passed to them.\n * Use the `onUnmount` option to customize this. For example, to flush pending items instead:\n *\n * ```tsx\n * const queuer = useAsyncQueuer(fn, {\n *   concurrency: 2,\n *   started: false,\n *   onUnmount: (q) => q.flush()\n * });\n * ```\n *\n * Note: For async utils, `flush()` returns a Promise and runs fire-and-forget in the cleanup.\n * If your task function updates React state, those updates may run after the component has\n * unmounted, which can cause \"setState on unmounted component\" warnings. Guard your callbacks\n * accordingly when using onUnmount with flush.\n *\n * @example\n * ```tsx\n * // Default behavior - no reactive state subscriptions\n * const asyncQueuer = useAsyncQueuer(\n *   async (item) => {\n *     const result = await processItem(item);\n *     return result;\n *   },\n *   { concurrency: 2, maxSize: 100, started: false }\n * );\n *\n * // Subscribe to state changes deep in component tree using Subscribe HOC\n * <asyncQueuer.Subscribe selector={(state) => ({ size: state.size, isRunning: state.isRunning })}>\n *   {({ size, isRunning }) => (\n *     <div>Queue: {size} items, {isRunning ? 'Processing' : 'Idle'}</div>\n *   )}\n * </asyncQueuer.Subscribe>\n *\n * // Opt-in to re-render when queue size changes at hook level (optimized for displaying queue length)\n * const asyncQueuer = useAsyncQueuer(\n *   async (item) => {\n *     const result = await processItem(item);\n *     return result;\n *   },\n *   { concurrency: 2, maxSize: 100, started: false },\n *   (state) => ({\n *     size: state.size,\n *     isEmpty: state.isEmpty,\n *     isFull: state.isFull\n *   })\n * );\n *\n * // Opt-in to re-render when processing state changes (optimized for loading indicators)\n * const asyncQueuer = useAsyncQueuer(\n *   async (item) => {\n *     const result = await processItem(item);\n *     return result;\n *   },\n *   { concurrency: 2, maxSize: 100, started: false },\n *   (state) => ({\n *     isRunning: state.isRunning,\n *     isIdle: state.isIdle,\n *     status: state.status,\n *     activeItems: state.activeItems,\n *     pendingTick: state.pendingTick\n *   })\n * );\n *\n * // Opt-in to re-render when execution metrics change (optimized for stats display)\n * const asyncQueuer = useAsyncQueuer(\n *   async (item) => {\n *     const result = await processItem(item);\n *     return result;\n *   },\n *   { concurrency: 2, maxSize: 100, started: false },\n *   (state) => ({\n *     successCount: state.successCount,\n *     errorCount: state.errorCount,\n *     settledCount: state.settledCount,\n *     expirationCount: state.expirationCount,\n *     rejectionCount: state.rejectionCount\n *   })\n * );\n *\n * // Opt-in to re-render when results are available (optimized for data display)\n * const asyncQueuer = useAsyncQueuer(\n *   async (item) => {\n *     const result = await processItem(item);\n *     return result;\n *   },\n *   {\n *     concurrency: 2,\n *     maxSize: 100,\n *     started: false,\n *     onSuccess: (result) => {\n *       console.log('Item processed:', result);\n *     },\n *     onError: (error) => {\n *       console.error('Processing failed:', error);\n *     }\n *   },\n *   (state) => ({\n *     lastResult: state.lastResult,\n *     successCount: state.successCount\n *   })\n * );\n *\n * // Add items to queue\n * asyncQueuer.addItem(newItem);\n *\n * // Start processing\n * asyncQueuer.start();\n *\n * // Access the selected state (will be empty object {} unless selector provided)\n * const { size, isRunning, activeItems } = asyncQueuer.state;\n * ```\n */\nexport function useAsyncQueuer<TValue, TSelected = {}>(\n  fn: (value: TValue) => Promise<any>,\n  options: ReactAsyncQueuerOptions<TValue, TSelected> = {},\n  selector: (state: AsyncQueuerState<TValue>) => TSelected = () =>\n    ({}) as TSelected,\n): ReactAsyncQueuer<TValue, TSelected> {\n  const mergedOptions = {\n    ...useDefaultPacerOptions().asyncQueuer,\n    ...options,\n  } as ReactAsyncQueuerOptions<TValue, TSelected>\n  const [asyncQueuer] = useState(() => {\n    const asyncQueuerInstance = new AsyncQueuer<TValue>(\n      fn,\n      mergedOptions,\n    ) as unknown as ReactAsyncQueuer<TValue, TSelected>\n\n    /* eslint-disable-next-line @eslint-react/component-hook-factories -- Subscribe attached once in useState lazy init; stable per instance */\n    asyncQueuerInstance.Subscribe = function Subscribe<TSelected>(props: {\n      selector: (state: AsyncQueuerState<TValue>) => TSelected\n      children: ((state: TSelected) => ReactNode) | ReactNode\n    }) {\n      const selected = useSelector(asyncQueuerInstance.store, props.selector, {\n        compare: shallow,\n      })\n\n      return typeof props.children === 'function'\n        ? props.children(selected)\n        : props.children\n    }\n\n    return asyncQueuerInstance\n  })\n\n  asyncQueuer.fn = fn\n  asyncQueuer.setOptions(mergedOptions)\n\n  /* eslint-disable react-hooks/exhaustive-deps, @eslint-react/exhaustive-deps, react-compiler/react-compiler -- unmount cleanup only; empty deps keep teardown stable */\n  useEffect(() => {\n    return () => {\n      if (mergedOptions.onUnmount) {\n        mergedOptions.onUnmount(asyncQueuer)\n      } else {\n        asyncQueuer.stop()\n        asyncQueuer.abort()\n      }\n    }\n  }, [])\n  /* eslint-enable react-hooks/exhaustive-deps, @eslint-react/exhaustive-deps, react-compiler/react-compiler */\n\n  const state = useSelector(asyncQueuer.store, selector, { compare: shallow })\n\n  return useMemo(\n    () =>\n      ({\n        ...asyncQueuer,\n        state,\n      }) as ReactAsyncQueuer<TValue, TSelected>, // omit `store` in favor of `state`\n    [asyncQueuer, state],\n  )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0OA,SAAgB,eACd,IACA,UAAsD,EAAE,EACxD,kBACG,EAAE,GACgC;CACrC,MAAM,gBAAgB;EACpB,GAAGA,8CAAwB,CAAC;EAC5B,GAAG;EACJ;CACD,MAAM,CAAC,yCAA8B;EACnC,MAAM,sBAAsB,IAAIC,yCAC9B,IACA,cACD;AAGD,sBAAoB,YAAY,SAAS,UAAqB,OAG3D;GACD,MAAM,kDAAuB,oBAAoB,OAAO,MAAM,UAAU,EACtE,SAASC,+BACV,CAAC;AAEF,UAAO,OAAO,MAAM,aAAa,aAC7B,MAAM,SAAS,SAAS,GACxB,MAAM;;AAGZ,SAAO;GACP;AAEF,aAAY,KAAK;AACjB,aAAY,WAAW,cAAc;AAGrC,4BAAgB;AACd,eAAa;AACX,OAAI,cAAc,UAChB,eAAc,UAAU,YAAY;QAC/B;AACL,gBAAY,MAAM;AAClB,gBAAY,OAAO;;;IAGtB,EAAE,CAAC;CAGN,MAAM,+CAAoB,YAAY,OAAO,UAAU,EAAE,SAASA,+BAAS,CAAC;AAE5E,kCAEK;EACC,GAAG;EACH;EACD,GACH,CAAC,aAAa,MAAM,CACrB"}