@tanstack/angular-query-experimental 5.24.8 → 5.26.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/build/esm2022/create-base-query.mjs +4 -5
- package/build/esm2022/inject-mutation-state.mjs +20 -17
- package/build/esm2022/inject-mutation.mjs +21 -17
- package/build/esm2022/inject-queries.mjs +1 -1
- package/build/esm2022/util/lazy-init/lazy-init.mjs +31 -0
- package/build/esm2022/util/lazy-signal-initializer/lazy-signal-initializer.mjs +14 -0
- package/build/fesm2022/tanstack-angular-query-experimental.mjs +52 -35
- package/build/fesm2022/tanstack-angular-query-experimental.mjs.map +1 -1
- package/build/inject-mutation.d.ts +1 -1
- package/build/inject-queries.d.ts +3 -3
- package/build/util/lazy-signal-initializer/lazy-signal-initializer.d.ts +4 -0
- package/package.json +2 -2
- package/src/__tests__/inject-mutation-state.test.ts +71 -2
- package/src/__tests__/inject-mutation.test.ts +102 -3
- package/src/__tests__/inject-query.test.ts +31 -2
- package/src/__tests__/test-utils.ts +48 -1
- package/src/__tests__/util/lazy-init/lazy-init.test.ts +126 -0
- package/src/__tests__/util/lazy-signal-initializer/lazy-signal-initializer.test.ts +130 -0
- package/src/create-base-query.ts +12 -15
- package/src/inject-mutation-state.ts +30 -24
- package/src/inject-mutation.ts +49 -32
- package/src/inject-queries.ts +7 -5
- package/src/{lazy-init.ts → util/lazy-init/lazy-init.ts} +3 -1
- package/src/util/lazy-signal-initializer/lazy-signal-initializer.ts +28 -0
- package/build/esm2022/lazy-init.mjs +0 -30
- /package/build/{lazy-init.d.ts → util/lazy-init/lazy-init.d.ts} +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { isSignal, untracked } from '@angular/core'
|
|
1
|
+
import { type InputSignal, isSignal, untracked } from '@angular/core'
|
|
2
|
+
import { SIGNAL, signalSetFn } from '@angular/core/primitives/signals'
|
|
3
|
+
import type { ComponentFixture } from '@angular/core/testing'
|
|
2
4
|
|
|
3
5
|
export function simpleFetcher(): Promise<string> {
|
|
4
6
|
return new Promise((resolve) => {
|
|
@@ -82,3 +84,48 @@ export const expectSignals = <T extends Record<string, any>>(
|
|
|
82
84
|
): void => {
|
|
83
85
|
expect(evaluateSignals(obj)).toMatchObject(expected)
|
|
84
86
|
}
|
|
87
|
+
|
|
88
|
+
type ToSignalInputUpdatableMap<T> = {
|
|
89
|
+
[K in keyof T as T[K] extends InputSignal<any>
|
|
90
|
+
? K
|
|
91
|
+
: never]: T[K] extends InputSignal<infer Value> ? Value : never
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function componentHasSignalInputProperty<TProperty extends string>(
|
|
95
|
+
component: object,
|
|
96
|
+
property: TProperty,
|
|
97
|
+
): component is { [key in TProperty]: InputSignal<unknown> } {
|
|
98
|
+
return (
|
|
99
|
+
component.hasOwnProperty(property) && (component as any)[property][SIGNAL]
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Set required signal input value to component fixture
|
|
105
|
+
* @see https://github.com/angular/angular/issues/54013
|
|
106
|
+
*/
|
|
107
|
+
export function setSignalInputs<T extends NonNullable<unknown>>(
|
|
108
|
+
component: T,
|
|
109
|
+
inputs: ToSignalInputUpdatableMap<T>,
|
|
110
|
+
) {
|
|
111
|
+
for (const inputKey in inputs) {
|
|
112
|
+
if (componentHasSignalInputProperty(component, inputKey)) {
|
|
113
|
+
signalSetFn(component[inputKey][SIGNAL], inputs[inputKey])
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function setFixtureSignalInputs<T extends NonNullable<unknown>>(
|
|
119
|
+
componentFixture: ComponentFixture<T>,
|
|
120
|
+
inputs: ToSignalInputUpdatableMap<T>,
|
|
121
|
+
options: { detectChanges: boolean } = { detectChanges: true },
|
|
122
|
+
) {
|
|
123
|
+
setSignalInputs(componentFixture.componentInstance, inputs)
|
|
124
|
+
if (options.detectChanges) {
|
|
125
|
+
componentFixture.detectChanges()
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function flushQueue() {
|
|
130
|
+
await new Promise(setImmediate)
|
|
131
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
ChangeDetectionStrategy,
|
|
4
|
+
Component,
|
|
5
|
+
type WritableSignal,
|
|
6
|
+
computed,
|
|
7
|
+
effect,
|
|
8
|
+
input,
|
|
9
|
+
signal,
|
|
10
|
+
} from '@angular/core'
|
|
11
|
+
import { TestBed } from '@angular/core/testing'
|
|
12
|
+
import { flushQueue, setFixtureSignalInputs } from '../../test-utils'
|
|
13
|
+
import { lazyInit } from '../../../util/lazy-init/lazy-init'
|
|
14
|
+
|
|
15
|
+
describe('lazyInit', () => {
|
|
16
|
+
test('should init lazily in next tick when not accessing manually', async () => {
|
|
17
|
+
const mockFn = vi.fn()
|
|
18
|
+
|
|
19
|
+
TestBed.runInInjectionContext(() => {
|
|
20
|
+
lazyInit(() => {
|
|
21
|
+
mockFn()
|
|
22
|
+
return {
|
|
23
|
+
data: signal(true),
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
expect(mockFn).not.toHaveBeenCalled()
|
|
29
|
+
|
|
30
|
+
await new Promise(setImmediate)
|
|
31
|
+
|
|
32
|
+
expect(mockFn).toHaveBeenCalled()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('should init eagerly accessing manually', async () => {
|
|
36
|
+
const mockFn = vi.fn()
|
|
37
|
+
|
|
38
|
+
TestBed.runInInjectionContext(() => {
|
|
39
|
+
const lazySignal = lazyInit(() => {
|
|
40
|
+
mockFn()
|
|
41
|
+
return {
|
|
42
|
+
data: signal(true),
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
lazySignal.data()
|
|
47
|
+
|
|
48
|
+
console.log(lazySignal)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
expect(mockFn).toHaveBeenCalled()
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('should init lazily and only once', async () => {
|
|
55
|
+
const initCallFn = vi.fn()
|
|
56
|
+
const registerDataValue = vi.fn<[number]>()
|
|
57
|
+
|
|
58
|
+
let value!: { data: WritableSignal<number> }
|
|
59
|
+
const outerSignal = signal(0)
|
|
60
|
+
|
|
61
|
+
TestBed.runInInjectionContext(() => {
|
|
62
|
+
value = lazyInit(() => {
|
|
63
|
+
initCallFn()
|
|
64
|
+
|
|
65
|
+
void outerSignal()
|
|
66
|
+
|
|
67
|
+
return { data: signal(0) }
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
effect(() => registerDataValue(value.data()))
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
value.data()
|
|
74
|
+
|
|
75
|
+
await flushQueue()
|
|
76
|
+
|
|
77
|
+
expect(outerSignal).toBeDefined()
|
|
78
|
+
|
|
79
|
+
expect(initCallFn).toHaveBeenCalledTimes(1)
|
|
80
|
+
|
|
81
|
+
outerSignal.set(1)
|
|
82
|
+
await flushQueue()
|
|
83
|
+
outerSignal.set(2)
|
|
84
|
+
await flushQueue()
|
|
85
|
+
value.data.set(4)
|
|
86
|
+
await flushQueue()
|
|
87
|
+
|
|
88
|
+
expect(initCallFn).toHaveBeenCalledTimes(1)
|
|
89
|
+
expect(registerDataValue).toHaveBeenCalledTimes(2)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test('should support required signal input', async () => {
|
|
93
|
+
@Component({
|
|
94
|
+
standalone: true,
|
|
95
|
+
template: `{{ call }} - {{ lazySignal.data() }}`,
|
|
96
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
97
|
+
})
|
|
98
|
+
class Test {
|
|
99
|
+
readonly title = input.required<string>()
|
|
100
|
+
call = 0
|
|
101
|
+
|
|
102
|
+
lazySignal = lazyInit(() => {
|
|
103
|
+
this.call++
|
|
104
|
+
return {
|
|
105
|
+
data: computed(() => this.title()),
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const fixture = TestBed.createComponent(Test)
|
|
111
|
+
|
|
112
|
+
setFixtureSignalInputs(fixture, { title: 'newValue' })
|
|
113
|
+
expect(fixture.debugElement.nativeElement.textContent).toBe('0 - newValue')
|
|
114
|
+
await flushQueue()
|
|
115
|
+
|
|
116
|
+
setFixtureSignalInputs(fixture, { title: 'updatedValue' })
|
|
117
|
+
expect(fixture.debugElement.nativeElement.textContent).toBe(
|
|
118
|
+
'1 - updatedValue',
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
setFixtureSignalInputs(fixture, { title: 'newUpdatedValue' })
|
|
122
|
+
expect(fixture.debugElement.nativeElement.textContent).toBe(
|
|
123
|
+
'1 - newUpdatedValue',
|
|
124
|
+
)
|
|
125
|
+
})
|
|
126
|
+
})
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
Component,
|
|
4
|
+
type Signal,
|
|
5
|
+
type WritableSignal,
|
|
6
|
+
effect,
|
|
7
|
+
input,
|
|
8
|
+
signal,
|
|
9
|
+
} from '@angular/core'
|
|
10
|
+
import { TestBed } from '@angular/core/testing'
|
|
11
|
+
import { lazySignalInitializer } from '../../../util/lazy-signal-initializer/lazy-signal-initializer'
|
|
12
|
+
import { flushQueue, setFixtureSignalInputs } from '../../test-utils'
|
|
13
|
+
|
|
14
|
+
describe('lazySignalInitializer', () => {
|
|
15
|
+
test('should init lazily in next tick when not accessing manually', async () => {
|
|
16
|
+
const mockFn = vi.fn()
|
|
17
|
+
|
|
18
|
+
TestBed.runInInjectionContext(() => {
|
|
19
|
+
lazySignalInitializer(() => {
|
|
20
|
+
mockFn()
|
|
21
|
+
return signal(true)
|
|
22
|
+
})
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
expect(mockFn).not.toHaveBeenCalled()
|
|
26
|
+
|
|
27
|
+
await new Promise(setImmediate)
|
|
28
|
+
|
|
29
|
+
expect(mockFn).toHaveBeenCalled()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('should init eagerly accessing manually', async () => {
|
|
33
|
+
const mockFn = vi.fn()
|
|
34
|
+
|
|
35
|
+
TestBed.runInInjectionContext(() => {
|
|
36
|
+
const lazySignal = lazySignalInitializer(() => {
|
|
37
|
+
mockFn()
|
|
38
|
+
return signal(true)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
lazySignal()
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
expect(mockFn).toHaveBeenCalled()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('should init lazily and only once', async () => {
|
|
48
|
+
const initCallFn = vi.fn()
|
|
49
|
+
const registerEffectValue = vi.fn<[number]>()
|
|
50
|
+
|
|
51
|
+
let value!: Signal<number>
|
|
52
|
+
const outerSignal = signal(0)
|
|
53
|
+
let innerSignal!: WritableSignal<number>
|
|
54
|
+
|
|
55
|
+
TestBed.runInInjectionContext(() => {
|
|
56
|
+
value = lazySignalInitializer(() => {
|
|
57
|
+
initCallFn()
|
|
58
|
+
innerSignal = signal(0)
|
|
59
|
+
|
|
60
|
+
void outerSignal()
|
|
61
|
+
|
|
62
|
+
return innerSignal
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
effect(() => registerEffectValue(value()))
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
value()
|
|
69
|
+
|
|
70
|
+
await flushQueue()
|
|
71
|
+
|
|
72
|
+
expect(outerSignal).toBeDefined()
|
|
73
|
+
expect(innerSignal).toBeDefined()
|
|
74
|
+
|
|
75
|
+
expect(initCallFn).toHaveBeenCalledTimes(1)
|
|
76
|
+
|
|
77
|
+
innerSignal.set(1)
|
|
78
|
+
await flushQueue()
|
|
79
|
+
outerSignal.set(2)
|
|
80
|
+
await flushQueue()
|
|
81
|
+
|
|
82
|
+
expect(initCallFn).toHaveBeenCalledTimes(1)
|
|
83
|
+
expect(registerEffectValue).toHaveBeenCalledTimes(2)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('should init lazily', async () => {
|
|
87
|
+
@Component({
|
|
88
|
+
standalone: true,
|
|
89
|
+
template: `{{ subscribed }}`,
|
|
90
|
+
})
|
|
91
|
+
class Test {
|
|
92
|
+
subscribed = false
|
|
93
|
+
|
|
94
|
+
lazySignal = lazySignalInitializer(() => {
|
|
95
|
+
this.subscribed = true
|
|
96
|
+
return signal('value')
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const fixture = TestBed.createComponent(Test)
|
|
101
|
+
const { debugElement } = fixture
|
|
102
|
+
fixture.detectChanges()
|
|
103
|
+
|
|
104
|
+
expect(debugElement.nativeElement.textContent).toBe('false')
|
|
105
|
+
|
|
106
|
+
await new Promise(setImmediate)
|
|
107
|
+
|
|
108
|
+
fixture.detectChanges()
|
|
109
|
+
|
|
110
|
+
expect(debugElement.nativeElement.textContent).toBe('true')
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test('should support required signal input', () => {
|
|
114
|
+
@Component({
|
|
115
|
+
standalone: true,
|
|
116
|
+
template: `{{ subscribed }}`,
|
|
117
|
+
})
|
|
118
|
+
class Test {
|
|
119
|
+
readonly title = input.required<string>()
|
|
120
|
+
subscribed = false
|
|
121
|
+
|
|
122
|
+
lazySignal = lazySignalInitializer(() => {
|
|
123
|
+
return signal(this.title())
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const fixture = TestBed.createComponent(Test)
|
|
128
|
+
setFixtureSignalInputs(fixture, { title: 'newValue' })
|
|
129
|
+
})
|
|
130
|
+
})
|
package/src/create-base-query.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from '@angular/core'
|
|
11
11
|
import { notifyManager } from '@tanstack/query-core'
|
|
12
12
|
import { signalProxy } from './signal-proxy'
|
|
13
|
-
import { lazyInit } from './lazy-init'
|
|
13
|
+
import { lazyInit } from './util/lazy-init/lazy-init'
|
|
14
14
|
import type { QueryClient, QueryKey, QueryObserver } from '@tanstack/query-core'
|
|
15
15
|
import type { CreateBaseQueryOptions, CreateBaseQueryResult } from './types'
|
|
16
16
|
|
|
@@ -67,20 +67,17 @@ export function createBaseQuery<
|
|
|
67
67
|
observer.getOptimisticResult(defaultedOptionsSignal()),
|
|
68
68
|
)
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
})
|
|
82
|
-
}),
|
|
83
|
-
)
|
|
70
|
+
effect(() => {
|
|
71
|
+
const defaultedOptions = defaultedOptionsSignal()
|
|
72
|
+
observer.setOptions(defaultedOptions, {
|
|
73
|
+
// Do not notify on updates because of changes in the options because
|
|
74
|
+
// these changes should already be reflected in the optimistic result.
|
|
75
|
+
listeners: false,
|
|
76
|
+
})
|
|
77
|
+
untracked(() => {
|
|
78
|
+
resultSignal.set(observer.getOptimisticResult(defaultedOptions))
|
|
79
|
+
})
|
|
80
|
+
})
|
|
84
81
|
|
|
85
82
|
// observer.trackResult is not used as this optimization is not needed for Angular
|
|
86
83
|
const unsubscribe = observer.subscribe(
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from '@tanstack/query-core'
|
|
11
11
|
import { assertInjector } from './util/assert-injector/assert-injector'
|
|
12
12
|
import { injectQueryClient } from './inject-query-client'
|
|
13
|
+
import { lazySignalInitializer } from './util/lazy-signal-initializer/lazy-signal-initializer'
|
|
13
14
|
import type { Injector, Signal } from '@angular/core'
|
|
14
15
|
|
|
15
16
|
type MutationStateOptions<TResult = MutationState> = {
|
|
@@ -49,33 +50,38 @@ export function injectMutationState<TResult = MutationState>(
|
|
|
49
50
|
|
|
50
51
|
const mutationCache = queryClient.getMutationCache()
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
return lazySignalInitializer((injector) => {
|
|
54
|
+
const result = signal<Array<TResult>>(
|
|
55
|
+
getResult(mutationCache, mutationStateOptionsFn()),
|
|
56
|
+
)
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
effect(
|
|
59
|
+
() => {
|
|
60
|
+
const mutationStateOptions = mutationStateOptionsFn()
|
|
61
|
+
untracked(() => {
|
|
62
|
+
// Setting the signal from an effect because it's both 'computed' from options()
|
|
63
|
+
// and needs to be set imperatively in the mutationCache listener.
|
|
64
|
+
result.set(getResult(mutationCache, mutationStateOptions))
|
|
65
|
+
})
|
|
66
|
+
},
|
|
67
|
+
{ injector },
|
|
68
|
+
)
|
|
64
69
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
const unsubscribe = mutationCache.subscribe(
|
|
71
|
+
notifyManager.batchCalls(() => {
|
|
72
|
+
const nextResult = replaceEqualDeep(
|
|
73
|
+
result(),
|
|
74
|
+
getResult(mutationCache, mutationStateOptionsFn()),
|
|
75
|
+
)
|
|
76
|
+
if (result() !== nextResult) {
|
|
77
|
+
result.set(nextResult)
|
|
78
|
+
}
|
|
79
|
+
}),
|
|
80
|
+
)
|
|
76
81
|
|
|
77
|
-
|
|
82
|
+
destroyRef.onDestroy(unsubscribe)
|
|
78
83
|
|
|
79
|
-
|
|
84
|
+
return result
|
|
85
|
+
})
|
|
80
86
|
})
|
|
81
87
|
}
|
package/src/inject-mutation.ts
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
DestroyRef,
|
|
3
|
+
Injector,
|
|
4
|
+
computed,
|
|
5
|
+
effect,
|
|
6
|
+
inject,
|
|
7
|
+
runInInjectionContext,
|
|
8
|
+
signal,
|
|
9
|
+
} from '@angular/core'
|
|
2
10
|
import { MutationObserver, notifyManager } from '@tanstack/query-core'
|
|
3
11
|
import { assertInjector } from './util/assert-injector/assert-injector'
|
|
4
12
|
import { signalProxy } from './signal-proxy'
|
|
5
13
|
import { injectQueryClient } from './inject-query-client'
|
|
6
14
|
import { noop } from './util'
|
|
7
|
-
import type { DefaultError, QueryClient } from '@tanstack/query-core'
|
|
8
|
-
import type { Injector } from '@angular/core'
|
|
9
15
|
|
|
16
|
+
import { lazyInit } from './util/lazy-init/lazy-init'
|
|
17
|
+
import type { DefaultError, QueryClient } from '@tanstack/query-core'
|
|
10
18
|
import type {
|
|
11
19
|
CreateMutateFunction,
|
|
12
20
|
CreateMutationOptions,
|
|
@@ -26,42 +34,51 @@ export function injectMutation<
|
|
|
26
34
|
): CreateMutationResult<TData, TError, TVariables, TContext> {
|
|
27
35
|
return assertInjector(injectMutation, injector, () => {
|
|
28
36
|
const queryClient = injectQueryClient()
|
|
37
|
+
const currentInjector = inject(Injector)
|
|
29
38
|
const destroyRef = inject(DestroyRef)
|
|
30
39
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
return lazyInit(() =>
|
|
41
|
+
runInInjectionContext(currentInjector, () => {
|
|
42
|
+
const observer = new MutationObserver<
|
|
43
|
+
TData,
|
|
44
|
+
TError,
|
|
45
|
+
TVariables,
|
|
46
|
+
TContext
|
|
47
|
+
>(queryClient, options(queryClient))
|
|
48
|
+
const mutate: CreateMutateFunction<
|
|
49
|
+
TData,
|
|
50
|
+
TError,
|
|
51
|
+
TVariables,
|
|
52
|
+
TContext
|
|
53
|
+
> = (variables, mutateOptions) => {
|
|
54
|
+
observer.mutate(variables, mutateOptions).catch(noop)
|
|
55
|
+
}
|
|
41
56
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
57
|
+
effect(() => {
|
|
58
|
+
observer.setOptions(options(queryClient))
|
|
59
|
+
})
|
|
45
60
|
|
|
46
|
-
|
|
61
|
+
const result = signal(observer.getCurrentResult())
|
|
47
62
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
63
|
+
const unsubscribe = observer.subscribe(
|
|
64
|
+
notifyManager.batchCalls((val) => result.set(val)),
|
|
65
|
+
)
|
|
51
66
|
|
|
52
|
-
|
|
67
|
+
destroyRef.onDestroy(unsubscribe)
|
|
53
68
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
69
|
+
const resultSignal = computed(() => ({
|
|
70
|
+
...result(),
|
|
71
|
+
mutate,
|
|
72
|
+
mutateAsync: result().mutate,
|
|
73
|
+
}))
|
|
59
74
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
75
|
+
return signalProxy(resultSignal) as unknown as CreateMutationResult<
|
|
76
|
+
TData,
|
|
77
|
+
TError,
|
|
78
|
+
TVariables,
|
|
79
|
+
TContext
|
|
80
|
+
>
|
|
81
|
+
}),
|
|
82
|
+
)
|
|
66
83
|
})
|
|
67
84
|
}
|
package/src/inject-queries.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
QueryKey,
|
|
12
12
|
QueryObserverOptions,
|
|
13
13
|
QueryObserverResult,
|
|
14
|
+
SkipToken,
|
|
14
15
|
ThrowOnError,
|
|
15
16
|
} from '@tanstack/query-core'
|
|
16
17
|
|
|
@@ -52,7 +53,9 @@ type GetOptions<T> =
|
|
|
52
53
|
? QueryObserverOptionsForCreateQueries<TQueryFnData>
|
|
53
54
|
: // Part 3: responsible for inferring and enforcing type if no explicit parameter was provided
|
|
54
55
|
T extends {
|
|
55
|
-
queryFn?:
|
|
56
|
+
queryFn?:
|
|
57
|
+
| QueryFunction<infer TQueryFnData, infer TQueryKey>
|
|
58
|
+
| SkipToken
|
|
56
59
|
select: (data: any) => infer TData
|
|
57
60
|
throwOnError?: ThrowOnError<any, infer TError, any, any>
|
|
58
61
|
}
|
|
@@ -63,10 +66,9 @@ type GetOptions<T> =
|
|
|
63
66
|
TQueryKey
|
|
64
67
|
>
|
|
65
68
|
: T extends {
|
|
66
|
-
queryFn?:
|
|
67
|
-
infer TQueryFnData,
|
|
68
|
-
|
|
69
|
-
>
|
|
69
|
+
queryFn?:
|
|
70
|
+
| QueryFunction<infer TQueryFnData, infer TQueryKey>
|
|
71
|
+
| SkipToken
|
|
70
72
|
throwOnError?: ThrowOnError<any, infer TError, any, any>
|
|
71
73
|
}
|
|
72
74
|
? QueryObserverOptionsForCreateQueries<
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { untracked } from '@angular/core'
|
|
2
|
+
|
|
1
3
|
export function lazyInit<T extends object>(initializer: () => T): T {
|
|
2
4
|
let object: T | null = null
|
|
3
5
|
|
|
4
6
|
const initializeObject = () => {
|
|
5
7
|
if (!object) {
|
|
6
|
-
object = initializer()
|
|
8
|
+
object = untracked(() => initializer())
|
|
7
9
|
}
|
|
8
10
|
}
|
|
9
11
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Injector,
|
|
3
|
+
type Signal,
|
|
4
|
+
computed,
|
|
5
|
+
inject,
|
|
6
|
+
untracked,
|
|
7
|
+
} from '@angular/core'
|
|
8
|
+
|
|
9
|
+
type SignalInitializerFn<T> = (injector: Injector) => Signal<T>
|
|
10
|
+
|
|
11
|
+
export function lazySignalInitializer<T>(
|
|
12
|
+
initializerFn: SignalInitializerFn<T>,
|
|
13
|
+
) {
|
|
14
|
+
const injector = inject(Injector)
|
|
15
|
+
|
|
16
|
+
let source: Signal<T> | null = null
|
|
17
|
+
|
|
18
|
+
const unwrapSignal = () => {
|
|
19
|
+
if (!source) {
|
|
20
|
+
source = untracked(() => initializerFn(injector))
|
|
21
|
+
}
|
|
22
|
+
return source()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
queueMicrotask(() => unwrapSignal())
|
|
26
|
+
|
|
27
|
+
return computed(unwrapSignal)
|
|
28
|
+
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export function lazyInit(initializer) {
|
|
2
|
-
let object = null;
|
|
3
|
-
const initializeObject = () => {
|
|
4
|
-
if (!object) {
|
|
5
|
-
object = initializer();
|
|
6
|
-
}
|
|
7
|
-
};
|
|
8
|
-
queueMicrotask(() => initializeObject());
|
|
9
|
-
return new Proxy({}, {
|
|
10
|
-
get(_, prop, receiver) {
|
|
11
|
-
initializeObject();
|
|
12
|
-
return Reflect.get(object, prop, receiver);
|
|
13
|
-
},
|
|
14
|
-
has(_, prop) {
|
|
15
|
-
initializeObject();
|
|
16
|
-
return Reflect.has(object, prop);
|
|
17
|
-
},
|
|
18
|
-
ownKeys() {
|
|
19
|
-
initializeObject();
|
|
20
|
-
return Reflect.ownKeys(object);
|
|
21
|
-
},
|
|
22
|
-
getOwnPropertyDescriptor() {
|
|
23
|
-
return {
|
|
24
|
-
enumerable: true,
|
|
25
|
-
configurable: true,
|
|
26
|
-
};
|
|
27
|
-
},
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGF6eS1pbml0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2xhenktaW5pdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxNQUFNLFVBQVUsUUFBUSxDQUFtQixXQUFvQjtJQUM3RCxJQUFJLE1BQU0sR0FBYSxJQUFJLENBQUE7SUFFM0IsTUFBTSxnQkFBZ0IsR0FBRyxHQUFHLEVBQUU7UUFDNUIsSUFBSSxDQUFDLE1BQU0sRUFBRTtZQUNYLE1BQU0sR0FBRyxXQUFXLEVBQUUsQ0FBQTtTQUN2QjtJQUNILENBQUMsQ0FBQTtJQUVELGNBQWMsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDLENBQUE7SUFFeEMsT0FBTyxJQUFJLEtBQUssQ0FBSSxFQUFPLEVBQUU7UUFDM0IsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsUUFBUTtZQUNuQixnQkFBZ0IsRUFBRSxDQUFBO1lBQ2xCLE9BQU8sT0FBTyxDQUFDLEdBQUcsQ0FBQyxNQUFXLEVBQUUsSUFBSSxFQUFFLFFBQVEsQ0FBQyxDQUFBO1FBQ2pELENBQUM7UUFDRCxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUk7WUFDVCxnQkFBZ0IsRUFBRSxDQUFBO1lBQ2xCLE9BQU8sT0FBTyxDQUFDLEdBQUcsQ0FBQyxNQUFXLEVBQUUsSUFBSSxDQUFDLENBQUE7UUFDdkMsQ0FBQztRQUNELE9BQU87WUFDTCxnQkFBZ0IsRUFBRSxDQUFBO1lBQ2xCLE9BQU8sT0FBTyxDQUFDLE9BQU8sQ0FBQyxNQUFXLENBQUMsQ0FBQTtRQUNyQyxDQUFDO1FBQ0Qsd0JBQXdCO1lBQ3RCLE9BQU87Z0JBQ0wsVUFBVSxFQUFFLElBQUk7Z0JBQ2hCLFlBQVksRUFBRSxJQUFJO2FBQ25CLENBQUE7UUFDSCxDQUFDO0tBQ0YsQ0FBQyxDQUFBO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBmdW5jdGlvbiBsYXp5SW5pdDxUIGV4dGVuZHMgb2JqZWN0Pihpbml0aWFsaXplcjogKCkgPT4gVCk6IFQge1xuICBsZXQgb2JqZWN0OiBUIHwgbnVsbCA9IG51bGxcblxuICBjb25zdCBpbml0aWFsaXplT2JqZWN0ID0gKCkgPT4ge1xuICAgIGlmICghb2JqZWN0KSB7XG4gICAgICBvYmplY3QgPSBpbml0aWFsaXplcigpXG4gICAgfVxuICB9XG5cbiAgcXVldWVNaWNyb3Rhc2soKCkgPT4gaW5pdGlhbGl6ZU9iamVjdCgpKVxuXG4gIHJldHVybiBuZXcgUHJveHk8VD4oe30gYXMgVCwge1xuICAgIGdldChfLCBwcm9wLCByZWNlaXZlcikge1xuICAgICAgaW5pdGlhbGl6ZU9iamVjdCgpXG4gICAgICByZXR1cm4gUmVmbGVjdC5nZXQob2JqZWN0IGFzIFQsIHByb3AsIHJlY2VpdmVyKVxuICAgIH0sXG4gICAgaGFzKF8sIHByb3ApIHtcbiAgICAgIGluaXRpYWxpemVPYmplY3QoKVxuICAgICAgcmV0dXJuIFJlZmxlY3QuaGFzKG9iamVjdCBhcyBULCBwcm9wKVxuICAgIH0sXG4gICAgb3duS2V5cygpIHtcbiAgICAgIGluaXRpYWxpemVPYmplY3QoKVxuICAgICAgcmV0dXJuIFJlZmxlY3Qub3duS2V5cyhvYmplY3QgYXMgVClcbiAgICB9LFxuICAgIGdldE93blByb3BlcnR5RGVzY3JpcHRvcigpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGVudW1lcmFibGU6IHRydWUsXG4gICAgICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZSxcbiAgICAgIH1cbiAgICB9LFxuICB9KVxufVxuIl19
|
|
File without changes
|