@tanstack/solid-query-persist-client 5.0.0-beta.21

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021-present Tanner Linsley
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/build/dev.cjs ADDED
@@ -0,0 +1,56 @@
1
+ 'use strict';
2
+
3
+ var queryPersistClientCore = require('@tanstack/query-persist-client-core');
4
+ var web = require('solid-js/web');
5
+ var solidJs = require('solid-js');
6
+ var solidQuery = require('@tanstack/solid-query');
7
+
8
+ // src/index.ts
9
+ exports.PersistQueryClientProvider = (props) => {
10
+ const [isRestoring, setIsRestoring] = solidJs.createSignal(true);
11
+ let unsub;
12
+ solidJs.createComputed((cleanup) => {
13
+ cleanup?.();
14
+ let isStale = false;
15
+ setIsRestoring(true);
16
+ const [unsubscribe, promise] = queryPersistClientCore.persistQueryClient({
17
+ ...props.persistOptions,
18
+ queryClient: props.client
19
+ });
20
+ promise.then(async () => {
21
+ if (isStale)
22
+ return;
23
+ try {
24
+ await props.onSuccess?.();
25
+ } finally {
26
+ setIsRestoring(false);
27
+ }
28
+ });
29
+ unsub = () => {
30
+ isStale = true;
31
+ unsubscribe();
32
+ };
33
+ return unsub;
34
+ });
35
+ solidJs.onCleanup(() => unsub?.());
36
+ return web.createComponent(solidQuery.QueryClientProvider, {
37
+ get client() {
38
+ return props.client;
39
+ },
40
+ get children() {
41
+ return web.createComponent(solidQuery.IsRestoringProvider, {
42
+ value: isRestoring,
43
+ get children() {
44
+ return props.children;
45
+ }
46
+ });
47
+ }
48
+ });
49
+ };
50
+
51
+ Object.keys(queryPersistClientCore).forEach(function (k) {
52
+ if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
53
+ enumerable: true,
54
+ get: function () { return queryPersistClientCore[k]; }
55
+ });
56
+ });
package/build/dev.js ADDED
@@ -0,0 +1,50 @@
1
+ import { persistQueryClient } from '@tanstack/query-persist-client-core';
2
+ export * from '@tanstack/query-persist-client-core';
3
+ import { createComponent } from 'solid-js/web';
4
+ import { createSignal, createComputed, onCleanup } from 'solid-js';
5
+ import { QueryClientProvider, IsRestoringProvider } from '@tanstack/solid-query';
6
+
7
+ // src/index.ts
8
+ var PersistQueryClientProvider = (props) => {
9
+ const [isRestoring, setIsRestoring] = createSignal(true);
10
+ let unsub;
11
+ createComputed((cleanup) => {
12
+ cleanup?.();
13
+ let isStale = false;
14
+ setIsRestoring(true);
15
+ const [unsubscribe, promise] = persistQueryClient({
16
+ ...props.persistOptions,
17
+ queryClient: props.client
18
+ });
19
+ promise.then(async () => {
20
+ if (isStale)
21
+ return;
22
+ try {
23
+ await props.onSuccess?.();
24
+ } finally {
25
+ setIsRestoring(false);
26
+ }
27
+ });
28
+ unsub = () => {
29
+ isStale = true;
30
+ unsubscribe();
31
+ };
32
+ return unsub;
33
+ });
34
+ onCleanup(() => unsub?.());
35
+ return createComponent(QueryClientProvider, {
36
+ get client() {
37
+ return props.client;
38
+ },
39
+ get children() {
40
+ return createComponent(IsRestoringProvider, {
41
+ value: isRestoring,
42
+ get children() {
43
+ return props.children;
44
+ }
45
+ });
46
+ }
47
+ });
48
+ };
49
+
50
+ export { PersistQueryClientProvider };
@@ -0,0 +1,56 @@
1
+ 'use strict';
2
+
3
+ var queryPersistClientCore = require('@tanstack/query-persist-client-core');
4
+ var web = require('solid-js/web');
5
+ var solidJs = require('solid-js');
6
+ var solidQuery = require('@tanstack/solid-query');
7
+
8
+ // src/index.ts
9
+ exports.PersistQueryClientProvider = (props) => {
10
+ const [isRestoring, setIsRestoring] = solidJs.createSignal(true);
11
+ let unsub;
12
+ solidJs.createComputed((cleanup) => {
13
+ cleanup?.();
14
+ let isStale = false;
15
+ setIsRestoring(true);
16
+ const [unsubscribe, promise] = queryPersistClientCore.persistQueryClient({
17
+ ...props.persistOptions,
18
+ queryClient: props.client
19
+ });
20
+ promise.then(async () => {
21
+ if (isStale)
22
+ return;
23
+ try {
24
+ await props.onSuccess?.();
25
+ } finally {
26
+ setIsRestoring(false);
27
+ }
28
+ });
29
+ unsub = () => {
30
+ isStale = true;
31
+ unsubscribe();
32
+ };
33
+ return unsub;
34
+ });
35
+ solidJs.onCleanup(() => unsub?.());
36
+ return web.createComponent(solidQuery.QueryClientProvider, {
37
+ get client() {
38
+ return props.client;
39
+ },
40
+ get children() {
41
+ return web.createComponent(solidQuery.IsRestoringProvider, {
42
+ value: isRestoring,
43
+ get children() {
44
+ return props.children;
45
+ }
46
+ });
47
+ }
48
+ });
49
+ };
50
+
51
+ Object.keys(queryPersistClientCore).forEach(function (k) {
52
+ if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
53
+ enumerable: true,
54
+ get: function () { return queryPersistClientCore[k]; }
55
+ });
56
+ });
@@ -0,0 +1,12 @@
1
+ import { PersistQueryClientOptions } from '@tanstack/query-persist-client-core';
2
+ export * from '@tanstack/query-persist-client-core';
3
+ import { QueryClientProviderProps } from '@tanstack/solid-query';
4
+ import { JSX } from 'solid-js';
5
+
6
+ type PersistQueryClientProviderProps = QueryClientProviderProps & {
7
+ persistOptions: Omit<PersistQueryClientOptions, 'queryClient'>;
8
+ onSuccess?: () => void;
9
+ };
10
+ declare const PersistQueryClientProvider: (props: PersistQueryClientProviderProps) => JSX.Element;
11
+
12
+ export { PersistQueryClientProvider, PersistQueryClientProviderProps };
@@ -0,0 +1,12 @@
1
+ import { PersistQueryClientOptions } from '@tanstack/query-persist-client-core';
2
+ export * from '@tanstack/query-persist-client-core';
3
+ import { QueryClientProviderProps } from '@tanstack/solid-query';
4
+ import { JSX } from 'solid-js';
5
+
6
+ type PersistQueryClientProviderProps = QueryClientProviderProps & {
7
+ persistOptions: Omit<PersistQueryClientOptions, 'queryClient'>;
8
+ onSuccess?: () => void;
9
+ };
10
+ declare const PersistQueryClientProvider: (props: PersistQueryClientProviderProps) => JSX.Element;
11
+
12
+ export { PersistQueryClientProvider, PersistQueryClientProviderProps };
package/build/index.js ADDED
@@ -0,0 +1,50 @@
1
+ import { persistQueryClient } from '@tanstack/query-persist-client-core';
2
+ export * from '@tanstack/query-persist-client-core';
3
+ import { createComponent } from 'solid-js/web';
4
+ import { createSignal, createComputed, onCleanup } from 'solid-js';
5
+ import { QueryClientProvider, IsRestoringProvider } from '@tanstack/solid-query';
6
+
7
+ // src/index.ts
8
+ var PersistQueryClientProvider = (props) => {
9
+ const [isRestoring, setIsRestoring] = createSignal(true);
10
+ let unsub;
11
+ createComputed((cleanup) => {
12
+ cleanup?.();
13
+ let isStale = false;
14
+ setIsRestoring(true);
15
+ const [unsubscribe, promise] = persistQueryClient({
16
+ ...props.persistOptions,
17
+ queryClient: props.client
18
+ });
19
+ promise.then(async () => {
20
+ if (isStale)
21
+ return;
22
+ try {
23
+ await props.onSuccess?.();
24
+ } finally {
25
+ setIsRestoring(false);
26
+ }
27
+ });
28
+ unsub = () => {
29
+ isStale = true;
30
+ unsubscribe();
31
+ };
32
+ return unsub;
33
+ });
34
+ onCleanup(() => unsub?.());
35
+ return createComponent(QueryClientProvider, {
36
+ get client() {
37
+ return props.client;
38
+ },
39
+ get children() {
40
+ return createComponent(IsRestoringProvider, {
41
+ value: isRestoring,
42
+ get children() {
43
+ return props.children;
44
+ }
45
+ });
46
+ }
47
+ });
48
+ };
49
+
50
+ export { PersistQueryClientProvider };
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@tanstack/solid-query-persist-client",
3
+ "version": "5.0.0-beta.21",
4
+ "description": "Solid.js bindings to work with persisters in TanStack/solid-query",
5
+ "author": "tannerlinsley",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/TanStack/query.git",
10
+ "directory": "packages/solid-query-persist-client"
11
+ },
12
+ "homepage": "https://tanstack.com/query",
13
+ "funding": {
14
+ "type": "github",
15
+ "url": "https://github.com/sponsors/tannerlinsley"
16
+ },
17
+ "type": "module",
18
+ "types": "./build/index.d.ts",
19
+ "main": "./build/index.cjs",
20
+ "module": "./build/index.js",
21
+ "exports": {
22
+ "development": {
23
+ "import": {
24
+ "types": "./build/index.d.ts",
25
+ "default": "./build/dev.js"
26
+ },
27
+ "require": {
28
+ "types": "./build/index.d.cts",
29
+ "default": "./build/dev.cjs"
30
+ }
31
+ },
32
+ "import": {
33
+ "types": "./build/index.d.ts",
34
+ "default": "./build/index.js"
35
+ },
36
+ "require": {
37
+ "types": "./build/index.d.cts",
38
+ "default": "./build/index.cjs"
39
+ }
40
+ },
41
+ "sideEffects": false,
42
+ "files": [
43
+ "build",
44
+ "src"
45
+ ],
46
+ "dependencies": {
47
+ "@tanstack/query-persist-client-core": "5.0.0-beta.20"
48
+ },
49
+ "devDependencies": {
50
+ "tsup-preset-solid": "^2.0.1",
51
+ "vite-plugin-solid": "^2.7.0",
52
+ "solid-js": "^1.7.8"
53
+ },
54
+ "peerDependencies": {
55
+ "solid-js": "^1.6.0",
56
+ "@tanstack/solid-query": "5.0.0-beta.21"
57
+ },
58
+ "scripts": {
59
+ "clean": "rimraf ./build && rimraf ./coverage",
60
+ "test:eslint": "eslint --ext .ts,.tsx ./src",
61
+ "test:types": "tsc",
62
+ "test:lib": "vitest run --coverage",
63
+ "test:lib:dev": "vitest watch --coverage",
64
+ "test:build": "publint --strict",
65
+ "build": "tsup"
66
+ }
67
+ }
@@ -0,0 +1,52 @@
1
+ import { persistQueryClient } from '@tanstack/query-persist-client-core'
2
+ import { createComputed, createSignal, onCleanup } from 'solid-js'
3
+ import { IsRestoringProvider, QueryClientProvider } from '@tanstack/solid-query'
4
+ import type { PersistQueryClientOptions } from '@tanstack/query-persist-client-core'
5
+ import type { QueryClientProviderProps } from '@tanstack/solid-query'
6
+ import type { JSX } from 'solid-js'
7
+
8
+ export type PersistQueryClientProviderProps = QueryClientProviderProps & {
9
+ persistOptions: Omit<PersistQueryClientOptions, 'queryClient'>
10
+ onSuccess?: () => void
11
+ }
12
+
13
+ export const PersistQueryClientProvider = (
14
+ props: PersistQueryClientProviderProps,
15
+ ): JSX.Element => {
16
+ const [isRestoring, setIsRestoring] = createSignal(true)
17
+
18
+ let unsub: undefined | (() => void)
19
+ createComputed<() => void>((cleanup) => {
20
+ cleanup?.()
21
+ let isStale = false
22
+ setIsRestoring(true)
23
+ const [unsubscribe, promise] = persistQueryClient({
24
+ ...props.persistOptions,
25
+ queryClient: props.client,
26
+ })
27
+
28
+ promise.then(async () => {
29
+ if (isStale) return
30
+ try {
31
+ await props.onSuccess?.()
32
+ } finally {
33
+ setIsRestoring(false)
34
+ }
35
+ })
36
+
37
+ unsub = () => {
38
+ isStale = true
39
+ unsubscribe()
40
+ }
41
+ return unsub
42
+ })
43
+
44
+ onCleanup(() => unsub?.())
45
+ return (
46
+ <QueryClientProvider client={props.client}>
47
+ <IsRestoringProvider value={isRestoring}>
48
+ {props.children}
49
+ </IsRestoringProvider>
50
+ </QueryClientProvider>
51
+ )
52
+ }
@@ -0,0 +1,582 @@
1
+ import '@testing-library/jest-dom'
2
+ import { render, screen, waitFor } from '@solidjs/testing-library'
3
+ import { QueryClient, createQueries, createQuery } from '@tanstack/solid-query'
4
+ import { persistQueryClientSave } from '@tanstack/query-persist-client-core'
5
+ import { createEffect, createSignal, onMount } from 'solid-js'
6
+
7
+ import { PersistQueryClientProvider } from '../PersistQueryClientProvider'
8
+ import { createQueryClient, queryKey, sleep } from './utils'
9
+ import type {
10
+ PersistedClient,
11
+ Persister,
12
+ } from '@tanstack/query-persist-client-core'
13
+
14
+ const createMockPersister = (): Persister => {
15
+ let storedState: PersistedClient | undefined
16
+
17
+ return {
18
+ async persistClient(persistClient: PersistedClient) {
19
+ storedState = persistClient
20
+ },
21
+ async restoreClient() {
22
+ await sleep(10)
23
+ return storedState
24
+ },
25
+ removeClient() {
26
+ storedState = undefined
27
+ },
28
+ }
29
+ }
30
+
31
+ const createMockErrorPersister = (
32
+ removeClient: Persister['removeClient'],
33
+ ): [Error, Persister] => {
34
+ const error = new Error('restore failed')
35
+ return [
36
+ error,
37
+ {
38
+ async persistClient() {
39
+ // noop
40
+ },
41
+ async restoreClient() {
42
+ await sleep(10)
43
+ throw error
44
+ },
45
+ removeClient,
46
+ },
47
+ ]
48
+ }
49
+
50
+ describe('PersistQueryClientProvider', () => {
51
+ test('restores cache from persister', async () => {
52
+ const key = queryKey()
53
+ const states: Array<{
54
+ status: string
55
+ fetchStatus: string
56
+ data: string | undefined
57
+ }> = []
58
+
59
+ const queryClient = createQueryClient()
60
+ await queryClient.prefetchQuery({
61
+ queryKey: key,
62
+ queryFn: () => Promise.resolve('hydrated'),
63
+ })
64
+
65
+ const persister = createMockPersister()
66
+
67
+ await persistQueryClientSave({ queryClient, persister })
68
+
69
+ queryClient.clear()
70
+
71
+ function Page() {
72
+ const state = createQuery(() => ({
73
+ queryKey: key,
74
+ queryFn: async () => {
75
+ await sleep(10)
76
+ return 'fetched'
77
+ },
78
+ }))
79
+ createEffect(() =>
80
+ states.push({
81
+ status: state.status,
82
+ fetchStatus: state.fetchStatus,
83
+ data: state.data,
84
+ }),
85
+ )
86
+
87
+ return (
88
+ <div>
89
+ <h1>{state.data}</h1>
90
+ <h2>fetchStatus: {state.fetchStatus}</h2>
91
+ </div>
92
+ )
93
+ }
94
+
95
+ render(() => (
96
+ <PersistQueryClientProvider
97
+ client={queryClient}
98
+ persistOptions={{ persister }}
99
+ >
100
+ <Page />
101
+ </PersistQueryClientProvider>
102
+ ))
103
+
104
+ await waitFor(() => screen.getByText('fetchStatus: idle'))
105
+ await waitFor(() => screen.getByText('hydrated'))
106
+ await waitFor(() => screen.getByText('fetched'))
107
+
108
+ expect(states).toHaveLength(3)
109
+
110
+ expect(states[0]).toMatchObject({
111
+ status: 'pending',
112
+ fetchStatus: 'idle',
113
+ data: undefined,
114
+ })
115
+
116
+ expect(states[1]).toMatchObject({
117
+ status: 'success',
118
+ fetchStatus: 'fetching',
119
+ data: 'hydrated',
120
+ })
121
+
122
+ expect(states[2]).toMatchObject({
123
+ status: 'success',
124
+ fetchStatus: 'idle',
125
+ data: 'fetched',
126
+ })
127
+ })
128
+
129
+ test('should also put useQueries into idle state', async () => {
130
+ const key = queryKey()
131
+ const states: Array<{
132
+ status: string
133
+ fetchStatus: string
134
+ data: string | undefined
135
+ }> = []
136
+
137
+ const queryClient = createQueryClient()
138
+ await queryClient.prefetchQuery({
139
+ queryKey: key,
140
+ queryFn: () => Promise.resolve('hydrated'),
141
+ })
142
+
143
+ const persister = createMockPersister()
144
+
145
+ await persistQueryClientSave({ queryClient, persister })
146
+
147
+ queryClient.clear()
148
+
149
+ function Page() {
150
+ const [state] = createQueries(() => ({
151
+ queries: [
152
+ {
153
+ queryKey: key,
154
+ queryFn: async (): Promise<string> => {
155
+ await sleep(10)
156
+ return 'fetched'
157
+ },
158
+ },
159
+ ] as const,
160
+ }))
161
+
162
+ createEffect(() =>
163
+ states.push({
164
+ status: state.status,
165
+ fetchStatus: state.fetchStatus,
166
+ data: state.data,
167
+ }),
168
+ )
169
+
170
+ return (
171
+ <div>
172
+ <h1>{state.data}</h1>
173
+ <h2>fetchStatus: {state.fetchStatus}</h2>
174
+ </div>
175
+ )
176
+ }
177
+
178
+ render(() => (
179
+ <PersistQueryClientProvider
180
+ client={queryClient}
181
+ persistOptions={{ persister }}
182
+ >
183
+ <Page />
184
+ </PersistQueryClientProvider>
185
+ ))
186
+
187
+ await waitFor(() => screen.getByText('fetchStatus: idle'))
188
+ await waitFor(() => screen.getByText('hydrated'))
189
+ await waitFor(() => screen.getByText('fetched'))
190
+
191
+ expect(states).toHaveLength(3)
192
+
193
+ expect(states[0]).toMatchObject({
194
+ status: 'pending',
195
+ fetchStatus: 'idle',
196
+ data: undefined,
197
+ })
198
+
199
+ expect(states[1]).toMatchObject({
200
+ status: 'success',
201
+ fetchStatus: 'fetching',
202
+ data: 'hydrated',
203
+ })
204
+
205
+ expect(states[2]).toMatchObject({
206
+ status: 'success',
207
+ fetchStatus: 'idle',
208
+ data: 'fetched',
209
+ })
210
+ })
211
+
212
+ test('should show initialData while restoring', async () => {
213
+ const key = queryKey()
214
+ const states: Array<{
215
+ status: string
216
+ fetchStatus: string
217
+ data: string | undefined
218
+ }> = []
219
+
220
+ const queryClient = createQueryClient()
221
+ await queryClient.prefetchQuery({
222
+ queryKey: key,
223
+ queryFn: () => Promise.resolve('hydrated'),
224
+ })
225
+
226
+ const persister = createMockPersister()
227
+
228
+ await persistQueryClientSave({ queryClient, persister })
229
+
230
+ queryClient.clear()
231
+
232
+ function Page() {
233
+ const state = createQuery(() => ({
234
+ queryKey: key,
235
+ queryFn: async () => {
236
+ await sleep(10)
237
+ return 'fetched'
238
+ },
239
+ initialData: 'initial',
240
+ // make sure that initial data is older than the hydration data
241
+ // otherwise initialData would be newer and takes precedence
242
+ initialDataUpdatedAt: 1,
243
+ }))
244
+
245
+ createEffect(() =>
246
+ states.push({
247
+ status: state.status,
248
+ fetchStatus: state.fetchStatus,
249
+ data: state.data,
250
+ }),
251
+ )
252
+
253
+ return (
254
+ <div>
255
+ <h1>{state.data}</h1>
256
+ <h2>fetchStatus: {state.fetchStatus}</h2>
257
+ </div>
258
+ )
259
+ }
260
+
261
+ render(() => (
262
+ <PersistQueryClientProvider
263
+ client={queryClient}
264
+ persistOptions={{ persister }}
265
+ >
266
+ <Page />
267
+ </PersistQueryClientProvider>
268
+ ))
269
+
270
+ await waitFor(() => screen.getByText('initial'))
271
+ await waitFor(() => screen.getByText('hydrated'))
272
+ await waitFor(() => screen.getByText('fetched'))
273
+
274
+ expect(states).toHaveLength(3)
275
+
276
+ expect(states[0]).toMatchObject({
277
+ status: 'success',
278
+ fetchStatus: 'idle',
279
+ data: 'initial',
280
+ })
281
+
282
+ expect(states[1]).toMatchObject({
283
+ status: 'success',
284
+ fetchStatus: 'fetching',
285
+ data: 'hydrated',
286
+ })
287
+
288
+ expect(states[2]).toMatchObject({
289
+ status: 'success',
290
+ fetchStatus: 'idle',
291
+ data: 'fetched',
292
+ })
293
+ })
294
+
295
+ test('should not refetch after restoring when data is fresh', async () => {
296
+ const key = queryKey()
297
+ const states: Array<{
298
+ status: string
299
+ fetchStatus: string
300
+ data: string | undefined
301
+ }> = []
302
+
303
+ const queryClient = createQueryClient()
304
+ await queryClient.prefetchQuery({
305
+ queryKey: key,
306
+ queryFn: () => Promise.resolve('hydrated'),
307
+ })
308
+
309
+ const persister = createMockPersister()
310
+
311
+ await persistQueryClientSave({ queryClient, persister })
312
+
313
+ queryClient.clear()
314
+
315
+ let fetched = false
316
+
317
+ function Page() {
318
+ const state = createQuery(() => ({
319
+ queryKey: key,
320
+ queryFn: async () => {
321
+ fetched = true
322
+ await sleep(10)
323
+ return 'fetched'
324
+ },
325
+ staleTime: Infinity,
326
+ }))
327
+
328
+ createEffect(
329
+ () =>
330
+ states.push({
331
+ status: state.status,
332
+ fetchStatus: state.fetchStatus,
333
+ data: state.data,
334
+ }),
335
+ console.log(state.data),
336
+ )
337
+
338
+ return (
339
+ <div>
340
+ <h1>data: {state.data ?? 'null'}</h1>
341
+ <h2>fetchStatus: {state.fetchStatus}</h2>
342
+ </div>
343
+ )
344
+ }
345
+
346
+ render(() => (
347
+ <PersistQueryClientProvider
348
+ client={queryClient}
349
+ persistOptions={{ persister }}
350
+ >
351
+ <Page />
352
+ </PersistQueryClientProvider>
353
+ ))
354
+
355
+ await waitFor(() => screen.getByText('data: null'))
356
+ await waitFor(() => screen.getByText('data: hydrated'))
357
+
358
+ expect(states).toHaveLength(2)
359
+
360
+ expect(fetched).toBe(false)
361
+
362
+ expect(states[0]).toMatchObject({
363
+ status: 'pending',
364
+ fetchStatus: 'idle',
365
+ data: undefined,
366
+ })
367
+
368
+ expect(states[1]).toMatchObject({
369
+ status: 'success',
370
+ fetchStatus: 'idle',
371
+ data: 'hydrated',
372
+ })
373
+ })
374
+
375
+ test('should call onSuccess after successful restoring', async () => {
376
+ const key = queryKey()
377
+
378
+ const queryClient = createQueryClient()
379
+ await queryClient.prefetchQuery({
380
+ queryKey: key,
381
+ queryFn: () => Promise.resolve('hydrated'),
382
+ })
383
+
384
+ const persister = createMockPersister()
385
+
386
+ await persistQueryClientSave({ queryClient, persister })
387
+
388
+ queryClient.clear()
389
+
390
+ function Page() {
391
+ const state = createQuery(() => ({
392
+ queryKey: key,
393
+ queryFn: async () => {
394
+ await sleep(10)
395
+ return 'fetched'
396
+ },
397
+ }))
398
+
399
+ return (
400
+ <div>
401
+ <h1>{state.data}</h1>
402
+ <h2>fetchStatus: {state.fetchStatus}</h2>
403
+ </div>
404
+ )
405
+ }
406
+
407
+ const onSuccess = vi.fn()
408
+
409
+ render(() => (
410
+ <PersistQueryClientProvider
411
+ client={queryClient}
412
+ persistOptions={{ persister }}
413
+ onSuccess={onSuccess}
414
+ >
415
+ <Page />
416
+ </PersistQueryClientProvider>
417
+ ))
418
+ expect(onSuccess).toHaveBeenCalledTimes(0)
419
+
420
+ await waitFor(() => screen.getByText('hydrated'))
421
+ expect(onSuccess).toHaveBeenCalledTimes(1)
422
+ await waitFor(() => screen.getByText('fetched'))
423
+ })
424
+
425
+ test('should remove cache after non-successful restoring', async () => {
426
+ const key = queryKey()
427
+
428
+ const onErrorMock = vi
429
+ .spyOn(console, 'error')
430
+ .mockImplementation(() => undefined)
431
+ const queryClient = createQueryClient()
432
+ const removeClient = vi.fn()
433
+
434
+ const [error, persister] = createMockErrorPersister(removeClient)
435
+
436
+ function Page() {
437
+ const state = createQuery(() => ({
438
+ queryKey: key,
439
+ queryFn: async () => {
440
+ await sleep(10)
441
+ return 'fetched'
442
+ },
443
+ }))
444
+
445
+ return (
446
+ <div>
447
+ <h1>{state.data}</h1>
448
+ <h2>fetchStatus: {state.fetchStatus}</h2>
449
+ </div>
450
+ )
451
+ }
452
+
453
+ render(() => (
454
+ <PersistQueryClientProvider
455
+ client={queryClient}
456
+ persistOptions={{ persister }}
457
+ >
458
+ <Page />
459
+ </PersistQueryClientProvider>
460
+ ))
461
+
462
+ await waitFor(() => screen.getByText('fetched'))
463
+ expect(removeClient).toHaveBeenCalledTimes(1)
464
+ expect(onErrorMock).toHaveBeenCalledTimes(1)
465
+ expect(onErrorMock).toHaveBeenNthCalledWith(1, error)
466
+ onErrorMock.mockRestore()
467
+ })
468
+
469
+ test('should be able to persist into multiple clients', async () => {
470
+ const key = queryKey()
471
+ const states: Array<{
472
+ status: string
473
+ fetchStatus: string
474
+ data: string | undefined
475
+ }> = []
476
+
477
+ const queryClient = createQueryClient()
478
+ await queryClient.prefetchQuery({
479
+ queryKey: key,
480
+ queryFn: () => Promise.resolve('hydrated'),
481
+ })
482
+
483
+ const persister = createMockPersister()
484
+
485
+ await persistQueryClientSave({ queryClient, persister })
486
+
487
+ queryClient.clear()
488
+
489
+ const onSuccess = vi.fn()
490
+
491
+ const queryFn1 = vi.fn().mockImplementation(async () => {
492
+ await sleep(10)
493
+ return 'queryFn1'
494
+ })
495
+ const queryFn2 = vi.fn().mockImplementation(async () => {
496
+ await sleep(10)
497
+ return 'queryFn2'
498
+ })
499
+
500
+ function App() {
501
+ const [client, setClient] = createSignal(
502
+ new QueryClient({
503
+ defaultOptions: {
504
+ queries: {
505
+ queryFn: queryFn1,
506
+ },
507
+ },
508
+ }),
509
+ )
510
+
511
+ onMount(() => {
512
+ setClient(
513
+ new QueryClient({
514
+ defaultOptions: {
515
+ queries: {
516
+ queryFn: queryFn2,
517
+ },
518
+ },
519
+ }),
520
+ )
521
+ })
522
+
523
+ return (
524
+ <PersistQueryClientProvider
525
+ client={client()}
526
+ persistOptions={{ persister }}
527
+ onSuccess={onSuccess}
528
+ >
529
+ <Page />
530
+ </PersistQueryClientProvider>
531
+ )
532
+ }
533
+
534
+ function Page() {
535
+ const state = createQuery(() => ({ queryKey: key }))
536
+
537
+ createEffect(() =>
538
+ states.push({
539
+ status: state.status,
540
+ fetchStatus: state.fetchStatus,
541
+ data: state.data as string | undefined,
542
+ }),
543
+ )
544
+
545
+ return (
546
+ <div>
547
+ <h1>{String(state.data)}</h1>
548
+ <h2>fetchStatus: {state.fetchStatus}</h2>
549
+ </div>
550
+ )
551
+ }
552
+
553
+ render(() => <App />)
554
+
555
+ await waitFor(() => screen.getByText('hydrated'))
556
+ await waitFor(() => screen.getByText('queryFn2'))
557
+
558
+ expect(queryFn1).toHaveBeenCalledTimes(0)
559
+ expect(queryFn2).toHaveBeenCalledTimes(1)
560
+ expect(onSuccess).toHaveBeenCalledTimes(1)
561
+
562
+ expect(states).toHaveLength(3)
563
+
564
+ expect(states[0]).toMatchObject({
565
+ status: 'pending',
566
+ fetchStatus: 'idle',
567
+ data: undefined,
568
+ })
569
+
570
+ expect(states[1]).toMatchObject({
571
+ status: 'success',
572
+ fetchStatus: 'fetching',
573
+ data: 'hydrated',
574
+ })
575
+
576
+ expect(states[2]).toMatchObject({
577
+ status: 'success',
578
+ fetchStatus: 'idle',
579
+ data: 'queryFn2',
580
+ })
581
+ })
582
+ })
@@ -0,0 +1,18 @@
1
+ import { QueryClient } from '@tanstack/solid-query'
2
+ import type { QueryClientConfig } from '@tanstack/solid-query'
3
+
4
+ export function createQueryClient(config?: QueryClientConfig): QueryClient {
5
+ return new QueryClient(config)
6
+ }
7
+
8
+ let queryKeyCount = 0
9
+ export function queryKey(): Array<string> {
10
+ queryKeyCount++
11
+ return [`query_${queryKeyCount}`]
12
+ }
13
+
14
+ export function sleep(timeout: number): Promise<void> {
15
+ return new Promise((resolve, _reject) => {
16
+ setTimeout(resolve, timeout)
17
+ })
18
+ }
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ // Re-export core
2
+ export * from '@tanstack/query-persist-client-core'
3
+
4
+ export * from './PersistQueryClientProvider'