@tanstack/query-persist-client-core 5.59.17 → 5.59.20
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/query-persist-client-core",
|
|
3
|
-
"version": "5.59.
|
|
3
|
+
"version": "5.59.20",
|
|
4
4
|
"description": "Set of utilities for interacting with persisters, which can save your queryClient for later use",
|
|
5
5
|
"author": "tannerlinsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,10 +34,11 @@
|
|
|
34
34
|
"sideEffects": false,
|
|
35
35
|
"files": [
|
|
36
36
|
"build",
|
|
37
|
-
"src"
|
|
37
|
+
"src",
|
|
38
|
+
"!src/__tests__"
|
|
38
39
|
],
|
|
39
40
|
"dependencies": {
|
|
40
|
-
"@tanstack/query-core": "5.59.
|
|
41
|
+
"@tanstack/query-core": "5.59.20"
|
|
41
42
|
},
|
|
42
43
|
"scripts": {}
|
|
43
44
|
}
|
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test, vi } from 'vitest'
|
|
2
|
-
import { Query, QueryCache, hashKey } from '@tanstack/query-core'
|
|
3
|
-
import {
|
|
4
|
-
PERSISTER_KEY_PREFIX,
|
|
5
|
-
experimental_createPersister,
|
|
6
|
-
} from '../createPersister'
|
|
7
|
-
import { sleep } from './utils'
|
|
8
|
-
import type { StoragePersisterOptions } from '../createPersister'
|
|
9
|
-
import type { QueryKey } from '@tanstack/query-core'
|
|
10
|
-
|
|
11
|
-
function getFreshStorage() {
|
|
12
|
-
const storage = new Map()
|
|
13
|
-
return {
|
|
14
|
-
getItem: (key: string) => Promise.resolve(storage.get(key)),
|
|
15
|
-
setItem: async (key: string, value: unknown) => {
|
|
16
|
-
storage.set(key, value)
|
|
17
|
-
},
|
|
18
|
-
removeItem: async (key: string) => {
|
|
19
|
-
storage.delete(key)
|
|
20
|
-
},
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function setupPersister(
|
|
25
|
-
queryKey: QueryKey,
|
|
26
|
-
persisterOptions: StoragePersisterOptions,
|
|
27
|
-
) {
|
|
28
|
-
const context = {
|
|
29
|
-
meta: { foo: 'bar' },
|
|
30
|
-
queryKey,
|
|
31
|
-
// @ts-expect-error
|
|
32
|
-
signal: undefined as AbortSignal,
|
|
33
|
-
}
|
|
34
|
-
const queryHash = hashKey(queryKey)
|
|
35
|
-
const storageKey = `${PERSISTER_KEY_PREFIX}-${queryHash}`
|
|
36
|
-
|
|
37
|
-
const queryFn = vi.fn()
|
|
38
|
-
|
|
39
|
-
const persisterFn = experimental_createPersister(persisterOptions)
|
|
40
|
-
|
|
41
|
-
const query = new Query({
|
|
42
|
-
cache: new QueryCache(),
|
|
43
|
-
queryHash,
|
|
44
|
-
queryKey,
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
context,
|
|
49
|
-
persisterFn,
|
|
50
|
-
query,
|
|
51
|
-
queryFn,
|
|
52
|
-
queryHash,
|
|
53
|
-
queryKey,
|
|
54
|
-
storageKey,
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
describe('createPersister', () => {
|
|
59
|
-
test('should fetch if storage is not provided', async () => {
|
|
60
|
-
const { context, persisterFn, query, queryFn } = setupPersister(['foo'], {
|
|
61
|
-
storage: undefined,
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
await persisterFn(queryFn, context, query)
|
|
65
|
-
|
|
66
|
-
expect(queryFn).toHaveBeenCalledOnce()
|
|
67
|
-
expect(queryFn).toHaveBeenCalledWith(context)
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
test('should fetch if there is no stored data', async () => {
|
|
71
|
-
const storage = getFreshStorage()
|
|
72
|
-
const { context, persisterFn, query, queryFn } = setupPersister(['foo'], {
|
|
73
|
-
storage,
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
await persisterFn(queryFn, context, query)
|
|
77
|
-
|
|
78
|
-
expect(queryFn).toHaveBeenCalledOnce()
|
|
79
|
-
expect(queryFn).toHaveBeenCalledWith(context)
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
test('should fetch if query already has data', async () => {
|
|
83
|
-
const storage = getFreshStorage()
|
|
84
|
-
const { context, persisterFn, query, queryFn } = setupPersister(['foo'], {
|
|
85
|
-
storage,
|
|
86
|
-
})
|
|
87
|
-
query.state.data = 'baz'
|
|
88
|
-
|
|
89
|
-
await persisterFn(queryFn, context, query)
|
|
90
|
-
|
|
91
|
-
expect(queryFn).toHaveBeenCalledOnce()
|
|
92
|
-
expect(queryFn).toHaveBeenCalledWith(context)
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
test('should fetch if deserialization fails', async () => {
|
|
96
|
-
const storage = getFreshStorage()
|
|
97
|
-
const { context, persisterFn, query, queryFn, storageKey } = setupPersister(
|
|
98
|
-
['foo'],
|
|
99
|
-
{
|
|
100
|
-
storage,
|
|
101
|
-
},
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
await storage.setItem(storageKey, '{invalid[item')
|
|
105
|
-
|
|
106
|
-
await persisterFn(queryFn, context, query)
|
|
107
|
-
|
|
108
|
-
expect(await storage.getItem(storageKey)).toBeUndefined()
|
|
109
|
-
|
|
110
|
-
expect(queryFn).toHaveBeenCalledOnce()
|
|
111
|
-
expect(queryFn).toHaveBeenCalledWith(context)
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
test('should remove stored item if `dataUpdatedAt` is empty', async () => {
|
|
115
|
-
const storage = getFreshStorage()
|
|
116
|
-
const { context, persisterFn, query, queryFn, storageKey } = setupPersister(
|
|
117
|
-
['foo'],
|
|
118
|
-
{
|
|
119
|
-
storage,
|
|
120
|
-
},
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
await storage.setItem(
|
|
124
|
-
storageKey,
|
|
125
|
-
JSON.stringify({
|
|
126
|
-
buster: '',
|
|
127
|
-
state: { dataUpdatedAt: undefined },
|
|
128
|
-
}),
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
await persisterFn(queryFn, context, query)
|
|
132
|
-
|
|
133
|
-
expect(await storage.getItem(storageKey)).toBeUndefined()
|
|
134
|
-
|
|
135
|
-
expect(queryFn).toHaveBeenCalledOnce()
|
|
136
|
-
expect(queryFn).toHaveBeenCalledWith(context)
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
test('should remove stored item if its expired', async () => {
|
|
140
|
-
const storage = getFreshStorage()
|
|
141
|
-
const { context, persisterFn, query, queryFn, storageKey } = setupPersister(
|
|
142
|
-
['foo'],
|
|
143
|
-
{
|
|
144
|
-
storage,
|
|
145
|
-
maxAge: 100,
|
|
146
|
-
},
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
await storage.setItem(
|
|
150
|
-
storageKey,
|
|
151
|
-
JSON.stringify({
|
|
152
|
-
buster: '',
|
|
153
|
-
state: { dataUpdatedAt: Date.now() - 200 },
|
|
154
|
-
}),
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
await persisterFn(queryFn, context, query)
|
|
158
|
-
|
|
159
|
-
expect(await storage.getItem(storageKey)).toBeUndefined()
|
|
160
|
-
|
|
161
|
-
expect(queryFn).toHaveBeenCalledOnce()
|
|
162
|
-
expect(queryFn).toHaveBeenCalledWith(context)
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
test('should remove stored item if its busted', async () => {
|
|
166
|
-
const storage = getFreshStorage()
|
|
167
|
-
const { context, persisterFn, query, queryFn, storageKey } = setupPersister(
|
|
168
|
-
['foo'],
|
|
169
|
-
{
|
|
170
|
-
storage,
|
|
171
|
-
},
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
await storage.setItem(
|
|
175
|
-
storageKey,
|
|
176
|
-
JSON.stringify({
|
|
177
|
-
buster: 'bust',
|
|
178
|
-
state: { dataUpdatedAt: Date.now() },
|
|
179
|
-
}),
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
await persisterFn(queryFn, context, query)
|
|
183
|
-
|
|
184
|
-
expect(await storage.getItem(storageKey)).toBeUndefined()
|
|
185
|
-
|
|
186
|
-
expect(queryFn).toHaveBeenCalledOnce()
|
|
187
|
-
expect(queryFn).toHaveBeenCalledWith(context)
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
test('should restore item from the storage and set proper `updatedAt` values', async () => {
|
|
191
|
-
const storage = getFreshStorage()
|
|
192
|
-
const { context, persisterFn, query, queryFn, storageKey } = setupPersister(
|
|
193
|
-
['foo'],
|
|
194
|
-
{
|
|
195
|
-
storage,
|
|
196
|
-
},
|
|
197
|
-
)
|
|
198
|
-
|
|
199
|
-
const dataUpdatedAt = Date.now()
|
|
200
|
-
|
|
201
|
-
await storage.setItem(
|
|
202
|
-
storageKey,
|
|
203
|
-
JSON.stringify({
|
|
204
|
-
buster: '',
|
|
205
|
-
state: { dataUpdatedAt },
|
|
206
|
-
}),
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
await persisterFn(queryFn, context, query)
|
|
210
|
-
query.state.data = 'data0'
|
|
211
|
-
query.fetch = vi.fn()
|
|
212
|
-
expect(query.state.dataUpdatedAt).toEqual(0)
|
|
213
|
-
|
|
214
|
-
await sleep(0)
|
|
215
|
-
|
|
216
|
-
expect(queryFn).toHaveBeenCalledTimes(0)
|
|
217
|
-
expect(query.fetch).toHaveBeenCalledTimes(0)
|
|
218
|
-
expect(query.state.dataUpdatedAt).toEqual(dataUpdatedAt)
|
|
219
|
-
})
|
|
220
|
-
|
|
221
|
-
test('should restore item from the storage and refetch when `stale`', async () => {
|
|
222
|
-
const storage = getFreshStorage()
|
|
223
|
-
const { context, persisterFn, query, queryFn, storageKey } = setupPersister(
|
|
224
|
-
['foo'],
|
|
225
|
-
{
|
|
226
|
-
storage,
|
|
227
|
-
},
|
|
228
|
-
)
|
|
229
|
-
|
|
230
|
-
await storage.setItem(
|
|
231
|
-
storageKey,
|
|
232
|
-
JSON.stringify({
|
|
233
|
-
buster: '',
|
|
234
|
-
state: { dataUpdatedAt: Date.now() },
|
|
235
|
-
}),
|
|
236
|
-
)
|
|
237
|
-
|
|
238
|
-
await persisterFn(queryFn, context, query)
|
|
239
|
-
query.state.isInvalidated = true
|
|
240
|
-
query.fetch = vi.fn()
|
|
241
|
-
|
|
242
|
-
await sleep(0)
|
|
243
|
-
|
|
244
|
-
expect(queryFn).toHaveBeenCalledTimes(0)
|
|
245
|
-
expect(query.fetch).toHaveBeenCalledTimes(1)
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
test('should store item after successful fetch', async () => {
|
|
249
|
-
const storage = getFreshStorage()
|
|
250
|
-
const {
|
|
251
|
-
context,
|
|
252
|
-
persisterFn,
|
|
253
|
-
query,
|
|
254
|
-
queryFn,
|
|
255
|
-
queryHash,
|
|
256
|
-
queryKey,
|
|
257
|
-
storageKey,
|
|
258
|
-
} = setupPersister(['foo'], {
|
|
259
|
-
storage,
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
await persisterFn(queryFn, context, query)
|
|
263
|
-
query.setData('baz')
|
|
264
|
-
|
|
265
|
-
await sleep(0)
|
|
266
|
-
|
|
267
|
-
expect(queryFn).toHaveBeenCalledOnce()
|
|
268
|
-
expect(queryFn).toHaveBeenCalledWith(context)
|
|
269
|
-
|
|
270
|
-
expect(JSON.parse(await storage.getItem(storageKey))).toMatchObject({
|
|
271
|
-
buster: '',
|
|
272
|
-
queryHash,
|
|
273
|
-
queryKey,
|
|
274
|
-
state: {
|
|
275
|
-
data: 'baz',
|
|
276
|
-
},
|
|
277
|
-
})
|
|
278
|
-
})
|
|
279
|
-
|
|
280
|
-
test('should skip stored item if not matched by filters', async () => {
|
|
281
|
-
const storage = getFreshStorage()
|
|
282
|
-
const { context, persisterFn, query, queryFn, storageKey } = setupPersister(
|
|
283
|
-
['foo'],
|
|
284
|
-
{
|
|
285
|
-
storage,
|
|
286
|
-
filters: {
|
|
287
|
-
predicate: () => {
|
|
288
|
-
return false
|
|
289
|
-
},
|
|
290
|
-
},
|
|
291
|
-
},
|
|
292
|
-
)
|
|
293
|
-
|
|
294
|
-
const dataUpdatedAt = Date.now()
|
|
295
|
-
|
|
296
|
-
await storage.setItem(
|
|
297
|
-
storageKey,
|
|
298
|
-
JSON.stringify({
|
|
299
|
-
buster: '',
|
|
300
|
-
state: { dataUpdatedAt },
|
|
301
|
-
}),
|
|
302
|
-
)
|
|
303
|
-
|
|
304
|
-
await persisterFn(queryFn, context, query)
|
|
305
|
-
query.fetch = vi.fn()
|
|
306
|
-
|
|
307
|
-
await sleep(0)
|
|
308
|
-
|
|
309
|
-
expect(queryFn).toHaveBeenCalledTimes(1)
|
|
310
|
-
expect(query.fetch).toHaveBeenCalledTimes(0)
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
test('should restore item from the storage with async deserializer', async () => {
|
|
314
|
-
const storage = getFreshStorage()
|
|
315
|
-
const { context, persisterFn, query, queryFn, storageKey } = setupPersister(
|
|
316
|
-
['foo'],
|
|
317
|
-
{
|
|
318
|
-
storage,
|
|
319
|
-
deserialize: (cachedString: string) =>
|
|
320
|
-
new Promise((resolve) => resolve(JSON.parse(cachedString))),
|
|
321
|
-
},
|
|
322
|
-
)
|
|
323
|
-
|
|
324
|
-
await storage.setItem(
|
|
325
|
-
storageKey,
|
|
326
|
-
JSON.stringify({
|
|
327
|
-
buster: '',
|
|
328
|
-
state: { dataUpdatedAt: Date.now() },
|
|
329
|
-
}),
|
|
330
|
-
)
|
|
331
|
-
|
|
332
|
-
await persisterFn(queryFn, context, query)
|
|
333
|
-
query.state.isInvalidated = true
|
|
334
|
-
query.fetch = vi.fn()
|
|
335
|
-
|
|
336
|
-
await sleep(0)
|
|
337
|
-
|
|
338
|
-
expect(queryFn).toHaveBeenCalledTimes(0)
|
|
339
|
-
expect(query.fetch).toHaveBeenCalledTimes(1)
|
|
340
|
-
})
|
|
341
|
-
|
|
342
|
-
test('should store item after successful fetch with async serializer', async () => {
|
|
343
|
-
const storage = getFreshStorage()
|
|
344
|
-
const {
|
|
345
|
-
context,
|
|
346
|
-
persisterFn,
|
|
347
|
-
query,
|
|
348
|
-
queryFn,
|
|
349
|
-
queryHash,
|
|
350
|
-
queryKey,
|
|
351
|
-
storageKey,
|
|
352
|
-
} = setupPersister(['foo'], {
|
|
353
|
-
storage,
|
|
354
|
-
serialize: (persistedQuery) =>
|
|
355
|
-
new Promise((resolve) => resolve(JSON.stringify(persistedQuery))),
|
|
356
|
-
})
|
|
357
|
-
|
|
358
|
-
await persisterFn(queryFn, context, query)
|
|
359
|
-
query.setData('baz')
|
|
360
|
-
|
|
361
|
-
await sleep(0)
|
|
362
|
-
|
|
363
|
-
expect(queryFn).toHaveBeenCalledOnce()
|
|
364
|
-
expect(queryFn).toHaveBeenCalledWith(context)
|
|
365
|
-
|
|
366
|
-
expect(JSON.parse(await storage.getItem(storageKey))).toMatchObject({
|
|
367
|
-
buster: '',
|
|
368
|
-
queryHash,
|
|
369
|
-
queryKey,
|
|
370
|
-
state: {
|
|
371
|
-
data: 'baz',
|
|
372
|
-
},
|
|
373
|
-
})
|
|
374
|
-
})
|
|
375
|
-
})
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test, vi } from 'vitest'
|
|
2
|
-
import { QueriesObserver } from '@tanstack/query-core'
|
|
3
|
-
import { persistQueryClientSubscribe } from '../persist'
|
|
4
|
-
import {
|
|
5
|
-
createMockPersister,
|
|
6
|
-
createQueryClient,
|
|
7
|
-
createSpyPersister,
|
|
8
|
-
} from './utils'
|
|
9
|
-
|
|
10
|
-
describe('persistQueryClientSubscribe', () => {
|
|
11
|
-
test('should persist mutations', async () => {
|
|
12
|
-
const queryClient = createQueryClient()
|
|
13
|
-
|
|
14
|
-
const persister = createMockPersister()
|
|
15
|
-
|
|
16
|
-
const unsubscribe = persistQueryClientSubscribe({
|
|
17
|
-
queryClient,
|
|
18
|
-
persister,
|
|
19
|
-
dehydrateOptions: { shouldDehydrateMutation: () => true },
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
queryClient.getMutationCache().build(queryClient, {
|
|
23
|
-
mutationFn: async (text: string) => text,
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
const result = await persister.restoreClient()
|
|
27
|
-
|
|
28
|
-
expect(result?.clientState.mutations).toHaveLength(1)
|
|
29
|
-
|
|
30
|
-
unsubscribe()
|
|
31
|
-
})
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
describe('persistQueryClientSave', () => {
|
|
35
|
-
test('should not be triggered on observer type events', async () => {
|
|
36
|
-
const queryClient = createQueryClient()
|
|
37
|
-
|
|
38
|
-
const persister = createSpyPersister()
|
|
39
|
-
|
|
40
|
-
const unsubscribe = persistQueryClientSubscribe({
|
|
41
|
-
queryClient,
|
|
42
|
-
persister,
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
const queryKey = ['test']
|
|
46
|
-
const queryFn = vi.fn().mockReturnValue(1)
|
|
47
|
-
const observer = new QueriesObserver(queryClient, [{ queryKey, queryFn }])
|
|
48
|
-
const unsubscribeObserver = observer.subscribe(vi.fn())
|
|
49
|
-
observer
|
|
50
|
-
.getObservers()[0]
|
|
51
|
-
?.setOptions({ queryKey, refetchOnWindowFocus: false })
|
|
52
|
-
unsubscribeObserver()
|
|
53
|
-
|
|
54
|
-
queryClient.setQueryData(queryKey, 2)
|
|
55
|
-
|
|
56
|
-
// persistClient should be called 3 times:
|
|
57
|
-
// 1. When query is added
|
|
58
|
-
// 2. When queryFn is resolved
|
|
59
|
-
// 3. When setQueryData is called
|
|
60
|
-
// All events fired by manipulating observers are ignored
|
|
61
|
-
expect(persister.persistClient).toHaveBeenCalledTimes(3)
|
|
62
|
-
|
|
63
|
-
unsubscribe()
|
|
64
|
-
})
|
|
65
|
-
})
|
package/src/__tests__/utils.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { vi } from 'vitest'
|
|
2
|
-
import { QueryClient } from '@tanstack/query-core'
|
|
3
|
-
import type { QueryClientConfig } from '@tanstack/query-core'
|
|
4
|
-
import type { PersistedClient, Persister } from '../persist'
|
|
5
|
-
|
|
6
|
-
export function createQueryClient(config?: QueryClientConfig): QueryClient {
|
|
7
|
-
return new QueryClient(config)
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function sleep(timeout: number): Promise<void> {
|
|
11
|
-
return new Promise((resolve, _reject) => {
|
|
12
|
-
setTimeout(resolve, timeout)
|
|
13
|
-
})
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function createMockPersister(): Persister {
|
|
17
|
-
let storedState: PersistedClient | undefined
|
|
18
|
-
|
|
19
|
-
return {
|
|
20
|
-
async persistClient(persistClient: PersistedClient) {
|
|
21
|
-
storedState = persistClient
|
|
22
|
-
},
|
|
23
|
-
async restoreClient() {
|
|
24
|
-
await sleep(10)
|
|
25
|
-
return storedState
|
|
26
|
-
},
|
|
27
|
-
removeClient() {
|
|
28
|
-
storedState = undefined
|
|
29
|
-
},
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function createSpyPersister(): Persister {
|
|
34
|
-
return {
|
|
35
|
-
persistClient: vi.fn(),
|
|
36
|
-
restoreClient: vi.fn(),
|
|
37
|
-
removeClient: vi.fn(),
|
|
38
|
-
}
|
|
39
|
-
}
|