@tanstack/solid-query 6.0.0-rc.2 → 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 +9 -0
- package/build/_tsup-dts-rollup.d.ts +9 -0
- package/build/dev.cjs +13 -1
- package/build/dev.js +14 -2
- package/build/index.cjs +13 -1
- package/build/index.js +14 -2
- package/package.json +1 -1
- package/src/QueryClientProvider.tsx +55 -3
|
@@ -318,6 +318,15 @@ export { FetchStatus }
|
|
|
318
318
|
* The slice's payload is a `DehydratedState`; the provider consumes it
|
|
319
319
|
* with `hydrate()`, so every mounted query on those keys updates before
|
|
320
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.
|
|
321
330
|
*/
|
|
322
331
|
declare const FLIGHT_DATA_SOURCE = "sq";
|
|
323
332
|
export { FLIGHT_DATA_SOURCE }
|
|
@@ -318,6 +318,15 @@ export { FetchStatus }
|
|
|
318
318
|
* The slice's payload is a `DehydratedState`; the provider consumes it
|
|
319
319
|
* with `hydrate()`, so every mounted query on those keys updates before
|
|
320
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.
|
|
321
330
|
*/
|
|
322
331
|
declare const FLIGHT_DATA_SOURCE = "sq";
|
|
323
332
|
export { FLIGHT_DATA_SOURCE }
|
package/build/dev.cjs
CHANGED
|
@@ -14,6 +14,17 @@ exports.QueryClient = class QueryClient extends queryCore.QueryClient {
|
|
|
14
14
|
var isServer = typeof window === "undefined";
|
|
15
15
|
var HYDRATION_KEY_PREFIX = "sq:";
|
|
16
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
|
+
}
|
|
17
28
|
exports.QueryClientContext = solidJs.createContext(null);
|
|
18
29
|
exports.useQueryClient = (queryClient) => {
|
|
19
30
|
if (queryClient) {
|
|
@@ -64,8 +75,9 @@ exports.QueryClientProvider = (props) => {
|
|
|
64
75
|
props.client.clear();
|
|
65
76
|
});
|
|
66
77
|
} else {
|
|
67
|
-
solidJs.onCleanup(serverFunctions.subscribeFlightData(exports.FLIGHT_DATA_SOURCE, (data) => {
|
|
78
|
+
solidJs.onCleanup(serverFunctions.subscribeFlightData(exports.FLIGHT_DATA_SOURCE, (data, context) => {
|
|
68
79
|
queryCore.hydrate(props.client, data);
|
|
80
|
+
sweepRevalidatedQueries(props.client, data, context.response);
|
|
69
81
|
}));
|
|
70
82
|
}
|
|
71
83
|
return web.createComponent(exports.QueryClientContext, {
|
package/build/dev.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
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
5
|
import { subscribeFlightData } from '@solidjs/web/server-functions';
|
|
6
6
|
|
|
7
7
|
// src/index.ts
|
|
@@ -13,6 +13,17 @@ var QueryClient = class extends QueryClient$1 {
|
|
|
13
13
|
var isServer = typeof window === "undefined";
|
|
14
14
|
var HYDRATION_KEY_PREFIX = "sq:";
|
|
15
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
|
+
}
|
|
16
27
|
var QueryClientContext = createContext(null);
|
|
17
28
|
var useQueryClient = (queryClient) => {
|
|
18
29
|
if (queryClient) {
|
|
@@ -63,8 +74,9 @@ var QueryClientProvider = (props) => {
|
|
|
63
74
|
props.client.clear();
|
|
64
75
|
});
|
|
65
76
|
} else {
|
|
66
|
-
onCleanup(subscribeFlightData(FLIGHT_DATA_SOURCE, (data) => {
|
|
77
|
+
onCleanup(subscribeFlightData(FLIGHT_DATA_SOURCE, (data, context) => {
|
|
67
78
|
hydrate(props.client, data);
|
|
79
|
+
sweepRevalidatedQueries(props.client, data, context.response);
|
|
68
80
|
}));
|
|
69
81
|
}
|
|
70
82
|
return createComponent(QueryClientContext, {
|
package/build/index.cjs
CHANGED
|
@@ -14,6 +14,17 @@ exports.QueryClient = class QueryClient extends queryCore.QueryClient {
|
|
|
14
14
|
var isServer = typeof window === "undefined";
|
|
15
15
|
var HYDRATION_KEY_PREFIX = "sq:";
|
|
16
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
|
+
}
|
|
17
28
|
exports.QueryClientContext = solidJs.createContext(null);
|
|
18
29
|
exports.useQueryClient = (queryClient) => {
|
|
19
30
|
if (queryClient) {
|
|
@@ -64,8 +75,9 @@ exports.QueryClientProvider = (props) => {
|
|
|
64
75
|
props.client.clear();
|
|
65
76
|
});
|
|
66
77
|
} else {
|
|
67
|
-
solidJs.onCleanup(serverFunctions.subscribeFlightData(exports.FLIGHT_DATA_SOURCE, (data) => {
|
|
78
|
+
solidJs.onCleanup(serverFunctions.subscribeFlightData(exports.FLIGHT_DATA_SOURCE, (data, context) => {
|
|
68
79
|
queryCore.hydrate(props.client, data);
|
|
80
|
+
sweepRevalidatedQueries(props.client, data, context.response);
|
|
69
81
|
}));
|
|
70
82
|
}
|
|
71
83
|
return web.createComponent(exports.QueryClientContext, {
|
package/build/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
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
5
|
import { subscribeFlightData } from '@solidjs/web/server-functions';
|
|
6
6
|
|
|
7
7
|
// src/index.ts
|
|
@@ -13,6 +13,17 @@ var QueryClient = class extends QueryClient$1 {
|
|
|
13
13
|
var isServer = typeof window === "undefined";
|
|
14
14
|
var HYDRATION_KEY_PREFIX = "sq:";
|
|
15
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
|
+
}
|
|
16
27
|
var QueryClientContext = createContext(null);
|
|
17
28
|
var useQueryClient = (queryClient) => {
|
|
18
29
|
if (queryClient) {
|
|
@@ -63,8 +74,9 @@ var QueryClientProvider = (props) => {
|
|
|
63
74
|
props.client.clear();
|
|
64
75
|
});
|
|
65
76
|
} else {
|
|
66
|
-
onCleanup(subscribeFlightData(FLIGHT_DATA_SOURCE, (data) => {
|
|
77
|
+
onCleanup(subscribeFlightData(FLIGHT_DATA_SOURCE, (data, context) => {
|
|
67
78
|
hydrate(props.client, data);
|
|
79
|
+
sweepRevalidatedQueries(props.client, data, context.response);
|
|
68
80
|
}));
|
|
69
81
|
}
|
|
70
82
|
return createComponent(QueryClientContext, {
|
package/package.json
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createContext, onCleanup, sharedConfig, useContext } from 'solid-js'
|
|
2
|
+
import { REVALIDATE_HEADER } from '@solidjs/web'
|
|
2
3
|
import { hydrate } from '@tanstack/query-core'
|
|
3
4
|
import { subscribeFlightData } from '@solidjs/web/server-functions'
|
|
4
5
|
import type { DehydratedState, Query } from '@tanstack/query-core'
|
|
@@ -35,9 +36,53 @@ export const HYDRATION_KEY_PREFIX = 'sq:'
|
|
|
35
36
|
* The slice's payload is a `DehydratedState`; the provider consumes it
|
|
36
37
|
* with `hydrate()`, so every mounted query on those keys updates before
|
|
37
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.
|
|
38
48
|
*/
|
|
39
49
|
export const FLIGHT_DATA_SOURCE = 'sq'
|
|
40
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
|
+
|
|
41
86
|
export const QueryClientContext = createContext<(() => QueryClient) | null>(
|
|
42
87
|
null,
|
|
43
88
|
)
|
|
@@ -160,9 +205,16 @@ export const QueryClientProvider = (
|
|
|
160
205
|
// Client-only: the server's consumer registry is module state shared
|
|
161
206
|
// across requests — registering there would leak between them.
|
|
162
207
|
onCleanup(
|
|
163
|
-
subscribeFlightData<DehydratedState>(
|
|
164
|
-
|
|
165
|
-
|
|
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
|
+
),
|
|
166
218
|
)
|
|
167
219
|
}
|
|
168
220
|
|