@veams/status-quo-query 0.8.0 → 0.9.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/README.md +12 -8
- package/dist/query.d.ts +6 -6
- package/dist/query.js +45 -30
- package/dist/query.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/query.spec.ts +31 -13
- package/src/__tests__/tracked.spec.ts +15 -2
- package/src/query.ts +68 -47
package/README.md
CHANGED
|
@@ -400,10 +400,10 @@ Use `dependsOn` when a query needs data from other queries before it can run.
|
|
|
400
400
|
|
|
401
401
|
`dependsOn` accepts a `QueryDependencyTuple`:
|
|
402
402
|
|
|
403
|
-
- an ordered list of source query
|
|
403
|
+
- an ordered list of source query services
|
|
404
404
|
- a `deriveOptions(...)` callback that returns only `queryKey` and/or `enabled`
|
|
405
405
|
|
|
406
|
-
The watcher starts on the first `subscribe(...)` or `refetch()`, reads the current
|
|
406
|
+
The watcher starts on the first `subscribe(...)` or `refetch()`, reads the current source snapshots immediately, and stops after the last unsubscribe. A downstream `refetch()` refetches all source services first, then refetches the derived query.
|
|
407
407
|
|
|
408
408
|
Untracked example:
|
|
409
409
|
|
|
@@ -416,8 +416,8 @@ type Config = { region: string; companyProfileEnabled: boolean };
|
|
|
416
416
|
|
|
417
417
|
const queryClient = new QueryClient();
|
|
418
418
|
const createQuery = setupQuery(queryClient);
|
|
419
|
-
const
|
|
420
|
-
const
|
|
419
|
+
const userQuery = createQuery(['user', 42] as const, () => fetchUser(42), { enabled: false });
|
|
420
|
+
const configQuery = createQuery(['config', 'global'] as const, fetchConfig, { enabled: false });
|
|
421
421
|
|
|
422
422
|
const companyProfileQuery = createQuery(
|
|
423
423
|
['company-profile', { companyId: undefined as string | undefined, region: undefined as string | undefined }],
|
|
@@ -425,7 +425,7 @@ const companyProfileQuery = createQuery(
|
|
|
425
425
|
{
|
|
426
426
|
enabled: false,
|
|
427
427
|
dependsOn: <QueryDependencyTuple<[User, Config]>>[
|
|
428
|
-
[
|
|
428
|
+
[userQuery, configQuery],
|
|
429
429
|
([userSnapshot, configSnapshot]) => {
|
|
430
430
|
if (!userSnapshot.data?.companyId || !configSnapshot.data?.region) {
|
|
431
431
|
return { enabled: false };
|
|
@@ -455,7 +455,11 @@ import { setupQueryManager } from '@veams/status-quo-query';
|
|
|
455
455
|
|
|
456
456
|
const queryClient = new QueryClient();
|
|
457
457
|
const manager = setupQueryManager(queryClient);
|
|
458
|
-
const
|
|
458
|
+
const selectionQuery = manager.createUntrackedQuery(
|
|
459
|
+
['selection'] as const,
|
|
460
|
+
fetchSelection,
|
|
461
|
+
{ enabled: false }
|
|
462
|
+
);
|
|
459
463
|
|
|
460
464
|
const productQuery = manager.createQuery(
|
|
461
465
|
['product', { deps: { applicationId: 'pending' }, view: { page: 0 } }],
|
|
@@ -463,7 +467,7 @@ const productQuery = manager.createQuery(
|
|
|
463
467
|
{
|
|
464
468
|
enabled: false,
|
|
465
469
|
dependsOn: [
|
|
466
|
-
[
|
|
470
|
+
[selectionQuery],
|
|
467
471
|
([selectionSnapshot]) =>
|
|
468
472
|
selectionSnapshot.data?.applicationId
|
|
469
473
|
? {
|
|
@@ -610,7 +614,7 @@ It also adds:
|
|
|
610
614
|
|
|
611
615
|
- `dependsOn?: QueryDependencyTuple<[...sources]>`
|
|
612
616
|
|
|
613
|
-
`dependsOn` observes the listed source
|
|
617
|
+
`dependsOn` observes the listed source query services and lets the downstream query derive only `queryKey` and `enabled`. Source services are activated while the downstream query is active, and downstream `refetch()` refetches the sources first. The public `QueryService` API does not change when this option is used.
|
|
614
618
|
|
|
615
619
|
`QueryService` methods:
|
|
616
620
|
|
package/dist/query.d.ts
CHANGED
|
@@ -42,8 +42,8 @@ type QueryDependencyDerivedOptions<TQueryKey extends QueryKey = QueryKey> = {
|
|
|
42
42
|
queryKey?: TQueryKey;
|
|
43
43
|
};
|
|
44
44
|
export type QueryDependencyTuple<TSources extends readonly unknown[], TQueryKey extends QueryKey = QueryKey> = readonly [
|
|
45
|
-
|
|
46
|
-
readonly [K in keyof TSources]:
|
|
45
|
+
sources: {
|
|
46
|
+
readonly [K in keyof TSources]: QueryService<TSources[K], Error>;
|
|
47
47
|
},
|
|
48
48
|
deriveOptions: (sourceSnapshots: {
|
|
49
49
|
readonly [K in keyof TSources]: QueryServiceSnapshot<TSources[K], Error>;
|
|
@@ -53,7 +53,7 @@ export type QueryDependencyTuple<TSources extends readonly unknown[], TQueryKey
|
|
|
53
53
|
* Function signature for the untracked query factory.
|
|
54
54
|
*/
|
|
55
55
|
export interface CreateUntrackedQuery {
|
|
56
|
-
<TQueryFnData = unknown, TError = Error, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(queryKey: TQueryKey, queryFn: QueryFunction<TQueryFnData, TQueryKey>, options?: QueryServiceOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>): QueryService<TData, TError>;
|
|
56
|
+
<TSources extends readonly unknown[] = [], TQueryFnData = unknown, TError = Error, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey>(queryKey: TQueryKey, queryFn: QueryFunction<TQueryFnData, TQueryKey>, options?: QueryServiceOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey, TSources>): QueryService<TData, TError>;
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
59
|
* Function signature for the default query factory that derives dependencies from the final
|
|
@@ -63,13 +63,13 @@ export interface CreateUntrackedQuery {
|
|
|
63
63
|
* The only extra behavior is invisible: dependency registration and on-demand re-registration.
|
|
64
64
|
*/
|
|
65
65
|
export interface CreateQuery {
|
|
66
|
-
<TDeps extends TrackedDependencyRecord, TQueryFnData = unknown, TError = Error, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends TrackedQueryKey<TDeps> = TrackedQueryKey<TDeps>>(queryKey: TQueryKey, queryFn: QueryFunction<TQueryFnData, TQueryKey>, options?: QueryServiceOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>): QueryService<TData, TError>;
|
|
66
|
+
<TDeps extends TrackedDependencyRecord, TSources extends readonly unknown[] = [], TQueryFnData = unknown, TError = Error, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends TrackedQueryKey<TDeps> = TrackedQueryKey<TDeps>>(queryKey: TQueryKey, queryFn: QueryFunction<TQueryFnData, TQueryKey>, options?: QueryServiceOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey, TSources>): QueryService<TData, TError>;
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
69
|
* Configuration options for creating a query service, excluding function and key.
|
|
70
70
|
*/
|
|
71
|
-
export type QueryServiceOptions<TQueryFnData = unknown, TError = Error, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey> = Omit<QueryObserverOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>, 'queryFn' | 'queryKey'> & {
|
|
72
|
-
dependsOn?: QueryDependencyTuple<
|
|
71
|
+
export type QueryServiceOptions<TQueryFnData = unknown, TError = Error, TData = TQueryFnData, TQueryData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, TSources extends readonly unknown[] = []> = Omit<QueryObserverOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>, 'queryFn' | 'queryKey'> & {
|
|
72
|
+
dependsOn?: QueryDependencyTuple<TSources, TQueryKey>;
|
|
73
73
|
};
|
|
74
74
|
/**
|
|
75
75
|
* Extracts and maps status and fetchStatus to our QueryMetaState interface.
|
package/dist/query.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
// Import QueryObserver to monitor and manage individual queries.
|
|
3
|
-
QueryObserver,
|
|
3
|
+
QueryObserver, } from '@tanstack/query-core';
|
|
4
4
|
import { extractTrackedDependencies, } from './tracking.js';
|
|
5
5
|
/**
|
|
6
6
|
* Extracts and maps status and fetchStatus to our QueryMetaState interface.
|
|
@@ -30,7 +30,7 @@ export function setupQuery(queryClient) {
|
|
|
30
30
|
if (!dependsOn) {
|
|
31
31
|
return service.service;
|
|
32
32
|
}
|
|
33
|
-
return bindQueryDependencies(
|
|
33
|
+
return bindQueryDependencies(service, queryKey, dependsOn);
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
@@ -63,7 +63,7 @@ export function setupTrackedQuery(queryClient, trackingRegistry) {
|
|
|
63
63
|
trackingRegistry.register(nextQueryHash, extractTrackedDependencies(service.getCurrentQueryKey()));
|
|
64
64
|
};
|
|
65
65
|
const dependencyController = dependsOn
|
|
66
|
-
? createDependencyController(
|
|
66
|
+
? createDependencyController(queryKey, applyTrackedDerivedState, dependsOn)
|
|
67
67
|
: undefined;
|
|
68
68
|
const ensureRegistered = () => {
|
|
69
69
|
// Build resolves the current live TanStack query for the stored observer options. This is
|
|
@@ -79,7 +79,7 @@ export function setupTrackedQuery(queryClient, trackingRegistry) {
|
|
|
79
79
|
return {
|
|
80
80
|
...service.service,
|
|
81
81
|
refetch: async (refetchOptions) => {
|
|
82
|
-
dependencyController?.
|
|
82
|
+
await dependencyController?.evaluateForRefetch();
|
|
83
83
|
// Refetch is one of the two explicit reactivation paths agreed on in the design.
|
|
84
84
|
ensureRegistered();
|
|
85
85
|
return service.service.refetch(refetchOptions);
|
|
@@ -158,13 +158,13 @@ function createQueryService(queryClient, queryKey, queryFn, options) {
|
|
|
158
158
|
},
|
|
159
159
|
};
|
|
160
160
|
}
|
|
161
|
-
function bindQueryDependencies(
|
|
162
|
-
const dependencyController = createDependencyController(
|
|
161
|
+
function bindQueryDependencies(queryService, queryKey, dependsOn) {
|
|
162
|
+
const dependencyController = createDependencyController(queryKey, queryService.setDerivedState, dependsOn);
|
|
163
163
|
let subscriberCount = 0;
|
|
164
164
|
return {
|
|
165
165
|
...queryService.service,
|
|
166
166
|
refetch: async (refetchOptions) => {
|
|
167
|
-
dependencyController.
|
|
167
|
+
await dependencyController.evaluateForRefetch();
|
|
168
168
|
return queryService.service.refetch(refetchOptions);
|
|
169
169
|
},
|
|
170
170
|
subscribe: (listener) => {
|
|
@@ -183,44 +183,59 @@ function bindQueryDependencies(queryClient, queryService, queryKey, dependsOn) {
|
|
|
183
183
|
},
|
|
184
184
|
};
|
|
185
185
|
}
|
|
186
|
-
function createDependencyController(
|
|
187
|
-
const [
|
|
188
|
-
let
|
|
189
|
-
let
|
|
190
|
-
|
|
191
|
-
|
|
186
|
+
function createDependencyController(baseQueryKey, setDerivedState, dependsOn) {
|
|
187
|
+
const [sources, deriveOptions] = dependsOn;
|
|
188
|
+
let isActive = false;
|
|
189
|
+
let scheduledEvaluation = false;
|
|
190
|
+
let sourceUnsubscribers = [];
|
|
191
|
+
const evaluateBinding = () => {
|
|
192
|
+
const snapshots = sources.map((source) => source.getSnapshot());
|
|
192
193
|
const derivedOptions = deriveOptions(snapshots);
|
|
193
194
|
setDerivedState({
|
|
194
195
|
queryKey: derivedOptions.queryKey ?? baseQueryKey,
|
|
195
196
|
...(derivedOptions.enabled === undefined ? {} : { enabled: derivedOptions.enabled }),
|
|
196
197
|
});
|
|
197
198
|
};
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
199
|
+
const scheduleEvaluate = () => {
|
|
200
|
+
if (scheduledEvaluation) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
scheduledEvaluation = true;
|
|
204
|
+
queueMicrotask(() => {
|
|
205
|
+
scheduledEvaluation = false;
|
|
206
|
+
if (!isActive) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
evaluateBinding();
|
|
210
|
+
});
|
|
211
|
+
};
|
|
202
212
|
return {
|
|
203
213
|
activate: () => {
|
|
204
|
-
if (
|
|
214
|
+
if (isActive) {
|
|
205
215
|
return;
|
|
206
216
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
217
|
+
isActive = true;
|
|
218
|
+
sourceUnsubscribers = sources.map((source) => source.subscribe(() => {
|
|
219
|
+
scheduleEvaluate();
|
|
220
|
+
}));
|
|
221
|
+
evaluateBinding();
|
|
210
222
|
},
|
|
211
223
|
deactivate: () => {
|
|
212
|
-
|
|
213
|
-
unsubscribe
|
|
214
|
-
|
|
224
|
+
isActive = false;
|
|
225
|
+
sourceUnsubscribers.forEach((unsubscribe) => {
|
|
226
|
+
unsubscribe();
|
|
227
|
+
});
|
|
228
|
+
sourceUnsubscribers = [];
|
|
215
229
|
},
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
230
|
+
evaluateForRefetch: async () => {
|
|
231
|
+
await Promise.all(sources.map(async (source) => {
|
|
232
|
+
await source.refetch();
|
|
233
|
+
}));
|
|
234
|
+
if (isActive) {
|
|
235
|
+
scheduleEvaluate();
|
|
219
236
|
return;
|
|
220
237
|
}
|
|
221
|
-
|
|
222
|
-
evaluateBinding(transientObserver.getCurrentResult());
|
|
223
|
-
transientObserver.destroy();
|
|
238
|
+
evaluateBinding();
|
|
224
239
|
},
|
|
225
240
|
};
|
|
226
241
|
}
|
package/dist/query.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO;AAaL,iEAAiE;AACjE,aAAa,
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -68,6 +68,12 @@ describe('Query Service', () => {
|
|
|
68
68
|
const createQuery = setupQuery(queryClient);
|
|
69
69
|
const userKey = ['user', 42] as const;
|
|
70
70
|
const configKey = ['config', 'global'] as const;
|
|
71
|
+
const userQuery = createQuery(userKey, jest.fn().mockResolvedValue({ companyId: 'company-1' }), {
|
|
72
|
+
enabled: false,
|
|
73
|
+
});
|
|
74
|
+
const configQuery = createQuery(configKey, jest.fn().mockResolvedValue({ region: 'eu' }), {
|
|
75
|
+
enabled: false,
|
|
76
|
+
});
|
|
71
77
|
const queryFn = jest
|
|
72
78
|
.fn()
|
|
73
79
|
.mockImplementation(({ queryKey }) => `${queryKey[1].companyId}:${queryKey[1].region}`);
|
|
@@ -78,7 +84,7 @@ describe('Query Service', () => {
|
|
|
78
84
|
{
|
|
79
85
|
enabled: false,
|
|
80
86
|
dependsOn: [
|
|
81
|
-
[
|
|
87
|
+
[userQuery, configQuery],
|
|
82
88
|
([userSnapshot, configSnapshot]) => {
|
|
83
89
|
if (!userSnapshot.data?.companyId || !configSnapshot.data?.region) {
|
|
84
90
|
return { enabled: false };
|
|
@@ -120,21 +126,21 @@ describe('Query Service', () => {
|
|
|
120
126
|
unsubscribe();
|
|
121
127
|
});
|
|
122
128
|
|
|
123
|
-
it('uses
|
|
129
|
+
it('uses source query data when dependsOn is evaluated on first refetch', async () => {
|
|
124
130
|
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: 0 } } });
|
|
125
131
|
const createQuery = setupQuery(queryClient);
|
|
126
132
|
const selectionKey = ['selection'] as const;
|
|
133
|
+
const selectionQueryFn = jest.fn().mockResolvedValue({ id: 'item-2' });
|
|
134
|
+
const selectionQuery = createQuery(selectionKey, selectionQueryFn, { enabled: false });
|
|
127
135
|
const queryFn = jest.fn().mockImplementation(async ({ queryKey }) => queryKey[1].id);
|
|
128
136
|
|
|
129
|
-
queryClient.setQueryData(selectionKey, { id: 'item-2' });
|
|
130
|
-
|
|
131
137
|
const service = createQuery(
|
|
132
138
|
['details', { id: undefined as string | undefined }],
|
|
133
139
|
queryFn,
|
|
134
140
|
{
|
|
135
141
|
enabled: false,
|
|
136
142
|
dependsOn: [
|
|
137
|
-
[
|
|
143
|
+
[selectionQuery],
|
|
138
144
|
([selectionSnapshot]) =>
|
|
139
145
|
selectionSnapshot.data?.id
|
|
140
146
|
? {
|
|
@@ -149,6 +155,7 @@ describe('Query Service', () => {
|
|
|
149
155
|
const result = await service.refetch();
|
|
150
156
|
|
|
151
157
|
expect(result.data).toBe('item-2');
|
|
158
|
+
expect(selectionQueryFn).toHaveBeenCalledTimes(1);
|
|
152
159
|
expect(queryFn).toHaveBeenCalledWith(
|
|
153
160
|
expect.objectContaining({
|
|
154
161
|
queryKey: ['details', { id: 'item-2' }],
|
|
@@ -159,20 +166,21 @@ describe('Query Service', () => {
|
|
|
159
166
|
it('preserves source snapshot tuple order in dependsOn', async () => {
|
|
160
167
|
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: 0 } } });
|
|
161
168
|
const createQuery = setupQuery(queryClient);
|
|
162
|
-
const
|
|
163
|
-
|
|
169
|
+
const firstQuery = createQuery(['source', 'first'] as const, jest.fn().mockResolvedValue({ label: 'first' }), {
|
|
170
|
+
enabled: false,
|
|
171
|
+
});
|
|
172
|
+
const secondQuery = createQuery(['source', 'second'] as const, jest.fn().mockResolvedValue({ label: 'second' }), {
|
|
173
|
+
enabled: false,
|
|
174
|
+
});
|
|
164
175
|
const observedOrders: Array<[string | undefined, string | undefined]> = [];
|
|
165
176
|
|
|
166
|
-
queryClient.setQueryData(firstKey, { label: 'first' });
|
|
167
|
-
queryClient.setQueryData(secondKey, { label: 'second' });
|
|
168
|
-
|
|
169
177
|
const service = createQuery(
|
|
170
178
|
['ordered', { left: undefined as string | undefined, right: undefined as string | undefined }],
|
|
171
179
|
jest.fn().mockResolvedValue('ok'),
|
|
172
180
|
{
|
|
173
181
|
enabled: false,
|
|
174
182
|
dependsOn: [
|
|
175
|
-
[
|
|
183
|
+
[firstQuery, secondQuery],
|
|
176
184
|
([firstSnapshot, secondSnapshot]) => {
|
|
177
185
|
observedOrders.push([firstSnapshot.data?.label, secondSnapshot.data?.label]);
|
|
178
186
|
|
|
@@ -201,6 +209,9 @@ describe('Query Service', () => {
|
|
|
201
209
|
const invalidateQueriesSpy = jest.spyOn(queryClient, 'invalidateQueries');
|
|
202
210
|
const createQuery = setupQuery(queryClient);
|
|
203
211
|
const selectionKey = ['selection'] as const;
|
|
212
|
+
const selectionQuery = createQuery(selectionKey, jest.fn().mockResolvedValue({ id: 'item-3' }), {
|
|
213
|
+
enabled: false,
|
|
214
|
+
});
|
|
204
215
|
|
|
205
216
|
queryClient.setQueryData(selectionKey, { id: 'item-3' });
|
|
206
217
|
|
|
@@ -210,7 +221,7 @@ describe('Query Service', () => {
|
|
|
210
221
|
{
|
|
211
222
|
enabled: false,
|
|
212
223
|
dependsOn: [
|
|
213
|
-
[
|
|
224
|
+
[selectionQuery],
|
|
214
225
|
([selectionSnapshot]) =>
|
|
215
226
|
selectionSnapshot.data?.id
|
|
216
227
|
? {
|
|
@@ -240,6 +251,13 @@ describe('Query Service', () => {
|
|
|
240
251
|
const invalidateQueriesSpy = jest.spyOn(queryClient, 'invalidateQueries');
|
|
241
252
|
const createQuery = setupQuery(queryClient);
|
|
242
253
|
const selectionKey = ['selection'] as const;
|
|
254
|
+
const selectionQuery = createQuery(
|
|
255
|
+
selectionKey,
|
|
256
|
+
jest.fn().mockResolvedValue({ id: 'item-2' }),
|
|
257
|
+
{
|
|
258
|
+
enabled: false,
|
|
259
|
+
}
|
|
260
|
+
);
|
|
243
261
|
const queryFn = jest.fn().mockImplementation(async ({ queryKey }) => queryKey[1].id);
|
|
244
262
|
|
|
245
263
|
queryClient.setQueryData(selectionKey, { id: 'item-1' });
|
|
@@ -250,7 +268,7 @@ describe('Query Service', () => {
|
|
|
250
268
|
{
|
|
251
269
|
enabled: false,
|
|
252
270
|
dependsOn: [
|
|
253
|
-
[
|
|
271
|
+
[selectionQuery],
|
|
254
272
|
([selectionSnapshot]) =>
|
|
255
273
|
selectionSnapshot.data?.id
|
|
256
274
|
? {
|
|
@@ -286,6 +286,11 @@ describe('Tracked Query Invalidation', () => {
|
|
|
286
286
|
const manager = setupQueryManager(queryClient);
|
|
287
287
|
const invalidateQueriesSpy = jest.spyOn(queryClient, 'invalidateQueries');
|
|
288
288
|
const selectionKey = ['selection'] as const;
|
|
289
|
+
const selectionQuery = manager.createUntrackedQuery(
|
|
290
|
+
selectionKey,
|
|
291
|
+
jest.fn().mockResolvedValue({ applicationId: 'app-1' }),
|
|
292
|
+
{ enabled: false }
|
|
293
|
+
);
|
|
289
294
|
|
|
290
295
|
queryClient.setQueryData(selectionKey, { applicationId: 'app-1' });
|
|
291
296
|
|
|
@@ -295,7 +300,7 @@ describe('Tracked Query Invalidation', () => {
|
|
|
295
300
|
{
|
|
296
301
|
enabled: false,
|
|
297
302
|
dependsOn: [
|
|
298
|
-
[
|
|
303
|
+
[selectionQuery],
|
|
299
304
|
([selectionSnapshot]) =>
|
|
300
305
|
selectionSnapshot.data?.applicationId
|
|
301
306
|
? {
|
|
@@ -337,6 +342,14 @@ describe('Tracked Query Invalidation', () => {
|
|
|
337
342
|
const manager = setupQueryManager(queryClient);
|
|
338
343
|
const invalidateQueriesSpy = jest.spyOn(queryClient, 'invalidateQueries');
|
|
339
344
|
const selectionKey = ['selection'] as const;
|
|
345
|
+
const selectionQuery = manager.createUntrackedQuery(
|
|
346
|
+
selectionKey,
|
|
347
|
+
jest
|
|
348
|
+
.fn()
|
|
349
|
+
.mockResolvedValueOnce({ applicationId: 'app-1' })
|
|
350
|
+
.mockResolvedValueOnce({ applicationId: 'app-2' }),
|
|
351
|
+
{ enabled: false }
|
|
352
|
+
);
|
|
340
353
|
const initialDerivedKey = [
|
|
341
354
|
'product',
|
|
342
355
|
{ deps: { applicationId: 'app-1' }, view: { page: 1 } },
|
|
@@ -354,7 +367,7 @@ describe('Tracked Query Invalidation', () => {
|
|
|
354
367
|
{
|
|
355
368
|
enabled: false,
|
|
356
369
|
dependsOn: [
|
|
357
|
-
[
|
|
370
|
+
[selectionQuery],
|
|
358
371
|
([selectionSnapshot]) =>
|
|
359
372
|
selectionSnapshot.data?.applicationId
|
|
360
373
|
? {
|
package/src/query.ts
CHANGED
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
type QueryKey,
|
|
14
14
|
// Import QueryObserver to monitor and manage individual queries.
|
|
15
15
|
QueryObserver,
|
|
16
|
-
QueriesObserver,
|
|
17
16
|
type QueryOptions,
|
|
18
17
|
// Import configuration options for the query observer.
|
|
19
18
|
type QueryObserverOptions,
|
|
@@ -109,7 +108,7 @@ export type QueryDependencyTuple<
|
|
|
109
108
|
TSources extends readonly unknown[],
|
|
110
109
|
TQueryKey extends QueryKey = QueryKey,
|
|
111
110
|
> = readonly [
|
|
112
|
-
|
|
111
|
+
sources: { readonly [K in keyof TSources]: QueryService<TSources[K], Error> },
|
|
113
112
|
deriveOptions: (
|
|
114
113
|
sourceSnapshots: { readonly [K in keyof TSources]: QueryServiceSnapshot<TSources[K], Error> }
|
|
115
114
|
) => QueryDependencyDerivedOptions<TQueryKey>,
|
|
@@ -120,6 +119,7 @@ export type QueryDependencyTuple<
|
|
|
120
119
|
*/
|
|
121
120
|
export interface CreateUntrackedQuery {
|
|
122
121
|
<
|
|
122
|
+
TSources extends readonly unknown[] = [],
|
|
123
123
|
TQueryFnData = unknown,
|
|
124
124
|
TError = Error,
|
|
125
125
|
TData = TQueryFnData,
|
|
@@ -131,7 +131,7 @@ export interface CreateUntrackedQuery {
|
|
|
131
131
|
// The asynchronous function that performs the data fetch.
|
|
132
132
|
queryFn: QueryFunction<TQueryFnData, TQueryKey>,
|
|
133
133
|
// Optional configuration for behavior like staleness, retry, and refetching.
|
|
134
|
-
options?: QueryServiceOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>
|
|
134
|
+
options?: QueryServiceOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey, TSources>
|
|
135
135
|
): QueryService<TData, TError>;
|
|
136
136
|
}
|
|
137
137
|
|
|
@@ -145,6 +145,7 @@ export interface CreateUntrackedQuery {
|
|
|
145
145
|
export interface CreateQuery {
|
|
146
146
|
<
|
|
147
147
|
TDeps extends TrackedDependencyRecord,
|
|
148
|
+
TSources extends readonly unknown[] = [],
|
|
148
149
|
TQueryFnData = unknown,
|
|
149
150
|
TError = Error,
|
|
150
151
|
TData = TQueryFnData,
|
|
@@ -153,7 +154,7 @@ export interface CreateQuery {
|
|
|
153
154
|
>(
|
|
154
155
|
queryKey: TQueryKey,
|
|
155
156
|
queryFn: QueryFunction<TQueryFnData, TQueryKey>,
|
|
156
|
-
options?: QueryServiceOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>
|
|
157
|
+
options?: QueryServiceOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey, TSources>
|
|
157
158
|
): QueryService<TData, TError>;
|
|
158
159
|
}
|
|
159
160
|
|
|
@@ -166,11 +167,12 @@ export type QueryServiceOptions<
|
|
|
166
167
|
TData = TQueryFnData,
|
|
167
168
|
TQueryData = TQueryFnData,
|
|
168
169
|
TQueryKey extends QueryKey = QueryKey,
|
|
170
|
+
TSources extends readonly unknown[] = [],
|
|
169
171
|
> = Omit<
|
|
170
172
|
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>,
|
|
171
173
|
'queryFn' | 'queryKey'
|
|
172
174
|
> & {
|
|
173
|
-
dependsOn?: QueryDependencyTuple<
|
|
175
|
+
dependsOn?: QueryDependencyTuple<TSources, TQueryKey>;
|
|
174
176
|
};
|
|
175
177
|
|
|
176
178
|
/**
|
|
@@ -200,6 +202,7 @@ export function isQueryLoading(query: QueryMetaState): boolean {
|
|
|
200
202
|
export function setupQuery(queryClient: QueryClient): CreateUntrackedQuery {
|
|
201
203
|
// Returns the actual factory function for creating individual query services.
|
|
202
204
|
return function createQuery<
|
|
205
|
+
TSources extends readonly unknown[] = [],
|
|
203
206
|
TQueryFnData = unknown,
|
|
204
207
|
TError = Error,
|
|
205
208
|
TData = TQueryFnData,
|
|
@@ -208,7 +211,7 @@ export function setupQuery(queryClient: QueryClient): CreateUntrackedQuery {
|
|
|
208
211
|
>(
|
|
209
212
|
queryKey: TQueryKey,
|
|
210
213
|
queryFn: QueryFunction<TQueryFnData, TQueryKey>,
|
|
211
|
-
options?: QueryServiceOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>
|
|
214
|
+
options?: QueryServiceOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey, TSources>
|
|
212
215
|
): QueryService<TData, TError> {
|
|
213
216
|
const { dependsOn, runtimeOptions } = splitQueryServiceOptions(options);
|
|
214
217
|
const service = createQueryService(queryClient, queryKey, queryFn, runtimeOptions);
|
|
@@ -217,7 +220,7 @@ export function setupQuery(queryClient: QueryClient): CreateUntrackedQuery {
|
|
|
217
220
|
return service.service;
|
|
218
221
|
}
|
|
219
222
|
|
|
220
|
-
return bindQueryDependencies(
|
|
223
|
+
return bindQueryDependencies(service, queryKey, dependsOn);
|
|
221
224
|
};
|
|
222
225
|
}
|
|
223
226
|
|
|
@@ -237,6 +240,7 @@ export function setupTrackedQuery(
|
|
|
237
240
|
): CreateQuery {
|
|
238
241
|
return function createQuery<
|
|
239
242
|
TDeps extends TrackedDependencyRecord,
|
|
243
|
+
TSources extends readonly unknown[] = [],
|
|
240
244
|
TQueryFnData = unknown,
|
|
241
245
|
TError = Error,
|
|
242
246
|
TData = TQueryFnData,
|
|
@@ -245,7 +249,7 @@ export function setupTrackedQuery(
|
|
|
245
249
|
>(
|
|
246
250
|
queryKey: TQueryKey,
|
|
247
251
|
queryFn: QueryFunction<TQueryFnData, TQueryKey>,
|
|
248
|
-
options?: QueryServiceOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>
|
|
252
|
+
options?: QueryServiceOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey, TSources>
|
|
249
253
|
): QueryService<TData, TError> {
|
|
250
254
|
const { dependsOn, runtimeOptions } = splitQueryServiceOptions(options);
|
|
251
255
|
// Reuse the same core query service implementation as the untracked API.
|
|
@@ -276,7 +280,6 @@ export function setupTrackedQuery(
|
|
|
276
280
|
|
|
277
281
|
const dependencyController = dependsOn
|
|
278
282
|
? createDependencyController(
|
|
279
|
-
queryClient,
|
|
280
283
|
queryKey,
|
|
281
284
|
applyTrackedDerivedState,
|
|
282
285
|
dependsOn
|
|
@@ -302,7 +305,7 @@ export function setupTrackedQuery(
|
|
|
302
305
|
return {
|
|
303
306
|
...service.service,
|
|
304
307
|
refetch: async (refetchOptions) => {
|
|
305
|
-
dependencyController?.
|
|
308
|
+
await dependencyController?.evaluateForRefetch();
|
|
306
309
|
// Refetch is one of the two explicit reactivation paths agreed on in the design.
|
|
307
310
|
ensureRegistered();
|
|
308
311
|
return service.service.refetch(refetchOptions);
|
|
@@ -423,21 +426,20 @@ function createQueryService<
|
|
|
423
426
|
}
|
|
424
427
|
|
|
425
428
|
function bindQueryDependencies<
|
|
429
|
+
TSources extends readonly unknown[] = [],
|
|
426
430
|
TQueryFnData = unknown,
|
|
427
431
|
TError = Error,
|
|
428
432
|
TData = TQueryFnData,
|
|
429
433
|
TQueryData = TQueryFnData,
|
|
430
434
|
TQueryKey extends QueryKey = QueryKey,
|
|
431
435
|
>(
|
|
432
|
-
queryClient: QueryClient,
|
|
433
436
|
queryService: ReturnType<
|
|
434
437
|
typeof createQueryService<TQueryFnData, TError, TData, TQueryData, TQueryKey>
|
|
435
438
|
>,
|
|
436
439
|
queryKey: TQueryKey,
|
|
437
|
-
dependsOn: QueryDependencyTuple<
|
|
440
|
+
dependsOn: QueryDependencyTuple<TSources, TQueryKey>
|
|
438
441
|
): QueryService<TData, TError> {
|
|
439
442
|
const dependencyController = createDependencyController(
|
|
440
|
-
queryClient,
|
|
441
443
|
queryKey,
|
|
442
444
|
queryService.setDerivedState,
|
|
443
445
|
dependsOn
|
|
@@ -447,7 +449,7 @@ function bindQueryDependencies<
|
|
|
447
449
|
return {
|
|
448
450
|
...queryService.service,
|
|
449
451
|
refetch: async (refetchOptions) => {
|
|
450
|
-
dependencyController.
|
|
452
|
+
await dependencyController.evaluateForRefetch();
|
|
451
453
|
return queryService.service.refetch(refetchOptions);
|
|
452
454
|
},
|
|
453
455
|
subscribe: (listener) => {
|
|
@@ -471,25 +473,24 @@ function bindQueryDependencies<
|
|
|
471
473
|
}
|
|
472
474
|
|
|
473
475
|
function createDependencyController<
|
|
476
|
+
TSources extends readonly unknown[] = [],
|
|
474
477
|
TQueryFnData = unknown,
|
|
475
478
|
TError = Error,
|
|
476
479
|
TData = TQueryFnData,
|
|
477
480
|
TQueryData = TQueryFnData,
|
|
478
481
|
TQueryKey extends QueryKey = QueryKey,
|
|
479
482
|
>(
|
|
480
|
-
queryClient: QueryClient,
|
|
481
483
|
baseQueryKey: TQueryKey,
|
|
482
484
|
setDerivedState: (derivedOptions: QueryDependencyDerivedOptions<TQueryKey>) => void,
|
|
483
|
-
dependsOn: QueryDependencyTuple<
|
|
485
|
+
dependsOn: QueryDependencyTuple<TSources, TQueryKey>
|
|
484
486
|
) {
|
|
485
|
-
const [
|
|
486
|
-
let
|
|
487
|
-
let
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
) as Parameters<typeof deriveOptions>[0];
|
|
487
|
+
const [sources, deriveOptions] = dependsOn;
|
|
488
|
+
let isActive = false;
|
|
489
|
+
let scheduledEvaluation = false;
|
|
490
|
+
let sourceUnsubscribers: Array<() => void> = [];
|
|
491
|
+
|
|
492
|
+
const evaluateBinding = () => {
|
|
493
|
+
const snapshots = sources.map((source) => source.getSnapshot()) as Parameters<typeof deriveOptions>[0];
|
|
493
494
|
const derivedOptions = deriveOptions(snapshots);
|
|
494
495
|
|
|
495
496
|
setDerivedState({
|
|
@@ -498,39 +499,58 @@ function createDependencyController<
|
|
|
498
499
|
});
|
|
499
500
|
};
|
|
500
501
|
|
|
501
|
-
const
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
)
|
|
502
|
+
const scheduleEvaluate = () => {
|
|
503
|
+
if (scheduledEvaluation) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
scheduledEvaluation = true;
|
|
508
|
+
|
|
509
|
+
queueMicrotask(() => {
|
|
510
|
+
scheduledEvaluation = false;
|
|
511
|
+
|
|
512
|
+
if (!isActive) {
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
evaluateBinding();
|
|
517
|
+
});
|
|
518
|
+
};
|
|
509
519
|
|
|
510
520
|
return {
|
|
511
521
|
activate: () => {
|
|
512
|
-
if (
|
|
522
|
+
if (isActive) {
|
|
513
523
|
return;
|
|
514
524
|
}
|
|
515
525
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
526
|
+
isActive = true;
|
|
527
|
+
sourceUnsubscribers = sources.map((source) =>
|
|
528
|
+
source.subscribe(() => {
|
|
529
|
+
scheduleEvaluate();
|
|
530
|
+
})
|
|
531
|
+
);
|
|
532
|
+
evaluateBinding();
|
|
519
533
|
},
|
|
520
534
|
deactivate: () => {
|
|
521
|
-
|
|
522
|
-
unsubscribe
|
|
523
|
-
|
|
535
|
+
isActive = false;
|
|
536
|
+
sourceUnsubscribers.forEach((unsubscribe) => {
|
|
537
|
+
unsubscribe();
|
|
538
|
+
});
|
|
539
|
+
sourceUnsubscribers = [];
|
|
524
540
|
},
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
541
|
+
evaluateForRefetch: async () => {
|
|
542
|
+
await Promise.all(
|
|
543
|
+
sources.map(async (source) => {
|
|
544
|
+
await source.refetch();
|
|
545
|
+
})
|
|
546
|
+
);
|
|
547
|
+
|
|
548
|
+
if (isActive) {
|
|
549
|
+
scheduleEvaluate();
|
|
528
550
|
return;
|
|
529
551
|
}
|
|
530
552
|
|
|
531
|
-
|
|
532
|
-
evaluateBinding(transientObserver.getCurrentResult());
|
|
533
|
-
transientObserver.destroy();
|
|
553
|
+
evaluateBinding();
|
|
534
554
|
},
|
|
535
555
|
};
|
|
536
556
|
}
|
|
@@ -541,10 +561,11 @@ function splitQueryServiceOptions<
|
|
|
541
561
|
TData = TQueryFnData,
|
|
542
562
|
TQueryData = TQueryFnData,
|
|
543
563
|
TQueryKey extends QueryKey = QueryKey,
|
|
564
|
+
TSources extends readonly unknown[] = [],
|
|
544
565
|
>(
|
|
545
|
-
options?: QueryServiceOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>
|
|
566
|
+
options?: QueryServiceOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey, TSources>
|
|
546
567
|
): {
|
|
547
|
-
dependsOn?: QueryDependencyTuple<
|
|
568
|
+
dependsOn?: QueryDependencyTuple<TSources, TQueryKey>;
|
|
548
569
|
runtimeOptions: QueryServiceRuntimeOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey> | undefined;
|
|
549
570
|
} {
|
|
550
571
|
if (options === undefined) {
|