{"version":3,"file":"useLiveInfiniteQuery.cjs","sources":["../../src/useLiveInfiniteQuery.ts"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from 'react'\nimport {\n  assertLiveQueryWindowManyResult,\n  compareLiveQueryWindowDependencies,\n  createLiveQueryCollection,\n  createLiveQueryWindowController,\n  fetchNextLiveQueryWindowPage,\n  getLiveQueryWindowCollectionWarning,\n  getLiveQueryWindowInputKind,\n  normalizeLiveQueryWindowPageSize,\n  resolveLiveQueryWindowInput,\n  shouldPreserveLiveQueryWindowPageCount,\n} from '@tanstack/db'\n// Type-only: used in `ReturnType<typeof useLiveQuery>` in UseLiveInfiniteQueryReturn.\nimport type { useLiveQuery } from './useLiveQuery'\nimport type {\n  Collection,\n  Context,\n  InferResultType,\n  InitialQueryBuilder,\n  LiveQueryWindowController,\n  NonSingleResult,\n  QueryBuilder,\n} from '@tanstack/db'\n\n// Live queries created here are cleaned up immediately (0 disables GC).\nconst DEFAULT_GC_TIME_MS = 1\n\nexport type UseLiveInfiniteQueryConfig<TContext extends Context> = {\n  pageSize?: number\n  initialPageParam?: number\n  /**\n   * @deprecated This callback is not used by the current implementation.\n   * Pagination is determined internally via a peek-ahead strategy.\n   * Provided for API compatibility with TanStack Query conventions.\n   */\n  getNextPageParam?: (\n    lastPage: Array<InferResultType<TContext>[number]>,\n    allPages: Array<Array<InferResultType<TContext>[number]>>,\n    lastPageParam: number,\n    allPageParams: Array<number>,\n  ) => number | undefined\n}\n\nexport type UseLiveInfiniteQueryReturn<TContext extends Context> = Omit<\n  ReturnType<typeof useLiveQuery<TContext>>,\n  `data`\n> & {\n  data: InferResultType<TContext>\n  pages: Array<Array<InferResultType<TContext>[number]>>\n  pageParams: Array<number>\n  fetchNextPage: () => Promise<void>\n  hasNextPage: boolean\n  isFetchingNextPage: boolean\n  error: unknown\n}\n\ntype EnabledLiveQueryReturn<TContext extends Context> = ReturnType<\n  typeof useLiveQuery<TContext>\n>\n\ntype InfiniteQueryRenderState = {\n  inputKind: `collection` | `query`\n  inputCollection: Collection<any, any, any> | null\n  dependencies: Array<unknown> | null\n  pageSize: number\n  initialPageParam: number\n  collection: Collection<any, any, any>\n  controller: LiveQueryWindowController<any, any>\n  warning: string | null\n  warned: boolean\n}\n\n/**\n * Create an infinite query using a query function with live updates\n *\n * Uses `utils.setWindow()` to dynamically adjust the limit/offset window\n * without recreating the live query collection on each page change.\n *\n * @param queryFn - Query function that defines what data to fetch. Must include `.orderBy()` for setWindow to work.\n * @param config - Configuration including pageSize and getNextPageParam\n * @param deps - Array of dependencies that trigger query re-execution when changed\n * @returns Object with pages, data, and pagination controls\n *\n * @example\n * // Basic infinite query\n * const { data, pages, fetchNextPage, hasNextPage } = useLiveInfiniteQuery(\n *   (q) => q\n *     .from({ posts: postsCollection })\n *     .orderBy(({ posts }) => posts.createdAt, 'desc')\n *     .select(({ posts }) => ({\n *       id: posts.id,\n *       title: posts.title\n *     })),\n *   {\n *     pageSize: 20,\n *     getNextPageParam: (lastPage, allPages) =>\n *       lastPage.length === 20 ? allPages.length : undefined\n *   }\n * )\n *\n * @example\n * // With dependencies\n * const { pages, fetchNextPage } = useLiveInfiniteQuery(\n *   (q) => q\n *     .from({ posts: postsCollection })\n *     .where(({ posts }) => eq(posts.category, category))\n *     .orderBy(({ posts }) => posts.createdAt, 'desc'),\n *   {\n *     pageSize: 10,\n *     getNextPageParam: (lastPage) =>\n *       lastPage.length === 10 ? lastPage.length : undefined\n *   },\n *   [category]\n * )\n *\n * @example\n * // Router loader pattern with pre-created collection\n * // In loader:\n * const postsQuery = createLiveQueryCollection({\n *   query: (q) => q\n *     .from({ posts: postsCollection })\n *     .orderBy(({ posts }) => posts.createdAt, 'desc')\n *     .limit(20)\n * })\n * await postsQuery.preload()\n * return { postsQuery }\n *\n * // In component:\n * const { postsQuery } = useLoaderData()\n * const { data, fetchNextPage, hasNextPage } = useLiveInfiniteQuery(\n *   postsQuery,\n *   {\n *     pageSize: 20,\n *     getNextPageParam: (lastPage) => lastPage.length === 20 ? lastPage.length : undefined\n *   }\n * )\n */\n\n// Overload for pre-created collection (non-single result)\nexport function useLiveInfiniteQuery<\n  TResult extends object,\n  TKey extends string | number,\n  TUtils extends Record<string, any>,\n>(\n  liveQueryCollection: Collection<TResult, TKey, TUtils> & NonSingleResult,\n  config: UseLiveInfiniteQueryConfig<any>,\n): UseLiveInfiniteQueryReturn<any>\n\n// Overload for query function\nexport function useLiveInfiniteQuery<TContext extends Context>(\n  queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,\n  config: UseLiveInfiniteQueryConfig<TContext>,\n  deps?: Array<unknown>,\n): UseLiveInfiniteQueryReturn<TContext>\n\n// Implementation\nexport function useLiveInfiniteQuery<TContext extends Context>(\n  queryFnOrCollection: any,\n  config: UseLiveInfiniteQueryConfig<TContext>,\n  deps: Array<unknown> = [],\n): UseLiveInfiniteQueryReturn<TContext> {\n  const pageSize = normalizeLiveQueryWindowPageSize(config.pageSize)\n  const initialPageParam = config.initialPageParam ?? 0\n\n  const inputIsCollection =\n    getLiveQueryWindowInputKind(queryFnOrCollection) === `collection`\n\n  const committedRef = useRef<InfiniteQueryRenderState | null>(null)\n  const committed = committedRef.current\n  const inputKind = inputIsCollection ? `collection` : `query`\n\n  const dependencyComparison = compareLiveQueryWindowDependencies(\n    committed?.dependencies,\n    deps,\n  )\n  const dependenciesChanged = !inputIsCollection && dependencyComparison.changed\n  const dependenciesStructurallyEqual =\n    !inputIsCollection && dependencyComparison.structurallyEqual\n  const needsNewCollection =\n    committed === null ||\n    committed.inputKind !== inputKind ||\n    (inputIsCollection && committed.inputCollection !== queryFnOrCollection) ||\n    dependenciesChanged\n  const pageShapeChanged =\n    committed === null ||\n    committed.pageSize !== pageSize ||\n    committed.initialPageParam !== initialPageParam\n  const needsNewController =\n    committed === null || needsNewCollection || pageShapeChanged\n\n  let renderState = committed\n  if (needsNewController) {\n    let collection = committed?.collection\n    let warning: string | null = null\n\n    if (needsNewCollection) {\n      const input = resolveLiveQueryWindowInput<TContext>(queryFnOrCollection)\n      if (input.kind === `collection`) {\n        collection = input.collection\n      } else {\n        // Wrap the query with the first page's peek-ahead window; the controller\n        // grows the limit from here via setWindow.\n        collection = createLiveQueryCollection({\n          query: input.query.limit(pageSize + 1).offset(0),\n          // Construction happens during render. Synchronization starts only when\n          // useSyncExternalStore commits the controller subscription.\n          startSync: false,\n          gcTime: DEFAULT_GC_TIME_MS,\n        })\n      }\n    }\n\n    if (!collection) {\n      throw new Error(`useLiveInfiniteQuery: Failed to create a collection.`)\n    }\n\n    if (inputIsCollection) {\n      warning =\n        getLiveQueryWindowCollectionWarning(collection, pageSize + 1) ?? null\n    } else {\n      assertLiveQueryWindowManyResult(collection)\n    }\n\n    const canPreservePageCount = shouldPreserveLiveQueryWindowPageCount({\n      hasPreviousController: committed !== null,\n      previousInputKind: committed?.inputKind,\n      inputKind,\n      sameCollection:\n        inputIsCollection && committed?.inputCollection === collection,\n      dependenciesChanged,\n      dependenciesStructurallyEqual,\n      pageShapeChanged,\n    })\n    const previousPageCount = committed\n      ? Math.max(1, committed.controller.getSnapshot().pages.length)\n      : 1\n    const initialPageCount = canPreservePageCount ? previousPageCount : 1\n    renderState = {\n      inputKind,\n      inputCollection: inputIsCollection ? collection : null,\n      dependencies: inputIsCollection ? null : [...deps],\n      pageSize,\n      initialPageParam,\n      collection,\n      controller: createLiveQueryWindowController(collection, {\n        pageSize,\n        initialPageParam,\n        initialPageCount,\n      }),\n      warning,\n      warned: false,\n    }\n  }\n  const currentRenderState = renderState!\n  const controller = currentRenderState.controller\n\n  const subscribe = useCallback(\n    (onStoreChange: () => void) => {\n      const unsubscribe = controller.subscribe(onStoreChange)\n      committedRef.current = currentRenderState\n      if (currentRenderState.warning && !currentRenderState.warned) {\n        currentRenderState.warned = true\n        console.warn(currentRenderState.warning)\n      }\n      return unsubscribe\n    },\n    [controller, currentRenderState],\n  )\n  const getSnapshot = useCallback(() => controller.getSnapshot(), [controller])\n  const snapshot = useSyncExternalStore(subscribe, getSnapshot)\n\n  const fetchNextPage = useCallback(\n    () => fetchNextLiveQueryWindowPage(controller),\n    [controller],\n  )\n\n  return {\n    data: snapshot.data as InferResultType<TContext>,\n    state: snapshot.state as EnabledLiveQueryReturn<TContext>[`state`],\n    status: snapshot.status as EnabledLiveQueryReturn<TContext>[`status`],\n    isLoading: snapshot.isLoading,\n    isReady: snapshot.isReady,\n    isIdle: snapshot.isIdle,\n    isError: snapshot.isError,\n    isCleanedUp: snapshot.isCleanedUp,\n    collection:\n      snapshot.collection as EnabledLiveQueryReturn<TContext>[`collection`],\n    isEnabled:\n      snapshot.isEnabled as EnabledLiveQueryReturn<TContext>[`isEnabled`],\n    pages: snapshot.pages as Array<Array<InferResultType<TContext>[number]>>,\n    pageParams: snapshot.pageParams as Array<number>,\n    fetchNextPage,\n    hasNextPage: snapshot.hasNextPage,\n    isFetchingNextPage: snapshot.isFetchingNextPage,\n    error: snapshot.error,\n  }\n}\n"],"names":["normalizeLiveQueryWindowPageSize","getLiveQueryWindowInputKind","useRef","compareLiveQueryWindowDependencies","resolveLiveQueryWindowInput","createLiveQueryCollection","getLiveQueryWindowCollectionWarning","assertLiveQueryWindowManyResult","shouldPreserveLiveQueryWindowPageCount","createLiveQueryWindowController","useCallback","useSyncExternalStore","fetchNextLiveQueryWindowPage"],"mappings":"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"}