@tanstack/angular-query-experimental 5.12.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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/build/createBaseQuery.d.ts +7 -0
  3. package/build/esm2022/createBaseQuery.mjs +35 -0
  4. package/build/esm2022/index.mjs +14 -0
  5. package/build/esm2022/infiniteQueryOptions.mjs +4 -0
  6. package/build/esm2022/injectIsFetching.mjs +25 -0
  7. package/build/esm2022/injectIsMutating.mjs +25 -0
  8. package/build/esm2022/injectMutation.mjs +30 -0
  9. package/build/esm2022/injectQueries.mjs +30 -0
  10. package/build/esm2022/injectQuery.mjs +11 -0
  11. package/build/esm2022/injectQueryClient.mjs +4 -0
  12. package/build/esm2022/providers.mjs +17 -0
  13. package/build/esm2022/query-proxy.mjs +37 -0
  14. package/build/esm2022/queryOptions.mjs +4 -0
  15. package/build/esm2022/tanstack-angular-query-experimental.mjs +5 -0
  16. package/build/esm2022/types.mjs +2 -0
  17. package/build/fesm2022/tanstack-angular-query-experimental.mjs +209 -0
  18. package/build/fesm2022/tanstack-angular-query-experimental.mjs.map +1 -0
  19. package/build/index.d.ts +12 -0
  20. package/build/infiniteQueryOptions.d.ts +3 -0
  21. package/build/injectIsFetching.d.ts +3 -0
  22. package/build/injectIsMutating.d.ts +3 -0
  23. package/build/injectMutation.d.ts +4 -0
  24. package/build/injectQueries.d.ts +71 -0
  25. package/build/injectQuery.d.ts +11 -0
  26. package/build/injectQueryClient.d.ts +13 -0
  27. package/build/providers.d.ts +3 -0
  28. package/build/query-proxy.d.ts +4 -0
  29. package/build/queryOptions.d.ts +12 -0
  30. package/build/types.d.ts +38 -0
  31. package/package.json +65 -0
  32. package/src/createBaseQuery.ts +87 -0
  33. package/src/index.ts +26 -0
  34. package/src/infiniteQueryOptions.ts +28 -0
  35. package/src/injectIsFetching.ts +36 -0
  36. package/src/injectIsMutating.ts +36 -0
  37. package/src/injectMutation.ts +63 -0
  38. package/src/injectQueries.ts +249 -0
  39. package/src/injectQuery.ts +61 -0
  40. package/src/injectQueryClient.ts +7 -0
  41. package/src/providers.ts +26 -0
  42. package/src/query-proxy.ts +51 -0
  43. package/src/queryOptions.ts +46 -0
  44. package/src/test-setup.ts +13 -0
  45. package/src/tests/injectIsFetching.test.ts +32 -0
  46. package/src/tests/injectIsMutating.test.ts +43 -0
  47. package/src/tests/injectMutation.test.ts +52 -0
  48. package/src/tests/injectQuery.test.ts +232 -0
  49. package/src/tests/test-utils.ts +37 -0
  50. package/src/types.ts +136 -0
@@ -0,0 +1,232 @@
1
+ import { computed, signal } from '@angular/core'
2
+ import { TestBed, fakeAsync, flush, tick } from '@angular/core/testing'
3
+ import { QueryClient } from '@tanstack/query-core'
4
+ import { expect, vi } from 'vitest'
5
+ import { injectQuery } from '../injectQuery'
6
+ import { provideAngularQuery } from '../providers'
7
+ import {
8
+ delayedFetcher,
9
+ getSimpleFetcherWithReturnData,
10
+ rejectFetcher,
11
+ simpleFetcher,
12
+ } from './test-utils'
13
+
14
+ describe('injectQuery', () => {
15
+ beforeEach(() => {
16
+ TestBed.configureTestingModule({
17
+ providers: [provideAngularQuery(new QueryClient())],
18
+ })
19
+ })
20
+
21
+ it('should return pending status initially', fakeAsync(() => {
22
+ const query = TestBed.runInInjectionContext(() => {
23
+ return injectQuery(() => ({
24
+ queryKey: ['key1'],
25
+ queryFn: simpleFetcher,
26
+ }))
27
+ })
28
+
29
+ expect(query.status()).toBe('pending')
30
+ expect(query.isPending()).toBe(true)
31
+ expect(query.isFetching()).toBe(true)
32
+ expect(query.isStale()).toBe(true)
33
+ expect(query.isFetched()).toBe(false)
34
+
35
+ flush()
36
+ }))
37
+
38
+ it('should resolve to success and update signal: createQuery', fakeAsync(() => {
39
+ const query = TestBed.runInInjectionContext(() => {
40
+ return injectQuery(() => ({
41
+ queryKey: ['key2'],
42
+ queryFn: getSimpleFetcherWithReturnData('result2'),
43
+ }))
44
+ })
45
+
46
+ flush()
47
+
48
+ expect(query.status()).toBe('success')
49
+ expect(query.data()).toBe('result2')
50
+ expect(query.isPending()).toBe(false)
51
+ expect(query.isFetching()).toBe(false)
52
+ expect(query.isFetched()).toBe(true)
53
+ expect(query.isSuccess()).toBe(true)
54
+ }))
55
+
56
+ it('should reject and update signal', fakeAsync(() => {
57
+ const query = TestBed.runInInjectionContext(() => {
58
+ return injectQuery(() => ({
59
+ retry: false,
60
+ queryKey: ['key3'],
61
+ queryFn: rejectFetcher,
62
+ }))
63
+ })
64
+
65
+ flush()
66
+
67
+ expect(query.status()).toBe('error')
68
+ expect(query.data()).toBe(undefined)
69
+ expect(query.error()).toMatchObject({ message: 'Some error' })
70
+ expect(query.isPending()).toBe(false)
71
+ expect(query.isFetching()).toBe(false)
72
+ expect(query.isError()).toBe(true)
73
+ expect(query.failureCount()).toBe(1)
74
+ expect(query.failureReason()).toMatchObject({ message: 'Some error' })
75
+ }))
76
+
77
+ it('should update query on options contained signal change', fakeAsync(() => {
78
+ const key = signal(['key6', 'key7'])
79
+ const spy = vi.fn(simpleFetcher)
80
+
81
+ const query = TestBed.runInInjectionContext(() => {
82
+ return injectQuery(() => ({
83
+ queryKey: key(),
84
+ queryFn: spy,
85
+ }))
86
+ })
87
+ flush()
88
+ expect(spy).toHaveBeenCalledTimes(1)
89
+
90
+ expect(query.status()).toBe('success')
91
+
92
+ key.set(['key8'])
93
+ TestBed.flushEffects()
94
+
95
+ expect(spy).toHaveBeenCalledTimes(2)
96
+
97
+ flush()
98
+ }))
99
+
100
+ it('should only run query once enabled is set to true', fakeAsync(() => {
101
+ const spy = vi.fn(simpleFetcher)
102
+ const enabled = signal(false)
103
+
104
+ const query = TestBed.runInInjectionContext(() => {
105
+ return injectQuery(() => ({
106
+ queryKey: ['key9'],
107
+ queryFn: spy,
108
+ enabled: enabled(),
109
+ }))
110
+ })
111
+
112
+ expect(spy).not.toHaveBeenCalled()
113
+ expect(query.status()).toBe('pending')
114
+
115
+ enabled.set(true)
116
+ TestBed.flushEffects()
117
+ flush()
118
+ expect(spy).toHaveBeenCalledTimes(1)
119
+ expect(query.status()).toBe('success')
120
+ }))
121
+
122
+ it('should properly execute dependant queries', fakeAsync(() => {
123
+ const query1 = TestBed.runInInjectionContext(() => {
124
+ return injectQuery(() => ({
125
+ queryKey: ['dependant1'],
126
+ queryFn: simpleFetcher,
127
+ }))
128
+ })
129
+
130
+ const dependentQueryFn = vi.fn().mockImplementation(delayedFetcher(1000))
131
+
132
+ const query2 = TestBed.runInInjectionContext(() => {
133
+ return injectQuery(
134
+ computed(() => ({
135
+ queryKey: ['dependant2'],
136
+ queryFn: dependentQueryFn,
137
+ enabled: !!query1.data(),
138
+ })),
139
+ )
140
+ })
141
+
142
+ expect(query1.data()).toStrictEqual(undefined)
143
+ expect(query2.fetchStatus()).toStrictEqual('idle')
144
+ expect(dependentQueryFn).not.toHaveBeenCalled()
145
+
146
+ tick()
147
+ TestBed.flushEffects()
148
+
149
+ expect(query1.data()).toStrictEqual('Some data')
150
+ // expect(query2().fetchStatus).toStrictEqual('fetching') // TODO: is this working correctly?
151
+
152
+ flush()
153
+
154
+ expect(query2.fetchStatus()).toStrictEqual('idle')
155
+ expect(query2.status()).toStrictEqual('success')
156
+ expect(dependentQueryFn).toHaveBeenCalledTimes(1)
157
+ expect(dependentQueryFn).toHaveBeenCalledWith(
158
+ expect.objectContaining({ queryKey: ['dependant2'] }),
159
+ )
160
+ }))
161
+
162
+ it('should use the current value for the queryKey when refetch is called', fakeAsync(() => {
163
+ const fetchFn = vi.fn()
164
+ const keySignal = signal('key11')
165
+
166
+ const query = TestBed.runInInjectionContext(() => {
167
+ return injectQuery(() => ({
168
+ queryKey: ['key10', keySignal()],
169
+ queryFn: fetchFn,
170
+ enabled: false,
171
+ }))
172
+ })
173
+
174
+ expect(fetchFn).not.toHaveBeenCalled()
175
+
176
+ query.refetch().then(() => {
177
+ expect(fetchFn).toHaveBeenCalledTimes(1)
178
+ expect(fetchFn).toHaveBeenCalledWith(
179
+ expect.objectContaining({
180
+ queryKey: ['key10', 'key11'],
181
+ }),
182
+ )
183
+ })
184
+
185
+ flush()
186
+
187
+ keySignal.set('key12')
188
+
189
+ TestBed.flushEffects()
190
+
191
+ query.refetch().then(() => {
192
+ expect(fetchFn).toHaveBeenCalledTimes(2)
193
+ expect(fetchFn).toHaveBeenCalledWith(
194
+ expect.objectContaining({
195
+ queryKey: ['key10', 'key12'],
196
+ }),
197
+ )
198
+ })
199
+
200
+ flush()
201
+ }))
202
+
203
+ it('should set state to error when queryFn returns reject promise', fakeAsync(() => {
204
+ const query = TestBed.runInInjectionContext(() => {
205
+ return injectQuery(() => ({
206
+ retry: false,
207
+ queryKey: ['key13'],
208
+ queryFn: rejectFetcher,
209
+ }))
210
+ })
211
+
212
+ expect(query.status()).toBe('pending')
213
+
214
+ flush()
215
+
216
+ expect(query.status()).toBe('error')
217
+ }))
218
+
219
+ it('should not update signal when notifyOnChangeProps is set without the changed property being in notifyOnChangeProps', fakeAsync(() => {
220
+ const query = TestBed.runInInjectionContext(() => {
221
+ return injectQuery(() => ({
222
+ queryKey: ['key14'],
223
+ queryFn: simpleFetcher,
224
+ notifyOnChangeProps: 'all',
225
+ }))
226
+ })
227
+
228
+ flush()
229
+
230
+ expect(query.status()).toBe('success')
231
+ }))
232
+ })
@@ -0,0 +1,37 @@
1
+ export function simpleFetcher(): Promise<string> {
2
+ return new Promise((resolve) => {
3
+ setTimeout(() => {
4
+ return resolve('Some data')
5
+ }, 0)
6
+ })
7
+ }
8
+
9
+ export function delayedFetcher(timeout = 0): () => Promise<string> {
10
+ return () =>
11
+ new Promise((resolve) => {
12
+ setTimeout(() => {
13
+ return resolve('Some data')
14
+ }, timeout)
15
+ })
16
+ }
17
+
18
+ export function getSimpleFetcherWithReturnData(returnData: unknown) {
19
+ return () =>
20
+ new Promise((resolve) => setTimeout(() => resolve(returnData), 0))
21
+ }
22
+
23
+ export function rejectFetcher(): Promise<Error> {
24
+ return new Promise((_, reject) => {
25
+ setTimeout(() => {
26
+ return reject(new Error('Some error'))
27
+ }, 0)
28
+ })
29
+ }
30
+
31
+ export function successMutator<T>(param: T): Promise<T> {
32
+ return new Promise((resolve) => {
33
+ setTimeout(() => {
34
+ return resolve(param)
35
+ }, 0)
36
+ })
37
+ }
package/src/types.ts ADDED
@@ -0,0 +1,136 @@
1
+ import type { Signal } from '@angular/core'
2
+
3
+ import type {
4
+ DefaultError,
5
+ DefinedQueryObserverResult,
6
+ InfiniteQueryObserverOptions,
7
+ InfiniteQueryObserverResult,
8
+ MutateFunction,
9
+ MutationObserverOptions,
10
+ MutationObserverResult,
11
+ QueryKey,
12
+ QueryObserverOptions,
13
+ QueryObserverResult,
14
+ } from '@tanstack/query-core'
15
+
16
+ /** Options for createBaseQuery */
17
+ export type CreateBaseQueryOptions<
18
+ TQueryFnData = unknown,
19
+ TError = DefaultError,
20
+ TData = TQueryFnData,
21
+ TQueryData = TQueryFnData,
22
+ TQueryKey extends QueryKey = QueryKey,
23
+ > = QueryObserverOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>
24
+
25
+ /** Result from createBaseQuery */
26
+ export type CreateBaseQueryResult<
27
+ TData = unknown,
28
+ TError = DefaultError,
29
+ State = QueryObserverResult<TData, TError>,
30
+ > = {
31
+ [K in keyof State]: State[K] extends Function ? State[K] : Signal<State[K]>
32
+ }
33
+ /** Result from createBaseQuery */
34
+
35
+ /** Options for createQuery */
36
+ export type CreateQueryOptions<
37
+ TQueryFnData = unknown,
38
+ TError = DefaultError,
39
+ TData = TQueryFnData,
40
+ TQueryKey extends QueryKey = QueryKey,
41
+ > = CreateBaseQueryOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>
42
+
43
+ /** Result from createQuery */
44
+ export type CreateQueryResult<
45
+ TData = unknown,
46
+ TError = DefaultError,
47
+ > = CreateBaseQueryResult<TData, TError>
48
+
49
+ /** Options for createInfiniteQuery */
50
+ export type CreateInfiniteQueryOptions<
51
+ TQueryFnData = unknown,
52
+ TError = DefaultError,
53
+ TData = TQueryFnData,
54
+ TQueryData = TQueryFnData,
55
+ TQueryKey extends QueryKey = QueryKey,
56
+ TPageParam = unknown,
57
+ > = InfiniteQueryObserverOptions<
58
+ TQueryFnData,
59
+ TError,
60
+ TData,
61
+ TQueryData,
62
+ TQueryKey,
63
+ TPageParam
64
+ >
65
+
66
+ /** Result from createInfiniteQuery */
67
+ export type CreateInfiniteQueryResult<
68
+ TData = unknown,
69
+ TError = DefaultError,
70
+ > = Signal<InfiniteQueryObserverResult<TData, TError>>
71
+
72
+ /** Options for createBaseQuery with initialData */
73
+ export type DefinedCreateBaseQueryResult<
74
+ TData = unknown,
75
+ TError = DefaultError,
76
+ DefinedQueryObserver = DefinedQueryObserverResult<TData, TError>,
77
+ > = {
78
+ [K in keyof DefinedQueryObserver]: DefinedQueryObserver[K] extends Function
79
+ ? DefinedQueryObserver[K]
80
+ : Signal<DefinedQueryObserver[K]>
81
+ }
82
+
83
+ /** Options for createQuery with initialData */
84
+ export type DefinedCreateQueryResult<
85
+ TData = unknown,
86
+ TError = DefaultError,
87
+ > = DefinedCreateBaseQueryResult<TData, TError>
88
+
89
+ /** Options for createMutation */
90
+ export type CreateMutationOptions<
91
+ TData = unknown,
92
+ TError = DefaultError,
93
+ TVariables = void,
94
+ TContext = unknown,
95
+ > = Omit<
96
+ MutationObserverOptions<TData, TError, TVariables, TContext>,
97
+ '_defaulted' | 'variables'
98
+ >
99
+
100
+ export type CreateMutateFunction<
101
+ TData = unknown,
102
+ TError = DefaultError,
103
+ TVariables = void,
104
+ TContext = unknown,
105
+ > = (
106
+ ...args: Parameters<MutateFunction<TData, TError, TVariables, TContext>>
107
+ ) => void
108
+
109
+ export type CreateMutateAsyncFunction<
110
+ TData = unknown,
111
+ TError = DefaultError,
112
+ TVariables = void,
113
+ TContext = unknown,
114
+ > = MutateFunction<TData, TError, TVariables, TContext>
115
+
116
+ export type CreateBaseMutationResult<
117
+ TData = unknown,
118
+ TError = DefaultError,
119
+ TVariables = unknown,
120
+ TContext = unknown,
121
+ > = Override<
122
+ MutationObserverResult<TData, TError, TVariables, TContext>,
123
+ { mutate: CreateMutateFunction<TData, TError, TVariables, TContext> }
124
+ > & {
125
+ mutateAsync: CreateMutateAsyncFunction<TData, TError, TVariables, TContext>
126
+ }
127
+
128
+ /** Result from createMutation */
129
+ export type CreateMutationResult<
130
+ TData = unknown,
131
+ TError = DefaultError,
132
+ TVariables = unknown,
133
+ TContext = unknown,
134
+ > = Signal<CreateBaseMutationResult<TData, TError, TVariables, TContext>>
135
+
136
+ type Override<A, B> = { [K in keyof A]: K extends keyof B ? B[K] : A[K] }