@tanstack/angular-query-experimental 5.60.0 → 5.60.3
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/build/{rollup.d.ts → index.d.ts} +280 -361
- package/build/index.js +572 -0
- package/build/index.js.map +1 -0
- package/package.json +12 -15
- package/src/create-base-query.ts +117 -0
- package/src/index.ts +28 -0
- package/src/infinite-query-options.ts +125 -0
- package/src/inject-infinite-query.ts +119 -0
- package/src/inject-is-fetching.ts +49 -0
- package/src/inject-is-mutating.ts +48 -0
- package/src/inject-mutation-state.ts +102 -0
- package/src/inject-mutation.ts +121 -0
- package/src/inject-queries.ts +246 -0
- package/src/inject-query-client.ts +22 -0
- package/src/inject-query.ts +207 -0
- package/src/providers.ts +351 -0
- package/src/query-options.ts +125 -0
- package/src/signal-proxy.ts +46 -0
- package/src/test-setup.ts +12 -0
- package/src/types.ts +328 -0
- package/src/util/assert-injector/assert-injector.test.ts +74 -0
- package/src/util/assert-injector/assert-injector.ts +81 -0
- package/src/util/index.ts +13 -0
- package/src/util/is-dev-mode/is-dev-mode.ts +3 -0
- package/src/util/lazy-init/lazy-init.ts +34 -0
- package/src/util/lazy-signal-initializer/lazy-signal-initializer.ts +23 -0
- package/build/README.md +0 -133
- package/build/esm2022/create-base-query.mjs +0 -62
- package/build/esm2022/index.mjs +0 -16
- package/build/esm2022/infinite-query-options.mjs +0 -12
- package/build/esm2022/inject-infinite-query.mjs +0 -15
- package/build/esm2022/inject-is-fetching.mjs +0 -38
- package/build/esm2022/inject-is-mutating.mjs +0 -37
- package/build/esm2022/inject-mutation-state.mjs +0 -47
- package/build/esm2022/inject-mutation.mjs +0 -51
- package/build/esm2022/inject-queries.mjs +0 -33
- package/build/esm2022/inject-query-client.mjs +0 -23
- package/build/esm2022/inject-query.mjs +0 -44
- package/build/esm2022/providers.mjs +0 -206
- package/build/esm2022/query-options.mjs +0 -26
- package/build/esm2022/signal-proxy.mjs +0 -38
- package/build/esm2022/tanstack-angular-query-experimental.mjs +0 -5
- package/build/esm2022/types.mjs +0 -3
- package/build/esm2022/util/assert-injector/assert-injector.mjs +0 -21
- package/build/esm2022/util/create-injection-token/create-injection-token.mjs +0 -61
- package/build/esm2022/util/index.mjs +0 -9
- package/build/esm2022/util/is-dev-mode/is-dev-mode.mjs +0 -3
- package/build/esm2022/util/lazy-init/lazy-init.mjs +0 -31
- package/build/esm2022/util/lazy-signal-initializer/lazy-signal-initializer.mjs +0 -14
- package/build/fesm2022/tanstack-angular-query-experimental.mjs +0 -738
- package/build/fesm2022/tanstack-angular-query-experimental.mjs.map +0 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { computed, untracked } from '@angular/core'
|
|
2
|
+
import type { Signal } from '@angular/core'
|
|
3
|
+
|
|
4
|
+
export type MapToSignals<T> = {
|
|
5
|
+
[K in keyof T]: T[K] extends Function ? T[K] : Signal<T[K]>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Exposes fields of an object passed via an Angular `Signal` as `Computed` signals.
|
|
10
|
+
* Functions on the object are passed through as-is.
|
|
11
|
+
* @param inputSignal - `Signal` that must return an object.
|
|
12
|
+
* @returns A proxy object with the same fields as the input object, but with each field wrapped in a `Computed` signal.
|
|
13
|
+
*/
|
|
14
|
+
export function signalProxy<TInput extends Record<string | symbol, any>>(
|
|
15
|
+
inputSignal: Signal<TInput>,
|
|
16
|
+
) {
|
|
17
|
+
const internalState = {} as MapToSignals<TInput>
|
|
18
|
+
|
|
19
|
+
return new Proxy<MapToSignals<TInput>>(internalState, {
|
|
20
|
+
get(target, prop) {
|
|
21
|
+
// first check if we have it in our internal state and return it
|
|
22
|
+
const computedField = target[prop]
|
|
23
|
+
if (computedField) return computedField
|
|
24
|
+
|
|
25
|
+
// then, check if it's a function on the resultState and return it
|
|
26
|
+
const targetField = untracked(inputSignal)[prop]
|
|
27
|
+
if (typeof targetField === 'function') return targetField
|
|
28
|
+
|
|
29
|
+
// finally, create a computed field, store it and return it
|
|
30
|
+
// @ts-expect-error
|
|
31
|
+
return (target[prop] = computed(() => inputSignal()[prop]))
|
|
32
|
+
},
|
|
33
|
+
has(_, prop) {
|
|
34
|
+
return !!untracked(inputSignal)[prop]
|
|
35
|
+
},
|
|
36
|
+
ownKeys() {
|
|
37
|
+
return Reflect.ownKeys(untracked(inputSignal))
|
|
38
|
+
},
|
|
39
|
+
getOwnPropertyDescriptor() {
|
|
40
|
+
return {
|
|
41
|
+
enumerable: true,
|
|
42
|
+
configurable: true,
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import '@analogjs/vite-plugin-angular/setup-vitest'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
BrowserDynamicTestingModule,
|
|
5
|
+
platformBrowserDynamicTesting,
|
|
6
|
+
} from '@angular/platform-browser-dynamic/testing'
|
|
7
|
+
import { getTestBed } from '@angular/core/testing'
|
|
8
|
+
|
|
9
|
+
getTestBed().initTestEnvironment(
|
|
10
|
+
BrowserDynamicTestingModule,
|
|
11
|
+
platformBrowserDynamicTesting(),
|
|
12
|
+
)
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/* istanbul ignore file */
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
DefaultError,
|
|
5
|
+
DefinedInfiniteQueryObserverResult,
|
|
6
|
+
DefinedQueryObserverResult,
|
|
7
|
+
InfiniteQueryObserverOptions,
|
|
8
|
+
InfiniteQueryObserverResult,
|
|
9
|
+
MutateFunction,
|
|
10
|
+
MutationObserverOptions,
|
|
11
|
+
MutationObserverResult,
|
|
12
|
+
OmitKeyof,
|
|
13
|
+
Override,
|
|
14
|
+
QueryKey,
|
|
15
|
+
QueryObserverOptions,
|
|
16
|
+
QueryObserverResult,
|
|
17
|
+
} from '@tanstack/query-core'
|
|
18
|
+
import type { Signal } from '@angular/core'
|
|
19
|
+
import type { MapToSignals } from './signal-proxy'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
export interface CreateBaseQueryOptions<
|
|
25
|
+
TQueryFnData = unknown,
|
|
26
|
+
TError = DefaultError,
|
|
27
|
+
TData = TQueryFnData,
|
|
28
|
+
TQueryData = TQueryFnData,
|
|
29
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
30
|
+
> extends QueryObserverOptions<
|
|
31
|
+
TQueryFnData,
|
|
32
|
+
TError,
|
|
33
|
+
TData,
|
|
34
|
+
TQueryData,
|
|
35
|
+
TQueryKey
|
|
36
|
+
> {}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @public
|
|
40
|
+
*/
|
|
41
|
+
export interface CreateQueryOptions<
|
|
42
|
+
TQueryFnData = unknown,
|
|
43
|
+
TError = DefaultError,
|
|
44
|
+
TData = TQueryFnData,
|
|
45
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
46
|
+
> extends OmitKeyof<
|
|
47
|
+
CreateBaseQueryOptions<
|
|
48
|
+
TQueryFnData,
|
|
49
|
+
TError,
|
|
50
|
+
TData,
|
|
51
|
+
TQueryFnData,
|
|
52
|
+
TQueryKey
|
|
53
|
+
>,
|
|
54
|
+
'suspense'
|
|
55
|
+
> {}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @public
|
|
59
|
+
*/
|
|
60
|
+
type CreateStatusBasedQueryResult<
|
|
61
|
+
TStatus extends QueryObserverResult['status'],
|
|
62
|
+
TData = unknown,
|
|
63
|
+
TError = DefaultError,
|
|
64
|
+
> = Extract<QueryObserverResult<TData, TError>, { status: TStatus }>
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @public
|
|
68
|
+
*/
|
|
69
|
+
export interface BaseQueryNarrowing<TData = unknown, TError = DefaultError> {
|
|
70
|
+
isSuccess: (
|
|
71
|
+
this: CreateBaseQueryResult<TData, TError>,
|
|
72
|
+
) => this is CreateBaseQueryResult<
|
|
73
|
+
TData,
|
|
74
|
+
TError,
|
|
75
|
+
CreateStatusBasedQueryResult<'success', TData, TError>
|
|
76
|
+
>
|
|
77
|
+
isError: (
|
|
78
|
+
this: CreateBaseQueryResult<TData, TError>,
|
|
79
|
+
) => this is CreateBaseQueryResult<
|
|
80
|
+
TData,
|
|
81
|
+
TError,
|
|
82
|
+
CreateStatusBasedQueryResult<'error', TData, TError>
|
|
83
|
+
>
|
|
84
|
+
isPending: (
|
|
85
|
+
this: CreateBaseQueryResult<TData, TError>,
|
|
86
|
+
) => this is CreateBaseQueryResult<
|
|
87
|
+
TData,
|
|
88
|
+
TError,
|
|
89
|
+
CreateStatusBasedQueryResult<'pending', TData, TError>
|
|
90
|
+
>
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @public
|
|
95
|
+
*/
|
|
96
|
+
export interface CreateInfiniteQueryOptions<
|
|
97
|
+
TQueryFnData = unknown,
|
|
98
|
+
TError = DefaultError,
|
|
99
|
+
TData = TQueryFnData,
|
|
100
|
+
TQueryData = TQueryFnData,
|
|
101
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
102
|
+
TPageParam = unknown,
|
|
103
|
+
> extends OmitKeyof<
|
|
104
|
+
InfiniteQueryObserverOptions<
|
|
105
|
+
TQueryFnData,
|
|
106
|
+
TError,
|
|
107
|
+
TData,
|
|
108
|
+
TQueryData,
|
|
109
|
+
TQueryKey,
|
|
110
|
+
TPageParam
|
|
111
|
+
>,
|
|
112
|
+
'suspense'
|
|
113
|
+
> {}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @public
|
|
117
|
+
*/
|
|
118
|
+
export type CreateBaseQueryResult<
|
|
119
|
+
TData = unknown,
|
|
120
|
+
TError = DefaultError,
|
|
121
|
+
TState = QueryObserverResult<TData, TError>,
|
|
122
|
+
> = BaseQueryNarrowing<TData, TError> &
|
|
123
|
+
MapToSignals<OmitKeyof<TState, keyof BaseQueryNarrowing, 'safely'>>
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @public
|
|
127
|
+
*/
|
|
128
|
+
export type CreateQueryResult<
|
|
129
|
+
TData = unknown,
|
|
130
|
+
TError = DefaultError,
|
|
131
|
+
> = CreateBaseQueryResult<TData, TError>
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @public
|
|
135
|
+
*/
|
|
136
|
+
export type DefinedCreateQueryResult<
|
|
137
|
+
TData = unknown,
|
|
138
|
+
TError = DefaultError,
|
|
139
|
+
TDefinedQueryObserver = DefinedQueryObserverResult<TData, TError>,
|
|
140
|
+
> = MapToSignals<TDefinedQueryObserver>
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @public
|
|
144
|
+
*/
|
|
145
|
+
export type CreateInfiniteQueryResult<
|
|
146
|
+
TData = unknown,
|
|
147
|
+
TError = DefaultError,
|
|
148
|
+
> = MapToSignals<InfiniteQueryObserverResult<TData, TError>>
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @public
|
|
152
|
+
*/
|
|
153
|
+
export type DefinedCreateInfiniteQueryResult<
|
|
154
|
+
TData = unknown,
|
|
155
|
+
TError = DefaultError,
|
|
156
|
+
TDefinedInfiniteQueryObserver = DefinedInfiniteQueryObserverResult<
|
|
157
|
+
TData,
|
|
158
|
+
TError
|
|
159
|
+
>,
|
|
160
|
+
> = MapToSignals<TDefinedInfiniteQueryObserver>
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* @public
|
|
164
|
+
*/
|
|
165
|
+
export interface CreateMutationOptions<
|
|
166
|
+
TData = unknown,
|
|
167
|
+
TError = DefaultError,
|
|
168
|
+
TVariables = void,
|
|
169
|
+
TContext = unknown,
|
|
170
|
+
> extends OmitKeyof<
|
|
171
|
+
MutationObserverOptions<TData, TError, TVariables, TContext>,
|
|
172
|
+
'_defaulted'
|
|
173
|
+
> {}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* @public
|
|
177
|
+
*/
|
|
178
|
+
export type CreateMutateFunction<
|
|
179
|
+
TData = unknown,
|
|
180
|
+
TError = DefaultError,
|
|
181
|
+
TVariables = void,
|
|
182
|
+
TContext = unknown,
|
|
183
|
+
> = (
|
|
184
|
+
...args: Parameters<MutateFunction<TData, TError, TVariables, TContext>>
|
|
185
|
+
) => void
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @public
|
|
189
|
+
*/
|
|
190
|
+
export type CreateMutateAsyncFunction<
|
|
191
|
+
TData = unknown,
|
|
192
|
+
TError = DefaultError,
|
|
193
|
+
TVariables = void,
|
|
194
|
+
TContext = unknown,
|
|
195
|
+
> = MutateFunction<TData, TError, TVariables, TContext>
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* @public
|
|
199
|
+
*/
|
|
200
|
+
export type CreateBaseMutationResult<
|
|
201
|
+
TData = unknown,
|
|
202
|
+
TError = DefaultError,
|
|
203
|
+
TVariables = unknown,
|
|
204
|
+
TContext = unknown,
|
|
205
|
+
> = Override<
|
|
206
|
+
MutationObserverResult<TData, TError, TVariables, TContext>,
|
|
207
|
+
{ mutate: CreateMutateFunction<TData, TError, TVariables, TContext> }
|
|
208
|
+
> & {
|
|
209
|
+
mutateAsync: CreateMutateAsyncFunction<TData, TError, TVariables, TContext>
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* @public
|
|
214
|
+
*/
|
|
215
|
+
type CreateStatusBasedMutationResult<
|
|
216
|
+
TStatus extends CreateBaseMutationResult['status'],
|
|
217
|
+
TData = unknown,
|
|
218
|
+
TError = DefaultError,
|
|
219
|
+
TVariables = unknown,
|
|
220
|
+
TContext = unknown,
|
|
221
|
+
> = Extract<
|
|
222
|
+
CreateBaseMutationResult<TData, TError, TVariables, TContext>,
|
|
223
|
+
{ status: TStatus }
|
|
224
|
+
>
|
|
225
|
+
|
|
226
|
+
type SignalFunction<T extends () => any> = T & Signal<ReturnType<T>>
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* @public
|
|
230
|
+
*/
|
|
231
|
+
export interface BaseMutationNarrowing<
|
|
232
|
+
TData = unknown,
|
|
233
|
+
TError = DefaultError,
|
|
234
|
+
TVariables = unknown,
|
|
235
|
+
TContext = unknown,
|
|
236
|
+
> {
|
|
237
|
+
isSuccess: SignalFunction<
|
|
238
|
+
(
|
|
239
|
+
this: CreateMutationResult<TData, TError, TVariables, TContext>,
|
|
240
|
+
) => this is CreateMutationResult<
|
|
241
|
+
TData,
|
|
242
|
+
TError,
|
|
243
|
+
TVariables,
|
|
244
|
+
TContext,
|
|
245
|
+
CreateStatusBasedMutationResult<
|
|
246
|
+
'success',
|
|
247
|
+
TData,
|
|
248
|
+
TError,
|
|
249
|
+
TVariables,
|
|
250
|
+
TContext
|
|
251
|
+
>
|
|
252
|
+
>
|
|
253
|
+
>
|
|
254
|
+
isError: SignalFunction<
|
|
255
|
+
(
|
|
256
|
+
this: CreateMutationResult<TData, TError, TVariables, TContext>,
|
|
257
|
+
) => this is CreateMutationResult<
|
|
258
|
+
TData,
|
|
259
|
+
TError,
|
|
260
|
+
TVariables,
|
|
261
|
+
TContext,
|
|
262
|
+
CreateStatusBasedMutationResult<
|
|
263
|
+
'error',
|
|
264
|
+
TData,
|
|
265
|
+
TError,
|
|
266
|
+
TVariables,
|
|
267
|
+
TContext
|
|
268
|
+
>
|
|
269
|
+
>
|
|
270
|
+
>
|
|
271
|
+
isPending: SignalFunction<
|
|
272
|
+
(
|
|
273
|
+
this: CreateMutationResult<TData, TError, TVariables, TContext>,
|
|
274
|
+
) => this is CreateMutationResult<
|
|
275
|
+
TData,
|
|
276
|
+
TError,
|
|
277
|
+
TVariables,
|
|
278
|
+
TContext,
|
|
279
|
+
CreateStatusBasedMutationResult<
|
|
280
|
+
'pending',
|
|
281
|
+
TData,
|
|
282
|
+
TError,
|
|
283
|
+
TVariables,
|
|
284
|
+
TContext
|
|
285
|
+
>
|
|
286
|
+
>
|
|
287
|
+
>
|
|
288
|
+
isIdle: SignalFunction<
|
|
289
|
+
(
|
|
290
|
+
this: CreateMutationResult<TData, TError, TVariables, TContext>,
|
|
291
|
+
) => this is CreateMutationResult<
|
|
292
|
+
TData,
|
|
293
|
+
TError,
|
|
294
|
+
TVariables,
|
|
295
|
+
TContext,
|
|
296
|
+
CreateStatusBasedMutationResult<
|
|
297
|
+
'idle',
|
|
298
|
+
TData,
|
|
299
|
+
TError,
|
|
300
|
+
TVariables,
|
|
301
|
+
TContext
|
|
302
|
+
>
|
|
303
|
+
>
|
|
304
|
+
>
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* @public
|
|
309
|
+
*/
|
|
310
|
+
export type CreateMutationResult<
|
|
311
|
+
TData = unknown,
|
|
312
|
+
TError = DefaultError,
|
|
313
|
+
TVariables = unknown,
|
|
314
|
+
TContext = unknown,
|
|
315
|
+
TState = CreateStatusBasedMutationResult<
|
|
316
|
+
CreateBaseMutationResult['status'],
|
|
317
|
+
TData,
|
|
318
|
+
TError,
|
|
319
|
+
TVariables,
|
|
320
|
+
TContext
|
|
321
|
+
>,
|
|
322
|
+
> = BaseMutationNarrowing<TData, TError, TVariables, TContext> &
|
|
323
|
+
MapToSignals<OmitKeyof<TState, keyof BaseMutationNarrowing, 'safely'>>
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* @public
|
|
327
|
+
*/
|
|
328
|
+
export type NonUndefinedGuard<T> = T extends undefined ? never : T
|
|
@@ -0,0 +1,74 @@
|
|
|
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
|
+
runInInjectionContext,
|
|
19
|
+
} from '@angular/core'
|
|
20
|
+
import { TestBed } from '@angular/core/testing'
|
|
21
|
+
import { assertInjector } from './assert-injector'
|
|
22
|
+
|
|
23
|
+
describe('assertInjector', () => {
|
|
24
|
+
const token = new InjectionToken('token', {
|
|
25
|
+
factory: () => 1,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
function injectDummy(injector?: Injector) {
|
|
29
|
+
injector = assertInjector(injectDummy, injector)
|
|
30
|
+
return runInInjectionContext(injector, () => inject(token))
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function injectDummyTwo(injector?: Injector) {
|
|
34
|
+
return assertInjector(injectDummyTwo, injector, () => inject(token) + 1)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
it('given no custom injector, when run in injection context, then return value', () => {
|
|
38
|
+
TestBed.runInInjectionContext(() => {
|
|
39
|
+
const value = injectDummy()
|
|
40
|
+
const valueTwo = injectDummyTwo()
|
|
41
|
+
expect(value).toEqual(1)
|
|
42
|
+
expect(valueTwo).toEqual(2)
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('given no custom injector, when run outside injection context, then throw', () => {
|
|
47
|
+
expect(() => injectDummy()).toThrowError(
|
|
48
|
+
/injectDummy\(\) can only be used within an injection context/i,
|
|
49
|
+
)
|
|
50
|
+
expect(() => injectDummyTwo()).toThrowError(
|
|
51
|
+
/injectDummyTwo\(\) can only be used within an injection context/i,
|
|
52
|
+
)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('given a custom injector, when run in that injector context without providing number, then throw', () => {
|
|
56
|
+
expect(() => injectDummy(Injector.create({ providers: [] }))).toThrowError(
|
|
57
|
+
/No provider for InjectionToken/i,
|
|
58
|
+
)
|
|
59
|
+
expect(() =>
|
|
60
|
+
injectDummyTwo(Injector.create({ providers: [] })),
|
|
61
|
+
).toThrowError(/No provider for InjectionToken/i)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('given a custom injector, when run in that injector context and providing number, then return value', () => {
|
|
65
|
+
const value = injectDummy(
|
|
66
|
+
Injector.create({ providers: [{ provide: token, useValue: 2 }] }),
|
|
67
|
+
)
|
|
68
|
+
const valueTwo = injectDummyTwo(
|
|
69
|
+
Injector.create({ providers: [{ provide: token, useValue: 2 }] }),
|
|
70
|
+
)
|
|
71
|
+
expect(value).toEqual(2)
|
|
72
|
+
expect(valueTwo).toEqual(3)
|
|
73
|
+
})
|
|
74
|
+
})
|
|
@@ -0,0 +1,81 @@
|
|
|
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
|
+
*
|
|
27
|
+
* @template {() => any} Runner - Runner is a function that can return anything
|
|
28
|
+
* @param {Function} fn - the Function to pass in `assertInInjectionContext`
|
|
29
|
+
* @param {Injector | undefined | null} injector - the optional "custom" Injector
|
|
30
|
+
* @param {Runner} runner - the runner fn
|
|
31
|
+
* @returns {ReturnType<Runner>} result - returns the result of the Runner
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* function injectValue(injector?: Injector) {
|
|
36
|
+
* return assertInjector(injectValue, injector, () => 'value');
|
|
37
|
+
* }
|
|
38
|
+
*
|
|
39
|
+
* injectValue(); // string
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export function assertInjector<TRunner extends () => any>(
|
|
43
|
+
fn: Function,
|
|
44
|
+
injector: Injector | undefined | null,
|
|
45
|
+
runner: TRunner,
|
|
46
|
+
): ReturnType<TRunner>
|
|
47
|
+
/**
|
|
48
|
+
* `assertInjector` extends `assertInInjectionContext` with an optional `Injector`
|
|
49
|
+
* After assertion, `assertInjector` returns a guaranteed `Injector` whether it is the default `Injector`
|
|
50
|
+
* within the current **Injection Context** or the custom `Injector` that was passed in.
|
|
51
|
+
*
|
|
52
|
+
* @param {Function} fn - the Function to pass in `assertInInjectionContext`
|
|
53
|
+
* @param {Injector | undefined | null} injector - the optional "custom" Injector
|
|
54
|
+
* @returns Injector
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* function injectDestroy(injector?: Injector) {
|
|
59
|
+
* injector = assertInjector(injectDestroy, injector);
|
|
60
|
+
*
|
|
61
|
+
* return runInInjectionContext(injector, () => {
|
|
62
|
+
* // code
|
|
63
|
+
* })
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export function assertInjector(
|
|
68
|
+
fn: Function,
|
|
69
|
+
injector: Injector | undefined | null,
|
|
70
|
+
): Injector
|
|
71
|
+
export function assertInjector(
|
|
72
|
+
fn: Function,
|
|
73
|
+
injector: Injector | undefined | null,
|
|
74
|
+
runner?: () => any,
|
|
75
|
+
) {
|
|
76
|
+
!injector && assertInInjectionContext(fn)
|
|
77
|
+
const assertedInjector = injector ?? inject(Injector)
|
|
78
|
+
|
|
79
|
+
if (!runner) return assertedInjector
|
|
80
|
+
return runInInjectionContext(assertedInjector, runner)
|
|
81
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function shouldThrowError<T extends (...args: Array<any>) => boolean>(
|
|
2
|
+
throwError: boolean | T | undefined,
|
|
3
|
+
params: Parameters<T>,
|
|
4
|
+
): boolean {
|
|
5
|
+
// Allow throwError function to override throwing behavior on a per-error basis
|
|
6
|
+
if (typeof throwError === 'function') {
|
|
7
|
+
return throwError(...params)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return !!throwError
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function noop() {}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { untracked } from '@angular/core'
|
|
2
|
+
|
|
3
|
+
export function lazyInit<T extends object>(initializer: () => T): T {
|
|
4
|
+
let object: T | null = null
|
|
5
|
+
|
|
6
|
+
const initializeObject = () => {
|
|
7
|
+
if (!object) {
|
|
8
|
+
object = untracked(() => initializer())
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
queueMicrotask(() => initializeObject())
|
|
13
|
+
|
|
14
|
+
return new Proxy<T>({} as T, {
|
|
15
|
+
get(_, prop, receiver) {
|
|
16
|
+
initializeObject()
|
|
17
|
+
return Reflect.get(object as T, prop, receiver)
|
|
18
|
+
},
|
|
19
|
+
has(_, prop) {
|
|
20
|
+
initializeObject()
|
|
21
|
+
return Reflect.has(object as T, prop)
|
|
22
|
+
},
|
|
23
|
+
ownKeys() {
|
|
24
|
+
initializeObject()
|
|
25
|
+
return Reflect.ownKeys(object as T)
|
|
26
|
+
},
|
|
27
|
+
getOwnPropertyDescriptor() {
|
|
28
|
+
return {
|
|
29
|
+
enumerable: true,
|
|
30
|
+
configurable: true,
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Injector, computed, inject, untracked } from '@angular/core'
|
|
2
|
+
import type { Signal } from '@angular/core'
|
|
3
|
+
|
|
4
|
+
type SignalInitializerFn<T> = (injector: Injector) => Signal<T>
|
|
5
|
+
|
|
6
|
+
export function lazySignalInitializer<T>(
|
|
7
|
+
initializerFn: SignalInitializerFn<T>,
|
|
8
|
+
) {
|
|
9
|
+
const injector = inject(Injector)
|
|
10
|
+
|
|
11
|
+
let source: Signal<T> | null = null
|
|
12
|
+
|
|
13
|
+
const unwrapSignal = () => {
|
|
14
|
+
if (!source) {
|
|
15
|
+
source = untracked(() => initializerFn(injector))
|
|
16
|
+
}
|
|
17
|
+
return source()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
queueMicrotask(() => unwrapSignal())
|
|
21
|
+
|
|
22
|
+
return computed(unwrapSignal)
|
|
23
|
+
}
|