@tanstack/angular-query-experimental 5.74.0 → 5.74.2

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.
@@ -7,7 +7,7 @@ import {
7
7
  } from '@angular/core'
8
8
  import type { Provider, Signal } from '@angular/core'
9
9
 
10
- const IsRestoring = new InjectionToken<Signal<boolean>>('IsRestoring')
10
+ const IS_RESTORING = new InjectionToken<Signal<boolean>>('')
11
11
 
12
12
  /**
13
13
  * The `Injector` in which to create the isRestoring signal.
@@ -30,7 +30,7 @@ export function injectIsRestoring(
30
30
  !options?.injector && assertInInjectionContext(injectIsRestoring)
31
31
  const injector = options?.injector ?? inject(Injector)
32
32
  return injector.get(
33
- IsRestoring,
33
+ IS_RESTORING,
34
34
  computed(() => false),
35
35
  { optional: true },
36
36
  )
@@ -44,7 +44,7 @@ export function injectIsRestoring(
44
44
  */
45
45
  export function provideIsRestoring(isRestoring: Signal<boolean>): Provider {
46
46
  return {
47
- provide: IsRestoring,
47
+ provide: IS_RESTORING,
48
48
  useValue: isRestoring,
49
49
  }
50
50
  }
@@ -1,11 +1,18 @@
1
- import { DestroyRef, NgZone, computed, inject, signal } from '@angular/core'
1
+ import {
2
+ DestroyRef,
3
+ Injector,
4
+ NgZone,
5
+ assertInInjectionContext,
6
+ computed,
7
+ inject,
8
+ signal,
9
+ } from '@angular/core'
2
10
  import {
3
11
  QueryClient,
4
12
  notifyManager,
5
13
  replaceEqualDeep,
6
14
  } from '@tanstack/query-core'
7
- import { assertInjector } from './util/assert-injector/assert-injector'
8
- import type { Injector, Signal } from '@angular/core'
15
+ import type { Signal } from '@angular/core'
9
16
  import type {
10
17
  Mutation,
11
18
  MutationCache,
@@ -58,62 +65,61 @@ export function injectMutationState<TResult = MutationState>(
58
65
  injectMutationStateFn: () => MutationStateOptions<TResult> = () => ({}),
59
66
  options?: InjectMutationStateOptions,
60
67
  ): Signal<Array<TResult>> {
61
- return assertInjector(injectMutationState, options?.injector, () => {
62
- const destroyRef = inject(DestroyRef)
63
- const ngZone = inject(NgZone)
64
- const queryClient = inject(QueryClient)
65
-
66
- const mutationCache = queryClient.getMutationCache()
68
+ !options?.injector && assertInInjectionContext(injectMutationState)
69
+ const injector = options?.injector ?? inject(Injector)
70
+ const destroyRef = injector.get(DestroyRef)
71
+ const ngZone = injector.get(NgZone)
72
+ const queryClient = injector.get(QueryClient)
73
+ const mutationCache = queryClient.getMutationCache()
67
74
 
68
- /**
69
- * Computed signal that gets result from mutation cache based on passed options
70
- * First element is the result, second element is the time when the result was set
71
- */
72
- const resultFromOptionsSignal = computed(() => {
73
- return [
74
- getResult(mutationCache, injectMutationStateFn()),
75
- performance.now(),
76
- ] as const
77
- })
75
+ /**
76
+ * Computed signal that gets result from mutation cache based on passed options
77
+ * First element is the result, second element is the time when the result was set
78
+ */
79
+ const resultFromOptionsSignal = computed(() => {
80
+ return [
81
+ getResult(mutationCache, injectMutationStateFn()),
82
+ performance.now(),
83
+ ] as const
84
+ })
78
85
 
79
- /**
80
- * Signal that contains result set by subscriber
81
- * First element is the result, second element is the time when the result was set
82
- */
83
- const resultFromSubscriberSignal = signal<[Array<TResult>, number] | null>(
84
- null,
85
- )
86
+ /**
87
+ * Signal that contains result set by subscriber
88
+ * First element is the result, second element is the time when the result was set
89
+ */
90
+ const resultFromSubscriberSignal = signal<[Array<TResult>, number] | null>(
91
+ null,
92
+ )
86
93
 
87
- /**
88
- * Returns the last result by either subscriber or options
89
- */
90
- const effectiveResultSignal = computed(() => {
91
- const optionsResult = resultFromOptionsSignal()
92
- const subscriberResult = resultFromSubscriberSignal()
93
- return subscriberResult && subscriberResult[1] > optionsResult[1]
94
- ? subscriberResult[0]
95
- : optionsResult[0]
96
- })
94
+ /**
95
+ * Returns the last result by either subscriber or options
96
+ */
97
+ const effectiveResultSignal = computed(() => {
98
+ const optionsResult = resultFromOptionsSignal()
99
+ const subscriberResult = resultFromSubscriberSignal()
100
+ return subscriberResult && subscriberResult[1] > optionsResult[1]
101
+ ? subscriberResult[0]
102
+ : optionsResult[0]
103
+ })
97
104
 
98
- const unsubscribe = ngZone.runOutsideAngular(() =>
99
- mutationCache.subscribe(
100
- notifyManager.batchCalls(() => {
101
- const [lastResult] = effectiveResultSignal()
102
- const nextResult = replaceEqualDeep(
103
- lastResult,
104
- getResult(mutationCache, injectMutationStateFn()),
105
- )
106
- if (lastResult !== nextResult) {
107
- ngZone.run(() => {
108
- resultFromSubscriberSignal.set([nextResult, performance.now()])
109
- })
110
- }
111
- }),
112
- ),
113
- )
105
+ const unsubscribe = ngZone.runOutsideAngular(() =>
106
+ mutationCache.subscribe(
107
+ notifyManager.batchCalls(() => {
108
+ const [lastResult] = effectiveResultSignal()
109
+ const nextResult = replaceEqualDeep(
110
+ lastResult,
111
+ getResult(mutationCache, injectMutationStateFn()),
112
+ )
113
+ if (lastResult !== nextResult) {
114
+ ngZone.run(() => {
115
+ resultFromSubscriberSignal.set([nextResult, performance.now()])
116
+ })
117
+ }
118
+ }),
119
+ ),
120
+ )
114
121
 
115
- destroyRef.onDestroy(unsubscribe)
122
+ destroyRef.onDestroy(unsubscribe)
116
123
 
117
- return effectiveResultSignal
118
- })
124
+ return effectiveResultSignal
119
125
  }
@@ -1,6 +1,8 @@
1
1
  import {
2
2
  DestroyRef,
3
+ Injector,
3
4
  NgZone,
5
+ assertInInjectionContext,
4
6
  computed,
5
7
  effect,
6
8
  inject,
@@ -12,10 +14,8 @@ import {
12
14
  QueryClient,
13
15
  notifyManager,
14
16
  } from '@tanstack/query-core'
15
- import { assertInjector } from './util/assert-injector/assert-injector'
16
17
  import { signalProxy } from './signal-proxy'
17
18
  import { noop, shouldThrowError } from './util'
18
- import type { Injector } from '@angular/core'
19
19
  import type { DefaultError, MutationObserverResult } from '@tanstack/query-core'
20
20
  import type { CreateMutateFunction, CreateMutationResult } from './types'
21
21
  import type { CreateMutationOptions } from './mutation-options'
@@ -52,123 +52,117 @@ export function injectMutation<
52
52
  >,
53
53
  options?: InjectMutationOptions,
54
54
  ): CreateMutationResult<TData, TError, TVariables, TContext> {
55
- return assertInjector(injectMutation, options?.injector, () => {
56
- const destroyRef = inject(DestroyRef)
57
- const ngZone = inject(NgZone)
58
- const queryClient = inject(QueryClient)
59
-
60
- /**
61
- * computed() is used so signals can be inserted into the options
62
- * making it reactive. Wrapping options in a function ensures embedded expressions
63
- * are preserved and can keep being applied after signal changes
64
- */
65
- const optionsSignal = computed(injectMutationFn)
66
-
67
- const observerSignal = (() => {
68
- let instance: MutationObserver<
69
- TData,
70
- TError,
71
- TVariables,
72
- TContext
73
- > | null = null
74
-
75
- return computed(() => {
76
- return (instance ||= new MutationObserver(queryClient, optionsSignal()))
77
- })
78
- })()
55
+ !options?.injector && assertInInjectionContext(injectMutation)
56
+ const injector = options?.injector ?? inject(Injector)
57
+ const destroyRef = injector.get(DestroyRef)
58
+ const ngZone = injector.get(NgZone)
59
+ const queryClient = injector.get(QueryClient)
79
60
 
80
- const mutateFnSignal = computed<
81
- CreateMutateFunction<TData, TError, TVariables, TContext>
82
- >(() => {
83
- const observer = observerSignal()
84
- return (variables, mutateOptions) => {
85
- observer.mutate(variables, mutateOptions).catch(noop)
86
- }
61
+ /**
62
+ * computed() is used so signals can be inserted into the options
63
+ * making it reactive. Wrapping options in a function ensures embedded expressions
64
+ * are preserved and can keep being applied after signal changes
65
+ */
66
+ const optionsSignal = computed(injectMutationFn)
67
+
68
+ const observerSignal = (() => {
69
+ let instance: MutationObserver<TData, TError, TVariables, TContext> | null =
70
+ null
71
+
72
+ return computed(() => {
73
+ return (instance ||= new MutationObserver(queryClient, optionsSignal()))
87
74
  })
75
+ })()
76
+
77
+ const mutateFnSignal = computed<
78
+ CreateMutateFunction<TData, TError, TVariables, TContext>
79
+ >(() => {
80
+ const observer = observerSignal()
81
+ return (variables, mutateOptions) => {
82
+ observer.mutate(variables, mutateOptions).catch(noop)
83
+ }
84
+ })
85
+
86
+ /**
87
+ * Computed signal that gets result from mutation cache based on passed options
88
+ */
89
+ const resultFromInitialOptionsSignal = computed(() => {
90
+ const observer = observerSignal()
91
+ return observer.getCurrentResult()
92
+ })
93
+
94
+ /**
95
+ * Signal that contains result set by subscriber
96
+ */
97
+ const resultFromSubscriberSignal = signal<MutationObserverResult<
98
+ TData,
99
+ TError,
100
+ TVariables,
101
+ TContext
102
+ > | null>(null)
88
103
 
89
- /**
90
- * Computed signal that gets result from mutation cache based on passed options
91
- */
92
- const resultFromInitialOptionsSignal = computed(() => {
104
+ effect(
105
+ () => {
93
106
  const observer = observerSignal()
94
- return observer.getCurrentResult()
95
- })
107
+ const observerOptions = optionsSignal()
96
108
 
97
- /**
98
- * Signal that contains result set by subscriber
99
- */
100
- const resultFromSubscriberSignal = signal<MutationObserverResult<
101
- TData,
102
- TError,
103
- TVariables,
104
- TContext
105
- > | null>(null)
106
-
107
- effect(
108
- () => {
109
- const observer = observerSignal()
110
- const observerOptions = optionsSignal()
111
-
112
- untracked(() => {
113
- observer.setOptions(observerOptions)
114
- })
115
- },
116
- {
117
- injector: options?.injector,
118
- },
119
- )
120
-
121
- effect(
122
- () => {
123
- // observer.trackResult is not used as this optimization is not needed for Angular
124
- const observer = observerSignal()
125
-
126
- untracked(() => {
127
- const unsubscribe = ngZone.runOutsideAngular(() =>
128
- observer.subscribe(
129
- notifyManager.batchCalls((state) => {
130
- ngZone.run(() => {
131
- if (
132
- state.isError &&
133
- shouldThrowError(observer.options.throwOnError, [
134
- state.error,
135
- ])
136
- ) {
137
- ngZone.onError.emit(state.error)
138
- throw state.error
139
- }
140
-
141
- resultFromSubscriberSignal.set(state)
142
- })
143
- }),
144
- ),
145
- )
146
- destroyRef.onDestroy(unsubscribe)
147
- })
148
- },
149
- {
150
- injector: options?.injector,
151
- },
152
- )
153
-
154
- const resultSignal = computed(() => {
155
- const resultFromSubscriber = resultFromSubscriberSignal()
156
- const resultFromInitialOptions = resultFromInitialOptionsSignal()
157
-
158
- const result = resultFromSubscriber ?? resultFromInitialOptions
159
-
160
- return {
161
- ...result,
162
- mutate: mutateFnSignal(),
163
- mutateAsync: result.mutate,
164
- }
165
- })
109
+ untracked(() => {
110
+ observer.setOptions(observerOptions)
111
+ })
112
+ },
113
+ {
114
+ injector,
115
+ },
116
+ )
117
+
118
+ effect(
119
+ () => {
120
+ // observer.trackResult is not used as this optimization is not needed for Angular
121
+ const observer = observerSignal()
166
122
 
167
- return signalProxy(resultSignal) as CreateMutationResult<
168
- TData,
169
- TError,
170
- TVariables,
171
- TContext
172
- >
123
+ untracked(() => {
124
+ const unsubscribe = ngZone.runOutsideAngular(() =>
125
+ observer.subscribe(
126
+ notifyManager.batchCalls((state) => {
127
+ ngZone.run(() => {
128
+ if (
129
+ state.isError &&
130
+ shouldThrowError(observer.options.throwOnError, [state.error])
131
+ ) {
132
+ ngZone.onError.emit(state.error)
133
+ throw state.error
134
+ }
135
+
136
+ resultFromSubscriberSignal.set(state)
137
+ })
138
+ }),
139
+ ),
140
+ )
141
+ destroyRef.onDestroy(unsubscribe)
142
+ })
143
+ },
144
+ {
145
+ injector,
146
+ },
147
+ )
148
+
149
+ const resultSignal = computed(() => {
150
+ const resultFromSubscriber = resultFromSubscriberSignal()
151
+ const resultFromInitialOptions = resultFromInitialOptionsSignal()
152
+
153
+ const result = resultFromSubscriber ?? resultFromInitialOptions
154
+
155
+ return {
156
+ ...result,
157
+ mutate: mutateFnSignal(),
158
+ mutateAsync: result.mutate,
159
+ }
173
160
  })
161
+
162
+ return signalProxy(resultSignal) as CreateMutationResult<
163
+ TData,
164
+ TError,
165
+ TVariables,
166
+ TContext
167
+ >
174
168
  }
@@ -5,15 +5,17 @@ import {
5
5
  } from '@tanstack/query-core'
6
6
  import {
7
7
  DestroyRef,
8
+ Injector,
8
9
  NgZone,
10
+ assertInInjectionContext,
9
11
  computed,
10
12
  effect,
11
13
  inject,
14
+ runInInjectionContext,
12
15
  signal,
13
16
  } from '@angular/core'
14
- import { assertInjector } from './util/assert-injector/assert-injector'
15
17
  import { injectIsRestoring } from './inject-is-restoring'
16
- import type { Injector, Signal } from '@angular/core'
18
+ import type { Signal } from '@angular/core'
17
19
  import type {
18
20
  DefaultError,
19
21
  OmitKeyof,
@@ -214,7 +216,8 @@ export function injectQueries<
214
216
  },
215
217
  injector?: Injector,
216
218
  ): Signal<TCombinedResult> {
217
- return assertInjector(injectQueries, injector, () => {
219
+ !injector && assertInInjectionContext(injectQueries)
220
+ return runInInjectionContext(injector ?? inject(Injector), () => {
218
221
  const destroyRef = inject(DestroyRef)
219
222
  const ngZone = inject(NgZone)
220
223
  const queryClient = inject(QueryClient)
@@ -1,7 +1,11 @@
1
1
  import { QueryObserver } from '@tanstack/query-core'
2
- import { assertInjector } from './util/assert-injector/assert-injector'
2
+ import {
3
+ Injector,
4
+ assertInInjectionContext,
5
+ inject,
6
+ runInInjectionContext,
7
+ } from '@angular/core'
3
8
  import { createBaseQuery } from './create-base-query'
4
- import type { Injector } from '@angular/core'
5
9
  import type { DefaultError, QueryKey } from '@tanstack/query-core'
6
10
  import type {
7
11
  CreateQueryOptions,
@@ -219,7 +223,8 @@ export function injectQuery(
219
223
  injectQueryFn: () => CreateQueryOptions,
220
224
  options?: InjectQueryOptions,
221
225
  ) {
222
- return assertInjector(injectQuery, options?.injector, () =>
226
+ !options?.injector && assertInInjectionContext(injectQuery)
227
+ return runInInjectionContext(options?.injector ?? inject(Injector), () =>
223
228
  createBaseQuery(injectQueryFn, QueryObserver),
224
229
  ) as unknown as CreateQueryResult
225
230
  }
@@ -1,78 +0,0 @@
1
- /* eslint-disable cspell/spellchecker */
2
- /**
3
- * The code in this file is adapted from NG Extension Platform at https://ngxtension.netlify.app.
4
- *
5
- * Original Author: Chau Tran
6
- *
7
- * NG Extension Platform is an open-source project licensed under the MIT license.
8
- *
9
- * For more information about the original code, see
10
- * https://github.com/nartc/ngxtension-platform
11
- */
12
- /* eslint-enable */
13
-
14
- import {
15
- InjectionToken,
16
- Injector,
17
- inject,
18
- provideExperimentalZonelessChangeDetection,
19
- runInInjectionContext,
20
- } from '@angular/core'
21
- import { TestBed } from '@angular/core/testing'
22
- import { assertInjector } from './assert-injector'
23
-
24
- describe('assertInjector', () => {
25
- const token = new InjectionToken('token', {
26
- factory: () => 1,
27
- })
28
-
29
- function injectDummy(injector?: Injector) {
30
- injector = assertInjector(injectDummy, injector)
31
- return runInInjectionContext(injector, () => inject(token))
32
- }
33
-
34
- function injectDummyTwo(injector?: Injector) {
35
- return assertInjector(injectDummyTwo, injector, () => inject(token) + 1)
36
- }
37
-
38
- it('given no custom injector, when run in injection context, then return value', () => {
39
- TestBed.configureTestingModule({
40
- providers: [provideExperimentalZonelessChangeDetection()],
41
- })
42
- TestBed.runInInjectionContext(() => {
43
- const value = injectDummy()
44
- const valueTwo = injectDummyTwo()
45
- expect(value).toEqual(1)
46
- expect(valueTwo).toEqual(2)
47
- })
48
- })
49
-
50
- it('given no custom injector, when run outside injection context, then throw', () => {
51
- expect(() => injectDummy()).toThrowError(
52
- /injectDummy\(\) can only be used within an injection context/i,
53
- )
54
- expect(() => injectDummyTwo()).toThrowError(
55
- /injectDummyTwo\(\) can only be used within an injection context/i,
56
- )
57
- })
58
-
59
- it('given a custom injector, when run in that injector context without providing number, then throw', () => {
60
- expect(() => injectDummy(Injector.create({ providers: [] }))).toThrowError(
61
- /No provider for InjectionToken/i,
62
- )
63
- expect(() =>
64
- injectDummyTwo(Injector.create({ providers: [] })),
65
- ).toThrowError(/No provider for InjectionToken/i)
66
- })
67
-
68
- it('given a custom injector, when run in that injector context and providing number, then return value', () => {
69
- const value = injectDummy(
70
- Injector.create({ providers: [{ provide: token, useValue: 2 }] }),
71
- )
72
- const valueTwo = injectDummyTwo(
73
- Injector.create({ providers: [{ provide: token, useValue: 2 }] }),
74
- )
75
- expect(value).toEqual(2)
76
- expect(valueTwo).toEqual(3)
77
- })
78
- })
@@ -1,83 +0,0 @@
1
- /* eslint-disable cspell/spellchecker */
2
- /**
3
- * The code in this file is adapted from NG Extension Platform at https://ngxtension.netlify.app.
4
- *
5
- * Original Author: Chau Tran
6
- *
7
- * NG Extension Platform is an open-source project licensed under the MIT license.
8
- *
9
- * For more information about the original code, see
10
- * https://github.com/nartc/ngxtension-platform
11
- */
12
- /* eslint-enable */
13
-
14
- import {
15
- Injector,
16
- assertInInjectionContext,
17
- inject,
18
- runInInjectionContext,
19
- } from '@angular/core'
20
-
21
- /**
22
- * `assertInjector` extends `assertInInjectionContext` with an optional `Injector`
23
- * After assertion, `assertInjector` runs the `runner` function with the guaranteed `Injector`
24
- * whether it is the default `Injector` within the current **Injection Context**
25
- * or the custom `Injector` that was passed in.
26
- * @template {() => any} Runner - Runner is a function that can return anything
27
- * @param fn - the Function to pass in `assertInInjectionContext`
28
- * @param injector - the optional "custom" Injector
29
- * @param runner - the runner fn
30
- * @returns result - returns the result of the Runner
31
- * @example
32
- * ```ts
33
- * function injectValue(injector?: Injector) {
34
- * return assertInjector(injectValue, injector, () => 'value');
35
- * }
36
- *
37
- * injectValue(); // string
38
- * ```
39
- */
40
- export function assertInjector<TRunner extends () => any>(
41
- fn: Function,
42
- injector: Injector | undefined | null,
43
- runner: TRunner,
44
- ): ReturnType<TRunner>
45
- /**
46
- * `assertInjector` extends `assertInInjectionContext` with an optional `Injector`
47
- * After assertion, `assertInjector` returns a guaranteed `Injector` whether it is the default `Injector`
48
- * within the current **Injection Context** or the custom `Injector` that was passed in.
49
- * @param fn - the Function to pass in `assertInInjectionContext`
50
- * @param injector - the optional "custom" Injector
51
- * @returns Injector
52
- * @example
53
- * ```ts
54
- * function injectDestroy(injector?: Injector) {
55
- * injector = assertInjector(injectDestroy, injector);
56
- *
57
- * return runInInjectionContext(injector, () => {
58
- * // code
59
- * })
60
- * }
61
- * ```
62
- */
63
- export function assertInjector(
64
- fn: Function,
65
- injector: Injector | undefined | null,
66
- ): Injector
67
- /**
68
- * @param fn - the Function to pass in `assertInInjectionContext`
69
- * @param injector - the optional "custom" Injector
70
- * @param runner - the runner fn
71
- * @returns any
72
- */
73
- export function assertInjector(
74
- fn: Function,
75
- injector: Injector | undefined | null,
76
- runner?: () => any,
77
- ) {
78
- !injector && assertInInjectionContext(fn)
79
- const assertedInjector = injector ?? inject(Injector)
80
-
81
- if (!runner) return assertedInjector
82
- return runInInjectionContext(assertedInjector, runner)
83
- }