@tanstack/react-db 0.5.12 → 1.0.0-rc.1
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/dist/cjs/live-query-internals.cjs.map +1 -1
- package/dist/cjs/live-query-internals.d.cts +3 -1
- package/dist/cjs/source-id-bindings.cjs +9 -42
- package/dist/cjs/source-id-bindings.cjs.map +1 -1
- package/dist/cjs/source-id-bindings.d.cts +6 -17
- package/dist/cjs/useLiveInfiniteQuery.cjs +93 -173
- package/dist/cjs/useLiveInfiniteQuery.cjs.map +1 -1
- package/dist/cjs/useLiveInfiniteQuery.d.cts +3 -14
- package/dist/cjs/useLiveQuery.cjs +187 -224
- package/dist/cjs/useLiveQuery.cjs.map +1 -1
- package/dist/cjs/useLiveQuery.d.cts +37 -136
- package/dist/cjs/useLiveSuspenseQuery.cjs +71 -64
- package/dist/cjs/useLiveSuspenseQuery.cjs.map +1 -1
- package/dist/cjs/useLiveSuspenseQuery.d.cts +10 -9
- package/dist/cjs/usePacedMutations.cjs.map +1 -1
- package/dist/cjs/usePacedMutations.d.cts +1 -1
- package/dist/esm/live-query-internals.d.ts +3 -1
- package/dist/esm/live-query-internals.js.map +1 -1
- package/dist/esm/source-id-bindings.d.ts +6 -17
- package/dist/esm/source-id-bindings.js +9 -40
- package/dist/esm/source-id-bindings.js.map +1 -1
- package/dist/esm/useLiveInfiniteQuery.d.ts +3 -14
- package/dist/esm/useLiveInfiniteQuery.js +95 -175
- package/dist/esm/useLiveInfiniteQuery.js.map +1 -1
- package/dist/esm/useLiveQuery.d.ts +37 -136
- package/dist/esm/useLiveQuery.js +189 -226
- package/dist/esm/useLiveQuery.js.map +1 -1
- package/dist/esm/useLiveSuspenseQuery.d.ts +10 -9
- package/dist/esm/useLiveSuspenseQuery.js +71 -64
- package/dist/esm/useLiveSuspenseQuery.js.map +1 -1
- package/dist/esm/usePacedMutations.d.ts +1 -1
- package/dist/esm/usePacedMutations.js.map +1 -1
- package/package.json +3 -3
- package/skills/react-db/SKILL.md +24 -15
- package/src/live-query-internals.ts +3 -0
- package/src/source-id-bindings.ts +16 -62
- package/src/useLiveInfiniteQuery.ts +138 -296
- package/src/useLiveQuery.ts +329 -495
- package/src/useLiveSuspenseQuery.ts +122 -132
- package/src/usePacedMutations.ts +1 -1
|
@@ -1,206 +1,127 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useRef, useCallback, useSyncExternalStore } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { readLiveQueryWindowPageShape, isCollection, getLiveQueryKey, bindLiveQueryWindow, rejectLiveQueryInput, releaseDeferredOnReclaim, resumeDeferredLiveQueryCollections, fetchNextLiveQueryWindowPage, createLiveQuerySourceIdClaims } from "@tanstack/db";
|
|
4
4
|
import { useOptionalDbClient } from "./DbProvider.js";
|
|
5
|
-
import {
|
|
6
|
-
import { warnDeprecatedDepsArray, prepareDerivedQuery, warnUnhashableDerivedIdentity, prepareQueryValue } from "./useLiveQuery.js";
|
|
7
|
-
const DEFAULT_GC_TIME_MS = 1;
|
|
5
|
+
import { prepareDerivedQuery, prepareQueryValue, getIdentityScope } from "./useLiveQuery.js";
|
|
8
6
|
const unpreparedQueryValue = /* @__PURE__ */ Symbol(`unpreparedQueryValue`);
|
|
9
|
-
function
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
function useLiveInfiniteQuery(queryFnOrCollection, config, deps) {
|
|
13
|
-
if (`getNextPageParam` in config) {
|
|
14
|
-
throw new Error(
|
|
15
|
-
`getNextPageParam is not supported by useLiveInfiniteQuery. Use an on-demand collection and fulfill meta.loadSubsetOptions in queryFn for server pagination.`
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
const pageSize = normalizeLiveQueryWindowPageSize(config.pageSize);
|
|
19
|
-
const initialPageParam = config.initialPageParam ?? 0;
|
|
7
|
+
function useLiveInfiniteQuery(queryFnOrCollection, config) {
|
|
8
|
+
const pageShape = readLiveQueryWindowPageShape(config);
|
|
20
9
|
const contextDbClient = useOptionalDbClient();
|
|
21
10
|
const dbClient = config.client ?? contextDbClient;
|
|
22
|
-
const inputIsCollection =
|
|
11
|
+
const inputIsCollection = isCollection(queryFnOrCollection);
|
|
23
12
|
const committedRef = useRef(null);
|
|
24
13
|
const committed = committedRef.current;
|
|
25
|
-
const
|
|
14
|
+
const renderedRef = useRef(null);
|
|
26
15
|
const sourceIdsRef = useRef(null);
|
|
27
|
-
const inputKind = inputIsCollection ? `collection` : `query`;
|
|
28
16
|
const derivedIdentityProfilerRef = useRef({
|
|
29
17
|
renderCount: 0,
|
|
30
18
|
totalMs: 0,
|
|
31
19
|
maxMs: 0,
|
|
32
20
|
warned: false
|
|
33
21
|
});
|
|
34
|
-
const legacyUnhashableIdentityRef = useRef([
|
|
35
|
-
`legacy-unhashable`
|
|
36
|
-
]);
|
|
37
22
|
const deferredCollections = /* @__PURE__ */ new Set();
|
|
23
|
+
const identityScope = () => getIdentityScope(
|
|
24
|
+
sourceIdsRef.current ??= createLiveQuerySourceIdClaims(),
|
|
25
|
+
dbClient,
|
|
26
|
+
`useLiveInfiniteQuery`
|
|
27
|
+
);
|
|
28
|
+
const queryKey = getLiveQueryKey(config);
|
|
38
29
|
let preparedQueryValue = unpreparedQueryValue;
|
|
39
|
-
let
|
|
40
|
-
let
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
identityMode = `derived`;
|
|
51
|
-
identityDeps = committed.dependencies ?? [];
|
|
52
|
-
} else {
|
|
53
|
-
identityMode = `derived`;
|
|
54
|
-
const preparation = prepareDerivedQuery(
|
|
55
|
-
queryFnOrCollection,
|
|
56
|
-
dbClient,
|
|
57
|
-
derivedIdentityProfilerRef.current,
|
|
58
|
-
deferredCollections
|
|
59
|
-
);
|
|
60
|
-
preparedQueryValue = preparation.value;
|
|
61
|
-
claimSourceIds(
|
|
62
|
-
sourceIdsRef.current ??= createSourceIdBindings(),
|
|
63
|
-
preparation.value,
|
|
64
|
-
dbClient,
|
|
65
|
-
`useLiveInfiniteQuery`,
|
|
66
|
-
() => resumeDeferredLiveQueryCollections(deferredCollections)
|
|
67
|
-
);
|
|
68
|
-
if (preparation.status === `hashable`) {
|
|
69
|
-
identityDeps = preparation.identityDeps;
|
|
30
|
+
let dependencies;
|
|
31
|
+
let derivedQuery = void 0;
|
|
32
|
+
const rendered = renderedRef.current;
|
|
33
|
+
let binding;
|
|
34
|
+
try {
|
|
35
|
+
if (!inputIsCollection) {
|
|
36
|
+
if (queryKey !== void 0) {
|
|
37
|
+
dependencies = queryKey;
|
|
38
|
+
} else if (committed !== null && committed.derivedQuery === queryFnOrCollection && committed.binding.scope === dbClient) {
|
|
39
|
+
derivedQuery = queryFnOrCollection;
|
|
40
|
+
dependencies = committed.binding.dependencies;
|
|
70
41
|
} else {
|
|
71
|
-
|
|
72
|
-
|
|
42
|
+
const preparation = prepareDerivedQuery(
|
|
43
|
+
queryFnOrCollection,
|
|
44
|
+
derivedIdentityProfilerRef.current,
|
|
45
|
+
identityScope(),
|
|
46
|
+
deferredCollections
|
|
47
|
+
);
|
|
48
|
+
preparedQueryValue = preparation.value;
|
|
49
|
+
derivedQuery = queryFnOrCollection;
|
|
50
|
+
dependencies = preparation.identityDeps;
|
|
73
51
|
}
|
|
74
52
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const dependenciesChanged = !inputIsCollection && !queryIdentityMatches(dependencyComparison, sameClient, usesLegacyDeps);
|
|
83
|
-
const dependenciesStructurallyEqual = usesLegacyDeps && sameClient && dependencyComparison.structurallyEqual;
|
|
84
|
-
const needsNewCollection = committed === null || committed.inputKind !== inputKind || inputIsCollection && committed.inputCollection !== queryFnOrCollection || dependenciesChanged;
|
|
85
|
-
const pageShapeChanged = committed === null || committed.pageSize !== pageSize || committed.initialPageParam !== initialPageParam;
|
|
86
|
-
const needsNewController = committed === null || needsNewCollection || pageShapeChanged;
|
|
87
|
-
let renderState = committed;
|
|
88
|
-
if (needsNewController) {
|
|
89
|
-
let collection = committed?.collection;
|
|
90
|
-
let warning = null;
|
|
91
|
-
let startInRender = false;
|
|
92
|
-
let suppliedCollection = false;
|
|
93
|
-
let createdCollection = false;
|
|
94
|
-
const canPreservePageCount = shouldPreserveLiveQueryWindowPageCount({
|
|
95
|
-
hasPreviousController: committed !== null,
|
|
96
|
-
previousInputKind: committed?.inputKind,
|
|
97
|
-
inputKind,
|
|
98
|
-
sameCollection: inputIsCollection && committed?.inputCollection === queryFnOrCollection,
|
|
99
|
-
dependenciesChanged,
|
|
100
|
-
dependenciesStructurallyEqual,
|
|
101
|
-
pageShapeChanged
|
|
102
|
-
});
|
|
103
|
-
const previousPageCount = committed ? Math.max(1, committed.controller.getSnapshot().pages.length) : 1;
|
|
104
|
-
const initialPageCount = canPreservePageCount ? previousPageCount : 1;
|
|
105
|
-
const requiredLimit = initialPageCount * pageSize + 1;
|
|
106
|
-
if (needsNewCollection) {
|
|
107
|
-
let inputValue = queryFnOrCollection;
|
|
108
|
-
if (!inputIsCollection) {
|
|
109
|
-
if (preparedQueryValue === unpreparedQueryValue) {
|
|
110
|
-
preparedQueryValue = prepareQueryValue(
|
|
111
|
-
queryFnOrCollection,
|
|
112
|
-
dbClient,
|
|
113
|
-
deferredCollections
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
inputValue = () => preparedQueryValue;
|
|
117
|
-
}
|
|
118
|
-
const rendered = renderedCollectionRef.current;
|
|
119
|
-
if (!inputIsCollection && rendered !== null && queryIdentityMatches(
|
|
120
|
-
compareLiveQueryWindowDependencies(
|
|
121
|
-
rendered.dependencies,
|
|
122
|
-
identityDeps
|
|
123
|
-
),
|
|
124
|
-
rendered.client === dbClient,
|
|
125
|
-
usesLegacyDeps
|
|
126
|
-
) && rendered.collection.status !== `cleaned-up` && rendered.collection.status !== `error` && liveQueryWindowMatches(rendered.collection, requiredLimit)) {
|
|
127
|
-
collection = rendered.collection;
|
|
128
|
-
for (const deferred of rendered.deferredCollections) {
|
|
129
|
-
deferredCollections.add(deferred);
|
|
130
|
-
}
|
|
131
|
-
} else {
|
|
132
|
-
const input = resolveLiveQueryWindowInput(inputValue);
|
|
133
|
-
if (input.kind === `collection`) {
|
|
134
|
-
collection = input.collection;
|
|
135
|
-
suppliedCollection = true;
|
|
136
|
-
renderedCollectionRef.current = null;
|
|
137
|
-
} else {
|
|
138
|
-
collection = createLiveQueryCollection({
|
|
139
|
-
query: input.query.limit(requiredLimit).offset(0),
|
|
140
|
-
gcTime: DEFAULT_GC_TIME_MS
|
|
141
|
-
});
|
|
142
|
-
startInRender = true;
|
|
143
|
-
createdCollection = true;
|
|
144
|
-
}
|
|
53
|
+
const compileQuery = () => {
|
|
54
|
+
if (preparedQueryValue === unpreparedQueryValue) {
|
|
55
|
+
preparedQueryValue = prepareQueryValue(
|
|
56
|
+
queryFnOrCollection,
|
|
57
|
+
dbClient,
|
|
58
|
+
deferredCollections
|
|
59
|
+
);
|
|
145
60
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (suppliedCollection) {
|
|
156
|
-
startInRender = liveQueryWindowMatches(collection, requiredLimit);
|
|
157
|
-
}
|
|
158
|
-
if (startInRender) collection.startSyncImmediate();
|
|
159
|
-
if (createdCollection) {
|
|
160
|
-
renderedCollectionRef.current = {
|
|
61
|
+
return preparedQueryValue;
|
|
62
|
+
};
|
|
63
|
+
binding = bindLiveQueryWindow(
|
|
64
|
+
committed?.binding ?? null,
|
|
65
|
+
{
|
|
66
|
+
...pageShape,
|
|
67
|
+
input: typeof queryFnOrCollection === `function` ? compileQuery : queryFnOrCollection,
|
|
68
|
+
dependencies,
|
|
69
|
+
scope: dbClient,
|
|
161
70
|
client: dbClient,
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
71
|
+
queryKey,
|
|
72
|
+
recompile: `identity`
|
|
73
|
+
},
|
|
74
|
+
rendered?.binding
|
|
75
|
+
);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
rejectLiveQueryInput(deferredCollections, error);
|
|
78
|
+
}
|
|
79
|
+
let currentRenderState = committed;
|
|
80
|
+
if (binding !== committed?.binding) {
|
|
81
|
+
const newCollection = binding.collection !== committed?.binding.collection;
|
|
82
|
+
if (newCollection && binding.collection === rendered?.binding.collection) {
|
|
83
|
+
for (const deferred of rendered.deferredCollections) {
|
|
84
|
+
deferredCollections.add(deferred);
|
|
85
|
+
}
|
|
166
86
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
controller: createLiveQueryWindowController(collection, {
|
|
178
|
-
pageSize,
|
|
179
|
-
initialPageParam,
|
|
180
|
-
initialPageCount
|
|
181
|
-
}),
|
|
182
|
-
warning,
|
|
183
|
-
warned: false,
|
|
184
|
-
deferredCollections
|
|
87
|
+
const reusesRendered = newCollection && binding.collection === rendered?.binding.collection;
|
|
88
|
+
currentRenderState = {
|
|
89
|
+
binding,
|
|
90
|
+
derivedQuery,
|
|
91
|
+
deferredCollections,
|
|
92
|
+
// A render that never commits releases its deferred sync starts when
|
|
93
|
+
// garbage collection reclaims the window collection it built. A reused
|
|
94
|
+
// collection keeps the release its first render registered.
|
|
95
|
+
cancelReclaimRelease: reusesRendered ? rendered.cancelReclaimRelease : newCollection && binding.ownsCollection ? releaseDeferredOnReclaim(binding.collection, deferredCollections) : void 0,
|
|
96
|
+
warned: false
|
|
185
97
|
};
|
|
98
|
+
if (binding.supplied) renderedRef.current = null;
|
|
99
|
+
else if (newCollection && binding.ownsCollection) {
|
|
100
|
+
renderedRef.current = currentRenderState;
|
|
101
|
+
}
|
|
186
102
|
}
|
|
187
|
-
const
|
|
188
|
-
const controller =
|
|
103
|
+
const renderState = currentRenderState;
|
|
104
|
+
const controller = renderState.binding.controller;
|
|
189
105
|
const subscribe = useCallback(
|
|
190
106
|
(onStoreChange) => {
|
|
191
107
|
const unsubscribe = controller.subscribe(onStoreChange);
|
|
192
|
-
committedRef.current =
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
console.warn(
|
|
108
|
+
committedRef.current = renderState;
|
|
109
|
+
if (renderState.binding.warning && !renderState.warned) {
|
|
110
|
+
renderState.warned = true;
|
|
111
|
+
console.warn(renderState.binding.warning);
|
|
196
112
|
}
|
|
197
|
-
|
|
113
|
+
renderState.cancelReclaimRelease?.();
|
|
114
|
+
resumeDeferredLiveQueryCollections(renderState.deferredCollections);
|
|
198
115
|
return unsubscribe;
|
|
199
116
|
},
|
|
200
|
-
[controller,
|
|
117
|
+
[controller, renderState]
|
|
201
118
|
);
|
|
202
119
|
const getSnapshot = useCallback(() => controller.getSnapshot(), [controller]);
|
|
203
|
-
const snapshot = useSyncExternalStore(
|
|
120
|
+
const snapshot = useSyncExternalStore(
|
|
121
|
+
subscribe,
|
|
122
|
+
getSnapshot,
|
|
123
|
+
() => controller.getServerSnapshot()
|
|
124
|
+
);
|
|
204
125
|
const fetchNextPage = useCallback(
|
|
205
126
|
() => fetchNextLiveQueryWindowPage(controller),
|
|
206
127
|
[controller]
|
|
@@ -213,12 +134,11 @@ function useLiveInfiniteQuery(queryFnOrCollection, config, deps) {
|
|
|
213
134
|
status: snapshot.status,
|
|
214
135
|
isLoading: snapshot.isLoading,
|
|
215
136
|
isReady: snapshot.isReady,
|
|
216
|
-
persistedStatus: snapshot.persistedStatus,
|
|
217
|
-
isPersistedReady: snapshot.isPersistedReady,
|
|
218
|
-
persistedError: snapshot.persistedError,
|
|
219
137
|
isIdle: snapshot.isIdle,
|
|
220
|
-
|
|
138
|
+
isLoadingError: snapshot.isLoadingError,
|
|
139
|
+
isCrashed: snapshot.isCrashed,
|
|
221
140
|
isCleanedUp: snapshot.isCleanedUp,
|
|
141
|
+
isSyncError: snapshot.isSyncError,
|
|
222
142
|
collection: snapshot.collection,
|
|
223
143
|
isEnabled: snapshot.isEnabled,
|
|
224
144
|
pages: snapshot.pages,
|
|
@@ -226,7 +146,7 @@ function useLiveInfiniteQuery(queryFnOrCollection, config, deps) {
|
|
|
226
146
|
fetchNextPage,
|
|
227
147
|
hasNextPage: snapshot.hasNextPage,
|
|
228
148
|
isFetchingNextPage: snapshot.isFetchingNextPage,
|
|
229
|
-
|
|
149
|
+
health: snapshot.health
|
|
230
150
|
};
|
|
231
151
|
}
|
|
232
152
|
export {
|
|
@@ -1 +1 @@
|
|
|
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 liveQueryWindowMatches,\n normalizeLiveQueryWindowPageSize,\n resolveLiveQueryWindowInput,\n shouldPreserveLiveQueryWindowPageCount,\n} from '@tanstack/db'\nimport { useOptionalDbClient } from './DbProvider'\nimport {\n claimSourceIds,\n createSourceIdBindings,\n resumeSyncStarts,\n} from './source-id-bindings'\nimport {\n prepareDerivedQuery,\n prepareQueryValue,\n warnDeprecatedDepsArray,\n warnUnhashableDerivedIdentity,\n} from './useLiveQuery'\nimport type { SourceIdBindings } from './source-id-bindings'\nimport type {\n DerivedIdentityProfiler,\n LiveQueryKey,\n useLiveQuery,\n} from './useLiveQuery'\nimport type {\n Collection,\n CollectionImpl as CollectionImplType,\n CollectionStatus,\n Context,\n DbClient,\n InferResultType,\n InitialQueryBuilder,\n LiveQueryPersistedStatus,\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\n// Keep the generic parameter for existing typed config wrappers.\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 /** First result-page label, not a server cursor or remote offset. */\n initialPageParam?: number\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\nexport type UseLiveInfiniteQueryReturnWithCollection<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n> = {\n data: Array<TResult>\n state: Map<TKey, TResult>\n collection: Collection<TResult, TKey, TUtils> & NonSingleResult\n status: CollectionStatus\n isLoading: boolean\n isReady: boolean\n persistedStatus: LiveQueryPersistedStatus\n isPersistedReady: boolean\n persistedError: unknown | undefined\n isIdle: boolean\n isError: boolean\n isCleanedUp: boolean\n isEnabled: true\n pages: Array<Array<TResult>>\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\ntype RenderedWindowCollection = {\n client: DbClient | undefined\n dependencies: Array<unknown>\n collection: Collection<any, any, any>\n deferredCollections: Set<\n CollectionImplType<any, string | number, any, any, any>\n >\n}\n\n/**\n * Whether a recorded query identity matches the current one. Legacy deps\n * compare by reference. Derived identities and query keys compare by structure.\n */\nfunction queryIdentityMatches(\n comparison: { changed: boolean; structurallyEqual: boolean },\n sameClient: boolean,\n usesLegacyDeps: boolean,\n): boolean {\n return (\n sameClient &&\n (usesLegacyDeps ? !comparison.changed : comparison.structurallyEqual)\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 an optional initial page label\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): UseLiveInfiniteQueryReturnWithCollection<TResult, TKey, TUtils>\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 if (`getNextPageParam` in config) {\n throw new Error(\n `getNextPageParam is not supported by useLiveInfiniteQuery. Use an on-demand collection and fulfill meta.loadSubsetOptions in queryFn for server pagination.`,\n )\n }\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 // Like useLiveQuery's instance memo, keep the window collection built by the\n // latest render. A second render before commit, such as a StrictMode double\n // render, reuses it instead of starting another one. Only the collection is\n // kept. Each render still builds its own controller and page count from the\n // committed state, which an uncommitted render never changes.\n const renderedCollectionRef = useRef<RenderedWindowCollection | null>(null)\n const sourceIdsRef = useRef<SourceIdBindings | null>(null)\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 claimSourceIds(\n (sourceIdsRef.current ??= createSourceIdBindings()),\n preparation.value,\n dbClient,\n `useLiveInfiniteQuery`,\n () => resumeSyncStarts(deferredCollections),\n )\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 !queryIdentityMatches(dependencyComparison, sameClient, usesLegacyDeps)\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 let startInRender = false\n let suppliedCollection = false\n let createdCollection = false\n\n const canPreservePageCount = shouldPreserveLiveQueryWindowPageCount({\n hasPreviousController: committed !== null,\n previousInputKind: committed?.inputKind,\n inputKind,\n sameCollection:\n inputIsCollection && committed?.inputCollection === queryFnOrCollection,\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 // The peek-ahead window for every retained page.\n const requiredLimit = initialPageCount * pageSize + 1\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 rendered = renderedCollectionRef.current\n if (\n !inputIsCollection &&\n rendered !== null &&\n queryIdentityMatches(\n compareLiveQueryWindowDependencies(\n rendered.dependencies,\n identityDeps,\n ),\n rendered.client === dbClient,\n usesLegacyDeps,\n ) &&\n rendered.collection.status !== `cleaned-up` &&\n rendered.collection.status !== `error` &&\n liveQueryWindowMatches(rendered.collection, requiredLimit)\n ) {\n // An earlier render built this collection and nothing has committed\n // since. Its window is exactly the retained pages, so its first\n // rows are correct. Sources it deferred still resume at commit. A\n // collection in terminal error, such as after its source restarted,\n // cannot serve the query again, so a new one replaces it.\n collection = rendered.collection\n for (const deferred of rendered.deferredCollections) {\n deferredCollections.add(deferred)\n }\n } else {\n const input = resolveLiveQueryWindowInput<TContext>(inputValue)\n if (input.kind === `collection`) {\n collection = input.collection\n suppliedCollection = true\n // A supplied collection is never reused, so release the last one.\n renderedCollectionRef.current = null\n } else {\n // Wrap the query with the peek-ahead window for every retained page,\n // so a collection that starts syncing now never publishes fewer rows\n // than the controller's pages. The controller grows the limit via\n // setWindow.\n collection = createLiveQueryCollection({\n query: input.query.limit(requiredLimit).offset(0),\n gcTime: DEFAULT_GC_TIME_MS,\n })\n startInRender = true\n createdCollection = true\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 // Like useLiveQuery, start sync during render once the input is valid. A\n // render reads only rows the sources already hold, such as a preloaded\n // collection's, and sends no request until the subscription commits. GC\n // reclaims a render that never commits. A supplied window that the\n // controller must still adjust waits for the subscription.\n if (suppliedCollection) {\n startInRender = liveQueryWindowMatches(collection, requiredLimit)\n }\n if (startInRender) collection.startSyncImmediate()\n // Keep a created collection for the next render only once it validated and\n // started, so a retry after a startup error builds a new one and throws.\n if (createdCollection) {\n renderedCollectionRef.current = {\n client: dbClient,\n dependencies: [...identityDeps],\n collection,\n deferredCollections,\n }\n }\n\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 resumeSyncStarts(currentRenderState.deferredCollections)\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 get state() {\n return snapshot.state as EnabledLiveQueryReturn<TContext>[`state`]\n },\n status: snapshot.status as EnabledLiveQueryReturn<TContext>[`status`],\n isLoading: snapshot.isLoading,\n isReady: snapshot.isReady,\n persistedStatus: snapshot.persistedStatus,\n isPersistedReady: snapshot.isPersistedReady,\n persistedError: snapshot.persistedError,\n isIdle: snapshot.isIdle,\n isError: snapshot.isError,\n isCleanedUp: snapshot.isCleanedUp,\n collection:\n snapshot.collection as EnabledLiveQueryReturn<TContext>[`collection`],\n isEnabled: snapshot.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":["resumeSyncStarts"],"mappings":";;;;;;AAiDA;AACA;AA2FA;AAKE;AAIF;AAgCO;AAKL;AACE;AAAU;AACR;AAAA;AAGJ;AACA;AACA;AACA;AAEA;AAGA;AACA;AAMA;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;AAAA;AAC4B;AACd;AACZ;AACA;AAC0C;AAE5C;AACE;AAA2B;AAE3B;AACA;AAA2C;AAC7C;AACF;AAGF;AAEA;AAA6B;AAChB;AACX;AAEF;AACA;AAGA;AAEA;AAKA;AAIA;AAGA;AACA;AACE;AACA;AACA;AACA;AACA;AAEA;AAAoE;AAC7B;AACP;AAC9B;AAEsD;AACtD;AACA;AACA;AAEF;AAGA;AAEA;AAEA;AACE;AACA;AACE;AACE;AAAqB;AACnB;AACA;AACA;AAAA;AAGJ;AAAmB;AAErB;AACA;AAGE;AACE;AACW;AACT;AAAA;AAEkB;AACpB;AAWF;AACA;AACE;AAAgC;AAClC;AAEA;AACA;AACE;AACA;AAEA;AAAgC;AAMhC;AAAuC;AACW;AACxC;AAEV;AACA;AAAoB;AACtB;AACF;AAGF;AACE;AAAsE;AAGxE;AACE;AACmE;AAEnE;AAA0C;AAO5C;AACE;AAAgE;AAElE;AAGA;AACE;AAAgC;AACtB;AACsB;AAC9B;AACA;AAAA;AAIJ;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;AAEzCA;AACA;AAAO;AACT;AAC+B;AAEjC;AACA;AAEA;AAAsB;AACyB;AAClC;AAGb;AAAO;AACU;AAEb;AAAgB;AAClB;AACiB;AACG;AACF;AACQ;AACC;AACF;AACR;AACC;AACI;AAEX;AACS;AACJ;AACK;AACrB;AACsB;AACO;AACb;AAEpB;;;;"}
|
|
1
|
+
{"version":3,"file":"useLiveInfiniteQuery.js","sources":["../../src/useLiveInfiniteQuery.ts"],"sourcesContent":["'use client'\n\nimport { useCallback, useRef, useSyncExternalStore } from 'react'\nimport {\n bindLiveQueryWindow,\n createLiveQuerySourceIdClaims,\n fetchNextLiveQueryWindowPage,\n getLiveQueryKey,\n isCollection,\n readLiveQueryWindowPageShape,\n rejectLiveQueryInput,\n releaseDeferredOnReclaim,\n resumeDeferredLiveQueryCollections,\n} from '@tanstack/db'\nimport { useOptionalDbClient } from './DbProvider'\nimport {\n getIdentityScope,\n prepareDerivedQuery,\n prepareQueryValue,\n} from './useLiveQuery'\nimport type {\n DerivedIdentityProfiler,\n LiveQueryKey,\n useLiveQuery,\n} from './useLiveQuery'\nimport type {\n Collection,\n CollectionImpl as CollectionImplType,\n CollectionStatus,\n Context,\n DbClient,\n InferResultType,\n InitialQueryBuilder,\n LiveQuerySourceIdClaims,\n LiveQueryStatusFlags,\n LiveQueryWindowBinding,\n NonSingleResult,\n QueryBuilder,\n} from '@tanstack/db'\n\nconst unpreparedQueryValue = Symbol(`unpreparedQueryValue`)\n\n// Keep the generic parameter for existing typed config wrappers.\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 /** First result-page label, not a server cursor or remote offset. */\n initialPageParam?: number\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}\n\nexport type UseLiveInfiniteQueryReturnWithCollection<\n TResult extends object,\n TKey extends string | number,\n TUtils extends Record<string, any>,\n> = LiveQueryStatusFlags & {\n data: Array<TResult>\n state: Map<TKey, TResult>\n collection: Collection<TResult, TKey, TUtils> & NonSingleResult\n status: CollectionStatus\n isEnabled: true\n pages: Array<Array<TResult>>\n pageParams: Array<number>\n fetchNextPage: () => Promise<void>\n hasNextPage: boolean\n isFetchingNextPage: boolean\n}\n\ntype EnabledLiveQueryReturn<TContext extends Context> = ReturnType<\n typeof useLiveQuery<TContext>\n>\n\n/** One render's window binding and what its commit still owes. */\ntype InfiniteQueryRender = {\n binding: LiveQueryWindowBinding\n /** The query callback whose identity this render derived, if any. */\n derivedQuery: unknown\n deferredCollections: Set<\n CollectionImplType<any, string | number, any, any, any>\n >\n /** Drops the reclaim release when this render commits. */\n cancelReclaimRelease: (() => void) | undefined\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 an optional initial page label\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): UseLiveInfiniteQueryReturnWithCollection<TResult, TKey, TUtils>\n\n// Overload for query function\nexport function useLiveInfiniteQuery<TContext extends Context>(\n queryFn: (q: InitialQueryBuilder) => QueryBuilder<TContext>,\n config: UseLiveInfiniteQueryConfig<TContext>,\n): UseLiveInfiniteQueryReturn<TContext>\n\n// Implementation\nexport function useLiveInfiniteQuery<TContext extends Context>(\n queryFnOrCollection: any,\n config: UseLiveInfiniteQueryConfig<TContext>,\n): UseLiveInfiniteQueryReturn<TContext> {\n const pageShape = readLiveQueryWindowPageShape(config)\n const contextDbClient = useOptionalDbClient()\n const dbClient = config.client ?? contextDbClient\n const inputIsCollection = isCollection(queryFnOrCollection)\n\n const committedRef = useRef<InfiniteQueryRender | null>(null)\n const committed = committedRef.current\n // Like useLiveQuery's instance memo, keep the latest render whose binding\n // built a window collection. A second render before commit, such as a\n // StrictMode double render, reuses that collection instead of starting\n // another. Each render still builds its own controller and page count from\n // the committed binding, which an uncommitted render never changes.\n const renderedRef = useRef<InfiniteQueryRender | null>(null)\n const sourceIdsRef = useRef<LiveQuerySourceIdClaims | null>(null)\n const derivedIdentityProfilerRef = useRef<DerivedIdentityProfiler>({\n renderCount: 0,\n totalMs: 0,\n maxMs: 0,\n warned: false,\n })\n const deferredCollections = new Set<\n CollectionImplType<any, string | number, any, any, any>\n >()\n const identityScope = () =>\n getIdentityScope(\n (sourceIdsRef.current ??= createLiveQuerySourceIdClaims()),\n dbClient,\n `useLiveInfiniteQuery`,\n )\n\n const queryKey = getLiveQueryKey(config)\n let preparedQueryValue: unknown | typeof unpreparedQueryValue =\n unpreparedQueryValue\n let dependencies: ReadonlyArray<unknown> | undefined\n let derivedQuery: unknown = undefined\n\n const rendered = renderedRef.current\n let binding: LiveQueryWindowBinding\n // The render boundary: a render that throws anywhere after preparation,\n // such as an unhashable, single-result, or uncompilable query, never\n // commits, so it releases the sync starts it deferred here.\n try {\n if (!inputIsCollection) {\n if (queryKey !== undefined) {\n dependencies = queryKey\n } else if (\n committed !== null &&\n committed.derivedQuery === queryFnOrCollection &&\n committed.binding.scope === dbClient\n ) {\n // The same callback in the same client scope keeps its identity.\n derivedQuery = queryFnOrCollection\n dependencies = committed.binding.dependencies\n } else {\n const preparation = prepareDerivedQuery(\n queryFnOrCollection,\n derivedIdentityProfilerRef.current,\n identityScope(),\n deferredCollections,\n )\n preparedQueryValue = preparation.value\n derivedQuery = queryFnOrCollection\n dependencies = preparation.identityDeps\n }\n }\n\n // The binding calls this only when it compiles a new window collection.\n const compileQuery = () => {\n if (preparedQueryValue === unpreparedQueryValue) {\n preparedQueryValue = prepareQueryValue(\n queryFnOrCollection,\n dbClient,\n deferredCollections,\n )\n }\n return preparedQueryValue\n }\n\n binding = bindLiveQueryWindow(\n committed?.binding ?? null,\n {\n ...pageShape,\n input:\n typeof queryFnOrCollection === `function`\n ? compileQuery\n : queryFnOrCollection,\n dependencies,\n scope: dbClient,\n client: dbClient,\n queryKey,\n recompile: `identity`,\n },\n rendered?.binding,\n )\n } catch (error) {\n rejectLiveQueryInput(deferredCollections, error)\n }\n\n let currentRenderState = committed\n if (binding !== committed?.binding) {\n const newCollection = binding.collection !== committed?.binding.collection\n if (newCollection && binding.collection === rendered?.binding.collection) {\n // Sources the earlier render deferred still resume at this commit.\n for (const deferred of rendered.deferredCollections) {\n deferredCollections.add(deferred)\n }\n }\n const reusesRendered =\n newCollection && binding.collection === rendered?.binding.collection\n currentRenderState = {\n binding,\n derivedQuery,\n deferredCollections,\n // A render that never commits releases its deferred sync starts when\n // garbage collection reclaims the window collection it built. A reused\n // collection keeps the release its first render registered.\n cancelReclaimRelease: reusesRendered\n ? rendered.cancelReclaimRelease\n : newCollection && binding.ownsCollection\n ? releaseDeferredOnReclaim(binding.collection, deferredCollections)\n : undefined,\n warned: false,\n }\n // Keep a created collection for the next render only once it validated\n // and started, so a retry after a startup error builds a new one.\n if (binding.supplied) renderedRef.current = null\n else if (newCollection && binding.ownsCollection) {\n renderedRef.current = currentRenderState\n }\n }\n const renderState = currentRenderState!\n const controller = renderState.binding.controller\n\n const subscribe = useCallback(\n (onStoreChange: () => void) => {\n const unsubscribe = controller.subscribe(onStoreChange)\n committedRef.current = renderState\n if (renderState.binding.warning && !renderState.warned) {\n renderState.warned = true\n console.warn(renderState.binding.warning)\n }\n // A commit releases the starts itself, so it drops the reclaim release.\n renderState.cancelReclaimRelease?.()\n resumeDeferredLiveQueryCollections(renderState.deferredCollections)\n return unsubscribe\n },\n [controller, renderState],\n )\n const getSnapshot = useCallback(() => controller.getSnapshot(), [controller])\n const snapshot = useSyncExternalStore(subscribe, getSnapshot, () =>\n controller.getServerSnapshot(),\n )\n\n const fetchNextPage = useCallback(\n () => fetchNextLiveQueryWindowPage(controller),\n [controller],\n )\n\n return {\n data: snapshot.data as InferResultType<TContext>,\n get state() {\n return snapshot.state as EnabledLiveQueryReturn<TContext>[`state`]\n },\n status: snapshot.status as EnabledLiveQueryReturn<TContext>[`status`],\n isLoading: snapshot.isLoading,\n isReady: snapshot.isReady,\n isIdle: snapshot.isIdle,\n isLoadingError: snapshot.isLoadingError,\n isCrashed: snapshot.isCrashed,\n isCleanedUp: snapshot.isCleanedUp,\n isSyncError: snapshot.isSyncError,\n collection:\n snapshot.collection as EnabledLiveQueryReturn<TContext>[`collection`],\n isEnabled: snapshot.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 health: snapshot.health,\n }\n}\n"],"names":[],"mappings":";;;;;AAwCA;AA2FO;AAIL;AACA;AACA;AACA;AAEA;AACA;AAMA;AACA;AACA;AAAmE;AACpD;AACJ;AACF;AACC;AAEV;AAGA;AACE;AAC4B;AAC1B;AACA;AAGJ;AACA;AAEA;AACA;AAEA;AACA;AAIA;AACE;AACE;AACE;AAAe;AAOf;AACA;AAAiC;AAEjC;AAAoB;AAClB;AAC2B;AAC3B;AACA;AAEF;AACA;AACA;AAA2B;AAC7B;AAIF;AACE;AACE;AAAqB;AACnB;AACA;AACA;AAAA;AAGJ;AAAO;AAGT;AAAU;AACc;AACtB;AACK;AAIG;AACN;AACO;AACC;AACR;AACW;AAAA;AAEH;AAAA;AAGZ;AAA+C;AAGjD;AACA;AACE;AACA;AAEE;AACE;AAAgC;AAClC;AAEF;AAEA;AAAqB;AACnB;AACA;AACA;AAAA;AAAA;AAAA;AAQM;AACE;AAIV;AAA4C;AAE1C;AAAsB;AACxB;AAEF;AACA;AAEA;AAAkB;AAEd;AACA;AACA;AACE;AACA;AAAwC;AAG1C;AACA;AACA;AAAO;AACT;AACwB;AAE1B;AACA;AAAiB;AAAqB;AAAW;AACpC;AAGb;AAAsB;AACyB;AAClC;AAGb;AAAO;AACU;AAEb;AAAgB;AAClB;AACiB;AACG;AACF;AACD;AACQ;AACL;AACE;AACA;AAEX;AACS;AACJ;AACK;AACrB;AACsB;AACO;AACZ;AAErB;;;;"}
|