@tanstack/solid-query 6.0.0-rc.1 → 6.0.0-rc.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/_tsup-dts-rollup.d.cts +69 -2
- package/build/_tsup-dts-rollup.d.ts +69 -2
- package/build/dev.cjs +60 -11
- package/build/dev.d.cts +2 -0
- package/build/dev.d.ts +2 -0
- package/build/dev.js +62 -14
- package/build/index.cjs +60 -11
- package/build/index.d.cts +2 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +62 -14
- package/package.json +5 -4
- package/src/QueryClientProvider.tsx +115 -4
- package/src/dehydrateSettled.ts +42 -0
- package/src/index.ts +2 -0
- package/src/useMutation.ts +39 -25
|
@@ -251,6 +251,33 @@ export { DehydratedState }
|
|
|
251
251
|
|
|
252
252
|
export { DehydrateOptions }
|
|
253
253
|
|
|
254
|
+
/**
|
|
255
|
+
* Waits for every fetch the client has in flight to settle, then
|
|
256
|
+
* dehydrates. This is the extraction half of a single-flight collector:
|
|
257
|
+
* after route data functions run for the mutation's target URL, loaders
|
|
258
|
+
* commonly kick off prefetches without awaiting them — plain
|
|
259
|
+
* `dehydrate()` would snapshot those mid-fetch and ship nothing.
|
|
260
|
+
*
|
|
261
|
+
* ```ts
|
|
262
|
+
* registerFlightDataSource(FLIGHT_DATA_SOURCE, (event, outcome) =>
|
|
263
|
+
* loadFlightTarget({
|
|
264
|
+
* router,
|
|
265
|
+
* event,
|
|
266
|
+
* outcome,
|
|
267
|
+
* collect: () => dehydrateSettled(queryClient),
|
|
268
|
+
* }),
|
|
269
|
+
* )
|
|
270
|
+
* ```
|
|
271
|
+
*
|
|
272
|
+
* Settling is chased to quiescence — awaiting one batch of fetches can
|
|
273
|
+
* dispatch more (dependent queries keyed off a first result) — so an
|
|
274
|
+
* unconditionally self-refetching query would keep this pending; that's
|
|
275
|
+
* an app bug mirrored, not guarded.
|
|
276
|
+
*/
|
|
277
|
+
declare function dehydrateSettled(client: QueryClient, options?: DehydrateOptions): Promise<DehydratedState>;
|
|
278
|
+
export { dehydrateSettled }
|
|
279
|
+
export { dehydrateSettled as dehydrateSettled_alias_1 }
|
|
280
|
+
|
|
254
281
|
export { DistributiveOmit }
|
|
255
282
|
|
|
256
283
|
export { EnsureInfiniteQueryDataOptions }
|
|
@@ -271,6 +298,40 @@ export { FetchQueryOptions }
|
|
|
271
298
|
|
|
272
299
|
export { FetchStatus }
|
|
273
300
|
|
|
301
|
+
/**
|
|
302
|
+
* The query cache's single-flight source id. Mutation responses can fold
|
|
303
|
+
* fresh data for multiple caches at once (Solid's multi-source
|
|
304
|
+
* single-flight protocol); this is the slice the query cache claims — the
|
|
305
|
+
* provider subscribes its consumer under it, and server collectors
|
|
306
|
+
* register under it to produce the data:
|
|
307
|
+
*
|
|
308
|
+
* ```ts
|
|
309
|
+
* import { registerFlightDataSource } from '@solidjs/web/server-functions/server'
|
|
310
|
+
* import { FLIGHT_DATA_SOURCE, dehydrate } from '@tanstack/solid-query'
|
|
311
|
+
*
|
|
312
|
+
* registerFlightDataSource(FLIGHT_DATA_SOURCE, async (event, outcome) => {
|
|
313
|
+
* // rebuild the data for outcome.targetUrl into a QueryClient, then
|
|
314
|
+
* return dehydrate(queryClient)
|
|
315
|
+
* })
|
|
316
|
+
* ```
|
|
317
|
+
*
|
|
318
|
+
* The slice's payload is a `DehydratedState`; the provider consumes it
|
|
319
|
+
* with `hydrate()`, so every mounted query on those keys updates before
|
|
320
|
+
* the mutation's promise resolves — no follow-up refetches.
|
|
321
|
+
*
|
|
322
|
+
* A mutation can additionally declare its invalidation SCOPE with
|
|
323
|
+
* `X-Revalidate` keys (the `revalidate` option of Solid's `reload`/
|
|
324
|
+
* `redirect` response helpers). The payload covers the slice of that scope
|
|
325
|
+
* the server could recompute; whatever a declared key matches beyond it is
|
|
326
|
+
* swept client-side (see the consumer below). For the sweep to be
|
|
327
|
+
* delivered, the collector must contribute a slice — return the dehydrated
|
|
328
|
+
* state even when it holds no queries if `outcome.revalidateKeys` is
|
|
329
|
+
* present, rather than skipping the source.
|
|
330
|
+
*/
|
|
331
|
+
declare const FLIGHT_DATA_SOURCE = "sq";
|
|
332
|
+
export { FLIGHT_DATA_SOURCE }
|
|
333
|
+
export { FLIGHT_DATA_SOURCE as FLIGHT_DATA_SOURCE_alias_1 }
|
|
334
|
+
|
|
274
335
|
export { focusManager }
|
|
275
336
|
|
|
276
337
|
export { GetNextPageParamFunction }
|
|
@@ -555,8 +616,14 @@ export { QueryClientContext as QueryClientContext_alias_1 }
|
|
|
555
616
|
/**
|
|
556
617
|
* Provides the QueryClient and manages its mount lifecycle. On the server
|
|
557
618
|
* it also registers the cache serializer above; on the client, hooks prime
|
|
558
|
-
* the cache from their hash-keyed registry entries themselves
|
|
559
|
-
* `useBaseQuery
|
|
619
|
+
* the cache from their hash-keyed registry entries themselves (see
|
|
620
|
+
* `useBaseQuery`) and the provider subscribes the cache's single-flight
|
|
621
|
+
* consumer: mutation responses carrying a `FLIGHT_DATA_SOURCE` slice (a
|
|
622
|
+
* `DehydratedState` produced by a server collector registered under the
|
|
623
|
+
* same id) hydrate this client before the mutation's promise resolves.
|
|
624
|
+
* Subscribing is inert when no server collector exists — the server just
|
|
625
|
+
* folds nothing — so it is unconditional. One consumer per source: with
|
|
626
|
+
* nested providers, the innermost mounted one owns the slice.
|
|
560
627
|
*/
|
|
561
628
|
declare const QueryClientProvider: (props: QueryClientProviderProps) => JSX.Element;
|
|
562
629
|
export { QueryClientProvider }
|
|
@@ -251,6 +251,33 @@ export { DehydratedState }
|
|
|
251
251
|
|
|
252
252
|
export { DehydrateOptions }
|
|
253
253
|
|
|
254
|
+
/**
|
|
255
|
+
* Waits for every fetch the client has in flight to settle, then
|
|
256
|
+
* dehydrates. This is the extraction half of a single-flight collector:
|
|
257
|
+
* after route data functions run for the mutation's target URL, loaders
|
|
258
|
+
* commonly kick off prefetches without awaiting them — plain
|
|
259
|
+
* `dehydrate()` would snapshot those mid-fetch and ship nothing.
|
|
260
|
+
*
|
|
261
|
+
* ```ts
|
|
262
|
+
* registerFlightDataSource(FLIGHT_DATA_SOURCE, (event, outcome) =>
|
|
263
|
+
* loadFlightTarget({
|
|
264
|
+
* router,
|
|
265
|
+
* event,
|
|
266
|
+
* outcome,
|
|
267
|
+
* collect: () => dehydrateSettled(queryClient),
|
|
268
|
+
* }),
|
|
269
|
+
* )
|
|
270
|
+
* ```
|
|
271
|
+
*
|
|
272
|
+
* Settling is chased to quiescence — awaiting one batch of fetches can
|
|
273
|
+
* dispatch more (dependent queries keyed off a first result) — so an
|
|
274
|
+
* unconditionally self-refetching query would keep this pending; that's
|
|
275
|
+
* an app bug mirrored, not guarded.
|
|
276
|
+
*/
|
|
277
|
+
declare function dehydrateSettled(client: QueryClient, options?: DehydrateOptions): Promise<DehydratedState>;
|
|
278
|
+
export { dehydrateSettled }
|
|
279
|
+
export { dehydrateSettled as dehydrateSettled_alias_1 }
|
|
280
|
+
|
|
254
281
|
export { DistributiveOmit }
|
|
255
282
|
|
|
256
283
|
export { EnsureInfiniteQueryDataOptions }
|
|
@@ -271,6 +298,40 @@ export { FetchQueryOptions }
|
|
|
271
298
|
|
|
272
299
|
export { FetchStatus }
|
|
273
300
|
|
|
301
|
+
/**
|
|
302
|
+
* The query cache's single-flight source id. Mutation responses can fold
|
|
303
|
+
* fresh data for multiple caches at once (Solid's multi-source
|
|
304
|
+
* single-flight protocol); this is the slice the query cache claims — the
|
|
305
|
+
* provider subscribes its consumer under it, and server collectors
|
|
306
|
+
* register under it to produce the data:
|
|
307
|
+
*
|
|
308
|
+
* ```ts
|
|
309
|
+
* import { registerFlightDataSource } from '@solidjs/web/server-functions/server'
|
|
310
|
+
* import { FLIGHT_DATA_SOURCE, dehydrate } from '@tanstack/solid-query'
|
|
311
|
+
*
|
|
312
|
+
* registerFlightDataSource(FLIGHT_DATA_SOURCE, async (event, outcome) => {
|
|
313
|
+
* // rebuild the data for outcome.targetUrl into a QueryClient, then
|
|
314
|
+
* return dehydrate(queryClient)
|
|
315
|
+
* })
|
|
316
|
+
* ```
|
|
317
|
+
*
|
|
318
|
+
* The slice's payload is a `DehydratedState`; the provider consumes it
|
|
319
|
+
* with `hydrate()`, so every mounted query on those keys updates before
|
|
320
|
+
* the mutation's promise resolves — no follow-up refetches.
|
|
321
|
+
*
|
|
322
|
+
* A mutation can additionally declare its invalidation SCOPE with
|
|
323
|
+
* `X-Revalidate` keys (the `revalidate` option of Solid's `reload`/
|
|
324
|
+
* `redirect` response helpers). The payload covers the slice of that scope
|
|
325
|
+
* the server could recompute; whatever a declared key matches beyond it is
|
|
326
|
+
* swept client-side (see the consumer below). For the sweep to be
|
|
327
|
+
* delivered, the collector must contribute a slice — return the dehydrated
|
|
328
|
+
* state even when it holds no queries if `outcome.revalidateKeys` is
|
|
329
|
+
* present, rather than skipping the source.
|
|
330
|
+
*/
|
|
331
|
+
declare const FLIGHT_DATA_SOURCE = "sq";
|
|
332
|
+
export { FLIGHT_DATA_SOURCE }
|
|
333
|
+
export { FLIGHT_DATA_SOURCE as FLIGHT_DATA_SOURCE_alias_1 }
|
|
334
|
+
|
|
274
335
|
export { focusManager }
|
|
275
336
|
|
|
276
337
|
export { GetNextPageParamFunction }
|
|
@@ -555,8 +616,14 @@ export { QueryClientContext as QueryClientContext_alias_1 }
|
|
|
555
616
|
/**
|
|
556
617
|
* Provides the QueryClient and manages its mount lifecycle. On the server
|
|
557
618
|
* it also registers the cache serializer above; on the client, hooks prime
|
|
558
|
-
* the cache from their hash-keyed registry entries themselves
|
|
559
|
-
* `useBaseQuery
|
|
619
|
+
* the cache from their hash-keyed registry entries themselves (see
|
|
620
|
+
* `useBaseQuery`) and the provider subscribes the cache's single-flight
|
|
621
|
+
* consumer: mutation responses carrying a `FLIGHT_DATA_SOURCE` slice (a
|
|
622
|
+
* `DehydratedState` produced by a server collector registered under the
|
|
623
|
+
* same id) hydrate this client before the mutation's promise resolves.
|
|
624
|
+
* Subscribing is inert when no server collector exists — the server just
|
|
625
|
+
* folds nothing — so it is unconditional. One consumer per source: with
|
|
626
|
+
* nested providers, the innermost mounted one owns the slice.
|
|
560
627
|
*/
|
|
561
628
|
declare const QueryClientProvider: (props: QueryClientProviderProps) => JSX.Element;
|
|
562
629
|
export { QueryClientProvider }
|
package/build/dev.cjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var queryCore = require('@tanstack/query-core');
|
|
4
4
|
var solidJs = require('solid-js');
|
|
5
5
|
var web = require('@solidjs/web');
|
|
6
|
+
var serverFunctions = require('@solidjs/web/server-functions');
|
|
6
7
|
|
|
7
8
|
// src/index.ts
|
|
8
9
|
exports.QueryClient = class QueryClient extends queryCore.QueryClient {
|
|
@@ -12,6 +13,18 @@ exports.QueryClient = class QueryClient extends queryCore.QueryClient {
|
|
|
12
13
|
};
|
|
13
14
|
var isServer = typeof window === "undefined";
|
|
14
15
|
var HYDRATION_KEY_PREFIX = "sq:";
|
|
16
|
+
exports.FLIGHT_DATA_SOURCE = "sq";
|
|
17
|
+
function sweepRevalidatedQueries(client, payload, response) {
|
|
18
|
+
const keys = response.headers.get(web.REVALIDATE_HEADER)?.split(",");
|
|
19
|
+
if (!keys) return;
|
|
20
|
+
const covered = new Set(payload.queries.map((query) => query.queryHash));
|
|
21
|
+
for (const key of keys) {
|
|
22
|
+
client.invalidateQueries({
|
|
23
|
+
queryKey: [key],
|
|
24
|
+
predicate: (query) => !covered.has(query.queryHash)
|
|
25
|
+
}).catch(() => void 0);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
15
28
|
exports.QueryClientContext = solidJs.createContext(null);
|
|
16
29
|
exports.useQueryClient = (queryClient) => {
|
|
17
30
|
if (queryClient) {
|
|
@@ -27,9 +40,11 @@ function serializeCacheOnServer(client) {
|
|
|
27
40
|
const ctx = solidJs.sharedConfig.context;
|
|
28
41
|
if (!ctx || !ctx.async || ctx.noHydrate) return;
|
|
29
42
|
const cache = client.getQueryCache();
|
|
43
|
+
const shouldDehydrateQuery = client.getDefaultOptions().dehydrate?.shouldDehydrateQuery;
|
|
30
44
|
const seen = /* @__PURE__ */ new Set();
|
|
31
45
|
const serializeQuery = (query) => {
|
|
32
46
|
if (seen.has(query.queryHash)) return;
|
|
47
|
+
if (shouldDehydrateQuery && !shouldDehydrateQuery(query)) return;
|
|
33
48
|
const state = query.state;
|
|
34
49
|
if (state.status === "success") {
|
|
35
50
|
seen.add(query.queryHash);
|
|
@@ -53,7 +68,18 @@ function serializeCacheOnServer(client) {
|
|
|
53
68
|
exports.QueryClientProvider = (props) => {
|
|
54
69
|
props.client.mount();
|
|
55
70
|
solidJs.onCleanup(() => props.client.unmount());
|
|
56
|
-
if (isServer)
|
|
71
|
+
if (isServer) {
|
|
72
|
+
serializeCacheOnServer(props.client);
|
|
73
|
+
solidJs.onCleanup(() => {
|
|
74
|
+
props.client.cancelQueries().catch(() => void 0);
|
|
75
|
+
props.client.clear();
|
|
76
|
+
});
|
|
77
|
+
} else {
|
|
78
|
+
solidJs.onCleanup(serverFunctions.subscribeFlightData(exports.FLIGHT_DATA_SOURCE, (data, context) => {
|
|
79
|
+
queryCore.hydrate(props.client, data);
|
|
80
|
+
sweepRevalidatedQueries(props.client, data, context.response);
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
57
83
|
return web.createComponent(exports.QueryClientContext, {
|
|
58
84
|
value: () => props.client,
|
|
59
85
|
get children() {
|
|
@@ -432,6 +458,15 @@ function useQuery(options, queryClient) {
|
|
|
432
458
|
function queryOptions(options) {
|
|
433
459
|
return options;
|
|
434
460
|
}
|
|
461
|
+
async function dehydrateSettled(client, options) {
|
|
462
|
+
const cache = client.getQueryCache();
|
|
463
|
+
for (; ; ) {
|
|
464
|
+
const pending = cache.getAll().filter((query) => query.state.fetchStatus !== "idle").map((query) => query.promise);
|
|
465
|
+
if (pending.length === 0) break;
|
|
466
|
+
await Promise.allSettled(pending);
|
|
467
|
+
}
|
|
468
|
+
return queryCore.dehydrate(client, options);
|
|
469
|
+
}
|
|
435
470
|
var isServer3 = typeof window === "undefined";
|
|
436
471
|
function createCacheAggregate(subscribe, read, empty) {
|
|
437
472
|
const hydratedMount = !isServer3 && solidJs.sharedConfig.hydrating === true;
|
|
@@ -592,15 +627,13 @@ function useMutation(options, queryClient) {
|
|
|
592
627
|
const [flight, setFlight] = solidJs.createOptimistic(null);
|
|
593
628
|
const [settled, setSettled] = solidJs.createSignal(IDLE);
|
|
594
629
|
let activeMutation = null;
|
|
630
|
+
let unsubscribeFlight = null;
|
|
595
631
|
const [flightVersion, setFlightVersion] = solidJs.createSignal(0);
|
|
596
|
-
if (!isServer4)
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
}
|
|
632
|
+
if (!isServer4)
|
|
633
|
+
solidJs.onCleanup(() => {
|
|
634
|
+
unsubscribeFlight?.();
|
|
635
|
+
unsubscribeFlight = null;
|
|
601
636
|
});
|
|
602
|
-
solidJs.onCleanup(unsubscribe);
|
|
603
|
-
}
|
|
604
637
|
async function* run(variables) {
|
|
605
638
|
const opts = solidJs.untrack(options);
|
|
606
639
|
const callbackContext = {
|
|
@@ -610,7 +643,8 @@ function useMutation(options, queryClient) {
|
|
|
610
643
|
};
|
|
611
644
|
if (!isServer4) setFlight({ variables });
|
|
612
645
|
opts.onMutate?.(variables, callbackContext);
|
|
613
|
-
const
|
|
646
|
+
const c = solidJs.untrack(client);
|
|
647
|
+
const mutation = c.getMutationCache().build(c, {
|
|
614
648
|
...opts,
|
|
615
649
|
// Options-level callbacks re-run below, inside the transaction —
|
|
616
650
|
// executing them here (inside the await gap) would let their
|
|
@@ -621,6 +655,20 @@ function useMutation(options, queryClient) {
|
|
|
621
655
|
onSettled: void 0
|
|
622
656
|
});
|
|
623
657
|
activeMutation = mutation;
|
|
658
|
+
if (!isServer4) {
|
|
659
|
+
unsubscribeFlight?.();
|
|
660
|
+
unsubscribeFlight = c.getMutationCache().subscribe((event) => {
|
|
661
|
+
if ("mutation" in event && event.mutation === activeMutation) {
|
|
662
|
+
setFlightVersion((v) => v + 1);
|
|
663
|
+
}
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
const settleFlight = () => {
|
|
667
|
+
if (activeMutation !== mutation) return;
|
|
668
|
+
activeMutation = null;
|
|
669
|
+
unsubscribeFlight?.();
|
|
670
|
+
unsubscribeFlight = null;
|
|
671
|
+
};
|
|
624
672
|
async function* settleFailure(error) {
|
|
625
673
|
try {
|
|
626
674
|
if (opts.onError)
|
|
@@ -648,7 +696,7 @@ function useMutation(options, queryClient) {
|
|
|
648
696
|
variables,
|
|
649
697
|
submittedAt: mutation.state.submittedAt
|
|
650
698
|
});
|
|
651
|
-
|
|
699
|
+
settleFlight();
|
|
652
700
|
throw error;
|
|
653
701
|
}
|
|
654
702
|
let result2;
|
|
@@ -688,7 +736,7 @@ function useMutation(options, queryClient) {
|
|
|
688
736
|
variables,
|
|
689
737
|
submittedAt: mutation.state.submittedAt
|
|
690
738
|
});
|
|
691
|
-
|
|
739
|
+
settleFlight();
|
|
692
740
|
return result2;
|
|
693
741
|
}
|
|
694
742
|
const invoke = isServer4 ? (variables) => drain(run(variables)) : solidJs.action(run);
|
|
@@ -826,6 +874,7 @@ function useQueries(queriesOptions, queryClient) {
|
|
|
826
874
|
});
|
|
827
875
|
}
|
|
828
876
|
|
|
877
|
+
exports.dehydrateSettled = dehydrateSettled;
|
|
829
878
|
exports.infiniteQueryOptions = infiniteQueryOptions;
|
|
830
879
|
exports.mutationOptions = mutationOptions;
|
|
831
880
|
exports.queryOptions = queryOptions;
|
package/build/dev.d.cts
CHANGED
|
@@ -23,10 +23,12 @@ export { useQuery } from './_tsup-dts-rollup.cjs';
|
|
|
23
23
|
export { queryOptions } from './_tsup-dts-rollup.cjs';
|
|
24
24
|
export { DefinedInitialDataOptions } from './_tsup-dts-rollup.cjs';
|
|
25
25
|
export { UndefinedInitialDataOptions } from './_tsup-dts-rollup.cjs';
|
|
26
|
+
export { FLIGHT_DATA_SOURCE_alias_1 as FLIGHT_DATA_SOURCE } from './_tsup-dts-rollup.cjs';
|
|
26
27
|
export { QueryClientContext_alias_1 as QueryClientContext } from './_tsup-dts-rollup.cjs';
|
|
27
28
|
export { QueryClientProvider_alias_1 as QueryClientProvider } from './_tsup-dts-rollup.cjs';
|
|
28
29
|
export { useQueryClient_alias_1 as useQueryClient } from './_tsup-dts-rollup.cjs';
|
|
29
30
|
export { QueryClientProviderProps_alias_1 as QueryClientProviderProps } from './_tsup-dts-rollup.cjs';
|
|
31
|
+
export { dehydrateSettled_alias_1 as dehydrateSettled } from './_tsup-dts-rollup.cjs';
|
|
30
32
|
export { useIsFetching } from './_tsup-dts-rollup.cjs';
|
|
31
33
|
export { useInfiniteQuery } from './_tsup-dts-rollup.cjs';
|
|
32
34
|
export { infiniteQueryOptions } from './_tsup-dts-rollup.cjs';
|
package/build/dev.d.ts
CHANGED
|
@@ -23,10 +23,12 @@ export { useQuery } from './_tsup-dts-rollup.js';
|
|
|
23
23
|
export { queryOptions } from './_tsup-dts-rollup.js';
|
|
24
24
|
export { DefinedInitialDataOptions } from './_tsup-dts-rollup.js';
|
|
25
25
|
export { UndefinedInitialDataOptions } from './_tsup-dts-rollup.js';
|
|
26
|
+
export { FLIGHT_DATA_SOURCE_alias_1 as FLIGHT_DATA_SOURCE } from './_tsup-dts-rollup.js';
|
|
26
27
|
export { QueryClientContext_alias_1 as QueryClientContext } from './_tsup-dts-rollup.js';
|
|
27
28
|
export { QueryClientProvider_alias_1 as QueryClientProvider } from './_tsup-dts-rollup.js';
|
|
28
29
|
export { useQueryClient_alias_1 as useQueryClient } from './_tsup-dts-rollup.js';
|
|
29
30
|
export { QueryClientProviderProps_alias_1 as QueryClientProviderProps } from './_tsup-dts-rollup.js';
|
|
31
|
+
export { dehydrateSettled_alias_1 as dehydrateSettled } from './_tsup-dts-rollup.js';
|
|
30
32
|
export { useIsFetching } from './_tsup-dts-rollup.js';
|
|
31
33
|
export { useInfiniteQuery } from './_tsup-dts-rollup.js';
|
|
32
34
|
export { infiniteQueryOptions } from './_tsup-dts-rollup.js';
|
package/build/dev.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { noop, QueryClient as QueryClient$1, shouldThrowError, InfiniteQueryObserver,
|
|
1
|
+
import { noop, QueryClient as QueryClient$1, hydrate, dehydrate, shouldThrowError, InfiniteQueryObserver, QueryObserver, replaceEqualDeep } from '@tanstack/query-core';
|
|
2
2
|
export * from '@tanstack/query-core';
|
|
3
3
|
import { createContext, useContext, onCleanup, createMemo, createOptimistic, createSignal, action, createRenderEffect, untrack, repeat, $TRACK, sharedConfig, createEffect, createProjection, runWithOwner, isPending, resolve } from 'solid-js';
|
|
4
|
-
import { createComponent } from '@solidjs/web';
|
|
4
|
+
import { createComponent, REVALIDATE_HEADER } from '@solidjs/web';
|
|
5
|
+
import { subscribeFlightData } from '@solidjs/web/server-functions';
|
|
5
6
|
|
|
6
7
|
// src/index.ts
|
|
7
8
|
var QueryClient = class extends QueryClient$1 {
|
|
@@ -11,6 +12,18 @@ var QueryClient = class extends QueryClient$1 {
|
|
|
11
12
|
};
|
|
12
13
|
var isServer = typeof window === "undefined";
|
|
13
14
|
var HYDRATION_KEY_PREFIX = "sq:";
|
|
15
|
+
var FLIGHT_DATA_SOURCE = "sq";
|
|
16
|
+
function sweepRevalidatedQueries(client, payload, response) {
|
|
17
|
+
const keys = response.headers.get(REVALIDATE_HEADER)?.split(",");
|
|
18
|
+
if (!keys) return;
|
|
19
|
+
const covered = new Set(payload.queries.map((query) => query.queryHash));
|
|
20
|
+
for (const key of keys) {
|
|
21
|
+
client.invalidateQueries({
|
|
22
|
+
queryKey: [key],
|
|
23
|
+
predicate: (query) => !covered.has(query.queryHash)
|
|
24
|
+
}).catch(() => void 0);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
14
27
|
var QueryClientContext = createContext(null);
|
|
15
28
|
var useQueryClient = (queryClient) => {
|
|
16
29
|
if (queryClient) {
|
|
@@ -26,9 +39,11 @@ function serializeCacheOnServer(client) {
|
|
|
26
39
|
const ctx = sharedConfig.context;
|
|
27
40
|
if (!ctx || !ctx.async || ctx.noHydrate) return;
|
|
28
41
|
const cache = client.getQueryCache();
|
|
42
|
+
const shouldDehydrateQuery = client.getDefaultOptions().dehydrate?.shouldDehydrateQuery;
|
|
29
43
|
const seen = /* @__PURE__ */ new Set();
|
|
30
44
|
const serializeQuery = (query) => {
|
|
31
45
|
if (seen.has(query.queryHash)) return;
|
|
46
|
+
if (shouldDehydrateQuery && !shouldDehydrateQuery(query)) return;
|
|
32
47
|
const state = query.state;
|
|
33
48
|
if (state.status === "success") {
|
|
34
49
|
seen.add(query.queryHash);
|
|
@@ -52,7 +67,18 @@ function serializeCacheOnServer(client) {
|
|
|
52
67
|
var QueryClientProvider = (props) => {
|
|
53
68
|
props.client.mount();
|
|
54
69
|
onCleanup(() => props.client.unmount());
|
|
55
|
-
if (isServer)
|
|
70
|
+
if (isServer) {
|
|
71
|
+
serializeCacheOnServer(props.client);
|
|
72
|
+
onCleanup(() => {
|
|
73
|
+
props.client.cancelQueries().catch(() => void 0);
|
|
74
|
+
props.client.clear();
|
|
75
|
+
});
|
|
76
|
+
} else {
|
|
77
|
+
onCleanup(subscribeFlightData(FLIGHT_DATA_SOURCE, (data, context) => {
|
|
78
|
+
hydrate(props.client, data);
|
|
79
|
+
sweepRevalidatedQueries(props.client, data, context.response);
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
56
82
|
return createComponent(QueryClientContext, {
|
|
57
83
|
value: () => props.client,
|
|
58
84
|
get children() {
|
|
@@ -431,6 +457,15 @@ function useQuery(options, queryClient) {
|
|
|
431
457
|
function queryOptions(options) {
|
|
432
458
|
return options;
|
|
433
459
|
}
|
|
460
|
+
async function dehydrateSettled(client, options) {
|
|
461
|
+
const cache = client.getQueryCache();
|
|
462
|
+
for (; ; ) {
|
|
463
|
+
const pending = cache.getAll().filter((query) => query.state.fetchStatus !== "idle").map((query) => query.promise);
|
|
464
|
+
if (pending.length === 0) break;
|
|
465
|
+
await Promise.allSettled(pending);
|
|
466
|
+
}
|
|
467
|
+
return dehydrate(client, options);
|
|
468
|
+
}
|
|
434
469
|
var isServer3 = typeof window === "undefined";
|
|
435
470
|
function createCacheAggregate(subscribe, read, empty) {
|
|
436
471
|
const hydratedMount = !isServer3 && sharedConfig.hydrating === true;
|
|
@@ -591,15 +626,13 @@ function useMutation(options, queryClient) {
|
|
|
591
626
|
const [flight, setFlight] = createOptimistic(null);
|
|
592
627
|
const [settled, setSettled] = createSignal(IDLE);
|
|
593
628
|
let activeMutation = null;
|
|
629
|
+
let unsubscribeFlight = null;
|
|
594
630
|
const [flightVersion, setFlightVersion] = createSignal(0);
|
|
595
|
-
if (!isServer4)
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
}
|
|
631
|
+
if (!isServer4)
|
|
632
|
+
onCleanup(() => {
|
|
633
|
+
unsubscribeFlight?.();
|
|
634
|
+
unsubscribeFlight = null;
|
|
600
635
|
});
|
|
601
|
-
onCleanup(unsubscribe);
|
|
602
|
-
}
|
|
603
636
|
async function* run(variables) {
|
|
604
637
|
const opts = untrack(options);
|
|
605
638
|
const callbackContext = {
|
|
@@ -609,7 +642,8 @@ function useMutation(options, queryClient) {
|
|
|
609
642
|
};
|
|
610
643
|
if (!isServer4) setFlight({ variables });
|
|
611
644
|
opts.onMutate?.(variables, callbackContext);
|
|
612
|
-
const
|
|
645
|
+
const c = untrack(client);
|
|
646
|
+
const mutation = c.getMutationCache().build(c, {
|
|
613
647
|
...opts,
|
|
614
648
|
// Options-level callbacks re-run below, inside the transaction —
|
|
615
649
|
// executing them here (inside the await gap) would let their
|
|
@@ -620,6 +654,20 @@ function useMutation(options, queryClient) {
|
|
|
620
654
|
onSettled: void 0
|
|
621
655
|
});
|
|
622
656
|
activeMutation = mutation;
|
|
657
|
+
if (!isServer4) {
|
|
658
|
+
unsubscribeFlight?.();
|
|
659
|
+
unsubscribeFlight = c.getMutationCache().subscribe((event) => {
|
|
660
|
+
if ("mutation" in event && event.mutation === activeMutation) {
|
|
661
|
+
setFlightVersion((v) => v + 1);
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
const settleFlight = () => {
|
|
666
|
+
if (activeMutation !== mutation) return;
|
|
667
|
+
activeMutation = null;
|
|
668
|
+
unsubscribeFlight?.();
|
|
669
|
+
unsubscribeFlight = null;
|
|
670
|
+
};
|
|
623
671
|
async function* settleFailure(error) {
|
|
624
672
|
try {
|
|
625
673
|
if (opts.onError)
|
|
@@ -647,7 +695,7 @@ function useMutation(options, queryClient) {
|
|
|
647
695
|
variables,
|
|
648
696
|
submittedAt: mutation.state.submittedAt
|
|
649
697
|
});
|
|
650
|
-
|
|
698
|
+
settleFlight();
|
|
651
699
|
throw error;
|
|
652
700
|
}
|
|
653
701
|
let result2;
|
|
@@ -687,7 +735,7 @@ function useMutation(options, queryClient) {
|
|
|
687
735
|
variables,
|
|
688
736
|
submittedAt: mutation.state.submittedAt
|
|
689
737
|
});
|
|
690
|
-
|
|
738
|
+
settleFlight();
|
|
691
739
|
return result2;
|
|
692
740
|
}
|
|
693
741
|
const invoke = isServer4 ? (variables) => drain(run(variables)) : action(run);
|
|
@@ -825,4 +873,4 @@ function useQueries(queriesOptions, queryClient) {
|
|
|
825
873
|
});
|
|
826
874
|
}
|
|
827
875
|
|
|
828
|
-
export { IsRestoringContext, QueryClient, QueryClientContext, QueryClientProvider, infiniteQueryOptions, mutationOptions, queryOptions, useInfiniteQuery, useIsFetching, useIsMutating, useIsRestoring, useMutation, useMutationState, useQueries, useQuery, useQueryClient };
|
|
876
|
+
export { FLIGHT_DATA_SOURCE, IsRestoringContext, QueryClient, QueryClientContext, QueryClientProvider, dehydrateSettled, infiniteQueryOptions, mutationOptions, queryOptions, useInfiniteQuery, useIsFetching, useIsMutating, useIsRestoring, useMutation, useMutationState, useQueries, useQuery, useQueryClient };
|
package/build/index.cjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var queryCore = require('@tanstack/query-core');
|
|
4
4
|
var solidJs = require('solid-js');
|
|
5
5
|
var web = require('@solidjs/web');
|
|
6
|
+
var serverFunctions = require('@solidjs/web/server-functions');
|
|
6
7
|
|
|
7
8
|
// src/index.ts
|
|
8
9
|
exports.QueryClient = class QueryClient extends queryCore.QueryClient {
|
|
@@ -12,6 +13,18 @@ exports.QueryClient = class QueryClient extends queryCore.QueryClient {
|
|
|
12
13
|
};
|
|
13
14
|
var isServer = typeof window === "undefined";
|
|
14
15
|
var HYDRATION_KEY_PREFIX = "sq:";
|
|
16
|
+
exports.FLIGHT_DATA_SOURCE = "sq";
|
|
17
|
+
function sweepRevalidatedQueries(client, payload, response) {
|
|
18
|
+
const keys = response.headers.get(web.REVALIDATE_HEADER)?.split(",");
|
|
19
|
+
if (!keys) return;
|
|
20
|
+
const covered = new Set(payload.queries.map((query) => query.queryHash));
|
|
21
|
+
for (const key of keys) {
|
|
22
|
+
client.invalidateQueries({
|
|
23
|
+
queryKey: [key],
|
|
24
|
+
predicate: (query) => !covered.has(query.queryHash)
|
|
25
|
+
}).catch(() => void 0);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
15
28
|
exports.QueryClientContext = solidJs.createContext(null);
|
|
16
29
|
exports.useQueryClient = (queryClient) => {
|
|
17
30
|
if (queryClient) {
|
|
@@ -27,9 +40,11 @@ function serializeCacheOnServer(client) {
|
|
|
27
40
|
const ctx = solidJs.sharedConfig.context;
|
|
28
41
|
if (!ctx || !ctx.async || ctx.noHydrate) return;
|
|
29
42
|
const cache = client.getQueryCache();
|
|
43
|
+
const shouldDehydrateQuery = client.getDefaultOptions().dehydrate?.shouldDehydrateQuery;
|
|
30
44
|
const seen = /* @__PURE__ */ new Set();
|
|
31
45
|
const serializeQuery = (query) => {
|
|
32
46
|
if (seen.has(query.queryHash)) return;
|
|
47
|
+
if (shouldDehydrateQuery && !shouldDehydrateQuery(query)) return;
|
|
33
48
|
const state = query.state;
|
|
34
49
|
if (state.status === "success") {
|
|
35
50
|
seen.add(query.queryHash);
|
|
@@ -53,7 +68,18 @@ function serializeCacheOnServer(client) {
|
|
|
53
68
|
exports.QueryClientProvider = (props) => {
|
|
54
69
|
props.client.mount();
|
|
55
70
|
solidJs.onCleanup(() => props.client.unmount());
|
|
56
|
-
if (isServer)
|
|
71
|
+
if (isServer) {
|
|
72
|
+
serializeCacheOnServer(props.client);
|
|
73
|
+
solidJs.onCleanup(() => {
|
|
74
|
+
props.client.cancelQueries().catch(() => void 0);
|
|
75
|
+
props.client.clear();
|
|
76
|
+
});
|
|
77
|
+
} else {
|
|
78
|
+
solidJs.onCleanup(serverFunctions.subscribeFlightData(exports.FLIGHT_DATA_SOURCE, (data, context) => {
|
|
79
|
+
queryCore.hydrate(props.client, data);
|
|
80
|
+
sweepRevalidatedQueries(props.client, data, context.response);
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
57
83
|
return web.createComponent(exports.QueryClientContext, {
|
|
58
84
|
value: () => props.client,
|
|
59
85
|
get children() {
|
|
@@ -432,6 +458,15 @@ function useQuery(options, queryClient) {
|
|
|
432
458
|
function queryOptions(options) {
|
|
433
459
|
return options;
|
|
434
460
|
}
|
|
461
|
+
async function dehydrateSettled(client, options) {
|
|
462
|
+
const cache = client.getQueryCache();
|
|
463
|
+
for (; ; ) {
|
|
464
|
+
const pending = cache.getAll().filter((query) => query.state.fetchStatus !== "idle").map((query) => query.promise);
|
|
465
|
+
if (pending.length === 0) break;
|
|
466
|
+
await Promise.allSettled(pending);
|
|
467
|
+
}
|
|
468
|
+
return queryCore.dehydrate(client, options);
|
|
469
|
+
}
|
|
435
470
|
var isServer3 = typeof window === "undefined";
|
|
436
471
|
function createCacheAggregate(subscribe, read, empty) {
|
|
437
472
|
const hydratedMount = !isServer3 && solidJs.sharedConfig.hydrating === true;
|
|
@@ -592,15 +627,13 @@ function useMutation(options, queryClient) {
|
|
|
592
627
|
const [flight, setFlight] = solidJs.createOptimistic(null);
|
|
593
628
|
const [settled, setSettled] = solidJs.createSignal(IDLE);
|
|
594
629
|
let activeMutation = null;
|
|
630
|
+
let unsubscribeFlight = null;
|
|
595
631
|
const [flightVersion, setFlightVersion] = solidJs.createSignal(0);
|
|
596
|
-
if (!isServer4)
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
}
|
|
632
|
+
if (!isServer4)
|
|
633
|
+
solidJs.onCleanup(() => {
|
|
634
|
+
unsubscribeFlight?.();
|
|
635
|
+
unsubscribeFlight = null;
|
|
601
636
|
});
|
|
602
|
-
solidJs.onCleanup(unsubscribe);
|
|
603
|
-
}
|
|
604
637
|
async function* run(variables) {
|
|
605
638
|
const opts = solidJs.untrack(options);
|
|
606
639
|
const callbackContext = {
|
|
@@ -610,7 +643,8 @@ function useMutation(options, queryClient) {
|
|
|
610
643
|
};
|
|
611
644
|
if (!isServer4) setFlight({ variables });
|
|
612
645
|
opts.onMutate?.(variables, callbackContext);
|
|
613
|
-
const
|
|
646
|
+
const c = solidJs.untrack(client);
|
|
647
|
+
const mutation = c.getMutationCache().build(c, {
|
|
614
648
|
...opts,
|
|
615
649
|
// Options-level callbacks re-run below, inside the transaction —
|
|
616
650
|
// executing them here (inside the await gap) would let their
|
|
@@ -621,6 +655,20 @@ function useMutation(options, queryClient) {
|
|
|
621
655
|
onSettled: void 0
|
|
622
656
|
});
|
|
623
657
|
activeMutation = mutation;
|
|
658
|
+
if (!isServer4) {
|
|
659
|
+
unsubscribeFlight?.();
|
|
660
|
+
unsubscribeFlight = c.getMutationCache().subscribe((event) => {
|
|
661
|
+
if ("mutation" in event && event.mutation === activeMutation) {
|
|
662
|
+
setFlightVersion((v) => v + 1);
|
|
663
|
+
}
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
const settleFlight = () => {
|
|
667
|
+
if (activeMutation !== mutation) return;
|
|
668
|
+
activeMutation = null;
|
|
669
|
+
unsubscribeFlight?.();
|
|
670
|
+
unsubscribeFlight = null;
|
|
671
|
+
};
|
|
624
672
|
async function* settleFailure(error) {
|
|
625
673
|
try {
|
|
626
674
|
if (opts.onError)
|
|
@@ -648,7 +696,7 @@ function useMutation(options, queryClient) {
|
|
|
648
696
|
variables,
|
|
649
697
|
submittedAt: mutation.state.submittedAt
|
|
650
698
|
});
|
|
651
|
-
|
|
699
|
+
settleFlight();
|
|
652
700
|
throw error;
|
|
653
701
|
}
|
|
654
702
|
let result2;
|
|
@@ -688,7 +736,7 @@ function useMutation(options, queryClient) {
|
|
|
688
736
|
variables,
|
|
689
737
|
submittedAt: mutation.state.submittedAt
|
|
690
738
|
});
|
|
691
|
-
|
|
739
|
+
settleFlight();
|
|
692
740
|
return result2;
|
|
693
741
|
}
|
|
694
742
|
const invoke = isServer4 ? (variables) => drain(run(variables)) : solidJs.action(run);
|
|
@@ -826,6 +874,7 @@ function useQueries(queriesOptions, queryClient) {
|
|
|
826
874
|
});
|
|
827
875
|
}
|
|
828
876
|
|
|
877
|
+
exports.dehydrateSettled = dehydrateSettled;
|
|
829
878
|
exports.infiniteQueryOptions = infiniteQueryOptions;
|
|
830
879
|
exports.mutationOptions = mutationOptions;
|
|
831
880
|
exports.queryOptions = queryOptions;
|
package/build/index.d.cts
CHANGED
|
@@ -23,10 +23,12 @@ export { useQuery } from './_tsup-dts-rollup.cjs';
|
|
|
23
23
|
export { queryOptions } from './_tsup-dts-rollup.cjs';
|
|
24
24
|
export { DefinedInitialDataOptions } from './_tsup-dts-rollup.cjs';
|
|
25
25
|
export { UndefinedInitialDataOptions } from './_tsup-dts-rollup.cjs';
|
|
26
|
+
export { FLIGHT_DATA_SOURCE_alias_1 as FLIGHT_DATA_SOURCE } from './_tsup-dts-rollup.cjs';
|
|
26
27
|
export { QueryClientContext_alias_1 as QueryClientContext } from './_tsup-dts-rollup.cjs';
|
|
27
28
|
export { QueryClientProvider_alias_1 as QueryClientProvider } from './_tsup-dts-rollup.cjs';
|
|
28
29
|
export { useQueryClient_alias_1 as useQueryClient } from './_tsup-dts-rollup.cjs';
|
|
29
30
|
export { QueryClientProviderProps_alias_1 as QueryClientProviderProps } from './_tsup-dts-rollup.cjs';
|
|
31
|
+
export { dehydrateSettled_alias_1 as dehydrateSettled } from './_tsup-dts-rollup.cjs';
|
|
30
32
|
export { useIsFetching } from './_tsup-dts-rollup.cjs';
|
|
31
33
|
export { useInfiniteQuery } from './_tsup-dts-rollup.cjs';
|
|
32
34
|
export { infiniteQueryOptions } from './_tsup-dts-rollup.cjs';
|
package/build/index.d.ts
CHANGED
|
@@ -23,10 +23,12 @@ export { useQuery } from './_tsup-dts-rollup.js';
|
|
|
23
23
|
export { queryOptions } from './_tsup-dts-rollup.js';
|
|
24
24
|
export { DefinedInitialDataOptions } from './_tsup-dts-rollup.js';
|
|
25
25
|
export { UndefinedInitialDataOptions } from './_tsup-dts-rollup.js';
|
|
26
|
+
export { FLIGHT_DATA_SOURCE_alias_1 as FLIGHT_DATA_SOURCE } from './_tsup-dts-rollup.js';
|
|
26
27
|
export { QueryClientContext_alias_1 as QueryClientContext } from './_tsup-dts-rollup.js';
|
|
27
28
|
export { QueryClientProvider_alias_1 as QueryClientProvider } from './_tsup-dts-rollup.js';
|
|
28
29
|
export { useQueryClient_alias_1 as useQueryClient } from './_tsup-dts-rollup.js';
|
|
29
30
|
export { QueryClientProviderProps_alias_1 as QueryClientProviderProps } from './_tsup-dts-rollup.js';
|
|
31
|
+
export { dehydrateSettled_alias_1 as dehydrateSettled } from './_tsup-dts-rollup.js';
|
|
30
32
|
export { useIsFetching } from './_tsup-dts-rollup.js';
|
|
31
33
|
export { useInfiniteQuery } from './_tsup-dts-rollup.js';
|
|
32
34
|
export { infiniteQueryOptions } from './_tsup-dts-rollup.js';
|
package/build/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { noop, QueryClient as QueryClient$1, shouldThrowError, InfiniteQueryObserver,
|
|
1
|
+
import { noop, QueryClient as QueryClient$1, hydrate, dehydrate, shouldThrowError, InfiniteQueryObserver, QueryObserver, replaceEqualDeep } from '@tanstack/query-core';
|
|
2
2
|
export * from '@tanstack/query-core';
|
|
3
3
|
import { createContext, useContext, onCleanup, createMemo, createOptimistic, createSignal, action, createRenderEffect, untrack, repeat, $TRACK, sharedConfig, createEffect, createProjection, runWithOwner, isPending, resolve } from 'solid-js';
|
|
4
|
-
import { createComponent } from '@solidjs/web';
|
|
4
|
+
import { createComponent, REVALIDATE_HEADER } from '@solidjs/web';
|
|
5
|
+
import { subscribeFlightData } from '@solidjs/web/server-functions';
|
|
5
6
|
|
|
6
7
|
// src/index.ts
|
|
7
8
|
var QueryClient = class extends QueryClient$1 {
|
|
@@ -11,6 +12,18 @@ var QueryClient = class extends QueryClient$1 {
|
|
|
11
12
|
};
|
|
12
13
|
var isServer = typeof window === "undefined";
|
|
13
14
|
var HYDRATION_KEY_PREFIX = "sq:";
|
|
15
|
+
var FLIGHT_DATA_SOURCE = "sq";
|
|
16
|
+
function sweepRevalidatedQueries(client, payload, response) {
|
|
17
|
+
const keys = response.headers.get(REVALIDATE_HEADER)?.split(",");
|
|
18
|
+
if (!keys) return;
|
|
19
|
+
const covered = new Set(payload.queries.map((query) => query.queryHash));
|
|
20
|
+
for (const key of keys) {
|
|
21
|
+
client.invalidateQueries({
|
|
22
|
+
queryKey: [key],
|
|
23
|
+
predicate: (query) => !covered.has(query.queryHash)
|
|
24
|
+
}).catch(() => void 0);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
14
27
|
var QueryClientContext = createContext(null);
|
|
15
28
|
var useQueryClient = (queryClient) => {
|
|
16
29
|
if (queryClient) {
|
|
@@ -26,9 +39,11 @@ function serializeCacheOnServer(client) {
|
|
|
26
39
|
const ctx = sharedConfig.context;
|
|
27
40
|
if (!ctx || !ctx.async || ctx.noHydrate) return;
|
|
28
41
|
const cache = client.getQueryCache();
|
|
42
|
+
const shouldDehydrateQuery = client.getDefaultOptions().dehydrate?.shouldDehydrateQuery;
|
|
29
43
|
const seen = /* @__PURE__ */ new Set();
|
|
30
44
|
const serializeQuery = (query) => {
|
|
31
45
|
if (seen.has(query.queryHash)) return;
|
|
46
|
+
if (shouldDehydrateQuery && !shouldDehydrateQuery(query)) return;
|
|
32
47
|
const state = query.state;
|
|
33
48
|
if (state.status === "success") {
|
|
34
49
|
seen.add(query.queryHash);
|
|
@@ -52,7 +67,18 @@ function serializeCacheOnServer(client) {
|
|
|
52
67
|
var QueryClientProvider = (props) => {
|
|
53
68
|
props.client.mount();
|
|
54
69
|
onCleanup(() => props.client.unmount());
|
|
55
|
-
if (isServer)
|
|
70
|
+
if (isServer) {
|
|
71
|
+
serializeCacheOnServer(props.client);
|
|
72
|
+
onCleanup(() => {
|
|
73
|
+
props.client.cancelQueries().catch(() => void 0);
|
|
74
|
+
props.client.clear();
|
|
75
|
+
});
|
|
76
|
+
} else {
|
|
77
|
+
onCleanup(subscribeFlightData(FLIGHT_DATA_SOURCE, (data, context) => {
|
|
78
|
+
hydrate(props.client, data);
|
|
79
|
+
sweepRevalidatedQueries(props.client, data, context.response);
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
56
82
|
return createComponent(QueryClientContext, {
|
|
57
83
|
value: () => props.client,
|
|
58
84
|
get children() {
|
|
@@ -431,6 +457,15 @@ function useQuery(options, queryClient) {
|
|
|
431
457
|
function queryOptions(options) {
|
|
432
458
|
return options;
|
|
433
459
|
}
|
|
460
|
+
async function dehydrateSettled(client, options) {
|
|
461
|
+
const cache = client.getQueryCache();
|
|
462
|
+
for (; ; ) {
|
|
463
|
+
const pending = cache.getAll().filter((query) => query.state.fetchStatus !== "idle").map((query) => query.promise);
|
|
464
|
+
if (pending.length === 0) break;
|
|
465
|
+
await Promise.allSettled(pending);
|
|
466
|
+
}
|
|
467
|
+
return dehydrate(client, options);
|
|
468
|
+
}
|
|
434
469
|
var isServer3 = typeof window === "undefined";
|
|
435
470
|
function createCacheAggregate(subscribe, read, empty) {
|
|
436
471
|
const hydratedMount = !isServer3 && sharedConfig.hydrating === true;
|
|
@@ -591,15 +626,13 @@ function useMutation(options, queryClient) {
|
|
|
591
626
|
const [flight, setFlight] = createOptimistic(null);
|
|
592
627
|
const [settled, setSettled] = createSignal(IDLE);
|
|
593
628
|
let activeMutation = null;
|
|
629
|
+
let unsubscribeFlight = null;
|
|
594
630
|
const [flightVersion, setFlightVersion] = createSignal(0);
|
|
595
|
-
if (!isServer4)
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
}
|
|
631
|
+
if (!isServer4)
|
|
632
|
+
onCleanup(() => {
|
|
633
|
+
unsubscribeFlight?.();
|
|
634
|
+
unsubscribeFlight = null;
|
|
600
635
|
});
|
|
601
|
-
onCleanup(unsubscribe);
|
|
602
|
-
}
|
|
603
636
|
async function* run(variables) {
|
|
604
637
|
const opts = untrack(options);
|
|
605
638
|
const callbackContext = {
|
|
@@ -609,7 +642,8 @@ function useMutation(options, queryClient) {
|
|
|
609
642
|
};
|
|
610
643
|
if (!isServer4) setFlight({ variables });
|
|
611
644
|
opts.onMutate?.(variables, callbackContext);
|
|
612
|
-
const
|
|
645
|
+
const c = untrack(client);
|
|
646
|
+
const mutation = c.getMutationCache().build(c, {
|
|
613
647
|
...opts,
|
|
614
648
|
// Options-level callbacks re-run below, inside the transaction —
|
|
615
649
|
// executing them here (inside the await gap) would let their
|
|
@@ -620,6 +654,20 @@ function useMutation(options, queryClient) {
|
|
|
620
654
|
onSettled: void 0
|
|
621
655
|
});
|
|
622
656
|
activeMutation = mutation;
|
|
657
|
+
if (!isServer4) {
|
|
658
|
+
unsubscribeFlight?.();
|
|
659
|
+
unsubscribeFlight = c.getMutationCache().subscribe((event) => {
|
|
660
|
+
if ("mutation" in event && event.mutation === activeMutation) {
|
|
661
|
+
setFlightVersion((v) => v + 1);
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
const settleFlight = () => {
|
|
666
|
+
if (activeMutation !== mutation) return;
|
|
667
|
+
activeMutation = null;
|
|
668
|
+
unsubscribeFlight?.();
|
|
669
|
+
unsubscribeFlight = null;
|
|
670
|
+
};
|
|
623
671
|
async function* settleFailure(error) {
|
|
624
672
|
try {
|
|
625
673
|
if (opts.onError)
|
|
@@ -647,7 +695,7 @@ function useMutation(options, queryClient) {
|
|
|
647
695
|
variables,
|
|
648
696
|
submittedAt: mutation.state.submittedAt
|
|
649
697
|
});
|
|
650
|
-
|
|
698
|
+
settleFlight();
|
|
651
699
|
throw error;
|
|
652
700
|
}
|
|
653
701
|
let result2;
|
|
@@ -687,7 +735,7 @@ function useMutation(options, queryClient) {
|
|
|
687
735
|
variables,
|
|
688
736
|
submittedAt: mutation.state.submittedAt
|
|
689
737
|
});
|
|
690
|
-
|
|
738
|
+
settleFlight();
|
|
691
739
|
return result2;
|
|
692
740
|
}
|
|
693
741
|
const invoke = isServer4 ? (variables) => drain(run(variables)) : action(run);
|
|
@@ -825,4 +873,4 @@ function useQueries(queriesOptions, queryClient) {
|
|
|
825
873
|
});
|
|
826
874
|
}
|
|
827
875
|
|
|
828
|
-
export { IsRestoringContext, QueryClient, QueryClientContext, QueryClientProvider, infiniteQueryOptions, mutationOptions, queryOptions, useInfiniteQuery, useIsFetching, useIsMutating, useIsRestoring, useMutation, useMutationState, useQueries, useQuery, useQueryClient };
|
|
876
|
+
export { FLIGHT_DATA_SOURCE, IsRestoringContext, QueryClient, QueryClientContext, QueryClientProvider, dehydrateSettled, infiniteQueryOptions, mutationOptions, queryOptions, useInfiniteQuery, useIsFetching, useIsMutating, useIsRestoring, useMutation, useMutationState, useQueries, useQuery, useQueryClient };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/solid-query",
|
|
3
|
-
"version": "6.0.0-rc.
|
|
3
|
+
"version": "6.0.0-rc.3",
|
|
4
4
|
"description": "Primitives for managing, caching and syncing asynchronous and remote data in Solid",
|
|
5
5
|
"author": "tannerlinsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -53,15 +53,16 @@
|
|
|
53
53
|
"@babel/preset-typescript": "^7.18.6",
|
|
54
54
|
"@solidjs/testing-library": "^0.8.10",
|
|
55
55
|
"@solidjs/vite-plugin": "^3.0.0-next.27",
|
|
56
|
-
"@solidjs/web": "^2.0.0-rc.
|
|
56
|
+
"@solidjs/web": "^2.0.0-rc.6",
|
|
57
57
|
"babel-preset-solid": "^2.0.0-rc.2",
|
|
58
58
|
"npm-run-all2": "^5.0.0",
|
|
59
|
-
"solid-js": "^2.0.0-rc.
|
|
59
|
+
"solid-js": "^2.0.0-rc.6",
|
|
60
60
|
"tsup-preset-solid": "^2.2.0",
|
|
61
61
|
"@tanstack/query-test-utils": "0.0.0"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
|
-
"
|
|
64
|
+
"@solidjs/web": ">=2.0.0-rc.6 <3.0.0",
|
|
65
|
+
"solid-js": ">=2.0.0-rc.6 <3.0.0"
|
|
65
66
|
},
|
|
66
67
|
"scripts": {
|
|
67
68
|
"clean": "premove ./build ./coverage ./dist-ts",
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { createContext, onCleanup, sharedConfig, useContext } from 'solid-js'
|
|
2
|
-
import
|
|
2
|
+
import { REVALIDATE_HEADER } from '@solidjs/web'
|
|
3
|
+
import { hydrate } from '@tanstack/query-core'
|
|
4
|
+
import { subscribeFlightData } from '@solidjs/web/server-functions'
|
|
5
|
+
import type { DehydratedState, Query } from '@tanstack/query-core'
|
|
3
6
|
import type { QueryClient } from './QueryClient'
|
|
4
7
|
import type { JSX } from '@solidjs/web'
|
|
5
8
|
|
|
@@ -13,6 +16,73 @@ const isServer = typeof window === 'undefined'
|
|
|
13
16
|
*/
|
|
14
17
|
export const HYDRATION_KEY_PREFIX = 'sq:'
|
|
15
18
|
|
|
19
|
+
/**
|
|
20
|
+
* The query cache's single-flight source id. Mutation responses can fold
|
|
21
|
+
* fresh data for multiple caches at once (Solid's multi-source
|
|
22
|
+
* single-flight protocol); this is the slice the query cache claims — the
|
|
23
|
+
* provider subscribes its consumer under it, and server collectors
|
|
24
|
+
* register under it to produce the data:
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { registerFlightDataSource } from '@solidjs/web/server-functions/server'
|
|
28
|
+
* import { FLIGHT_DATA_SOURCE, dehydrate } from '@tanstack/solid-query'
|
|
29
|
+
*
|
|
30
|
+
* registerFlightDataSource(FLIGHT_DATA_SOURCE, async (event, outcome) => {
|
|
31
|
+
* // rebuild the data for outcome.targetUrl into a QueryClient, then
|
|
32
|
+
* return dehydrate(queryClient)
|
|
33
|
+
* })
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* The slice's payload is a `DehydratedState`; the provider consumes it
|
|
37
|
+
* with `hydrate()`, so every mounted query on those keys updates before
|
|
38
|
+
* the mutation's promise resolves — no follow-up refetches.
|
|
39
|
+
*
|
|
40
|
+
* A mutation can additionally declare its invalidation SCOPE with
|
|
41
|
+
* `X-Revalidate` keys (the `revalidate` option of Solid's `reload`/
|
|
42
|
+
* `redirect` response helpers). The payload covers the slice of that scope
|
|
43
|
+
* the server could recompute; whatever a declared key matches beyond it is
|
|
44
|
+
* swept client-side (see the consumer below). For the sweep to be
|
|
45
|
+
* delivered, the collector must contribute a slice — return the dehydrated
|
|
46
|
+
* state even when it holds no queries if `outcome.revalidateKeys` is
|
|
47
|
+
* present, rather than skipping the source.
|
|
48
|
+
*/
|
|
49
|
+
export const FLIGHT_DATA_SOURCE = 'sq'
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The client half of key-scoped invalidation. A mutation declares its
|
|
53
|
+
* invalidation scope with `X-Revalidate` keys; the flight payload covers
|
|
54
|
+
* the part of that scope the server's collector recomputed (typically the
|
|
55
|
+
* target page's loader graph). Whatever a declared key matches BEYOND the
|
|
56
|
+
* payload — parameterized instances only this client holds, queries no
|
|
57
|
+
* loader owns — is invalidated here: active queries refetch in the
|
|
58
|
+
* background, inactive ones are marked stale for their next mount. This
|
|
59
|
+
* mirrors how Solid Router consumes the same header for its own cache.
|
|
60
|
+
*
|
|
61
|
+
* A key matches by queryKey prefix — `revalidate: 'users'` sweeps every
|
|
62
|
+
* query whose key begins with `'users'`. Payload-covered hashes are exempt:
|
|
63
|
+
* they hydrated with fresh data a moment ago, and refetching them would
|
|
64
|
+
* spend the round trip single flight just saved.
|
|
65
|
+
*/
|
|
66
|
+
function sweepRevalidatedQueries(
|
|
67
|
+
client: QueryClient,
|
|
68
|
+
payload: DehydratedState,
|
|
69
|
+
response: Response,
|
|
70
|
+
): void {
|
|
71
|
+
const keys = response.headers.get(REVALIDATE_HEADER)?.split(',')
|
|
72
|
+
if (!keys) return
|
|
73
|
+
const covered = new Set(payload.queries.map((query) => query.queryHash))
|
|
74
|
+
for (const key of keys) {
|
|
75
|
+
client
|
|
76
|
+
.invalidateQueries({
|
|
77
|
+
queryKey: [key],
|
|
78
|
+
predicate: (query) => !covered.has(query.queryHash),
|
|
79
|
+
})
|
|
80
|
+
// Background refetch failures surface through the queries' own error
|
|
81
|
+
// states; an unhandled rejection here would fail the mutation instead.
|
|
82
|
+
.catch(() => undefined)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
16
86
|
export const QueryClientContext = createContext<(() => QueryClient) | null>(
|
|
17
87
|
null,
|
|
18
88
|
)
|
|
@@ -64,9 +134,18 @@ function serializeCacheOnServer(client: QueryClient): void {
|
|
|
64
134
|
if (!ctx || !ctx.async || ctx.noHydrate) return
|
|
65
135
|
|
|
66
136
|
const cache = client.getQueryCache()
|
|
137
|
+
// The standard dehydrate filter gates the wire here too, so apps keep
|
|
138
|
+
// sensitive or oversized queries out of the HTML with the same option
|
|
139
|
+
// they'd pass any other transport.
|
|
140
|
+
const shouldDehydrateQuery =
|
|
141
|
+
client.getDefaultOptions().dehydrate?.shouldDehydrateQuery
|
|
67
142
|
const seen = new Set<string>()
|
|
68
143
|
const serializeQuery = (query: Query<any, any, any, any>) => {
|
|
69
144
|
if (seen.has(query.queryHash)) return
|
|
145
|
+
// Consulted per cache event until it passes, so a filter that rejects
|
|
146
|
+
// pending queries (e.g. the core default) still admits the settled
|
|
147
|
+
// value if it lands while the request's serialization context is live.
|
|
148
|
+
if (shouldDehydrateQuery && !shouldDehydrateQuery(query)) return
|
|
70
149
|
const state = query.state
|
|
71
150
|
if (state.status === 'success') {
|
|
72
151
|
seen.add(query.queryHash)
|
|
@@ -97,15 +176,47 @@ function serializeCacheOnServer(client: QueryClient): void {
|
|
|
97
176
|
/**
|
|
98
177
|
* Provides the QueryClient and manages its mount lifecycle. On the server
|
|
99
178
|
* it also registers the cache serializer above; on the client, hooks prime
|
|
100
|
-
* the cache from their hash-keyed registry entries themselves
|
|
101
|
-
* `useBaseQuery
|
|
179
|
+
* the cache from their hash-keyed registry entries themselves (see
|
|
180
|
+
* `useBaseQuery`) and the provider subscribes the cache's single-flight
|
|
181
|
+
* consumer: mutation responses carrying a `FLIGHT_DATA_SOURCE` slice (a
|
|
182
|
+
* `DehydratedState` produced by a server collector registered under the
|
|
183
|
+
* same id) hydrate this client before the mutation's promise resolves.
|
|
184
|
+
* Subscribing is inert when no server collector exists — the server just
|
|
185
|
+
* folds nothing — so it is unconditional. One consumer per source: with
|
|
186
|
+
* nested providers, the innermost mounted one owns the slice.
|
|
102
187
|
*/
|
|
103
188
|
export const QueryClientProvider = (
|
|
104
189
|
props: QueryClientProviderProps,
|
|
105
190
|
): JSX.Element => {
|
|
106
191
|
props.client.mount()
|
|
107
192
|
onCleanup(() => props.client.unmount())
|
|
108
|
-
if (isServer)
|
|
193
|
+
if (isServer) {
|
|
194
|
+
serializeCacheOnServer(props.client)
|
|
195
|
+
// Render disposal ends the request: abort what's still in flight and
|
|
196
|
+
// drop the cache so user-configured finite gcTime timers can't pin
|
|
197
|
+
// the per-request client (and whatever its queries closed over) until
|
|
198
|
+
// they fire. Serialized promises are already in seroval's hands, so
|
|
199
|
+
// clearing here can't affect the streamed payload.
|
|
200
|
+
onCleanup(() => {
|
|
201
|
+
props.client.cancelQueries().catch(() => undefined)
|
|
202
|
+
props.client.clear()
|
|
203
|
+
})
|
|
204
|
+
} else {
|
|
205
|
+
// Client-only: the server's consumer registry is module state shared
|
|
206
|
+
// across requests — registering there would leak between them.
|
|
207
|
+
onCleanup(
|
|
208
|
+
subscribeFlightData<DehydratedState>(
|
|
209
|
+
FLIGHT_DATA_SOURCE,
|
|
210
|
+
(data, context) => {
|
|
211
|
+
hydrate(props.client, data)
|
|
212
|
+
// Fresh data first, then the declared-scope sweep: stale marks
|
|
213
|
+
// land synchronously, refetches run in the background — the
|
|
214
|
+
// mutation's promise never waits on them.
|
|
215
|
+
sweepRevalidatedQueries(props.client, data, context.response)
|
|
216
|
+
},
|
|
217
|
+
),
|
|
218
|
+
)
|
|
219
|
+
}
|
|
109
220
|
|
|
110
221
|
return (
|
|
111
222
|
<QueryClientContext value={() => props.client}>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { dehydrate } from '@tanstack/query-core'
|
|
2
|
+
import type { DehydrateOptions, DehydratedState } from '@tanstack/query-core'
|
|
3
|
+
import type { QueryClient } from './QueryClient'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Waits for every fetch the client has in flight to settle, then
|
|
7
|
+
* dehydrates. This is the extraction half of a single-flight collector:
|
|
8
|
+
* after route data functions run for the mutation's target URL, loaders
|
|
9
|
+
* commonly kick off prefetches without awaiting them — plain
|
|
10
|
+
* `dehydrate()` would snapshot those mid-fetch and ship nothing.
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* registerFlightDataSource(FLIGHT_DATA_SOURCE, (event, outcome) =>
|
|
14
|
+
* loadFlightTarget({
|
|
15
|
+
* router,
|
|
16
|
+
* event,
|
|
17
|
+
* outcome,
|
|
18
|
+
* collect: () => dehydrateSettled(queryClient),
|
|
19
|
+
* }),
|
|
20
|
+
* )
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* Settling is chased to quiescence — awaiting one batch of fetches can
|
|
24
|
+
* dispatch more (dependent queries keyed off a first result) — so an
|
|
25
|
+
* unconditionally self-refetching query would keep this pending; that's
|
|
26
|
+
* an app bug mirrored, not guarded.
|
|
27
|
+
*/
|
|
28
|
+
export async function dehydrateSettled(
|
|
29
|
+
client: QueryClient,
|
|
30
|
+
options?: DehydrateOptions,
|
|
31
|
+
): Promise<DehydratedState> {
|
|
32
|
+
const cache = client.getQueryCache()
|
|
33
|
+
for (;;) {
|
|
34
|
+
const pending = cache
|
|
35
|
+
.getAll()
|
|
36
|
+
.filter((query) => query.state.fetchStatus !== 'idle')
|
|
37
|
+
.map((query) => query.promise)
|
|
38
|
+
if (pending.length === 0) break
|
|
39
|
+
await Promise.allSettled(pending)
|
|
40
|
+
}
|
|
41
|
+
return dehydrate(client, options)
|
|
42
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -39,11 +39,13 @@ export type {
|
|
|
39
39
|
UndefinedInitialDataOptions,
|
|
40
40
|
} from './queryOptions'
|
|
41
41
|
export {
|
|
42
|
+
FLIGHT_DATA_SOURCE,
|
|
42
43
|
QueryClientContext,
|
|
43
44
|
QueryClientProvider,
|
|
44
45
|
useQueryClient,
|
|
45
46
|
} from './QueryClientProvider'
|
|
46
47
|
export type { QueryClientProviderProps } from './QueryClientProvider'
|
|
48
|
+
export { dehydrateSettled } from './dehydrateSettled'
|
|
47
49
|
export { useIsFetching } from './useIsFetching'
|
|
48
50
|
export { useInfiniteQuery } from './useInfiniteQuery'
|
|
49
51
|
export { infiniteQueryOptions } from './infiniteQueryOptions'
|
package/src/useMutation.ts
CHANGED
|
@@ -114,20 +114,20 @@ export function useMutation<
|
|
|
114
114
|
* Flight metadata (retry failures, offline pause) lives on the Mutation
|
|
115
115
|
* instance and changes during the await gap; a version bump per cache
|
|
116
116
|
* event for the active mutation keeps reads current without cloning
|
|
117
|
-
* observer results.
|
|
117
|
+
* observer results. The subscription is per-flight, created in `run`
|
|
118
|
+
* against the same client the mutation is built on: nothing reactive is
|
|
119
|
+
* read at setup (no client read to untrack), the listener and the
|
|
120
|
+
* mutation can never sit on different clients, and an idle hook holds no
|
|
121
|
+
* cache subscription at all.
|
|
118
122
|
*/
|
|
119
123
|
let activeMutation: Mutation<TData, TError, TVariables> | null = null
|
|
124
|
+
let unsubscribeFlight: (() => void) | null = null
|
|
120
125
|
const [flightVersion, setFlightVersion] = createSignal(0)
|
|
121
|
-
if (!isServer)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
setFlightVersion((v) => v + 1)
|
|
127
|
-
}
|
|
128
|
-
})
|
|
129
|
-
onCleanup(unsubscribe)
|
|
130
|
-
}
|
|
126
|
+
if (!isServer)
|
|
127
|
+
onCleanup(() => {
|
|
128
|
+
unsubscribeFlight?.()
|
|
129
|
+
unsubscribeFlight = null
|
|
130
|
+
})
|
|
131
131
|
|
|
132
132
|
async function* run(
|
|
133
133
|
variables: TVariables,
|
|
@@ -144,19 +144,33 @@ export function useMutation<
|
|
|
144
144
|
if (!isServer) setFlight({ variables })
|
|
145
145
|
opts.onMutate?.(variables, callbackContext)
|
|
146
146
|
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}) as unknown as Mutation<TData, TError, TVariables>
|
|
147
|
+
const c = untrack(client)
|
|
148
|
+
const mutation = c.getMutationCache().build(c, {
|
|
149
|
+
...opts,
|
|
150
|
+
// Options-level callbacks re-run below, inside the transaction —
|
|
151
|
+
// executing them here (inside the await gap) would let their
|
|
152
|
+
// cache writes and invalidations escape the atomic settle.
|
|
153
|
+
onMutate: undefined,
|
|
154
|
+
onSuccess: undefined,
|
|
155
|
+
onError: undefined,
|
|
156
|
+
onSettled: undefined,
|
|
157
|
+
}) as unknown as Mutation<TData, TError, TVariables>
|
|
159
158
|
activeMutation = mutation
|
|
159
|
+
if (!isServer) {
|
|
160
|
+
// A rapid re-mutate replaces the previous flight's listener.
|
|
161
|
+
unsubscribeFlight?.()
|
|
162
|
+
unsubscribeFlight = c.getMutationCache().subscribe((event) => {
|
|
163
|
+
if ('mutation' in event && event.mutation === activeMutation) {
|
|
164
|
+
setFlightVersion((v) => v + 1)
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
const settleFlight = () => {
|
|
169
|
+
if (activeMutation !== mutation) return
|
|
170
|
+
activeMutation = null
|
|
171
|
+
unsubscribeFlight?.()
|
|
172
|
+
unsubscribeFlight = null
|
|
173
|
+
}
|
|
160
174
|
|
|
161
175
|
/**
|
|
162
176
|
* Failure settle, shared by both paths. Callback routing mirrors
|
|
@@ -202,7 +216,7 @@ export function useMutation<
|
|
|
202
216
|
variables,
|
|
203
217
|
submittedAt: mutation.state.submittedAt,
|
|
204
218
|
})
|
|
205
|
-
|
|
219
|
+
settleFlight()
|
|
206
220
|
throw error
|
|
207
221
|
}
|
|
208
222
|
|
|
@@ -250,7 +264,7 @@ export function useMutation<
|
|
|
250
264
|
variables,
|
|
251
265
|
submittedAt: mutation.state.submittedAt,
|
|
252
266
|
})
|
|
253
|
-
|
|
267
|
+
settleFlight()
|
|
254
268
|
return result
|
|
255
269
|
}
|
|
256
270
|
|