@tanstack/react-db 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +12 -0
  2. package/dist/cjs/DbProvider.cjs +23 -0
  3. package/dist/cjs/DbProvider.cjs.map +1 -0
  4. package/dist/cjs/DbProvider.d.cts +9 -0
  5. package/dist/cjs/HydrationBoundary.cjs +16 -0
  6. package/dist/cjs/HydrationBoundary.cjs.map +1 -0
  7. package/dist/cjs/HydrationBoundary.d.cts +7 -0
  8. package/dist/cjs/index.cjs +6 -0
  9. package/dist/cjs/index.cjs.map +1 -1
  10. package/dist/cjs/index.d.cts +4 -1
  11. package/dist/cjs/live-query-internals.cjs +19 -0
  12. package/dist/cjs/live-query-internals.cjs.map +1 -0
  13. package/dist/cjs/live-query-internals.d.cts +9 -0
  14. package/dist/cjs/useLiveInfiniteQuery.cjs +76 -8
  15. package/dist/cjs/useLiveInfiniteQuery.cjs.map +1 -1
  16. package/dist/cjs/useLiveInfiniteQuery.d.cts +12 -58
  17. package/dist/cjs/useLiveQuery.cjs +263 -41
  18. package/dist/cjs/useLiveQuery.cjs.map +1 -1
  19. package/dist/cjs/useLiveQuery.d.cts +79 -33
  20. package/dist/cjs/useLiveSuspenseQuery.cjs +19 -5
  21. package/dist/cjs/useLiveSuspenseQuery.cjs.map +1 -1
  22. package/dist/cjs/useLiveSuspenseQuery.d.cts +22 -19
  23. package/dist/esm/DbProvider.d.ts +9 -0
  24. package/dist/esm/DbProvider.js +23 -0
  25. package/dist/esm/DbProvider.js.map +1 -0
  26. package/dist/esm/HydrationBoundary.d.ts +7 -0
  27. package/dist/esm/HydrationBoundary.js +16 -0
  28. package/dist/esm/HydrationBoundary.js.map +1 -0
  29. package/dist/esm/index.d.ts +4 -1
  30. package/dist/esm/index.js +6 -0
  31. package/dist/esm/index.js.map +1 -1
  32. package/dist/esm/live-query-internals.d.ts +9 -0
  33. package/dist/esm/live-query-internals.js +19 -0
  34. package/dist/esm/live-query-internals.js.map +1 -0
  35. package/dist/esm/useLiveInfiniteQuery.d.ts +12 -58
  36. package/dist/esm/useLiveInfiniteQuery.js +76 -8
  37. package/dist/esm/useLiveInfiniteQuery.js.map +1 -1
  38. package/dist/esm/useLiveQuery.d.ts +79 -33
  39. package/dist/esm/useLiveQuery.js +265 -43
  40. package/dist/esm/useLiveQuery.js.map +1 -1
  41. package/dist/esm/useLiveSuspenseQuery.d.ts +22 -19
  42. package/dist/esm/useLiveSuspenseQuery.js +19 -5
  43. package/dist/esm/useLiveSuspenseQuery.js.map +1 -1
  44. package/package.json +2 -2
  45. package/skills/react-db/SKILL.md +107 -84
  46. package/src/DbProvider.tsx +32 -0
  47. package/src/HydrationBoundary.tsx +25 -0
  48. package/src/index.ts +8 -1
  49. package/src/live-query-internals.ts +36 -0
  50. package/src/useLiveInfiniteQuery.ts +119 -65
  51. package/src/useLiveQuery.ts +441 -92
  52. package/src/useLiveSuspenseQuery.ts +56 -29
package/README.md CHANGED
@@ -18,3 +18,15 @@
18
18
  # @tanstack/react-db
19
19
 
20
20
  React hooks for TanStack DB. See [TanStack/db](https://github.com/TanStack/db) for more details.
21
+
22
+ ```tsx
23
+ import { useLiveQuery } from '@tanstack/react-db'
24
+
25
+ function TodoList() {
26
+ const { data: todos } = useLiveQuery({
27
+ query: (q) => q.from({ todo: todoCollection }),
28
+ })
29
+
30
+ return todos.map((todo) => <div key={todo.id}>{todo.text}</div>)
31
+ }
32
+ ```
@@ -0,0 +1,23 @@
1
+ "use client";
2
+ "use strict";
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
+ const jsxRuntime = require("react/jsx-runtime");
5
+ const react = require("react");
6
+ const DbContext = react.createContext(void 0);
7
+ function DbProvider(props) {
8
+ return /* @__PURE__ */ jsxRuntime.jsx(DbContext.Provider, { value: props.client, children: props.children });
9
+ }
10
+ function useDbClient() {
11
+ const client = react.useContext(DbContext);
12
+ if (!client) {
13
+ throw new Error(`useDbClient must be used within a DbProvider.`);
14
+ }
15
+ return client;
16
+ }
17
+ function useOptionalDbClient() {
18
+ return react.useContext(DbContext);
19
+ }
20
+ exports.DbProvider = DbProvider;
21
+ exports.useDbClient = useDbClient;
22
+ exports.useOptionalDbClient = useOptionalDbClient;
23
+ //# sourceMappingURL=DbProvider.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DbProvider.cjs","sources":["../../src/DbProvider.tsx"],"sourcesContent":["'use client'\n\nimport { createContext, useContext } from 'react'\nimport type { DbClient } from '@tanstack/db'\nimport type { ReactNode } from 'react'\n\nconst DbContext = createContext<DbClient | undefined>(undefined)\n\nexport type DbProviderProps = {\n client: DbClient\n children?: ReactNode\n}\n\nexport function DbProvider(props: DbProviderProps) {\n return (\n <DbContext.Provider value={props.client}>\n {props.children}\n </DbContext.Provider>\n )\n}\n\nexport function useDbClient(): DbClient {\n const client = useContext(DbContext)\n if (!client) {\n throw new Error(`useDbClient must be used within a DbProvider.`)\n }\n return client\n}\n\nexport function useOptionalDbClient(): DbClient | undefined {\n return useContext(DbContext)\n}\n"],"names":[],"mappings":";;;;;AAMA;AAOO;AACL;AAKF;AAEO;AACL;AACA;AACE;AAA+D;AAEjE;AACF;AAEO;AACL;AACF;;;;"}
@@ -0,0 +1,9 @@
1
+ import { DbClient } from '@tanstack/db';
2
+ import { ReactNode } from 'react';
3
+ export type DbProviderProps = {
4
+ client: DbClient;
5
+ children?: ReactNode;
6
+ };
7
+ export declare function DbProvider(props: DbProviderProps): import("react/jsx-runtime").JSX.Element;
8
+ export declare function useDbClient(): DbClient;
9
+ export declare function useOptionalDbClient(): DbClient | undefined;
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ "use strict";
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
+ const react = require("react");
5
+ const DbProvider = require("./DbProvider.cjs");
6
+ function HydrationBoundary({ state, children }) {
7
+ const client = DbProvider.useDbClient();
8
+ const hydrated = react.useRef(void 0);
9
+ if (hydrated.current?.client !== client || hydrated.current.state !== state) {
10
+ client.hydrate(state);
11
+ hydrated.current = { client, state };
12
+ }
13
+ return children;
14
+ }
15
+ exports.HydrationBoundary = HydrationBoundary;
16
+ //# sourceMappingURL=HydrationBoundary.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HydrationBoundary.cjs","sources":["../../src/HydrationBoundary.tsx"],"sourcesContent":["'use client'\n\nimport { useRef } from 'react'\nimport { useDbClient } from './DbProvider'\nimport type { DehydratedDbState } from '@tanstack/db'\nimport type { ReactNode } from 'react'\n\nexport type HydrationBoundaryProps = {\n state: DehydratedDbState\n children?: ReactNode\n}\n\nexport function HydrationBoundary({ state, children }: HydrationBoundaryProps) {\n const client = useDbClient()\n const hydrated = useRef<\n { client: typeof client; state: DehydratedDbState } | undefined\n >(undefined)\n\n if (hydrated.current?.client !== client || hydrated.current.state !== state) {\n client.hydrate(state)\n hydrated.current = { client, state }\n }\n\n return children\n}\n"],"names":[],"mappings":";;;;;AAYO;AACL;AACA;AAIA;AACE;AACA;AAA6B;AAG/B;AACF;;"}
@@ -0,0 +1,7 @@
1
+ import { DehydratedDbState } from '@tanstack/db';
2
+ import { ReactNode } from 'react';
3
+ export type HydrationBoundaryProps = {
4
+ state: DehydratedDbState;
5
+ children?: ReactNode;
6
+ };
7
+ export declare function HydrationBoundary({ state, children }: HydrationBoundaryProps): ReactNode;
@@ -1,12 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const useLiveQuery = require("./useLiveQuery.cjs");
4
+ const DbProvider = require("./DbProvider.cjs");
5
+ const HydrationBoundary = require("./HydrationBoundary.cjs");
4
6
  const useLiveSuspenseQuery = require("./useLiveSuspenseQuery.cjs");
5
7
  const usePacedMutations = require("./usePacedMutations.cjs");
6
8
  const useLiveInfiniteQuery = require("./useLiveInfiniteQuery.cjs");
7
9
  const useLiveQueryEffect = require("./useLiveQueryEffect.cjs");
8
10
  const db = require("@tanstack/db");
9
11
  exports.useLiveQuery = useLiveQuery.useLiveQuery;
12
+ exports.DbProvider = DbProvider.DbProvider;
13
+ exports.useDbClient = DbProvider.useDbClient;
14
+ exports.useOptionalDbClient = DbProvider.useOptionalDbClient;
15
+ exports.HydrationBoundary = HydrationBoundary.HydrationBoundary;
10
16
  exports.useLiveSuspenseQuery = useLiveSuspenseQuery.useLiveSuspenseQuery;
11
17
  exports.usePacedMutations = usePacedMutations.usePacedMutations;
12
18
  exports.useLiveInfiniteQuery = useLiveInfiniteQuery.useLiveInfiniteQuery;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,4 +1,7 @@
1
- export * from './useLiveQuery.cjs';
1
+ export { useLiveQuery } from './useLiveQuery.cjs';
2
+ export type { LiveQueryKey, UseLiveQueryConfig, UseLiveQueryStatus, } from './useLiveQuery.cjs';
3
+ export * from './DbProvider.cjs';
4
+ export * from './HydrationBoundary.cjs';
2
5
  export * from './useLiveSuspenseQuery.cjs';
3
6
  export * from './usePacedMutations.cjs';
4
7
  export * from './useLiveInfiniteQuery.cjs';
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const liveQueryResultInfo = /* @__PURE__ */ Symbol(`liveQueryResultInfo`);
4
+ function setLiveQueryResultInfo(result, info) {
5
+ Object.defineProperty(result, liveQueryResultInfo, {
6
+ configurable: true,
7
+ value: info
8
+ });
9
+ }
10
+ function getLiveQueryResultInfo(result) {
11
+ const info = result[liveQueryResultInfo];
12
+ if (!info) {
13
+ throw new Error(`Missing internal live query result information.`);
14
+ }
15
+ return info;
16
+ }
17
+ exports.getLiveQueryResultInfo = getLiveQueryResultInfo;
18
+ exports.setLiveQueryResultInfo = setLiveQueryResultInfo;
19
+ //# sourceMappingURL=live-query-internals.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live-query-internals.cjs","sources":["../../src/live-query-internals.ts"],"sourcesContent":["import type {\n DbClient,\n LiveQueryObserver,\n UnhashableQueryIRError,\n} from '@tanstack/db'\n\nconst liveQueryResultInfo = Symbol(`liveQueryResultInfo`)\n\nexport type LiveQueryResultInfo = {\n client: DbClient | undefined\n queryHash: string | undefined\n identityError: UnhashableQueryIRError | undefined\n observer: LiveQueryObserver<object, string | number>\n}\n\ntype ResultWithInfo = {\n [liveQueryResultInfo]?: LiveQueryResultInfo\n}\n\nexport function setLiveQueryResultInfo(\n result: object,\n info: LiveQueryResultInfo,\n): void {\n Object.defineProperty(result, liveQueryResultInfo, {\n configurable: true,\n value: info,\n })\n}\n\nexport function getLiveQueryResultInfo(result: object): LiveQueryResultInfo {\n const info = (result as ResultWithInfo)[liveQueryResultInfo]\n if (!info) {\n throw new Error(`Missing internal live query result information.`)\n }\n return info\n}\n"],"names":[],"mappings":";;AAMA,MAAM,6CAA6B,qBAAqB;AAajD,SAAS,uBACd,QACA,MACM;AACN,SAAO,eAAe,QAAQ,qBAAqB;AAAA,IACjD,cAAc;AAAA,IACd,OAAO;AAAA,EAAA,CACR;AACH;AAEO,SAAS,uBAAuB,QAAqC;AAC1E,QAAM,OAAQ,OAA0B,mBAAmB;AAC3D,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,MAAM,iDAAiD;AAAA,EACnE;AACA,SAAO;AACT;;;"}
@@ -0,0 +1,9 @@
1
+ import { DbClient, LiveQueryObserver, UnhashableQueryIRError } from '@tanstack/db';
2
+ export type LiveQueryResultInfo = {
3
+ client: DbClient | undefined;
4
+ queryHash: string | undefined;
5
+ identityError: UnhashableQueryIRError | undefined;
6
+ observer: LiveQueryObserver<object, string | number>;
7
+ };
8
+ export declare function setLiveQueryResultInfo(result: object, info: LiveQueryResultInfo): void;
9
+ export declare function getLiveQueryResultInfo(result: object): LiveQueryResultInfo;
@@ -1,21 +1,70 @@
1
+ "use client";
1
2
  "use strict";
2
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
4
  const react = require("react");
4
5
  const db = require("@tanstack/db");
6
+ const DbProvider = require("./DbProvider.cjs");
7
+ const useLiveQuery = require("./useLiveQuery.cjs");
5
8
  const DEFAULT_GC_TIME_MS = 1;
6
- function useLiveInfiniteQuery(queryFnOrCollection, config, deps = []) {
9
+ const unpreparedQueryValue = /* @__PURE__ */ Symbol(`unpreparedQueryValue`);
10
+ function useLiveInfiniteQuery(queryFnOrCollection, config, deps) {
7
11
  const pageSize = db.normalizeLiveQueryWindowPageSize(config.pageSize);
8
12
  const initialPageParam = config.initialPageParam ?? 0;
13
+ const contextDbClient = DbProvider.useOptionalDbClient();
14
+ const dbClient = config.client ?? contextDbClient;
9
15
  const inputIsCollection = db.getLiveQueryWindowInputKind(queryFnOrCollection) === `collection`;
10
16
  const committedRef = react.useRef(null);
11
17
  const committed = committedRef.current;
12
18
  const inputKind = inputIsCollection ? `collection` : `query`;
19
+ const derivedIdentityProfilerRef = react.useRef({
20
+ renderCount: 0,
21
+ totalMs: 0,
22
+ maxMs: 0,
23
+ warned: false
24
+ });
25
+ const legacyUnhashableIdentityRef = react.useRef([
26
+ `legacy-unhashable`
27
+ ]);
28
+ const deferredCollections = /* @__PURE__ */ new Set();
29
+ let preparedQueryValue = unpreparedQueryValue;
30
+ let identityDeps = [];
31
+ let identityMode = `collection`;
32
+ if (!inputIsCollection) {
33
+ if (config.queryKey !== void 0) {
34
+ identityMode = `queryKey`;
35
+ identityDeps = config.queryKey;
36
+ } else if (deps !== void 0) {
37
+ identityMode = `legacyDeps`;
38
+ identityDeps = deps;
39
+ useLiveQuery.warnDeprecatedDepsArray(`useLiveInfiniteQuery`);
40
+ } else if (committed?.identityMode === `derived` && committed.inputQuery === queryFnOrCollection && committed.client === dbClient) {
41
+ identityMode = `derived`;
42
+ identityDeps = committed.dependencies ?? [];
43
+ } else {
44
+ identityMode = `derived`;
45
+ const preparation = useLiveQuery.prepareDerivedQuery(
46
+ queryFnOrCollection,
47
+ dbClient,
48
+ derivedIdentityProfilerRef.current,
49
+ deferredCollections
50
+ );
51
+ preparedQueryValue = preparation.value;
52
+ if (preparation.status === `hashable`) {
53
+ identityDeps = preparation.identityDeps;
54
+ } else {
55
+ useLiveQuery.warnUnhashableDerivedIdentity(preparation.error);
56
+ identityDeps = legacyUnhashableIdentityRef.current;
57
+ }
58
+ }
59
+ }
60
+ const usesLegacyDeps = !inputIsCollection && config.queryKey === void 0 && deps !== void 0;
13
61
  const dependencyComparison = db.compareLiveQueryWindowDependencies(
14
62
  committed?.dependencies,
15
- deps
63
+ identityDeps
16
64
  );
17
- const dependenciesChanged = !inputIsCollection && dependencyComparison.changed;
18
- const dependenciesStructurallyEqual = !inputIsCollection && dependencyComparison.structurallyEqual;
65
+ const sameClient = committed?.client === dbClient;
66
+ const dependenciesChanged = !inputIsCollection && (!sameClient || (usesLegacyDeps ? dependencyComparison.changed : !dependencyComparison.structurallyEqual));
67
+ const dependenciesStructurallyEqual = usesLegacyDeps && sameClient && dependencyComparison.structurallyEqual;
19
68
  const needsNewCollection = committed === null || committed.inputKind !== inputKind || inputIsCollection && committed.inputCollection !== queryFnOrCollection || dependenciesChanged;
20
69
  const pageShapeChanged = committed === null || committed.pageSize !== pageSize || committed.initialPageParam !== initialPageParam;
21
70
  const needsNewController = committed === null || needsNewCollection || pageShapeChanged;
@@ -24,7 +73,18 @@ function useLiveInfiniteQuery(queryFnOrCollection, config, deps = []) {
24
73
  let collection = committed?.collection;
25
74
  let warning = null;
26
75
  if (needsNewCollection) {
27
- const input = db.resolveLiveQueryWindowInput(queryFnOrCollection);
76
+ let inputValue = queryFnOrCollection;
77
+ if (!inputIsCollection) {
78
+ if (preparedQueryValue === unpreparedQueryValue) {
79
+ preparedQueryValue = useLiveQuery.prepareQueryValue(
80
+ queryFnOrCollection,
81
+ dbClient,
82
+ deferredCollections
83
+ );
84
+ }
85
+ inputValue = () => preparedQueryValue;
86
+ }
87
+ const input = db.resolveLiveQueryWindowInput(inputValue);
28
88
  if (input.kind === `collection`) {
29
89
  collection = input.collection;
30
90
  } else {
@@ -59,7 +119,10 @@ function useLiveInfiniteQuery(queryFnOrCollection, config, deps = []) {
59
119
  renderState = {
60
120
  inputKind,
61
121
  inputCollection: inputIsCollection ? collection : null,
62
- dependencies: inputIsCollection ? null : [...deps],
122
+ inputQuery: inputIsCollection ? null : queryFnOrCollection,
123
+ client: dbClient,
124
+ identityMode,
125
+ dependencies: inputIsCollection ? null : [...identityDeps],
63
126
  pageSize,
64
127
  initialPageParam,
65
128
  collection,
@@ -69,7 +132,8 @@ function useLiveInfiniteQuery(queryFnOrCollection, config, deps = []) {
69
132
  initialPageCount
70
133
  }),
71
134
  warning,
72
- warned: false
135
+ warned: false,
136
+ deferredCollections
73
137
  };
74
138
  }
75
139
  const currentRenderState = renderState;
@@ -82,12 +146,16 @@ function useLiveInfiniteQuery(queryFnOrCollection, config, deps = []) {
82
146
  currentRenderState.warned = true;
83
147
  console.warn(currentRenderState.warning);
84
148
  }
149
+ for (const collection of currentRenderState.deferredCollections) {
150
+ collection._resumeSyncStart();
151
+ }
152
+ currentRenderState.deferredCollections.clear();
85
153
  return unsubscribe;
86
154
  },
87
155
  [controller, currentRenderState]
88
156
  );
89
157
  const getSnapshot = react.useCallback(() => controller.getSnapshot(), [controller]);
90
- const snapshot = react.useSyncExternalStore(subscribe, getSnapshot);
158
+ const snapshot = react.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
91
159
  const fetchNextPage = react.useCallback(
92
160
  () => db.fetchNextLiveQueryWindowPage(controller),
93
161
  [controller]
@@ -1 +1 @@
1
- {"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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"}
1
+ {"version":3,"file":"useLiveInfiniteQuery.cjs","sources":["../../src/useLiveInfiniteQuery.ts"],"sourcesContent":["'use client'\n\nimport { 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'\nimport { useOptionalDbClient } from './DbProvider'\nimport {\n prepareDerivedQuery,\n prepareQueryValue,\n warnDeprecatedDepsArray,\n warnUnhashableDerivedIdentity,\n} from './useLiveQuery'\nimport type {\n DerivedIdentityProfiler,\n LiveQueryKey,\n useLiveQuery,\n} from './useLiveQuery'\nimport type {\n Collection,\n CollectionImpl as CollectionImplType,\n Context,\n DbClient,\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\nconst unpreparedQueryValue = Symbol(`unpreparedQueryValue`)\n\nexport type UseLiveInfiniteQueryConfig<TContext extends Context> = {\n /**\n * Explicit identity for queries that contain opaque functional variants or\n * are hot enough that deriving identity from structured IR is too expensive.\n * Structured queries should omit this so DB can derive identity directly.\n */\n queryKey?: LiveQueryKey\n /** Override the nearest DbProvider for this query. */\n client?: DbClient\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 inputQuery: unknown\n client: DbClient | undefined\n identityMode: `collection` | `queryKey` | `legacyDeps` | `derived`\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 deferredCollections: Set<\n CollectionImplType<any, string | number, any, any, any>\n >\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 - Deprecated array of dependencies that trigger query re-execution when changed\n * @returns Object with pages, data, and pagination controls\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 const contextDbClient = useOptionalDbClient()\n const dbClient = config.client ?? contextDbClient\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 const derivedIdentityProfilerRef = useRef<DerivedIdentityProfiler>({\n renderCount: 0,\n totalMs: 0,\n maxMs: 0,\n warned: false,\n })\n const legacyUnhashableIdentityRef = useRef<Array<unknown>>([\n `legacy-unhashable`,\n ])\n const deferredCollections = new Set<\n CollectionImplType<any, string | number, any, any, any>\n >()\n\n let preparedQueryValue: unknown | typeof unpreparedQueryValue =\n unpreparedQueryValue\n let identityDeps: ReadonlyArray<unknown> = []\n let identityMode: InfiniteQueryRenderState[`identityMode`] = `collection`\n\n if (!inputIsCollection) {\n if (config.queryKey !== undefined) {\n identityMode = `queryKey`\n identityDeps = config.queryKey\n } else if (deps !== undefined) {\n identityMode = `legacyDeps`\n identityDeps = deps\n warnDeprecatedDepsArray(`useLiveInfiniteQuery`)\n } else if (\n committed?.identityMode === `derived` &&\n committed.inputQuery === queryFnOrCollection &&\n committed.client === dbClient\n ) {\n identityMode = `derived`\n identityDeps = committed.dependencies ?? []\n } else {\n identityMode = `derived`\n const preparation = prepareDerivedQuery(\n queryFnOrCollection,\n dbClient,\n derivedIdentityProfilerRef.current,\n deferredCollections,\n )\n preparedQueryValue = preparation.value\n if (preparation.status === `hashable`) {\n identityDeps = preparation.identityDeps\n } else {\n warnUnhashableDerivedIdentity(preparation.error)\n identityDeps = legacyUnhashableIdentityRef.current\n }\n }\n }\n\n const usesLegacyDeps =\n !inputIsCollection && config.queryKey === undefined && deps !== undefined\n const dependencyComparison = compareLiveQueryWindowDependencies(\n committed?.dependencies,\n identityDeps,\n )\n const sameClient = committed?.client === dbClient\n const dependenciesChanged =\n !inputIsCollection &&\n (!sameClient ||\n (usesLegacyDeps\n ? dependencyComparison.changed\n : !dependencyComparison.structurallyEqual))\n const dependenciesStructurallyEqual =\n usesLegacyDeps && sameClient && 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 let inputValue = queryFnOrCollection\n if (!inputIsCollection) {\n if (preparedQueryValue === unpreparedQueryValue) {\n preparedQueryValue = prepareQueryValue(\n queryFnOrCollection,\n dbClient,\n deferredCollections,\n )\n }\n inputValue = () => preparedQueryValue\n }\n const input = resolveLiveQueryWindowInput<TContext>(inputValue)\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 inputQuery: inputIsCollection ? null : queryFnOrCollection,\n client: dbClient,\n identityMode,\n dependencies: inputIsCollection ? null : [...identityDeps],\n pageSize,\n initialPageParam,\n collection,\n controller: createLiveQueryWindowController(collection, {\n pageSize,\n initialPageParam,\n initialPageCount,\n }),\n warning,\n warned: false,\n deferredCollections,\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 for (const collection of currentRenderState.deferredCollections) {\n collection._resumeSyncStart()\n }\n currentRenderState.deferredCollections.clear()\n return unsubscribe\n },\n [controller, currentRenderState],\n )\n const getSnapshot = useCallback(() => controller.getSnapshot(), [controller])\n const snapshot = useSyncExternalStore(subscribe, getSnapshot, 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":["warnDeprecatedDepsArray","prepareDerivedQuery","warnUnhashableDerivedIdentity","compareLiveQueryWindowDependencies","prepareQueryValue","assertLiveQueryWindowManyResult","useCallback"],"mappings":";;;;;;;AAwCA;AACA;AA2FO;AAKL;AACA;AACA;AACA;AAEA;AAGA;AACA;AACA;AACA;AAAmE;AACpD;AACJ;AACF;AACC;AAEV;AAA2D;AACzD;AAEF;AAIA;AAEA;AACA;AAEA;AACE;AACE;AACA;AAAsB;AAEtB;AACA;AACAA;AAA8C;AAM9C;AACA;AAAyC;AAEzC;AACA;AAAoBC;AAClB;AACA;AAC2B;AAC3B;AAEF;AACA;AACE;AAA2B;AAE3BC;AACA;AAA2C;AAC7C;AACF;AAGF;AAEA;AAA6BC;AAChB;AACX;AAEF;AACA;AAMA;AAEA;AAKA;AAIA;AAGA;AACA;AACE;AACA;AAEA;AACE;AACA;AACE;AACE;AAAqBC;AACnB;AACA;AACA;AAAA;AAGJ;AAAmB;AAErB;AACA;AACE;AAAmB;AAInB;AAAuC;AACU;AAAA;AAAA;AAGpC;AACH;AACT;AACH;AAGF;AACE;AAAsE;AAGxE;AACE;AACmE;AAEnEC;AAA0C;AAG5C;AAAoE;AAC7B;AACP;AAC9B;AAEsD;AACtD;AACA;AACA;AAEF;AAGA;AACA;AAAc;AACZ;AACkD;AACX;AAC/B;AACR;AACyD;AACzD;AACA;AACA;AACwD;AACtD;AACA;AACA;AACD;AACD;AACQ;AACR;AAAA;AAGJ;AACA;AAEA;AAAkBC;AAEd;AACA;AACA;AACE;AACA;AAAuC;AAEzC;AACE;AAAW;AAEb;AACA;AAAO;AACT;AAC+B;AAEjC;AACA;AAEA;AAAsBA;AACyB;AAClC;AAGb;AAAO;AACU;AACC;AACC;AACG;AACF;AACD;AACC;AACI;AAEX;AAEA;AACK;AACK;AACrB;AACsB;AACO;AACb;AAEpB;;"}
@@ -1,6 +1,14 @@
1
- import { useLiveQuery } from './useLiveQuery.cjs';
2
- import { Collection, Context, InferResultType, InitialQueryBuilder, NonSingleResult, QueryBuilder } from '@tanstack/db';
1
+ import { LiveQueryKey, useLiveQuery } from './useLiveQuery.cjs';
2
+ import { Collection, Context, DbClient, InferResultType, InitialQueryBuilder, NonSingleResult, QueryBuilder } from '@tanstack/db';
3
3
  export type UseLiveInfiniteQueryConfig<TContext extends Context> = {
4
+ /**
5
+ * Explicit identity for queries that contain opaque functional variants or
6
+ * are hot enough that deriving identity from structured IR is too expensive.
7
+ * Structured queries should omit this so DB can derive identity directly.
8
+ */
9
+ queryKey?: LiveQueryKey;
10
+ /** Override the nearest DbProvider for this query. */
11
+ client?: DbClient;
4
12
  pageSize?: number;
5
13
  initialPageParam?: number;
6
14
  /**
@@ -20,69 +28,15 @@ export type UseLiveInfiniteQueryReturn<TContext extends Context> = Omit<ReturnTy
20
28
  error: unknown;
21
29
  };
22
30
  /**
23
- * Create an infinite query using a query function with live updates
31
+ * Create an infinite query using a query function with live updates.
24
32
  *
25
33
  * Uses `utils.setWindow()` to dynamically adjust the limit/offset window
26
34
  * without recreating the live query collection on each page change.
27
35
  *
28
36
  * @param queryFn - Query function that defines what data to fetch. Must include `.orderBy()` for setWindow to work.
29
37
  * @param config - Configuration including pageSize and getNextPageParam
30
- * @param deps - Array of dependencies that trigger query re-execution when changed
38
+ * @param deps - Deprecated array of dependencies that trigger query re-execution when changed
31
39
  * @returns Object with pages, data, and pagination controls
32
- *
33
- * @example
34
- * // Basic infinite query
35
- * const { data, pages, fetchNextPage, hasNextPage } = useLiveInfiniteQuery(
36
- * (q) => q
37
- * .from({ posts: postsCollection })
38
- * .orderBy(({ posts }) => posts.createdAt, 'desc')
39
- * .select(({ posts }) => ({
40
- * id: posts.id,
41
- * title: posts.title
42
- * })),
43
- * {
44
- * pageSize: 20,
45
- * getNextPageParam: (lastPage, allPages) =>
46
- * lastPage.length === 20 ? allPages.length : undefined
47
- * }
48
- * )
49
- *
50
- * @example
51
- * // With dependencies
52
- * const { pages, fetchNextPage } = useLiveInfiniteQuery(
53
- * (q) => q
54
- * .from({ posts: postsCollection })
55
- * .where(({ posts }) => eq(posts.category, category))
56
- * .orderBy(({ posts }) => posts.createdAt, 'desc'),
57
- * {
58
- * pageSize: 10,
59
- * getNextPageParam: (lastPage) =>
60
- * lastPage.length === 10 ? lastPage.length : undefined
61
- * },
62
- * [category]
63
- * )
64
- *
65
- * @example
66
- * // Router loader pattern with pre-created collection
67
- * // In loader:
68
- * const postsQuery = createLiveQueryCollection({
69
- * query: (q) => q
70
- * .from({ posts: postsCollection })
71
- * .orderBy(({ posts }) => posts.createdAt, 'desc')
72
- * .limit(20)
73
- * })
74
- * await postsQuery.preload()
75
- * return { postsQuery }
76
- *
77
- * // In component:
78
- * const { postsQuery } = useLoaderData()
79
- * const { data, fetchNextPage, hasNextPage } = useLiveInfiniteQuery(
80
- * postsQuery,
81
- * {
82
- * pageSize: 20,
83
- * getNextPageParam: (lastPage) => lastPage.length === 20 ? lastPage.length : undefined
84
- * }
85
- * )
86
40
  */
87
41
  export declare function useLiveInfiniteQuery<TResult extends object, TKey extends string | number, TUtils extends Record<string, any>>(liveQueryCollection: Collection<TResult, TKey, TUtils> & NonSingleResult, config: UseLiveInfiniteQueryConfig<any>): UseLiveInfiniteQueryReturn<any>;
88
42
  export declare function useLiveInfiniteQuery<TContext extends Context>(queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>, config: UseLiveInfiniteQueryConfig<TContext>, deps?: Array<unknown>): UseLiveInfiniteQueryReturn<TContext>;