@tanstack/query-persist-client-core 5.76.0 → 5.76.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/build/legacy/createPersister.cjs +127 -37
- package/build/legacy/createPersister.cjs.map +1 -1
- package/build/legacy/createPersister.d.cts +11 -3
- package/build/legacy/createPersister.d.ts +11 -3
- package/build/legacy/createPersister.js +126 -36
- package/build/legacy/createPersister.js.map +1 -1
- package/build/legacy/index.d.cts +1 -1
- package/build/legacy/index.d.ts +1 -1
- package/build/modern/createPersister.cjs +127 -37
- package/build/modern/createPersister.cjs.map +1 -1
- package/build/modern/createPersister.d.cts +11 -3
- package/build/modern/createPersister.d.ts +11 -3
- package/build/modern/createPersister.js +126 -36
- package/build/modern/createPersister.js.map +1 -1
- package/build/modern/index.d.cts +1 -1
- package/build/modern/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/createPersister.ts +155 -43
package/src/createPersister.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { matchQuery } from '@tanstack/query-core'
|
|
2
2
|
import type {
|
|
3
3
|
Query,
|
|
4
|
+
QueryClient,
|
|
4
5
|
QueryFilters,
|
|
5
6
|
QueryFunctionContext,
|
|
6
7
|
QueryKey,
|
|
@@ -20,6 +21,7 @@ export interface AsyncStorage<TStorageValue = string> {
|
|
|
20
21
|
getItem: (key: string) => MaybePromise<TStorageValue | undefined | null>
|
|
21
22
|
setItem: (key: string, value: TStorageValue) => MaybePromise<unknown>
|
|
22
23
|
removeItem: (key: string) => MaybePromise<void>
|
|
24
|
+
entries?: () => MaybePromise<Array<[key: string, value: TStorageValue]>>
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export interface StoragePersisterOptions<TStorageValue = string> {
|
|
@@ -78,7 +80,7 @@ export const PERSISTER_KEY_PREFIX = 'tanstack-query'
|
|
|
78
80
|
})
|
|
79
81
|
```
|
|
80
82
|
*/
|
|
81
|
-
export function
|
|
83
|
+
export function experimental_createQueryPersister<TStorageValue = string>({
|
|
82
84
|
storage,
|
|
83
85
|
buster = '',
|
|
84
86
|
maxAge = 1000 * 60 * 60 * 24,
|
|
@@ -91,45 +93,42 @@ export function experimental_createPersister<TStorageValue = string>({
|
|
|
91
93
|
prefix = PERSISTER_KEY_PREFIX,
|
|
92
94
|
filters,
|
|
93
95
|
}: StoragePersisterOptions<TStorageValue>) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const storageKey = `${prefix}-${query.queryHash}`
|
|
100
|
-
const matchesFilter = filters ? matchQuery(filters, query) : true
|
|
96
|
+
function isExpiredOrBusted(persistedQuery: PersistedQuery) {
|
|
97
|
+
if (persistedQuery.state.dataUpdatedAt) {
|
|
98
|
+
const queryAge = Date.now() - persistedQuery.state.dataUpdatedAt
|
|
99
|
+
const expired = queryAge > maxAge
|
|
100
|
+
const busted = persistedQuery.buster !== buster
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
if (expired || busted) {
|
|
103
|
+
return true
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return false
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return true
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function retrieveQuery<T>(
|
|
113
|
+
queryHash: string,
|
|
114
|
+
afterRestoreMacroTask?: (persistedQuery: PersistedQuery) => void,
|
|
115
|
+
) {
|
|
116
|
+
if (storage != null) {
|
|
117
|
+
const storageKey = `${prefix}-${queryHash}`
|
|
104
118
|
try {
|
|
105
119
|
const storedData = await storage.getItem(storageKey)
|
|
106
120
|
if (storedData) {
|
|
107
121
|
const persistedQuery = await deserialize(storedData)
|
|
108
122
|
|
|
109
|
-
if (persistedQuery
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (expired || busted) {
|
|
114
|
-
await storage.removeItem(storageKey)
|
|
115
|
-
} else {
|
|
123
|
+
if (isExpiredOrBusted(persistedQuery)) {
|
|
124
|
+
await storage.removeItem(storageKey)
|
|
125
|
+
} else {
|
|
126
|
+
if (afterRestoreMacroTask) {
|
|
116
127
|
// Just after restoring we want to get fresh data from the server if it's stale
|
|
117
|
-
setTimeout(() =>
|
|
118
|
-
// Set proper updatedAt, since resolving in the first pass overrides those values
|
|
119
|
-
query.setState({
|
|
120
|
-
dataUpdatedAt: persistedQuery.state.dataUpdatedAt,
|
|
121
|
-
errorUpdatedAt: persistedQuery.state.errorUpdatedAt,
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
if (query.isStale()) {
|
|
125
|
-
query.fetch()
|
|
126
|
-
}
|
|
127
|
-
}, 0)
|
|
128
|
-
// We must resolve the promise here, as otherwise we will have `loading` state in the app until `queryFn` resolves
|
|
129
|
-
return Promise.resolve(persistedQuery.state.data as T)
|
|
128
|
+
setTimeout(() => afterRestoreMacroTask(persistedQuery), 0)
|
|
130
129
|
}
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
// We must resolve the promise here, as otherwise we will have `loading` state in the app until `queryFn` resolves
|
|
131
|
+
return persistedQuery.state.data as T
|
|
133
132
|
}
|
|
134
133
|
}
|
|
135
134
|
} catch (err) {
|
|
@@ -143,24 +142,137 @@ export function experimental_createPersister<TStorageValue = string>({
|
|
|
143
142
|
}
|
|
144
143
|
}
|
|
145
144
|
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function persistQueryByKey(
|
|
149
|
+
queryKey: QueryKey,
|
|
150
|
+
queryClient: QueryClient,
|
|
151
|
+
) {
|
|
152
|
+
if (storage != null) {
|
|
153
|
+
const query = queryClient.getQueryCache().find({ queryKey })
|
|
154
|
+
if (query) {
|
|
155
|
+
await persistQuery(query)
|
|
156
|
+
} else {
|
|
157
|
+
if (process.env.NODE_ENV === 'development') {
|
|
158
|
+
console.warn(
|
|
159
|
+
'Could not find query to be persisted. QueryKey:',
|
|
160
|
+
JSON.stringify(queryKey),
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function persistQuery(query: Query) {
|
|
168
|
+
if (storage != null) {
|
|
169
|
+
const storageKey = `${prefix}-${query.queryHash}`
|
|
170
|
+
storage.setItem(
|
|
171
|
+
storageKey,
|
|
172
|
+
await serialize({
|
|
173
|
+
state: query.state,
|
|
174
|
+
queryKey: query.queryKey,
|
|
175
|
+
queryHash: query.queryHash,
|
|
176
|
+
buster: buster,
|
|
177
|
+
}),
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async function persisterFn<T, TQueryKey extends QueryKey>(
|
|
183
|
+
queryFn: (context: QueryFunctionContext<TQueryKey>) => T | Promise<T>,
|
|
184
|
+
ctx: QueryFunctionContext<TQueryKey>,
|
|
185
|
+
query: Query,
|
|
186
|
+
) {
|
|
187
|
+
const matchesFilter = filters ? matchQuery(filters, query) : true
|
|
188
|
+
|
|
189
|
+
// Try to restore only if we do not have any data in the cache and we have persister defined
|
|
190
|
+
if (matchesFilter && query.state.data === undefined && storage != null) {
|
|
191
|
+
const restoredData = await retrieveQuery(
|
|
192
|
+
query.queryHash,
|
|
193
|
+
(persistedQuery: PersistedQuery) => {
|
|
194
|
+
// Set proper updatedAt, since resolving in the first pass overrides those values
|
|
195
|
+
query.setState({
|
|
196
|
+
dataUpdatedAt: persistedQuery.state.dataUpdatedAt,
|
|
197
|
+
errorUpdatedAt: persistedQuery.state.errorUpdatedAt,
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
if (query.isStale()) {
|
|
201
|
+
query.fetch()
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
if (restoredData != null) {
|
|
207
|
+
return Promise.resolve(restoredData as T)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
146
211
|
// If we did not restore, or restoration failed - fetch
|
|
147
|
-
const queryFnResult = await queryFn(
|
|
212
|
+
const queryFnResult = await queryFn(ctx)
|
|
148
213
|
|
|
149
214
|
if (matchesFilter && storage != null) {
|
|
150
215
|
// Persist if we have storage defined, we use timeout to get proper state to be persisted
|
|
151
|
-
setTimeout(
|
|
152
|
-
|
|
153
|
-
storageKey,
|
|
154
|
-
await serialize({
|
|
155
|
-
state: query.state,
|
|
156
|
-
queryKey: query.queryKey,
|
|
157
|
-
queryHash: query.queryHash,
|
|
158
|
-
buster: buster,
|
|
159
|
-
}),
|
|
160
|
-
)
|
|
216
|
+
setTimeout(() => {
|
|
217
|
+
persistQuery(query)
|
|
161
218
|
}, 0)
|
|
162
219
|
}
|
|
163
220
|
|
|
164
221
|
return Promise.resolve(queryFnResult)
|
|
165
222
|
}
|
|
223
|
+
|
|
224
|
+
async function persisterGc() {
|
|
225
|
+
if (storage?.entries) {
|
|
226
|
+
const entries = await storage.entries()
|
|
227
|
+
for (const [key, value] of entries) {
|
|
228
|
+
if (key.startsWith(prefix)) {
|
|
229
|
+
const persistedQuery = await deserialize(value)
|
|
230
|
+
|
|
231
|
+
if (isExpiredOrBusted(persistedQuery)) {
|
|
232
|
+
await storage.removeItem(key)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
} else if (process.env.NODE_ENV === 'development') {
|
|
237
|
+
throw new Error(
|
|
238
|
+
'Provided storage does not implement `entries` method. Garbage collection is not possible without ability to iterate over storage items.',
|
|
239
|
+
)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function persisterRestoreAll(queryClient: QueryClient) {
|
|
244
|
+
if (storage?.entries) {
|
|
245
|
+
const entries = await storage.entries()
|
|
246
|
+
for (const [key, value] of entries) {
|
|
247
|
+
if (key.startsWith(prefix)) {
|
|
248
|
+
const persistedQuery = await deserialize(value)
|
|
249
|
+
|
|
250
|
+
if (isExpiredOrBusted(persistedQuery)) {
|
|
251
|
+
await storage.removeItem(key)
|
|
252
|
+
} else {
|
|
253
|
+
queryClient.setQueryData(
|
|
254
|
+
persistedQuery.queryKey,
|
|
255
|
+
persistedQuery.state.data,
|
|
256
|
+
{
|
|
257
|
+
updatedAt: persistedQuery.state.dataUpdatedAt,
|
|
258
|
+
},
|
|
259
|
+
)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
} else if (process.env.NODE_ENV === 'development') {
|
|
264
|
+
throw new Error(
|
|
265
|
+
'Provided storage does not implement `entries` method. Restoration of all stored entries is not possible without ability to iterate over storage items.',
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
persisterFn,
|
|
272
|
+
persistQuery,
|
|
273
|
+
persistQueryByKey,
|
|
274
|
+
retrieveQuery,
|
|
275
|
+
persisterGc,
|
|
276
|
+
persisterRestoreAll,
|
|
277
|
+
}
|
|
166
278
|
}
|