@tanstack/svelte-query 4.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/build/lib/.gitignore +9 -0
- package/build/lib/Hydrate.svelte +7 -0
- package/build/lib/Hydrate.svelte.d.ts +20 -0
- package/build/lib/QueryClientProvider.svelte +14 -0
- package/build/lib/QueryClientProvider.svelte.d.ts +19 -0
- package/build/lib/context.d.ts +5 -0
- package/build/lib/context.js +14 -0
- package/build/lib/createBaseQuery.d.ts +5 -0
- package/build/lib/createBaseQuery.js +35 -0
- package/build/lib/createInfiniteQuery.d.ts +5 -0
- package/build/lib/createInfiniteQuery.js +6 -0
- package/build/lib/createMutation.d.ts +6 -0
- package/build/lib/createMutation.js +27 -0
- package/build/lib/createQueries.d.ts +48 -0
- package/build/lib/createQueries.js +27 -0
- package/build/lib/createQuery.d.ts +23 -0
- package/build/lib/createQuery.js +7 -0
- package/build/lib/index.d.ts +12 -0
- package/build/lib/index.js +15 -0
- package/build/lib/types.d.ts +40 -0
- package/build/lib/types.js +1 -0
- package/build/lib/useHydrate.d.ts +2 -0
- package/build/lib/useHydrate.js +8 -0
- package/build/lib/useIsFetching.d.ts +4 -0
- package/build/lib/useIsFetching.js +21 -0
- package/build/lib/useIsMutating.d.ts +4 -0
- package/build/lib/useIsMutating.js +21 -0
- package/build/lib/useQueryClient.d.ts +2 -0
- package/build/lib/useQueryClient.js +5 -0
- package/package.json +54 -0
- package/src/__tests__/CreateMutation.svelte +13 -0
- package/src/__tests__/CreateQuery.svelte +23 -0
- package/src/__tests__/createMutation.test.ts +22 -0
- package/src/__tests__/createQuery.test.ts +28 -0
- package/src/__tests__/utils.ts +72 -0
- package/src/lib/Hydrate.svelte +11 -0
- package/src/lib/QueryClientProvider.svelte +19 -0
- package/src/lib/context.ts +21 -0
- package/src/lib/createBaseQuery.ts +76 -0
- package/src/lib/createInfiniteQuery.ts +103 -0
- package/src/lib/createMutation.ts +110 -0
- package/src/lib/createQueries.ts +178 -0
- package/src/lib/createQuery.ts +143 -0
- package/src/lib/index.ts +17 -0
- package/src/lib/types.ts +146 -0
- package/src/lib/useHydrate.ts +14 -0
- package/src/lib/useIsFetching.ts +41 -0
- package/src/lib/useIsMutating.ts +41 -0
- package/src/lib/useQueryClient.ts +7 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { render, screen } from '@testing-library/svelte'
|
|
3
|
+
import CreateQuery from './CreateQuery.svelte'
|
|
4
|
+
import { sleep } from './utils'
|
|
5
|
+
|
|
6
|
+
describe('createQuery', () => {
|
|
7
|
+
it('Render and wait for success', async () => {
|
|
8
|
+
render(CreateQuery, {
|
|
9
|
+
props: {
|
|
10
|
+
queryKey: ['test'],
|
|
11
|
+
queryFn: async () => {
|
|
12
|
+
await sleep(100)
|
|
13
|
+
return 'Success'
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
expect(screen.queryByText('Loading')).toBeInTheDocument()
|
|
19
|
+
expect(screen.queryByText('Error')).not.toBeInTheDocument()
|
|
20
|
+
expect(screen.queryByText('Success')).not.toBeInTheDocument()
|
|
21
|
+
|
|
22
|
+
await sleep(200)
|
|
23
|
+
|
|
24
|
+
expect(screen.queryByText('Success')).toBeInTheDocument()
|
|
25
|
+
expect(screen.queryByText('Loading')).not.toBeInTheDocument()
|
|
26
|
+
expect(screen.queryByText('Error')).not.toBeInTheDocument()
|
|
27
|
+
})
|
|
28
|
+
})
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { vi } from 'vitest'
|
|
2
|
+
import { act } from '@testing-library/svelte'
|
|
3
|
+
import {
|
|
4
|
+
QueryClient,
|
|
5
|
+
type QueryClientConfig,
|
|
6
|
+
type MutationOptions,
|
|
7
|
+
} from '../lib'
|
|
8
|
+
|
|
9
|
+
export function createQueryClient(config?: QueryClientConfig): QueryClient {
|
|
10
|
+
vi.spyOn(console, 'error').mockImplementation(() => undefined)
|
|
11
|
+
return new QueryClient({ logger: mockLogger, ...config })
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function mockVisibilityState(value: DocumentVisibilityState) {
|
|
15
|
+
return vi.spyOn(document, 'visibilityState', 'get').mockReturnValue(value)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function mockNavigatorOnLine(value: boolean) {
|
|
19
|
+
return vi.spyOn(navigator, 'onLine', 'get').mockReturnValue(value)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const mockLogger = {
|
|
23
|
+
log: vi.fn(),
|
|
24
|
+
warn: vi.fn(),
|
|
25
|
+
error: vi.fn(),
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let queryKeyCount = 0
|
|
29
|
+
export function queryKey(): Array<string> {
|
|
30
|
+
queryKeyCount++
|
|
31
|
+
return [`query_${queryKeyCount}`]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function sleep(timeout: number): Promise<void> {
|
|
35
|
+
return new Promise((resolve, _reject) => {
|
|
36
|
+
setTimeout(resolve, timeout)
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function simplefetcher() {
|
|
41
|
+
await sleep(10)
|
|
42
|
+
return 'test'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function setActTimeout(fn: () => void, ms?: number) {
|
|
46
|
+
return setTimeout(() => {
|
|
47
|
+
act(() => {
|
|
48
|
+
fn()
|
|
49
|
+
})
|
|
50
|
+
}, ms)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Assert the parameter is of a specific type.
|
|
55
|
+
*/
|
|
56
|
+
export function expectType<T>(_: T): void {
|
|
57
|
+
return undefined
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Assert the parameter is not typed as `any`
|
|
62
|
+
*/
|
|
63
|
+
export function expectTypeNotAny<T>(_: 0 extends 1 & T ? never : T): void {
|
|
64
|
+
return undefined
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function executeMutation(
|
|
68
|
+
queryClient: QueryClient,
|
|
69
|
+
options: MutationOptions<any, any, any, any>,
|
|
70
|
+
): Promise<unknown> {
|
|
71
|
+
return queryClient.getMutationCache().build(queryClient, options).execute()
|
|
72
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { DehydratedState, HydrateOptions } from '@tanstack/query-core'
|
|
3
|
+
import { useHydrate } from './useHydrate'
|
|
4
|
+
|
|
5
|
+
export let state: DehydratedState
|
|
6
|
+
export let options: HydrateOptions | undefined = undefined
|
|
7
|
+
|
|
8
|
+
useHydrate(state, options)
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<slot />
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount, onDestroy } from 'svelte'
|
|
3
|
+
import { QueryClient } from '@tanstack/query-core'
|
|
4
|
+
import { setQueryClientContext } from './context'
|
|
5
|
+
|
|
6
|
+
export let client = new QueryClient()
|
|
7
|
+
|
|
8
|
+
onMount(() => {
|
|
9
|
+
client.mount()
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
setQueryClientContext(client)
|
|
13
|
+
|
|
14
|
+
onDestroy(() => {
|
|
15
|
+
client.unmount()
|
|
16
|
+
})
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<slot />
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { setContext, getContext } from 'svelte'
|
|
2
|
+
import type { QueryClient } from '@tanstack/query-core'
|
|
3
|
+
|
|
4
|
+
const _contextKey = '$$_queryClient'
|
|
5
|
+
|
|
6
|
+
/** Retrieves a Client from Svelte's context */
|
|
7
|
+
export const getQueryClientContext = (): QueryClient => {
|
|
8
|
+
const client = getContext(_contextKey)
|
|
9
|
+
if (!client) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
'No QueryClient was found in Svelte context. Did you forget to wrap your component with QueryClientProvider?',
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return client as QueryClient
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Sets a QueryClient on Svelte's context */
|
|
19
|
+
export const setQueryClientContext = (client: QueryClient): void => {
|
|
20
|
+
setContext(_contextKey, client)
|
|
21
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {
|
|
2
|
+
notifyManager,
|
|
3
|
+
type QueryKey,
|
|
4
|
+
type QueryObserver,
|
|
5
|
+
} from '@tanstack/query-core'
|
|
6
|
+
import type { CreateBaseQueryOptions } from './types'
|
|
7
|
+
import { useQueryClient } from './useQueryClient'
|
|
8
|
+
import { derived, readable } from 'svelte/store'
|
|
9
|
+
|
|
10
|
+
export function createBaseQuery<
|
|
11
|
+
TQueryFnData,
|
|
12
|
+
TError,
|
|
13
|
+
TData,
|
|
14
|
+
TQueryData,
|
|
15
|
+
TQueryKey extends QueryKey,
|
|
16
|
+
>(
|
|
17
|
+
options: CreateBaseQueryOptions<
|
|
18
|
+
TQueryFnData,
|
|
19
|
+
TError,
|
|
20
|
+
TData,
|
|
21
|
+
TQueryData,
|
|
22
|
+
TQueryKey
|
|
23
|
+
>,
|
|
24
|
+
Observer: typeof QueryObserver,
|
|
25
|
+
) {
|
|
26
|
+
const queryClient = useQueryClient()
|
|
27
|
+
const defaultedOptions = queryClient.defaultQueryOptions(options)
|
|
28
|
+
defaultedOptions._optimisticResults = 'optimistic'
|
|
29
|
+
|
|
30
|
+
let observer = new Observer<
|
|
31
|
+
TQueryFnData,
|
|
32
|
+
TError,
|
|
33
|
+
TData,
|
|
34
|
+
TQueryData,
|
|
35
|
+
TQueryKey
|
|
36
|
+
>(queryClient, defaultedOptions)
|
|
37
|
+
|
|
38
|
+
// Include callbacks in batch renders
|
|
39
|
+
if (defaultedOptions.onError) {
|
|
40
|
+
defaultedOptions.onError = notifyManager.batchCalls(
|
|
41
|
+
defaultedOptions.onError,
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (defaultedOptions.onSuccess) {
|
|
46
|
+
defaultedOptions.onSuccess = notifyManager.batchCalls(
|
|
47
|
+
defaultedOptions.onSuccess,
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (defaultedOptions.onSettled) {
|
|
52
|
+
defaultedOptions.onSettled = notifyManager.batchCalls(
|
|
53
|
+
defaultedOptions.onSettled,
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
readable(observer).subscribe(($observer) => {
|
|
58
|
+
observer = $observer
|
|
59
|
+
// Do not notify on updates because of changes in the options because
|
|
60
|
+
// these changes should already be reflected in the optimistic result.
|
|
61
|
+
observer.setOptions(defaultedOptions, { listeners: false })
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const result = readable(observer.getCurrentResult(), (set) => {
|
|
65
|
+
return observer.subscribe(notifyManager.batchCalls(set))
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const { subscribe } = derived(result, ($result) => {
|
|
69
|
+
$result = observer.getOptimisticResult(defaultedOptions)
|
|
70
|
+
return !defaultedOptions.notifyOnChangeProps
|
|
71
|
+
? observer.trackResult($result)
|
|
72
|
+
: $result
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
return { subscribe }
|
|
76
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InfiniteQueryObserver,
|
|
3
|
+
parseQueryArgs,
|
|
4
|
+
type QueryObserver,
|
|
5
|
+
type QueryFunction,
|
|
6
|
+
type QueryKey,
|
|
7
|
+
} from '@tanstack/query-core'
|
|
8
|
+
import type {
|
|
9
|
+
CreateInfiniteQueryOptions,
|
|
10
|
+
CreateInfiniteQueryResult,
|
|
11
|
+
} from './types'
|
|
12
|
+
import { createBaseQuery } from './createBaseQuery'
|
|
13
|
+
|
|
14
|
+
export function createInfiniteQuery<
|
|
15
|
+
TQueryFnData = unknown,
|
|
16
|
+
TError = unknown,
|
|
17
|
+
TData = TQueryFnData,
|
|
18
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
19
|
+
>(
|
|
20
|
+
options: CreateInfiniteQueryOptions<
|
|
21
|
+
TQueryFnData,
|
|
22
|
+
TError,
|
|
23
|
+
TData,
|
|
24
|
+
TQueryFnData,
|
|
25
|
+
TQueryKey
|
|
26
|
+
>,
|
|
27
|
+
): CreateInfiniteQueryResult<TData, TError>
|
|
28
|
+
export function createInfiniteQuery<
|
|
29
|
+
TQueryFnData = unknown,
|
|
30
|
+
TError = unknown,
|
|
31
|
+
TData = TQueryFnData,
|
|
32
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
33
|
+
>(
|
|
34
|
+
queryKey: TQueryKey,
|
|
35
|
+
options?: Omit<
|
|
36
|
+
CreateInfiniteQueryOptions<
|
|
37
|
+
TQueryFnData,
|
|
38
|
+
TError,
|
|
39
|
+
TData,
|
|
40
|
+
TQueryFnData,
|
|
41
|
+
TQueryKey
|
|
42
|
+
>,
|
|
43
|
+
'queryKey'
|
|
44
|
+
>,
|
|
45
|
+
): CreateInfiniteQueryResult<TData, TError>
|
|
46
|
+
export function createInfiniteQuery<
|
|
47
|
+
TQueryFnData = unknown,
|
|
48
|
+
TError = unknown,
|
|
49
|
+
TData = TQueryFnData,
|
|
50
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
51
|
+
>(
|
|
52
|
+
queryKey: TQueryKey,
|
|
53
|
+
queryFn: QueryFunction<TQueryFnData, TQueryKey>,
|
|
54
|
+
options?: Omit<
|
|
55
|
+
CreateInfiniteQueryOptions<
|
|
56
|
+
TQueryFnData,
|
|
57
|
+
TError,
|
|
58
|
+
TData,
|
|
59
|
+
TQueryFnData,
|
|
60
|
+
TQueryKey
|
|
61
|
+
>,
|
|
62
|
+
'queryKey' | 'queryFn'
|
|
63
|
+
>,
|
|
64
|
+
): CreateInfiniteQueryResult<TData, TError>
|
|
65
|
+
|
|
66
|
+
export function createInfiniteQuery<
|
|
67
|
+
TQueryFnData,
|
|
68
|
+
TError,
|
|
69
|
+
TData = TQueryFnData,
|
|
70
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
71
|
+
>(
|
|
72
|
+
arg1:
|
|
73
|
+
| TQueryKey
|
|
74
|
+
| CreateInfiniteQueryOptions<
|
|
75
|
+
TQueryFnData,
|
|
76
|
+
TError,
|
|
77
|
+
TData,
|
|
78
|
+
TQueryFnData,
|
|
79
|
+
TQueryKey
|
|
80
|
+
>,
|
|
81
|
+
arg2?:
|
|
82
|
+
| QueryFunction<TQueryFnData, TQueryKey>
|
|
83
|
+
| CreateInfiniteQueryOptions<
|
|
84
|
+
TQueryFnData,
|
|
85
|
+
TError,
|
|
86
|
+
TData,
|
|
87
|
+
TQueryFnData,
|
|
88
|
+
TQueryKey
|
|
89
|
+
>,
|
|
90
|
+
arg3?: CreateInfiniteQueryOptions<
|
|
91
|
+
TQueryFnData,
|
|
92
|
+
TError,
|
|
93
|
+
TData,
|
|
94
|
+
TQueryFnData,
|
|
95
|
+
TQueryKey
|
|
96
|
+
>,
|
|
97
|
+
): CreateInfiniteQueryResult<TData, TError> {
|
|
98
|
+
const options = parseQueryArgs(arg1, arg2, arg3)
|
|
99
|
+
return createBaseQuery(
|
|
100
|
+
options,
|
|
101
|
+
InfiniteQueryObserver as typeof QueryObserver,
|
|
102
|
+
) as CreateInfiniteQueryResult<TData, TError>
|
|
103
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { readable, derived } from 'svelte/store'
|
|
2
|
+
import {
|
|
3
|
+
type MutationFunction,
|
|
4
|
+
type MutationKey,
|
|
5
|
+
MutationObserver,
|
|
6
|
+
notifyManager,
|
|
7
|
+
parseMutationArgs,
|
|
8
|
+
} from '@tanstack/query-core'
|
|
9
|
+
import type {
|
|
10
|
+
UseMutateFunction,
|
|
11
|
+
CreateMutationOptions,
|
|
12
|
+
MutationStoreResult,
|
|
13
|
+
} from './types'
|
|
14
|
+
import { useQueryClient } from './useQueryClient'
|
|
15
|
+
|
|
16
|
+
export function createMutation<
|
|
17
|
+
TData = unknown,
|
|
18
|
+
TError = unknown,
|
|
19
|
+
TVariables = unknown,
|
|
20
|
+
TContext = unknown,
|
|
21
|
+
>(
|
|
22
|
+
options: CreateMutationOptions<TData, TError, TVariables, TContext>,
|
|
23
|
+
): MutationStoreResult<TData, TError, TVariables, TContext>
|
|
24
|
+
|
|
25
|
+
export function createMutation<
|
|
26
|
+
TData = unknown,
|
|
27
|
+
TError = unknown,
|
|
28
|
+
TVariables = unknown,
|
|
29
|
+
TContext = unknown,
|
|
30
|
+
>(
|
|
31
|
+
mutationFn: MutationFunction<TData, TVariables>,
|
|
32
|
+
options?: Omit<
|
|
33
|
+
CreateMutationOptions<TData, TError, TVariables, TContext>,
|
|
34
|
+
'mutationFn'
|
|
35
|
+
>,
|
|
36
|
+
): MutationStoreResult<TData, TError, TVariables, TContext>
|
|
37
|
+
|
|
38
|
+
export function createMutation<
|
|
39
|
+
TData = unknown,
|
|
40
|
+
TError = unknown,
|
|
41
|
+
TVariables = unknown,
|
|
42
|
+
TContext = unknown,
|
|
43
|
+
>(
|
|
44
|
+
mutationKey: MutationKey,
|
|
45
|
+
options?: Omit<
|
|
46
|
+
CreateMutationOptions<TData, TError, TVariables, TContext>,
|
|
47
|
+
'mutationKey'
|
|
48
|
+
>,
|
|
49
|
+
): MutationStoreResult<TData, TError, TVariables, TContext>
|
|
50
|
+
|
|
51
|
+
export function createMutation<
|
|
52
|
+
TData = unknown,
|
|
53
|
+
TError = unknown,
|
|
54
|
+
TVariables = unknown,
|
|
55
|
+
TContext = unknown,
|
|
56
|
+
>(
|
|
57
|
+
mutationKey: MutationKey,
|
|
58
|
+
mutationFn?: MutationFunction<TData, TVariables>,
|
|
59
|
+
options?: Omit<
|
|
60
|
+
CreateMutationOptions<TData, TError, TVariables, TContext>,
|
|
61
|
+
'mutationKey' | 'mutationFn'
|
|
62
|
+
>,
|
|
63
|
+
): MutationStoreResult<TData, TError, TVariables, TContext>
|
|
64
|
+
|
|
65
|
+
export function createMutation<
|
|
66
|
+
TData = unknown,
|
|
67
|
+
TError = unknown,
|
|
68
|
+
TVariables = unknown,
|
|
69
|
+
TContext = unknown,
|
|
70
|
+
>(
|
|
71
|
+
arg1:
|
|
72
|
+
| MutationKey
|
|
73
|
+
| MutationFunction<TData, TVariables>
|
|
74
|
+
| CreateMutationOptions<TData, TError, TVariables, TContext>,
|
|
75
|
+
arg2?:
|
|
76
|
+
| MutationFunction<TData, TVariables>
|
|
77
|
+
| CreateMutationOptions<TData, TError, TVariables, TContext>,
|
|
78
|
+
arg3?: CreateMutationOptions<TData, TError, TVariables, TContext>,
|
|
79
|
+
): MutationStoreResult<TData, TError, TVariables, TContext> {
|
|
80
|
+
const options = parseMutationArgs(arg1, arg2, arg3)
|
|
81
|
+
const queryClient = useQueryClient()
|
|
82
|
+
let observer = new MutationObserver<TData, TError, TVariables, TContext>(
|
|
83
|
+
queryClient,
|
|
84
|
+
options,
|
|
85
|
+
)
|
|
86
|
+
let mutate: UseMutateFunction<TData, TError, TVariables, TContext>
|
|
87
|
+
|
|
88
|
+
readable(observer).subscribe(($observer) => {
|
|
89
|
+
observer = $observer
|
|
90
|
+
mutate = (variables, mutateOptions) => {
|
|
91
|
+
observer.mutate(variables, mutateOptions).catch(noop)
|
|
92
|
+
}
|
|
93
|
+
observer.setOptions(options)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
const result = readable(observer.getCurrentResult(), (set) => {
|
|
97
|
+
return observer.subscribe(notifyManager.batchCalls((val) => set(val)))
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
const { subscribe } = derived(result, ($result) => ({
|
|
101
|
+
...$result,
|
|
102
|
+
mutate,
|
|
103
|
+
mutateAsync: $result.mutate,
|
|
104
|
+
}))
|
|
105
|
+
|
|
106
|
+
return { subscribe }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
110
|
+
function noop() {}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import type { QueryKey, QueryFunction, QueryClient } from '@tanstack/query-core'
|
|
2
|
+
|
|
3
|
+
import { notifyManager, QueriesObserver } from '@tanstack/query-core'
|
|
4
|
+
import { readable, type Readable } from 'svelte/store'
|
|
5
|
+
|
|
6
|
+
import type { CreateQueryOptions, CreateQueryResult } from './types'
|
|
7
|
+
import { useQueryClient } from './useQueryClient'
|
|
8
|
+
|
|
9
|
+
// This defines the `CreateQueryOptions` that are accepted in `QueriesOptions` & `GetOptions`.
|
|
10
|
+
// - `context` is omitted as it is passed as a root-level option to `createQueries` instead.
|
|
11
|
+
type CreateQueryOptionsForCreateQueries<
|
|
12
|
+
TQueryFnData = unknown,
|
|
13
|
+
TError = unknown,
|
|
14
|
+
TData = TQueryFnData,
|
|
15
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
16
|
+
> = Omit<CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>, 'context'>
|
|
17
|
+
|
|
18
|
+
// Avoid TS depth-limit error in case of large array literal
|
|
19
|
+
type MAXIMUM_DEPTH = 20
|
|
20
|
+
|
|
21
|
+
type GetOptions<T> =
|
|
22
|
+
// Part 1: responsible for applying explicit type parameter to function arguments, if object { queryFnData: TQueryFnData, error: TError, data: TData }
|
|
23
|
+
T extends {
|
|
24
|
+
queryFnData: infer TQueryFnData
|
|
25
|
+
error?: infer TError
|
|
26
|
+
data: infer TData
|
|
27
|
+
}
|
|
28
|
+
? CreateQueryOptionsForCreateQueries<TQueryFnData, TError, TData>
|
|
29
|
+
: T extends { queryFnData: infer TQueryFnData; error?: infer TError }
|
|
30
|
+
? CreateQueryOptionsForCreateQueries<TQueryFnData, TError>
|
|
31
|
+
: T extends { data: infer TData; error?: infer TError }
|
|
32
|
+
? CreateQueryOptionsForCreateQueries<unknown, TError, TData>
|
|
33
|
+
: // Part 2: responsible for applying explicit type parameter to function arguments, if tuple [TQueryFnData, TError, TData]
|
|
34
|
+
T extends [infer TQueryFnData, infer TError, infer TData]
|
|
35
|
+
? CreateQueryOptionsForCreateQueries<TQueryFnData, TError, TData>
|
|
36
|
+
: T extends [infer TQueryFnData, infer TError]
|
|
37
|
+
? CreateQueryOptionsForCreateQueries<TQueryFnData, TError>
|
|
38
|
+
: T extends [infer TQueryFnData]
|
|
39
|
+
? CreateQueryOptionsForCreateQueries<TQueryFnData>
|
|
40
|
+
: // Part 3: responsible for inferring and enforcing type if no explicit parameter was provided
|
|
41
|
+
T extends {
|
|
42
|
+
queryFn?: QueryFunction<infer TQueryFnData, infer TQueryKey>
|
|
43
|
+
select: (data: any) => infer TData
|
|
44
|
+
}
|
|
45
|
+
? CreateQueryOptionsForCreateQueries<
|
|
46
|
+
TQueryFnData,
|
|
47
|
+
unknown,
|
|
48
|
+
TData,
|
|
49
|
+
TQueryKey
|
|
50
|
+
>
|
|
51
|
+
: T extends { queryFn?: QueryFunction<infer TQueryFnData, infer TQueryKey> }
|
|
52
|
+
? CreateQueryOptionsForCreateQueries<
|
|
53
|
+
TQueryFnData,
|
|
54
|
+
unknown,
|
|
55
|
+
TQueryFnData,
|
|
56
|
+
TQueryKey
|
|
57
|
+
>
|
|
58
|
+
: // Fallback
|
|
59
|
+
CreateQueryOptionsForCreateQueries
|
|
60
|
+
|
|
61
|
+
type GetResults<T> =
|
|
62
|
+
// Part 1: responsible for mapping explicit type parameter to function result, if object
|
|
63
|
+
T extends { queryFnData: any; error?: infer TError; data: infer TData }
|
|
64
|
+
? CreateQueryResult<TData, TError>
|
|
65
|
+
: T extends { queryFnData: infer TQueryFnData; error?: infer TError }
|
|
66
|
+
? CreateQueryResult<TQueryFnData, TError>
|
|
67
|
+
: T extends { data: infer TData; error?: infer TError }
|
|
68
|
+
? CreateQueryResult<TData, TError>
|
|
69
|
+
: // Part 2: responsible for mapping explicit type parameter to function result, if tuple
|
|
70
|
+
T extends [any, infer TError, infer TData]
|
|
71
|
+
? CreateQueryResult<TData, TError>
|
|
72
|
+
: T extends [infer TQueryFnData, infer TError]
|
|
73
|
+
? CreateQueryResult<TQueryFnData, TError>
|
|
74
|
+
: T extends [infer TQueryFnData]
|
|
75
|
+
? CreateQueryResult<TQueryFnData>
|
|
76
|
+
: // Part 3: responsible for mapping inferred type to results, if no explicit parameter was provided
|
|
77
|
+
T extends {
|
|
78
|
+
queryFn?: QueryFunction<unknown, any>
|
|
79
|
+
select: (data: any) => infer TData
|
|
80
|
+
}
|
|
81
|
+
? CreateQueryResult<TData>
|
|
82
|
+
: T extends { queryFn?: QueryFunction<infer TQueryFnData, any> }
|
|
83
|
+
? CreateQueryResult<TQueryFnData>
|
|
84
|
+
: // Fallback
|
|
85
|
+
CreateQueryResult
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* QueriesOptions reducer recursively unwraps function arguments to infer/enforce type param
|
|
89
|
+
*/
|
|
90
|
+
export type QueriesOptions<
|
|
91
|
+
T extends any[],
|
|
92
|
+
Result extends any[] = [],
|
|
93
|
+
Depth extends ReadonlyArray<number> = [],
|
|
94
|
+
> = Depth['length'] extends MAXIMUM_DEPTH
|
|
95
|
+
? CreateQueryOptionsForCreateQueries[]
|
|
96
|
+
: T extends []
|
|
97
|
+
? []
|
|
98
|
+
: T extends [infer Head]
|
|
99
|
+
? [...Result, GetOptions<Head>]
|
|
100
|
+
: T extends [infer Head, ...infer Tail]
|
|
101
|
+
? QueriesOptions<[...Tail], [...Result, GetOptions<Head>], [...Depth, 1]>
|
|
102
|
+
: unknown[] extends T
|
|
103
|
+
? T
|
|
104
|
+
: // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogenous type!
|
|
105
|
+
// use this to infer the param types in the case of Array.map() argument
|
|
106
|
+
T extends CreateQueryOptionsForCreateQueries<
|
|
107
|
+
infer TQueryFnData,
|
|
108
|
+
infer TError,
|
|
109
|
+
infer TData,
|
|
110
|
+
infer TQueryKey
|
|
111
|
+
>[]
|
|
112
|
+
? CreateQueryOptionsForCreateQueries<TQueryFnData, TError, TData, TQueryKey>[]
|
|
113
|
+
: // Fallback
|
|
114
|
+
CreateQueryOptionsForCreateQueries[]
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* QueriesResults reducer recursively maps type param to results
|
|
118
|
+
*/
|
|
119
|
+
export type QueriesResults<
|
|
120
|
+
T extends any[],
|
|
121
|
+
Result extends any[] = [],
|
|
122
|
+
Depth extends ReadonlyArray<number> = [],
|
|
123
|
+
> = Depth['length'] extends MAXIMUM_DEPTH
|
|
124
|
+
? CreateQueryResult[]
|
|
125
|
+
: T extends []
|
|
126
|
+
? []
|
|
127
|
+
: T extends [infer Head]
|
|
128
|
+
? [...Result, GetResults<Head>]
|
|
129
|
+
: T extends [infer Head, ...infer Tail]
|
|
130
|
+
? QueriesResults<[...Tail], [...Result, GetResults<Head>], [...Depth, 1]>
|
|
131
|
+
: T extends CreateQueryOptionsForCreateQueries<
|
|
132
|
+
infer TQueryFnData,
|
|
133
|
+
infer TError,
|
|
134
|
+
infer TData,
|
|
135
|
+
any
|
|
136
|
+
>[]
|
|
137
|
+
? // Dynamic-size (homogenous) CreateQueryOptions array: map directly to array of results
|
|
138
|
+
CreateQueryResult<unknown extends TData ? TQueryFnData : TData, TError>[]
|
|
139
|
+
: // Fallback
|
|
140
|
+
CreateQueryResult[]
|
|
141
|
+
|
|
142
|
+
export type CreateQueriesResult<T extends any[]> = Readable<QueriesResults<T>>
|
|
143
|
+
|
|
144
|
+
export function createQueries<T extends any[]>(
|
|
145
|
+
queries: readonly [...QueriesOptions<T>],
|
|
146
|
+
): CreateQueriesResult<T> {
|
|
147
|
+
const client: QueryClient = useQueryClient()
|
|
148
|
+
// const isRestoring = useIsRestoring()
|
|
149
|
+
|
|
150
|
+
function getDefaultQuery(newQueries: readonly [...QueriesOptions<T>]) {
|
|
151
|
+
return newQueries.map((options) => {
|
|
152
|
+
const defaultedOptions = client.defaultQueryOptions(options)
|
|
153
|
+
// Make sure the results are already in fetching state before subscribing or updating options
|
|
154
|
+
defaultedOptions._optimisticResults = 'optimistic'
|
|
155
|
+
|
|
156
|
+
return defaultedOptions
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const defaultedQueries = getDefaultQuery(queries)
|
|
161
|
+
let observer = new QueriesObserver(client, defaultedQueries)
|
|
162
|
+
|
|
163
|
+
readable(observer).subscribe(($observer) => {
|
|
164
|
+
observer = $observer
|
|
165
|
+
// Do not notify on updates because of changes in the options because
|
|
166
|
+
// these changes should already be reflected in the optimistic result.
|
|
167
|
+
observer.setQueries(defaultedQueries, { listeners: false })
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
const { subscribe } = readable(
|
|
171
|
+
observer.getOptimisticResult(defaultedQueries) as any,
|
|
172
|
+
(set) => {
|
|
173
|
+
return observer.subscribe(notifyManager.batchCalls(set))
|
|
174
|
+
},
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
return { subscribe }
|
|
178
|
+
}
|