@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.
- package/README.md +12 -0
- package/dist/cjs/DbProvider.cjs +23 -0
- package/dist/cjs/DbProvider.cjs.map +1 -0
- package/dist/cjs/DbProvider.d.cts +9 -0
- package/dist/cjs/HydrationBoundary.cjs +16 -0
- package/dist/cjs/HydrationBoundary.cjs.map +1 -0
- package/dist/cjs/HydrationBoundary.d.cts +7 -0
- package/dist/cjs/index.cjs +6 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +4 -1
- package/dist/cjs/live-query-internals.cjs +19 -0
- package/dist/cjs/live-query-internals.cjs.map +1 -0
- package/dist/cjs/live-query-internals.d.cts +9 -0
- package/dist/cjs/useLiveInfiniteQuery.cjs +76 -8
- package/dist/cjs/useLiveInfiniteQuery.cjs.map +1 -1
- package/dist/cjs/useLiveInfiniteQuery.d.cts +12 -58
- package/dist/cjs/useLiveQuery.cjs +263 -41
- package/dist/cjs/useLiveQuery.cjs.map +1 -1
- package/dist/cjs/useLiveQuery.d.cts +79 -33
- package/dist/cjs/useLiveSuspenseQuery.cjs +19 -5
- package/dist/cjs/useLiveSuspenseQuery.cjs.map +1 -1
- package/dist/cjs/useLiveSuspenseQuery.d.cts +22 -19
- package/dist/esm/DbProvider.d.ts +9 -0
- package/dist/esm/DbProvider.js +23 -0
- package/dist/esm/DbProvider.js.map +1 -0
- package/dist/esm/HydrationBoundary.d.ts +7 -0
- package/dist/esm/HydrationBoundary.js +16 -0
- package/dist/esm/HydrationBoundary.js.map +1 -0
- package/dist/esm/index.d.ts +4 -1
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/live-query-internals.d.ts +9 -0
- package/dist/esm/live-query-internals.js +19 -0
- package/dist/esm/live-query-internals.js.map +1 -0
- package/dist/esm/useLiveInfiniteQuery.d.ts +12 -58
- package/dist/esm/useLiveInfiniteQuery.js +76 -8
- package/dist/esm/useLiveInfiniteQuery.js.map +1 -1
- package/dist/esm/useLiveQuery.d.ts +79 -33
- package/dist/esm/useLiveQuery.js +265 -43
- package/dist/esm/useLiveQuery.js.map +1 -1
- package/dist/esm/useLiveSuspenseQuery.d.ts +22 -19
- package/dist/esm/useLiveSuspenseQuery.js +19 -5
- package/dist/esm/useLiveSuspenseQuery.js.map +1 -1
- package/package.json +2 -2
- package/skills/react-db/SKILL.md +107 -84
- package/src/DbProvider.tsx +32 -0
- package/src/HydrationBoundary.tsx +25 -0
- package/src/index.ts +8 -1
- package/src/live-query-internals.ts +36 -0
- package/src/useLiveInfiniteQuery.ts +119 -65
- package/src/useLiveQuery.ts +441 -92
- package/src/useLiveSuspenseQuery.ts +56 -29
|
@@ -1,12 +1,15 @@
|
|
|
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 useLiveQuery = require("./useLiveQuery.cjs");
|
|
5
|
-
|
|
6
|
+
const liveQueryInternals = require("./live-query-internals.cjs");
|
|
7
|
+
function useLiveSuspenseQuery(configOrQueryOrCollection, deps) {
|
|
6
8
|
const promiseRef = react.useRef(null);
|
|
7
9
|
const collectionRef = react.useRef(null);
|
|
8
10
|
const hasBeenReadyRef = react.useRef(false);
|
|
9
|
-
const result = useLiveQuery.useLiveQuery(configOrQueryOrCollection, deps);
|
|
11
|
+
const result = deps === void 0 ? useLiveQuery.useLiveQuery(configOrQueryOrCollection) : useLiveQuery.useLiveQuery(configOrQueryOrCollection, deps);
|
|
12
|
+
const queryInfo = liveQueryInternals.getLiveQueryResultInfo(result);
|
|
10
13
|
if (collectionRef.current !== result.collection) {
|
|
11
14
|
promiseRef.current = null;
|
|
12
15
|
collectionRef.current = result.collection;
|
|
@@ -18,17 +21,28 @@ function useLiveSuspenseQuery(configOrQueryOrCollection, deps = []) {
|
|
|
18
21
|
);
|
|
19
22
|
}
|
|
20
23
|
const collectionStatus = result.collection.status;
|
|
21
|
-
if (
|
|
24
|
+
if (result.isReady) {
|
|
22
25
|
hasBeenReadyRef.current = true;
|
|
23
26
|
promiseRef.current = null;
|
|
24
27
|
}
|
|
28
|
+
const observerError = queryInfo.observer.getError();
|
|
29
|
+
if (observerError !== void 0 && !hasBeenReadyRef.current) {
|
|
30
|
+
promiseRef.current = null;
|
|
31
|
+
throw observerError;
|
|
32
|
+
}
|
|
25
33
|
if (collectionStatus === `error` && !hasBeenReadyRef.current) {
|
|
26
34
|
promiseRef.current = null;
|
|
27
35
|
throw new Error(`Collection "${result.collection.id}" failed to load`);
|
|
28
36
|
}
|
|
29
|
-
if (
|
|
37
|
+
if (!hasBeenReadyRef.current && (result.isLoading || result.isIdle)) {
|
|
38
|
+
if (queryInfo.client?._isSsrStreamingEnabled() && !queryInfo.queryHash) {
|
|
39
|
+
const reason = queryInfo.identityError ? `${queryInfo.identityError.reason} at ${queryInfo.identityError.path}` : `the query has no stable identity`;
|
|
40
|
+
throw new Error(
|
|
41
|
+
`Cannot stream this live query during SSR because ${reason}. Provide an explicit serializable queryKey.`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
30
44
|
if (!promiseRef.current) {
|
|
31
|
-
promiseRef.current =
|
|
45
|
+
promiseRef.current = queryInfo.observer.preload();
|
|
32
46
|
}
|
|
33
47
|
throw promiseRef.current;
|
|
34
48
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLiveSuspenseQuery.cjs","sources":["../../src/useLiveSuspenseQuery.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"useLiveSuspenseQuery.cjs","sources":["../../src/useLiveSuspenseQuery.ts"],"sourcesContent":["'use client'\n\nimport { useRef } from 'react'\nimport { useLiveQuery } from './useLiveQuery'\nimport { getLiveQueryResultInfo } from './live-query-internals'\nimport type { UseLiveQueryConfig } from './useLiveQuery'\nimport type {\n Collection,\n Context,\n GetResult,\n InferResultType,\n InitialQueryBuilder,\n LiveQueryCollectionConfig,\n NonSingleResult,\n QueryBuilder,\n SingleResult,\n} from '@tanstack/db'\n\n/**\n * Create a live query with React Suspense support\n * @param queryFn - Query function that defines what data to fetch\n * @param deps - Deprecated array of dependencies that trigger query re-execution when changed\n * @returns Object with reactive data and state - data is guaranteed to be defined\n * @throws Promise when data is loading (caught by Suspense boundary)\n * @throws Error when collection fails (caught by Error boundary)\n * @example\n * // Basic usage with Suspense\n * function TodoList() {\n * const { data } = useLiveSuspenseQuery({\n * query: (q) =>\n * q.from({ todos: todosCollection })\n * .where(({ todos }) => eq(todos.completed, false))\n * .select(({ todos }) => ({ id: todos.id, text: todos.text }))\n * })\n *\n * return (\n * <ul>\n * {data.map(todo => <li key={todo.id}>{todo.text}</li>)}\n * </ul>\n * )\n * }\n *\n * function App() {\n * return (\n * <Suspense fallback={<div>Loading...</div>}>\n * <TodoList />\n * </Suspense>\n * )\n * }\n *\n * @example\n * // Single result query\n * const { data } = useLiveSuspenseQuery(\n * (q) => q.from({ todos: todosCollection })\n * .where(({ todos }) => eq(todos.id, 1))\n * .findOne()\n * )\n * // data is guaranteed to be the single item (or undefined if not found)\n *\n * @example\n * // Structured captured values are included in derived query identity and trigger re-suspension\n * const { data } = useLiveSuspenseQuery({\n * query: (q) => q.from({ todos: todosCollection })\n * .where(({ todos }) => gt(todos.priority, minPriority)),\n * })\n *\n * @example\n * // With Error boundary\n * function App() {\n * return (\n * <ErrorBoundary fallback={<div>Error loading data</div>}>\n * <Suspense fallback={<div>Loading...</div>}>\n * <TodoList />\n * </Suspense>\n * </ErrorBoundary>\n * )\n * }\n *\n * @remarks\n * **Important:** This hook does NOT support disabled queries (returning undefined/null).\n * Following TanStack Query's useSuspenseQuery design, the query callback must always\n * return a valid query, collection, or config object.\n *\n * ❌ **This will cause a type error:**\n * ```ts\n * useLiveSuspenseQuery(\n * (q) => userId ? q.from({ users }) : undefined // ❌ Error!\n * )\n * ```\n *\n * ✅ **Use conditional rendering instead:**\n * ```ts\n * function Profile({ userId }: { userId: string }) {\n * const { data } = useLiveSuspenseQuery({\n * query: (q) => q.from({ users }).where(({ users }) => eq(users.id, userId)),\n * })\n * return <div>{data.name}</div>\n * }\n *\n * // In parent component:\n * {userId ? <Profile userId={userId} /> : <div>No user</div>}\n * ```\n *\n * ✅ **For optional inputs, conditionally render a component with complete query inputs:**\n * ```ts\n * {userId ? <Profile userId={userId} /> : <div>No user</div>}\n * ```\n */\n// Overload 1: Accept query function that always returns QueryBuilder\nexport function useLiveSuspenseQuery<TContext extends Context>(\n queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,\n deps?: Array<unknown>,\n): {\n state: Map<string | number, GetResult<TContext>>\n data: InferResultType<TContext>\n collection: Collection<GetResult<TContext>, string | number, {}>\n}\n\n// Overload 2: Accept config object\nexport function useLiveSuspenseQuery<TContext extends Context>(\n config: UseLiveQueryConfig<TContext>,\n): {\n state: Map<string | number, GetResult<TContext>>\n data: InferResultType<TContext>\n collection: Collection<GetResult<TContext>, string | number, {}>\n}\n\n// Overload 3: Accept legacy config object\nexport function useLiveSuspenseQuery<TContext extends Context>(\n config: LiveQueryCollectionConfig<TContext>,\n deps?: Array<unknown>,\n): {\n state: Map<string | number, GetResult<TContext>>\n data: InferResultType<TContext>\n collection: Collection<GetResult<TContext>, string | number, {}>\n}\n\n// Overload 4: Accept pre-created live query collection\nexport function useLiveSuspenseQuery<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n>(\n liveQueryCollection: Collection<TResult, TKey, TUtils> & NonSingleResult,\n): {\n state: Map<TKey, TResult>\n data: Array<TResult>\n collection: Collection<TResult, TKey, TUtils>\n}\n\n// Overload 5: Accept pre-created live query collection with singleResult: true\nexport function useLiveSuspenseQuery<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n>(\n liveQueryCollection: Collection<TResult, TKey, TUtils> & SingleResult,\n): {\n state: Map<TKey, TResult>\n data: TResult | undefined\n collection: Collection<TResult, TKey, TUtils> & SingleResult\n}\n\n// Implementation - uses useLiveQuery internally and adds Suspense logic\nexport function useLiveSuspenseQuery(\n configOrQueryOrCollection: any,\n deps?: Array<unknown>,\n) {\n const promiseRef = useRef<Promise<void> | null>(null)\n const collectionRef = useRef<Collection<any, any, any> | null>(null)\n const hasBeenReadyRef = useRef(false)\n\n // Use useLiveQuery to handle collection management and reactivity\n const result =\n deps === undefined\n ? useLiveQuery(configOrQueryOrCollection)\n : useLiveQuery(configOrQueryOrCollection, deps)\n const queryInfo = getLiveQueryResultInfo(result)\n\n // Reset promise and ready state when query identity changes\n if (collectionRef.current !== result.collection) {\n promiseRef.current = null\n collectionRef.current = result.collection\n hasBeenReadyRef.current = false\n }\n\n // SUSPENSE LOGIC: Throw promise or error based on collection status\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!result.isEnabled) {\n // Suspense queries cannot be disabled - this matches TanStack Query's useSuspenseQuery behavior\n throw new Error(\n `useLiveSuspenseQuery does not support disabled queries (callback returned undefined/null). ` +\n `The Suspense pattern requires data to always be defined (T, not T | undefined). ` +\n `Solutions: ` +\n `1) Use conditional rendering - don't render the component until the condition is met. ` +\n `2) Use useLiveQuery instead, which supports disabled queries with the 'isEnabled' flag.`,\n )\n }\n\n const collectionStatus = result.collection.status\n\n // Track when we reach ready state\n if (result.isReady) {\n hasBeenReadyRef.current = true\n promiseRef.current = null\n }\n\n const observerError = queryInfo.observer.getError()\n if (observerError !== undefined && !hasBeenReadyRef.current) {\n promiseRef.current = null\n throw observerError\n }\n\n // Only throw errors during initial load (before first ready)\n // After success, errors surface as stale data (matches TanStack Query behavior)\n if (collectionStatus === `error` && !hasBeenReadyRef.current) {\n promiseRef.current = null\n // TODO: Once collections hold a reference to their last error object (#671),\n // we should rethrow that actual error instead of creating a generic message\n throw new Error(`Collection \"${result.collection.id}\" failed to load`)\n }\n\n if (!hasBeenReadyRef.current && (result.isLoading || result.isIdle)) {\n if (queryInfo.client?._isSsrStreamingEnabled() && !queryInfo.queryHash) {\n const reason = queryInfo.identityError\n ? `${queryInfo.identityError.reason} at ${queryInfo.identityError.path}`\n : `the query has no stable identity`\n throw new Error(\n `Cannot stream this live query during SSR because ${reason}. Provide an explicit serializable queryKey.`,\n )\n }\n\n // Create or reuse promise for current collection\n if (!promiseRef.current) {\n promiseRef.current = queryInfo.observer.preload()\n }\n // THROW PROMISE - React Suspense catches this (React 18+ required)\n // Note: We don't check React version here. In React <18, this will be caught\n // by an Error Boundary, which provides a reasonable failure mode.\n throw promiseRef.current\n }\n\n // Return data without status/loading flags (handled by Suspense/ErrorBoundary)\n // If error after success, return last known good state (stale data)\n return {\n state: result.state,\n data: result.data,\n collection: result.collection,\n }\n}\n"],"names":[],"mappings":";;;;;;AAoKO;AAIL;AACA;AACA;AAGA;AAIA;AAGA;AACE;AACA;AACA;AAA0B;AAK5B;AAEE;AAAU;AACR;AAAA;AAQJ;AAGA;AACE;AACA;AAAqB;AAGvB;AACA;AACE;AACA;AAAM;AAKR;AACE;AAGA;AAAqE;AAGvE;AACE;AACE;AAGA;AAAU;AACkD;AAAA;AAK9D;AACE;AAAwC;AAK1C;AAAiB;AAKnB;AAAO;AACS;AACD;AACM;AAEvB;;"}
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
+
import { UseLiveQueryConfig } from './useLiveQuery.cjs';
|
|
1
2
|
import { Collection, Context, GetResult, InferResultType, InitialQueryBuilder, LiveQueryCollectionConfig, NonSingleResult, QueryBuilder, SingleResult } from '@tanstack/db';
|
|
2
3
|
/**
|
|
3
4
|
* Create a live query with React Suspense support
|
|
4
5
|
* @param queryFn - Query function that defines what data to fetch
|
|
5
|
-
* @param deps -
|
|
6
|
+
* @param deps - Deprecated array of dependencies that trigger query re-execution when changed
|
|
6
7
|
* @returns Object with reactive data and state - data is guaranteed to be defined
|
|
7
8
|
* @throws Promise when data is loading (caught by Suspense boundary)
|
|
8
9
|
* @throws Error when collection fails (caught by Error boundary)
|
|
9
10
|
* @example
|
|
10
11
|
* // Basic usage with Suspense
|
|
11
12
|
* function TodoList() {
|
|
12
|
-
* const { data } = useLiveSuspenseQuery(
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* const { data } = useLiveSuspenseQuery({
|
|
14
|
+
* query: (q) =>
|
|
15
|
+
* q.from({ todos: todosCollection })
|
|
16
|
+
* .where(({ todos }) => eq(todos.completed, false))
|
|
17
|
+
* .select(({ todos }) => ({ id: todos.id, text: todos.text }))
|
|
18
|
+
* })
|
|
17
19
|
*
|
|
18
20
|
* return (
|
|
19
21
|
* <ul>
|
|
@@ -40,12 +42,11 @@ import { Collection, Context, GetResult, InferResultType, InitialQueryBuilder, L
|
|
|
40
42
|
* // data is guaranteed to be the single item (or undefined if not found)
|
|
41
43
|
*
|
|
42
44
|
* @example
|
|
43
|
-
* //
|
|
44
|
-
* const { data } = useLiveSuspenseQuery(
|
|
45
|
-
* (q) => q.from({ todos: todosCollection })
|
|
45
|
+
* // Structured captured values are included in derived query identity and trigger re-suspension
|
|
46
|
+
* const { data } = useLiveSuspenseQuery({
|
|
47
|
+
* query: (q) => q.from({ todos: todosCollection })
|
|
46
48
|
* .where(({ todos }) => gt(todos.priority, minPriority)),
|
|
47
|
-
*
|
|
48
|
-
* )
|
|
49
|
+
* })
|
|
49
50
|
*
|
|
50
51
|
* @example
|
|
51
52
|
* // With Error boundary
|
|
@@ -74,9 +75,9 @@ import { Collection, Context, GetResult, InferResultType, InitialQueryBuilder, L
|
|
|
74
75
|
* ✅ **Use conditional rendering instead:**
|
|
75
76
|
* ```ts
|
|
76
77
|
* function Profile({ userId }: { userId: string }) {
|
|
77
|
-
* const { data } = useLiveSuspenseQuery(
|
|
78
|
-
* (q) => q.from({ users }).where(({ users }) => eq(users.id, userId))
|
|
79
|
-
* )
|
|
78
|
+
* const { data } = useLiveSuspenseQuery({
|
|
79
|
+
* query: (q) => q.from({ users }).where(({ users }) => eq(users.id, userId)),
|
|
80
|
+
* })
|
|
80
81
|
* return <div>{data.name}</div>
|
|
81
82
|
* }
|
|
82
83
|
*
|
|
@@ -84,12 +85,9 @@ import { Collection, Context, GetResult, InferResultType, InitialQueryBuilder, L
|
|
|
84
85
|
* {userId ? <Profile userId={userId} /> : <div>No user</div>}
|
|
85
86
|
* ```
|
|
86
87
|
*
|
|
87
|
-
* ✅ **
|
|
88
|
+
* ✅ **For optional inputs, conditionally render a component with complete query inputs:**
|
|
88
89
|
* ```ts
|
|
89
|
-
*
|
|
90
|
-
* (q) => userId ? q.from({ users }) : undefined, // ✅ Supported!
|
|
91
|
-
* [userId]
|
|
92
|
-
* )
|
|
90
|
+
* {userId ? <Profile userId={userId} /> : <div>No user</div>}
|
|
93
91
|
* ```
|
|
94
92
|
*/
|
|
95
93
|
export declare function useLiveSuspenseQuery<TContext extends Context>(queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>, deps?: Array<unknown>): {
|
|
@@ -97,6 +95,11 @@ export declare function useLiveSuspenseQuery<TContext extends Context>(queryFn:
|
|
|
97
95
|
data: InferResultType<TContext>;
|
|
98
96
|
collection: Collection<GetResult<TContext>, string | number, {}>;
|
|
99
97
|
};
|
|
98
|
+
export declare function useLiveSuspenseQuery<TContext extends Context>(config: UseLiveQueryConfig<TContext>): {
|
|
99
|
+
state: Map<string | number, GetResult<TContext>>;
|
|
100
|
+
data: InferResultType<TContext>;
|
|
101
|
+
collection: Collection<GetResult<TContext>, string | number, {}>;
|
|
102
|
+
};
|
|
100
103
|
export declare function useLiveSuspenseQuery<TContext extends Context>(config: LiveQueryCollectionConfig<TContext>, deps?: Array<unknown>): {
|
|
101
104
|
state: Map<string | number, GetResult<TContext>>;
|
|
102
105
|
data: InferResultType<TContext>;
|
|
@@ -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,23 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useContext } from "react";
|
|
4
|
+
const DbContext = createContext(void 0);
|
|
5
|
+
function DbProvider(props) {
|
|
6
|
+
return /* @__PURE__ */ jsx(DbContext.Provider, { value: props.client, children: props.children });
|
|
7
|
+
}
|
|
8
|
+
function useDbClient() {
|
|
9
|
+
const client = useContext(DbContext);
|
|
10
|
+
if (!client) {
|
|
11
|
+
throw new Error(`useDbClient must be used within a DbProvider.`);
|
|
12
|
+
}
|
|
13
|
+
return client;
|
|
14
|
+
}
|
|
15
|
+
function useOptionalDbClient() {
|
|
16
|
+
return useContext(DbContext);
|
|
17
|
+
}
|
|
18
|
+
export {
|
|
19
|
+
DbProvider,
|
|
20
|
+
useDbClient,
|
|
21
|
+
useOptionalDbClient
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=DbProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DbProvider.js","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,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;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useRef } from "react";
|
|
3
|
+
import { useDbClient } from "./DbProvider.js";
|
|
4
|
+
function HydrationBoundary({ state, children }) {
|
|
5
|
+
const client = useDbClient();
|
|
6
|
+
const hydrated = useRef(void 0);
|
|
7
|
+
if (hydrated.current?.client !== client || hydrated.current.state !== state) {
|
|
8
|
+
client.hydrate(state);
|
|
9
|
+
hydrated.current = { client, state };
|
|
10
|
+
}
|
|
11
|
+
return children;
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
HydrationBoundary
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=HydrationBoundary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HydrationBoundary.js","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;;;;"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export
|
|
1
|
+
export { useLiveQuery } from './useLiveQuery.js';
|
|
2
|
+
export type { LiveQueryKey, UseLiveQueryConfig, UseLiveQueryStatus, } from './useLiveQuery.js';
|
|
3
|
+
export * from './DbProvider.js';
|
|
4
|
+
export * from './HydrationBoundary.js';
|
|
2
5
|
export * from './useLiveSuspenseQuery.js';
|
|
3
6
|
export * from './usePacedMutations.js';
|
|
4
7
|
export * from './useLiveInfiniteQuery.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { useLiveQuery } from "./useLiveQuery.js";
|
|
2
|
+
import { DbProvider, useDbClient, useOptionalDbClient } from "./DbProvider.js";
|
|
3
|
+
import { HydrationBoundary } from "./HydrationBoundary.js";
|
|
2
4
|
import { useLiveSuspenseQuery } from "./useLiveSuspenseQuery.js";
|
|
3
5
|
import { usePacedMutations } from "./usePacedMutations.js";
|
|
4
6
|
import { useLiveInfiniteQuery } from "./useLiveInfiniteQuery.js";
|
|
@@ -6,11 +8,15 @@ import { useLiveQueryEffect } from "./useLiveQueryEffect.js";
|
|
|
6
8
|
export * from "@tanstack/db";
|
|
7
9
|
import { createTransaction } from "@tanstack/db";
|
|
8
10
|
export {
|
|
11
|
+
DbProvider,
|
|
12
|
+
HydrationBoundary,
|
|
9
13
|
createTransaction,
|
|
14
|
+
useDbClient,
|
|
10
15
|
useLiveInfiniteQuery,
|
|
11
16
|
useLiveQuery,
|
|
12
17
|
useLiveQueryEffect,
|
|
13
18
|
useLiveSuspenseQuery,
|
|
19
|
+
useOptionalDbClient,
|
|
14
20
|
usePacedMutations
|
|
15
21
|
};
|
|
16
22
|
//# sourceMappingURL=index.js.map
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
|
|
@@ -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;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const liveQueryResultInfo = /* @__PURE__ */ Symbol(`liveQueryResultInfo`);
|
|
2
|
+
function setLiveQueryResultInfo(result, info) {
|
|
3
|
+
Object.defineProperty(result, liveQueryResultInfo, {
|
|
4
|
+
configurable: true,
|
|
5
|
+
value: info
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
function getLiveQueryResultInfo(result) {
|
|
9
|
+
const info = result[liveQueryResultInfo];
|
|
10
|
+
if (!info) {
|
|
11
|
+
throw new Error(`Missing internal live query result information.`);
|
|
12
|
+
}
|
|
13
|
+
return info;
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
getLiveQueryResultInfo,
|
|
17
|
+
setLiveQueryResultInfo
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=live-query-internals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live-query-internals.js","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;"}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
import { useLiveQuery } from './useLiveQuery.js';
|
|
2
|
-
import { Collection, Context, InferResultType, InitialQueryBuilder, NonSingleResult, QueryBuilder } from '@tanstack/db';
|
|
1
|
+
import { LiveQueryKey, useLiveQuery } from './useLiveQuery.js';
|
|
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 -
|
|
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>;
|
|
@@ -1,19 +1,68 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { useRef, useCallback, useSyncExternalStore } from "react";
|
|
2
3
|
import { normalizeLiveQueryWindowPageSize, getLiveQueryWindowInputKind, compareLiveQueryWindowDependencies, resolveLiveQueryWindowInput, createLiveQueryCollection, getLiveQueryWindowCollectionWarning, assertLiveQueryWindowManyResult, shouldPreserveLiveQueryWindowPageCount, createLiveQueryWindowController, fetchNextLiveQueryWindowPage } from "@tanstack/db";
|
|
4
|
+
import { useOptionalDbClient } from "./DbProvider.js";
|
|
5
|
+
import { warnDeprecatedDepsArray, prepareDerivedQuery, warnUnhashableDerivedIdentity, prepareQueryValue } from "./useLiveQuery.js";
|
|
3
6
|
const DEFAULT_GC_TIME_MS = 1;
|
|
4
|
-
|
|
7
|
+
const unpreparedQueryValue = /* @__PURE__ */ Symbol(`unpreparedQueryValue`);
|
|
8
|
+
function useLiveInfiniteQuery(queryFnOrCollection, config, deps) {
|
|
5
9
|
const pageSize = normalizeLiveQueryWindowPageSize(config.pageSize);
|
|
6
10
|
const initialPageParam = config.initialPageParam ?? 0;
|
|
11
|
+
const contextDbClient = useOptionalDbClient();
|
|
12
|
+
const dbClient = config.client ?? contextDbClient;
|
|
7
13
|
const inputIsCollection = getLiveQueryWindowInputKind(queryFnOrCollection) === `collection`;
|
|
8
14
|
const committedRef = useRef(null);
|
|
9
15
|
const committed = committedRef.current;
|
|
10
16
|
const inputKind = inputIsCollection ? `collection` : `query`;
|
|
17
|
+
const derivedIdentityProfilerRef = useRef({
|
|
18
|
+
renderCount: 0,
|
|
19
|
+
totalMs: 0,
|
|
20
|
+
maxMs: 0,
|
|
21
|
+
warned: false
|
|
22
|
+
});
|
|
23
|
+
const legacyUnhashableIdentityRef = useRef([
|
|
24
|
+
`legacy-unhashable`
|
|
25
|
+
]);
|
|
26
|
+
const deferredCollections = /* @__PURE__ */ new Set();
|
|
27
|
+
let preparedQueryValue = unpreparedQueryValue;
|
|
28
|
+
let identityDeps = [];
|
|
29
|
+
let identityMode = `collection`;
|
|
30
|
+
if (!inputIsCollection) {
|
|
31
|
+
if (config.queryKey !== void 0) {
|
|
32
|
+
identityMode = `queryKey`;
|
|
33
|
+
identityDeps = config.queryKey;
|
|
34
|
+
} else if (deps !== void 0) {
|
|
35
|
+
identityMode = `legacyDeps`;
|
|
36
|
+
identityDeps = deps;
|
|
37
|
+
warnDeprecatedDepsArray(`useLiveInfiniteQuery`);
|
|
38
|
+
} else if (committed?.identityMode === `derived` && committed.inputQuery === queryFnOrCollection && committed.client === dbClient) {
|
|
39
|
+
identityMode = `derived`;
|
|
40
|
+
identityDeps = committed.dependencies ?? [];
|
|
41
|
+
} else {
|
|
42
|
+
identityMode = `derived`;
|
|
43
|
+
const preparation = prepareDerivedQuery(
|
|
44
|
+
queryFnOrCollection,
|
|
45
|
+
dbClient,
|
|
46
|
+
derivedIdentityProfilerRef.current,
|
|
47
|
+
deferredCollections
|
|
48
|
+
);
|
|
49
|
+
preparedQueryValue = preparation.value;
|
|
50
|
+
if (preparation.status === `hashable`) {
|
|
51
|
+
identityDeps = preparation.identityDeps;
|
|
52
|
+
} else {
|
|
53
|
+
warnUnhashableDerivedIdentity(preparation.error);
|
|
54
|
+
identityDeps = legacyUnhashableIdentityRef.current;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const usesLegacyDeps = !inputIsCollection && config.queryKey === void 0 && deps !== void 0;
|
|
11
59
|
const dependencyComparison = compareLiveQueryWindowDependencies(
|
|
12
60
|
committed?.dependencies,
|
|
13
|
-
|
|
61
|
+
identityDeps
|
|
14
62
|
);
|
|
15
|
-
const
|
|
16
|
-
const
|
|
63
|
+
const sameClient = committed?.client === dbClient;
|
|
64
|
+
const dependenciesChanged = !inputIsCollection && (!sameClient || (usesLegacyDeps ? dependencyComparison.changed : !dependencyComparison.structurallyEqual));
|
|
65
|
+
const dependenciesStructurallyEqual = usesLegacyDeps && sameClient && dependencyComparison.structurallyEqual;
|
|
17
66
|
const needsNewCollection = committed === null || committed.inputKind !== inputKind || inputIsCollection && committed.inputCollection !== queryFnOrCollection || dependenciesChanged;
|
|
18
67
|
const pageShapeChanged = committed === null || committed.pageSize !== pageSize || committed.initialPageParam !== initialPageParam;
|
|
19
68
|
const needsNewController = committed === null || needsNewCollection || pageShapeChanged;
|
|
@@ -22,7 +71,18 @@ function useLiveInfiniteQuery(queryFnOrCollection, config, deps = []) {
|
|
|
22
71
|
let collection = committed?.collection;
|
|
23
72
|
let warning = null;
|
|
24
73
|
if (needsNewCollection) {
|
|
25
|
-
|
|
74
|
+
let inputValue = queryFnOrCollection;
|
|
75
|
+
if (!inputIsCollection) {
|
|
76
|
+
if (preparedQueryValue === unpreparedQueryValue) {
|
|
77
|
+
preparedQueryValue = prepareQueryValue(
|
|
78
|
+
queryFnOrCollection,
|
|
79
|
+
dbClient,
|
|
80
|
+
deferredCollections
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
inputValue = () => preparedQueryValue;
|
|
84
|
+
}
|
|
85
|
+
const input = resolveLiveQueryWindowInput(inputValue);
|
|
26
86
|
if (input.kind === `collection`) {
|
|
27
87
|
collection = input.collection;
|
|
28
88
|
} else {
|
|
@@ -57,7 +117,10 @@ function useLiveInfiniteQuery(queryFnOrCollection, config, deps = []) {
|
|
|
57
117
|
renderState = {
|
|
58
118
|
inputKind,
|
|
59
119
|
inputCollection: inputIsCollection ? collection : null,
|
|
60
|
-
|
|
120
|
+
inputQuery: inputIsCollection ? null : queryFnOrCollection,
|
|
121
|
+
client: dbClient,
|
|
122
|
+
identityMode,
|
|
123
|
+
dependencies: inputIsCollection ? null : [...identityDeps],
|
|
61
124
|
pageSize,
|
|
62
125
|
initialPageParam,
|
|
63
126
|
collection,
|
|
@@ -67,7 +130,8 @@ function useLiveInfiniteQuery(queryFnOrCollection, config, deps = []) {
|
|
|
67
130
|
initialPageCount
|
|
68
131
|
}),
|
|
69
132
|
warning,
|
|
70
|
-
warned: false
|
|
133
|
+
warned: false,
|
|
134
|
+
deferredCollections
|
|
71
135
|
};
|
|
72
136
|
}
|
|
73
137
|
const currentRenderState = renderState;
|
|
@@ -80,12 +144,16 @@ function useLiveInfiniteQuery(queryFnOrCollection, config, deps = []) {
|
|
|
80
144
|
currentRenderState.warned = true;
|
|
81
145
|
console.warn(currentRenderState.warning);
|
|
82
146
|
}
|
|
147
|
+
for (const collection of currentRenderState.deferredCollections) {
|
|
148
|
+
collection._resumeSyncStart();
|
|
149
|
+
}
|
|
150
|
+
currentRenderState.deferredCollections.clear();
|
|
83
151
|
return unsubscribe;
|
|
84
152
|
},
|
|
85
153
|
[controller, currentRenderState]
|
|
86
154
|
);
|
|
87
155
|
const getSnapshot = useCallback(() => controller.getSnapshot(), [controller]);
|
|
88
|
-
const snapshot = useSyncExternalStore(subscribe, getSnapshot);
|
|
156
|
+
const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
89
157
|
const fetchNextPage = useCallback(
|
|
90
158
|
() => fetchNextLiveQueryWindowPage(controller),
|
|
91
159
|
[controller]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLiveInfiniteQuery.js","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":[],"mappings":";;AA0BA,MAAM,qBAAqB;AAmIpB,SAAS,qBACd,qBACA,QACA,OAAuB,CAAA,GACe;AACtC,QAAM,WAAW,iCAAiC,OAAO,QAAQ;AACjE,QAAM,mBAAmB,OAAO,oBAAoB;AAEpD,QAAM,oBACJ,4BAA4B,mBAAmB,MAAM;AAEvD,QAAM,eAAe,OAAwC,IAAI;AACjE,QAAM,YAAY,aAAa;AAC/B,QAAM,YAAY,oBAAoB,eAAe;AAErD,QAAM,uBAAuB;AAAA,IAC3B,WAAW;AAAA,IACX;AAAA,EAAA;AAEF,QAAM,sBAAsB,CAAC,qBAAqB,qBAAqB;AACvE,QAAM,gCACJ,CAAC,qBAAqB,qBAAqB;AAC7C,QAAM,qBACJ,cAAc,QACd,UAAU,cAAc,aACvB,qBAAqB,UAAU,oBAAoB,uBACpD;AACF,QAAM,mBACJ,cAAc,QACd,UAAU,aAAa,YACvB,UAAU,qBAAqB;AACjC,QAAM,qBACJ,cAAc,QAAQ,sBAAsB;AAE9C,MAAI,cAAc;AAClB,MAAI,oBAAoB;AACtB,QAAI,aAAa,WAAW;AAC5B,QAAI,UAAyB;AAE7B,QAAI,oBAAoB;AACtB,YAAM,QAAQ,4BAAsC,mBAAmB;AACvE,UAAI,MAAM,SAAS,cAAc;AAC/B,qBAAa,MAAM;AAAA,MACrB,OAAO;AAGL,qBAAa,0BAA0B;AAAA,UACrC,OAAO,MAAM,MAAM,MAAM,WAAW,CAAC,EAAE,OAAO,CAAC;AAAA;AAAA;AAAA,UAG/C,WAAW;AAAA,UACX,QAAQ;AAAA,QAAA,CACT;AAAA,MACH;AAAA,IACF;AAEA,QAAI,CAAC,YAAY;AACf,YAAM,IAAI,MAAM,sDAAsD;AAAA,IACxE;AAEA,QAAI,mBAAmB;AACrB,gBACE,oCAAoC,YAAY,WAAW,CAAC,KAAK;AAAA,IACrE,OAAO;AACL,sCAAgC,UAAU;AAAA,IAC5C;AAEA,UAAM,uBAAuB,uCAAuC;AAAA,MAClE,uBAAuB,cAAc;AAAA,MACrC,mBAAmB,WAAW;AAAA,MAC9B;AAAA,MACA,gBACE,qBAAqB,WAAW,oBAAoB;AAAA,MACtD;AAAA,MACA;AAAA,MACA;AAAA,IAAA,CACD;AACD,UAAM,oBAAoB,YACtB,KAAK,IAAI,GAAG,UAAU,WAAW,YAAA,EAAc,MAAM,MAAM,IAC3D;AACJ,UAAM,mBAAmB,uBAAuB,oBAAoB;AACpE,kBAAc;AAAA,MACZ;AAAA,MACA,iBAAiB,oBAAoB,aAAa;AAAA,MAClD,cAAc,oBAAoB,OAAO,CAAC,GAAG,IAAI;AAAA,MACjD;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,gCAAgC,YAAY;AAAA,QACtD;AAAA,QACA;AAAA,QACA;AAAA,MAAA,CACD;AAAA,MACD;AAAA,MACA,QAAQ;AAAA,IAAA;AAAA,EAEZ;AACA,QAAM,qBAAqB;AAC3B,QAAM,aAAa,mBAAmB;AAEtC,QAAM,YAAY;AAAA,IAChB,CAAC,kBAA8B;AAC7B,YAAM,cAAc,WAAW,UAAU,aAAa;AACtD,mBAAa,UAAU;AACvB,UAAI,mBAAmB,WAAW,CAAC,mBAAmB,QAAQ;AAC5D,2BAAmB,SAAS;AAC5B,gBAAQ,KAAK,mBAAmB,OAAO;AAAA,MACzC;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,YAAY,kBAAkB;AAAA,EAAA;AAEjC,QAAM,cAAc,YAAY,MAAM,WAAW,eAAe,CAAC,UAAU,CAAC;AAC5E,QAAM,WAAW,qBAAqB,WAAW,WAAW;AAE5D,QAAM,gBAAgB;AAAA,IACpB,MAAM,6BAA6B,UAAU;AAAA,IAC7C,CAAC,UAAU;AAAA,EAAA;AAGb,SAAO;AAAA,IACL,MAAM,SAAS;AAAA,IACf,OAAO,SAAS;AAAA,IAChB,QAAQ,SAAS;AAAA,IACjB,WAAW,SAAS;AAAA,IACpB,SAAS,SAAS;AAAA,IAClB,QAAQ,SAAS;AAAA,IACjB,SAAS,SAAS;AAAA,IAClB,aAAa,SAAS;AAAA,IACtB,YACE,SAAS;AAAA,IACX,WACE,SAAS;AAAA,IACX,OAAO,SAAS;AAAA,IAChB,YAAY,SAAS;AAAA,IACrB;AAAA,IACA,aAAa,SAAS;AAAA,IACtB,oBAAoB,SAAS;AAAA,IAC7B,OAAO,SAAS;AAAA,EAAA;AAEpB;"}
|
|
1
|
+
{"version":3,"file":"useLiveInfiniteQuery.js","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":[],"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;AACA;AAA8C;AAM9C;AACA;AAAyC;AAEzC;AACA;AAAoB;AAClB;AACA;AAC2B;AAC3B;AAEF;AACA;AACE;AAA2B;AAE3B;AACA;AAA2C;AAC7C;AACF;AAGF;AAEA;AAA6B;AAChB;AACX;AAEF;AACA;AAMA;AAEA;AAKA;AAIA;AAGA;AACA;AACE;AACA;AAEA;AACE;AACA;AACE;AACE;AAAqB;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;AAEnE;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;AAAkB;AAEd;AACA;AACA;AACE;AACA;AAAuC;AAEzC;AACE;AAAW;AAEb;AACA;AAAO;AACT;AAC+B;AAEjC;AACA;AAEA;AAAsB;AACyB;AAClC;AAGb;AAAO;AACU;AACC;AACC;AACG;AACF;AACD;AACC;AACI;AAEX;AAEA;AACK;AACK;AACrB;AACsB;AACO;AACb;AAEpB;;;;"}
|