@tanstack/solid-query 6.0.0-rc.0 → 6.0.0-rc.1
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 +15 -1
- package/build/_tsup-dts-rollup.d.cts +150 -157
- package/build/_tsup-dts-rollup.d.ts +150 -157
- package/build/dev.cjs +718 -559
- package/build/dev.d.cts +0 -22
- package/build/dev.d.ts +0 -22
- package/build/dev.js +721 -559
- package/build/index.cjs +718 -550
- package/build/index.d.cts +0 -22
- package/build/index.d.ts +0 -22
- package/build/index.js +721 -550
- package/package.json +6 -6
- package/src/QueryClient.ts +24 -45
- package/src/QueryClientProvider.tsx +77 -58
- package/src/cacheAggregate.ts +90 -0
- package/src/index.ts +1 -29
- package/src/types.ts +62 -44
- package/src/useBaseQuery.ts +674 -412
- package/src/useInfiniteQuery.ts +156 -4
- package/src/useIsFetching.ts +6 -16
- package/src/useIsMutating.ts +6 -16
- package/src/useMutation.ts +304 -57
- package/src/useMutationState.ts +9 -22
- package/src/useQueries.ts +61 -110
- package/src/hydrationChannel.ts +0 -260
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.1",
|
|
4
4
|
"description": "Primitives for managing, caching and syncing asynchronous and remote data in Solid",
|
|
5
5
|
"author": "tannerlinsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -46,22 +46,22 @@
|
|
|
46
46
|
"!src/__tests__"
|
|
47
47
|
],
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@tanstack/query-core": "5.101.
|
|
49
|
+
"@tanstack/query-core": "5.101.4"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@babel/core": "^7.28.0",
|
|
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.
|
|
57
|
-
"babel-preset-solid": "^2.0.0-rc.
|
|
56
|
+
"@solidjs/web": "^2.0.0-rc.3",
|
|
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.4",
|
|
60
60
|
"tsup-preset-solid": "^2.2.0",
|
|
61
61
|
"@tanstack/query-test-utils": "0.0.0"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
|
-
"solid-js": ">=2.0.0-rc.
|
|
64
|
+
"solid-js": ">=2.0.0-rc.4 <3.0.0"
|
|
65
65
|
},
|
|
66
66
|
"scripts": {
|
|
67
67
|
"clean": "premove ./build ./coverage ./dist-ts",
|
package/src/QueryClient.ts
CHANGED
|
@@ -9,63 +9,42 @@ import type {
|
|
|
9
9
|
QueryKey,
|
|
10
10
|
} from '@tanstack/query-core'
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Core observer options pass through unchanged. The old adapter omitted
|
|
14
|
+
* `structuralSharing` and replaced it with a store-level `reconcile`
|
|
15
|
+
* option; with reads derived directly from cache state, core's
|
|
16
|
+
* cache-level structural sharing is exactly what keeps the data memo
|
|
17
|
+
* referentially stable, so it is exposed again and `reconcile` is gone.
|
|
18
|
+
*/
|
|
19
|
+
export type QueryObserverOptions<
|
|
13
20
|
TQueryFnData = unknown,
|
|
14
21
|
TError = DefaultError,
|
|
15
22
|
TData = TQueryFnData,
|
|
16
23
|
TQueryData = TQueryFnData,
|
|
17
24
|
TQueryKey extends QueryKey = QueryKey,
|
|
18
25
|
TPageParam = never,
|
|
19
|
-
>
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
>,
|
|
28
|
-
'structuralSharing'
|
|
29
|
-
> {
|
|
30
|
-
/**
|
|
31
|
-
* Set this to a reconciliation key to enable reconciliation between query results.
|
|
32
|
-
* Set this to `false` to disable reconciliation between query results.
|
|
33
|
-
* Set this to a function which accepts the old and new data and returns resolved data of the same type to implement custom reconciliation logic.
|
|
34
|
-
* Defaults reconciliation to false.
|
|
35
|
-
*/
|
|
36
|
-
reconcile?:
|
|
37
|
-
| string
|
|
38
|
-
| false
|
|
39
|
-
| ((oldData: TData | undefined, newData: TData) => TData)
|
|
40
|
-
}
|
|
26
|
+
> = QueryCoreObserverOptions<
|
|
27
|
+
TQueryFnData,
|
|
28
|
+
TError,
|
|
29
|
+
TData,
|
|
30
|
+
TQueryData,
|
|
31
|
+
TQueryKey,
|
|
32
|
+
TPageParam
|
|
33
|
+
>
|
|
41
34
|
|
|
42
|
-
export
|
|
35
|
+
export type InfiniteQueryObserverOptions<
|
|
43
36
|
TQueryFnData = unknown,
|
|
44
37
|
TError = DefaultError,
|
|
45
38
|
TData = TQueryFnData,
|
|
46
39
|
TQueryKey extends QueryKey = QueryKey,
|
|
47
40
|
TPageParam = unknown,
|
|
48
|
-
>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
>,
|
|
56
|
-
'structuralSharing'
|
|
57
|
-
> {
|
|
58
|
-
/**
|
|
59
|
-
* Set this to a reconciliation key to enable reconciliation between query results.
|
|
60
|
-
* Set this to `false` to disable reconciliation between query results.
|
|
61
|
-
* Set this to a function which accepts the old and new data and returns resolved data of the same type to implement custom reconciliation logic.
|
|
62
|
-
* Defaults reconciliation to false.
|
|
63
|
-
*/
|
|
64
|
-
reconcile?:
|
|
65
|
-
| string
|
|
66
|
-
| false
|
|
67
|
-
| ((oldData: TData | undefined, newData: TData) => TData)
|
|
68
|
-
}
|
|
41
|
+
> = QueryCoreInfiniteQueryObserverOptions<
|
|
42
|
+
TQueryFnData,
|
|
43
|
+
TError,
|
|
44
|
+
TData,
|
|
45
|
+
TQueryKey,
|
|
46
|
+
TPageParam
|
|
47
|
+
>
|
|
69
48
|
|
|
70
49
|
export interface DefaultOptions<
|
|
71
50
|
TError = DefaultError,
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
createRenderEffect,
|
|
4
|
-
createSignal,
|
|
5
|
-
onCleanup,
|
|
6
|
-
useContext,
|
|
7
|
-
} from 'solid-js'
|
|
8
|
-
import {
|
|
9
|
-
HydrationCoordinatorContext,
|
|
10
|
-
createHydrationCoordinator,
|
|
11
|
-
createServerDehydrationChannel,
|
|
12
|
-
} from './hydrationChannel'
|
|
13
|
-
import type { DehydrationChannelYield } from './hydrationChannel'
|
|
1
|
+
import { createContext, onCleanup, sharedConfig, useContext } from 'solid-js'
|
|
2
|
+
import type { Query } from '@tanstack/query-core'
|
|
14
3
|
import type { QueryClient } from './QueryClient'
|
|
15
4
|
import type { JSX } from '@solidjs/web'
|
|
16
5
|
|
|
17
6
|
const isServer = typeof window === 'undefined'
|
|
18
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Namespace prefix for cache entries in Solid's hydration registry — the
|
|
10
|
+
* registry is shared with positional node ids and other libraries'
|
|
11
|
+
* content-addressed keys (Solid Router uses its own cache keys), so query
|
|
12
|
+
* hashes get their own prefix.
|
|
13
|
+
*/
|
|
14
|
+
export const HYDRATION_KEY_PREFIX = 'sq:'
|
|
15
|
+
|
|
19
16
|
export const QueryClientContext = createContext<(() => QueryClient) | null>(
|
|
20
17
|
null,
|
|
21
18
|
)
|
|
@@ -38,59 +35,81 @@ export type QueryClientProviderProps = {
|
|
|
38
35
|
children?: JSX.Element
|
|
39
36
|
}
|
|
40
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Server side of hydration: serialize every query the request touches into
|
|
40
|
+
* Solid's hydration registry, content-addressed by query hash (the Solid
|
|
41
|
+
* Router `query()` pattern). Serialization happens at fetch-DISPATCH time —
|
|
42
|
+
* synchronously, while the request's serialization context is live — by
|
|
43
|
+
* handing seroval the fetch promise; it streams the `{ data, t }` payload
|
|
44
|
+
* whenever the fetch settles. Settled entries (initialData, setQueryData,
|
|
45
|
+
* loader prefetches that already landed) serialize their value directly.
|
|
46
|
+
*
|
|
47
|
+
* Content addressing is what makes this cover MORE than rendered
|
|
48
|
+
* components: a query prefetched in a loader and never read by any
|
|
49
|
+
* component still transfers, and any client hook (hydrating or mounted
|
|
50
|
+
* long after) finds it by hash. One entry per hash regardless of how many
|
|
51
|
+
* hooks read it; the payload object is the same reference the data nodes
|
|
52
|
+
* serialize, so seroval's cross-reference dedupe emits it once.
|
|
53
|
+
*/
|
|
54
|
+
function serializeCacheOnServer(client: QueryClient): void {
|
|
55
|
+
const ctx = (
|
|
56
|
+
sharedConfig as unknown as {
|
|
57
|
+
context?: {
|
|
58
|
+
async?: boolean
|
|
59
|
+
noHydrate?: boolean
|
|
60
|
+
serialize: (key: string, value: unknown) => void
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
).context
|
|
64
|
+
if (!ctx || !ctx.async || ctx.noHydrate) return
|
|
65
|
+
|
|
66
|
+
const cache = client.getQueryCache()
|
|
67
|
+
const seen = new Set<string>()
|
|
68
|
+
const serializeQuery = (query: Query<any, any, any, any>) => {
|
|
69
|
+
if (seen.has(query.queryHash)) return
|
|
70
|
+
const state = query.state
|
|
71
|
+
if (state.status === 'success') {
|
|
72
|
+
seen.add(query.queryHash)
|
|
73
|
+
ctx.serialize(HYDRATION_KEY_PREFIX + query.queryHash, {
|
|
74
|
+
data: state.data,
|
|
75
|
+
t: state.dataUpdatedAt,
|
|
76
|
+
})
|
|
77
|
+
} else if (state.fetchStatus !== 'idle') {
|
|
78
|
+
// Serialize the PROMISE now, while the context is live — the settle
|
|
79
|
+
// event fires from IO, where no request context exists anymore.
|
|
80
|
+
const promise = query.promise as Promise<unknown> | undefined
|
|
81
|
+
if (!promise) return
|
|
82
|
+
seen.add(query.queryHash)
|
|
83
|
+
ctx.serialize(
|
|
84
|
+
HYDRATION_KEY_PREFIX + query.queryHash,
|
|
85
|
+
promise.then(() => ({
|
|
86
|
+
data: query.state.data,
|
|
87
|
+
t: query.state.dataUpdatedAt,
|
|
88
|
+
})),
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
for (const query of cache.getAll()) serializeQuery(query)
|
|
94
|
+
onCleanup(cache.subscribe((event) => serializeQuery(event.query)))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Provides the QueryClient and manages its mount lifecycle. On the server
|
|
99
|
+
* it also registers the cache serializer above; on the client, hooks prime
|
|
100
|
+
* the cache from their hash-keyed registry entries themselves — see
|
|
101
|
+
* `useBaseQuery`.
|
|
102
|
+
*/
|
|
41
103
|
export const QueryClientProvider = (
|
|
42
104
|
props: QueryClientProviderProps,
|
|
43
105
|
): JSX.Element => {
|
|
44
106
|
props.client.mount()
|
|
45
107
|
onCleanup(() => props.client.unmount())
|
|
46
|
-
|
|
47
|
-
// Library-owned serialization channel for SSR dehydration.
|
|
48
|
-
//
|
|
49
|
-
// Server: the computation's value IS the channel's async iterable, so
|
|
50
|
-
// Solid serializes it through its normal per-computation signal path:
|
|
51
|
-
// the server runtime tees the iterator into the hydration serializer
|
|
52
|
-
// (`ctx.serialize(id, tapped)` in solid-js' `processResult`) and
|
|
53
|
-
// seroval streams each cumulative dehydrated-cache snapshot to the
|
|
54
|
-
// client as a chunk riding the SSR stream, with the entry objects
|
|
55
|
-
// inside deduplicated by reference against everything else in the
|
|
56
|
-
// payload. Nothing reads the signal during SSR, so it never suspends
|
|
57
|
-
// anything.
|
|
58
|
-
//
|
|
59
|
-
// Client, hydrating: Solid replays the serialized iterable through the
|
|
60
|
-
// per-computation signal path (`hydrateSignalFromAsyncIterable`).
|
|
61
|
-
// Yields that were still buffered when hydration began are conflated
|
|
62
|
-
// to the LATEST yield (`normalizeIterator`) — lossless here because
|
|
63
|
-
// every yield is a cumulative snapshot — and live yields after that
|
|
64
|
-
// apply one at a time. Requires a solid-js build with the buffered
|
|
65
|
-
// async-iterable replay conflation fix (> 2.0.0-beta.32): before it,
|
|
66
|
-
// the replay dropped every buffered yield after the first, including
|
|
67
|
-
// the terminal `done` snapshot. The render effect below hands each
|
|
68
|
-
// signal value to the coordinator, which primes the QueryClient via
|
|
69
|
-
// query-core hydrate() (newer-wins) and unblocks `useBaseQuery`
|
|
70
|
-
// subscribers waiting on their query's entry.
|
|
71
|
-
//
|
|
72
|
-
// Client, fresh mount: the compute returns undefined and the effect
|
|
73
|
-
// never fires.
|
|
74
|
-
const [channelValue] = createSignal<DehydrationChannelYield | undefined>(
|
|
75
|
-
() => (isServer ? createServerDehydrationChannel(props.client) : undefined),
|
|
76
|
-
)
|
|
77
|
-
const coordinator = isServer
|
|
78
|
-
? null
|
|
79
|
-
: createHydrationCoordinator(() => props.client)
|
|
80
|
-
createRenderEffect(
|
|
81
|
-
() => (isServer ? undefined : channelValue()),
|
|
82
|
-
(value) => {
|
|
83
|
-
if (value && coordinator) {
|
|
84
|
-
coordinator.applyYield(value)
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
)
|
|
108
|
+
if (isServer) serializeCacheOnServer(props.client)
|
|
88
109
|
|
|
89
110
|
return (
|
|
90
111
|
<QueryClientContext value={() => props.client}>
|
|
91
|
-
|
|
92
|
-
{props.children}
|
|
93
|
-
</HydrationCoordinatorContext>
|
|
112
|
+
{props.children}
|
|
94
113
|
</QueryClientContext>
|
|
95
114
|
)
|
|
96
115
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createEffect,
|
|
3
|
+
createOptimistic,
|
|
4
|
+
createSignal,
|
|
5
|
+
onCleanup,
|
|
6
|
+
sharedConfig,
|
|
7
|
+
untrack,
|
|
8
|
+
} from 'solid-js'
|
|
9
|
+
import type { Accessor } from 'solid-js'
|
|
10
|
+
|
|
11
|
+
const isServer = typeof window === 'undefined'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Shared reactive core for the cross-cache aggregate hooks — `useIsFetching`,
|
|
15
|
+
* `useIsMutating`, `useMutationState` — which all reduce to "a value derived
|
|
16
|
+
* from a whole cache, refreshed on its events". Two invariants live here so
|
|
17
|
+
* each hook doesn't restate them:
|
|
18
|
+
*
|
|
19
|
+
* Dual write. Cache events can fire inside an action transaction
|
|
20
|
+
* (`useMutation` rides core `action`; an onSuccess invalidation dispatches
|
|
21
|
+
* refetches mid-action), where a plain signal write is held until settle —
|
|
22
|
+
* the in-flight value would be invisible. An optimistic override alone fails
|
|
23
|
+
* the other way: outside a transaction, overrides drop at batch end. So a
|
|
24
|
+
* durable signal carries committed state and an optimistic node tracks it as
|
|
25
|
+
* its base; in-transaction values surface through the override, and the held
|
|
26
|
+
* durable write becomes the base when the settle lands.
|
|
27
|
+
*
|
|
28
|
+
* Hydration. A cross-cache aggregate is not tied to a single async source,
|
|
29
|
+
* so it cannot ride the boundary contract per-query meta uses (suspend until
|
|
30
|
+
* settled). Its contract is fixed by construction instead: a hydrating
|
|
31
|
+
* client can only ever observe the empty value at claim time — its own
|
|
32
|
+
* fetches are held inside the hydration window, channel-primed entries are
|
|
33
|
+
* settled, and mutations do not transfer across SSR. So the server
|
|
34
|
+
* serializes the empty value, and a hydrated mount latches there until its
|
|
35
|
+
* window closes: the subscription and first sync run from an effect half,
|
|
36
|
+
* deferred past hydration — the earliest globally-safe moment for a
|
|
37
|
+
* cross-cache read to go live (on a streamed page, an already-hydrated
|
|
38
|
+
* region's refetch can be in flight while this region is still claiming).
|
|
39
|
+
*
|
|
40
|
+
* Every node here is created on the server too, where it is never read or
|
|
41
|
+
* written: hydration id assignment is positional, so both sides must create
|
|
42
|
+
* the same reactive nodes or every id downstream shifts and the subtree
|
|
43
|
+
* key-misses.
|
|
44
|
+
*/
|
|
45
|
+
export function createCacheAggregate<T>(
|
|
46
|
+
subscribe: (onEvent: () => void) => () => void,
|
|
47
|
+
read: (prev: T) => T,
|
|
48
|
+
empty: T,
|
|
49
|
+
): Accessor<T> {
|
|
50
|
+
const hydratedMount =
|
|
51
|
+
!isServer && (sharedConfig as { hydrating?: boolean }).hydrating === true
|
|
52
|
+
|
|
53
|
+
// Cast: createSignal's value overload excludes functions (a function
|
|
54
|
+
// argument means a compute); aggregate values are counts and arrays.
|
|
55
|
+
const [durable, setDurable] = createSignal<T>(
|
|
56
|
+
(hydratedMount || isServer ? empty : untrack(() => read(empty))) as Exclude<
|
|
57
|
+
T,
|
|
58
|
+
Function
|
|
59
|
+
>,
|
|
60
|
+
{ ownedWrite: true },
|
|
61
|
+
)
|
|
62
|
+
const [value, setValue] = createOptimistic(() => durable(), {
|
|
63
|
+
ownedWrite: true,
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const sync = () => {
|
|
67
|
+
const next = untrack(() => read(untrack(durable)))
|
|
68
|
+
setDurable(() => next)
|
|
69
|
+
setValue(() => next)
|
|
70
|
+
}
|
|
71
|
+
let unsubscribe: (() => void) | null = null
|
|
72
|
+
const attach = () => {
|
|
73
|
+
unsubscribe ??= untrack(() => subscribe(sync))
|
|
74
|
+
}
|
|
75
|
+
onCleanup(() => unsubscribe?.())
|
|
76
|
+
|
|
77
|
+
createEffect(
|
|
78
|
+
() => undefined,
|
|
79
|
+
() => {
|
|
80
|
+
if (hydratedMount) {
|
|
81
|
+
attach()
|
|
82
|
+
sync()
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if (isServer) return () => empty
|
|
88
|
+
if (!hydratedMount) attach()
|
|
89
|
+
return value
|
|
90
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
/* istanbul ignore file */
|
|
2
2
|
|
|
3
|
-
import { useQuery } from './useQuery'
|
|
4
|
-
import { useInfiniteQuery } from './useInfiniteQuery'
|
|
5
|
-
import { useMutation } from './useMutation'
|
|
6
|
-
import { useQueries } from './useQueries'
|
|
7
|
-
|
|
8
3
|
// Re-export core
|
|
9
4
|
export * from '@tanstack/query-core'
|
|
10
5
|
|
|
@@ -23,27 +18,11 @@ export type {
|
|
|
23
18
|
UseBaseQueryResult,
|
|
24
19
|
UseInfiniteQueryOptions,
|
|
25
20
|
UseInfiniteQueryResult,
|
|
26
|
-
UseMutateAsyncFunction,
|
|
27
21
|
UseMutateFunction,
|
|
28
22
|
UseMutationOptions,
|
|
29
23
|
UseMutationResult,
|
|
30
24
|
UseQueryOptions,
|
|
31
25
|
UseQueryResult,
|
|
32
|
-
// Aliases (create* and use* are both supported)
|
|
33
|
-
UseBaseQueryOptions as CreateBaseQueryOptions,
|
|
34
|
-
UseBaseQueryResult as CreateBaseQueryResult,
|
|
35
|
-
UseInfiniteQueryOptions as CreateInfiniteQueryOptions,
|
|
36
|
-
UseInfiniteQueryResult as CreateInfiniteQueryResult,
|
|
37
|
-
UseMutateAsyncFunction as CreateMutateAsyncFunction,
|
|
38
|
-
UseMutateFunction as CreateMutateFunction,
|
|
39
|
-
UseMutationOptions as CreateMutationOptions,
|
|
40
|
-
UseMutationResult as CreateMutationResult,
|
|
41
|
-
UseBaseMutationResult as CreateBaseMutationResult,
|
|
42
|
-
UseQueryOptions as CreateQueryOptions,
|
|
43
|
-
UseQueryResult as CreateQueryResult,
|
|
44
|
-
DefinedUseBaseQueryResult as DefinedCreateBaseQueryResult,
|
|
45
|
-
DefinedUseInfiniteQueryResult as DefinedCreateInfiniteQueryResult,
|
|
46
|
-
DefinedUseQueryResult as DefinedCreateQueryResult,
|
|
47
26
|
} from './types'
|
|
48
27
|
|
|
49
28
|
export { QueryClient } from './QueryClient'
|
|
@@ -54,7 +33,6 @@ export type {
|
|
|
54
33
|
InfiniteQueryObserverOptions,
|
|
55
34
|
} from './QueryClient'
|
|
56
35
|
export { useQuery } from './useQuery'
|
|
57
|
-
export const createQuery = useQuery
|
|
58
36
|
export { queryOptions } from './queryOptions'
|
|
59
37
|
export type {
|
|
60
38
|
DefinedInitialDataOptions,
|
|
@@ -67,9 +45,7 @@ export {
|
|
|
67
45
|
} from './QueryClientProvider'
|
|
68
46
|
export type { QueryClientProviderProps } from './QueryClientProvider'
|
|
69
47
|
export { useIsFetching } from './useIsFetching'
|
|
70
|
-
export {
|
|
71
|
-
export { useInfiniteQuery }
|
|
72
|
-
export const createInfiniteQuery = useInfiniteQuery
|
|
48
|
+
export { useInfiniteQuery } from './useInfiniteQuery'
|
|
73
49
|
export { infiniteQueryOptions } from './infiniteQueryOptions'
|
|
74
50
|
export type {
|
|
75
51
|
DefinedInitialDataInfiniteOptions,
|
|
@@ -77,11 +53,7 @@ export type {
|
|
|
77
53
|
} from './infiniteQueryOptions'
|
|
78
54
|
export { useMutation } from './useMutation'
|
|
79
55
|
export { mutationOptions } from './mutationOptions'
|
|
80
|
-
export const createMutation = useMutation
|
|
81
56
|
export { useIsMutating } from './useIsMutating'
|
|
82
|
-
export { useIsMutating as createIsMutating } from './useIsMutating'
|
|
83
57
|
export { useMutationState } from './useMutationState'
|
|
84
|
-
export { useMutationState as createMutationState } from './useMutationState'
|
|
85
58
|
export { useQueries } from './useQueries'
|
|
86
|
-
export const createQueries = useQueries
|
|
87
59
|
export { useIsRestoring, IsRestoringContext } from './isRestoring'
|
package/src/types.ts
CHANGED
|
@@ -5,7 +5,6 @@ import type {
|
|
|
5
5
|
DefinedInfiniteQueryObserverResult,
|
|
6
6
|
DefinedQueryObserverResult,
|
|
7
7
|
InfiniteQueryObserverResult,
|
|
8
|
-
MutateFunction,
|
|
9
8
|
MutationObserverOptions,
|
|
10
9
|
MutationObserverResult,
|
|
11
10
|
OmitKeyof,
|
|
@@ -30,18 +29,22 @@ export interface UseBaseQueryOptions<
|
|
|
30
29
|
'suspense'
|
|
31
30
|
> {
|
|
32
31
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
32
|
+
* Defer the SSR stream flush until this query resolves on the server,
|
|
33
|
+
* instead of letting the surrounding `<Loading>` boundary render its
|
|
34
|
+
* fallback into the HTML. Passed through to Solid's own per-computation
|
|
35
|
+
* `deferStream` option; server-only, ignored on the client.
|
|
37
36
|
*/
|
|
38
37
|
deferStream?: boolean
|
|
39
38
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
39
|
+
* Reconciliation key for the data store. `data` is served as a Solid
|
|
40
|
+
* store projection: every landing (refetch, invalidation, placeholder
|
|
41
|
+
* upgrade) reconciles into the existing proxy graph instead of replacing
|
|
42
|
+
* it, so deep reads are fine-grained and item identity survives across
|
|
43
|
+
* fetches. This sets the identity key for that reconciliation — a
|
|
44
|
+
* property name, a key-extraction function, or `null` to merge
|
|
45
|
+
* positionally. Defaults to `'id'`. Read once at creation.
|
|
43
46
|
*/
|
|
44
|
-
|
|
47
|
+
reconcile?: string | ((item: NonNullable<any>) => any) | null
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
export interface QueryOptions<
|
|
@@ -66,20 +69,39 @@ export type UseQueryOptions<
|
|
|
66
69
|
|
|
67
70
|
/* --- Create Query and Create Base Query Types --- */
|
|
68
71
|
|
|
72
|
+
/**
|
|
73
|
+
* `data` is non-optional: it is a suspending async read. It never returns
|
|
74
|
+
* `undefined` — a read either suspends into the nearest `<Loading>`
|
|
75
|
+
* boundary (first fetch in flight, disabled, restoring), returns a value
|
|
76
|
+
* (committed, placeholder, initial), or throws (`<Errored>` /
|
|
77
|
+
* `throwOnError`). The v5 `TData | undefined` face existed because reads
|
|
78
|
+
* could observe the pre-fetch gap; here that gap is suspension.
|
|
79
|
+
*/
|
|
69
80
|
export type UseBaseQueryResult<
|
|
70
81
|
TData = unknown,
|
|
71
82
|
TError = DefaultError,
|
|
72
|
-
> =
|
|
83
|
+
> = Override<
|
|
84
|
+
OmitKeyof<QueryObserverResult<TData, TError>, 'isInitialLoading'>,
|
|
85
|
+
{ data: TData }
|
|
86
|
+
>
|
|
73
87
|
|
|
74
88
|
export type UseQueryResult<
|
|
75
89
|
TData = unknown,
|
|
76
90
|
TError = DefaultError,
|
|
77
91
|
> = UseBaseQueryResult<TData, TError>
|
|
78
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Distributes an `isInitialLoading` omit over result unions so status
|
|
95
|
+
* discriminants survive (a plain Omit over a union collapses it).
|
|
96
|
+
*/
|
|
97
|
+
type OmitIsInitialLoading<T> = T extends unknown
|
|
98
|
+
? OmitKeyof<T, 'isInitialLoading' & keyof T>
|
|
99
|
+
: never
|
|
100
|
+
|
|
79
101
|
export type DefinedUseBaseQueryResult<
|
|
80
102
|
TData = unknown,
|
|
81
103
|
TError = DefaultError,
|
|
82
|
-
> = DefinedQueryObserverResult<TData, TError
|
|
104
|
+
> = OmitIsInitialLoading<DefinedQueryObserverResult<TData, TError>>
|
|
83
105
|
|
|
84
106
|
export type DefinedUseQueryResult<
|
|
85
107
|
TData = unknown,
|
|
@@ -105,18 +127,16 @@ export interface InfiniteQueryOptions<
|
|
|
105
127
|
> {
|
|
106
128
|
queryKey: TQueryKey
|
|
107
129
|
/**
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* This can be useful to avoid sending a loading state to the client before the query has resolved.
|
|
111
|
-
* Defaults to `false`.
|
|
130
|
+
* Defer the SSR stream flush until this query resolves on the server —
|
|
131
|
+
* see {@link UseBaseQueryOptions.deferStream}.
|
|
112
132
|
*/
|
|
113
133
|
deferStream?: boolean
|
|
114
134
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
135
|
+
* Reconciliation key for the data store — see
|
|
136
|
+
* {@link UseBaseQueryOptions.reconcile}. For infinite queries this keys
|
|
137
|
+
* items within pages; pages themselves merge positionally.
|
|
118
138
|
*/
|
|
119
|
-
|
|
139
|
+
reconcile?: string | ((item: NonNullable<any>) => any) | null
|
|
120
140
|
}
|
|
121
141
|
|
|
122
142
|
export type UseInfiniteQueryOptions<
|
|
@@ -129,15 +149,19 @@ export type UseInfiniteQueryOptions<
|
|
|
129
149
|
InfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>
|
|
130
150
|
>
|
|
131
151
|
|
|
152
|
+
/** Non-optional `data` for the same reason as {@link UseBaseQueryResult}. */
|
|
132
153
|
export type UseInfiniteQueryResult<
|
|
133
154
|
TData = unknown,
|
|
134
155
|
TError = DefaultError,
|
|
135
|
-
> =
|
|
156
|
+
> = Override<
|
|
157
|
+
OmitKeyof<InfiniteQueryObserverResult<TData, TError>, 'isInitialLoading'>,
|
|
158
|
+
{ data: TData }
|
|
159
|
+
>
|
|
136
160
|
|
|
137
161
|
export type DefinedUseInfiniteQueryResult<
|
|
138
162
|
TData = unknown,
|
|
139
163
|
TError = DefaultError,
|
|
140
|
-
> = DefinedInfiniteQueryObserverResult<TData, TError
|
|
164
|
+
> = OmitIsInitialLoading<DefinedInfiniteQueryObserverResult<TData, TError>>
|
|
141
165
|
|
|
142
166
|
/* --- Create Mutation Types --- */
|
|
143
167
|
export interface MutationOptions<
|
|
@@ -157,23 +181,21 @@ export type UseMutationOptions<
|
|
|
157
181
|
TOnMutateResult = unknown,
|
|
158
182
|
> = Accessor<MutationOptions<TData, TError, TVariables, TOnMutateResult>>
|
|
159
183
|
|
|
184
|
+
/**
|
|
185
|
+
* One `mutate`, returning a safe-to-ignore promise: errors are also routed
|
|
186
|
+
* into reactive state, and ignoring the promise never surfaces an
|
|
187
|
+
* unhandled rejection. Call-site callbacks are gone — settle logic that
|
|
188
|
+
* used to live in `mutate(vars, { onSuccess })` is linear code after
|
|
189
|
+
* `await mutate(vars)` or a config-level callback.
|
|
190
|
+
*/
|
|
160
191
|
export type UseMutateFunction<
|
|
161
192
|
TData = unknown,
|
|
162
|
-
|
|
193
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
194
|
+
_TError = DefaultError,
|
|
163
195
|
TVariables = void,
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
MutateFunction<TData, TError, TVariables, TOnMutateResult>
|
|
168
|
-
>
|
|
169
|
-
) => void
|
|
170
|
-
|
|
171
|
-
export type UseMutateAsyncFunction<
|
|
172
|
-
TData = unknown,
|
|
173
|
-
TError = DefaultError,
|
|
174
|
-
TVariables = void,
|
|
175
|
-
TOnMutateResult = unknown,
|
|
176
|
-
> = MutateFunction<TData, TError, TVariables, TOnMutateResult>
|
|
196
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
197
|
+
_TOnMutateResult = unknown,
|
|
198
|
+
> = (variables: TVariables) => Promise<TData>
|
|
177
199
|
|
|
178
200
|
export type UseBaseMutationResult<
|
|
179
201
|
TData = unknown,
|
|
@@ -181,16 +203,12 @@ export type UseBaseMutationResult<
|
|
|
181
203
|
TVariables = unknown,
|
|
182
204
|
TOnMutateResult = unknown,
|
|
183
205
|
> = Override<
|
|
184
|
-
|
|
206
|
+
OmitKeyof<
|
|
207
|
+
MutationObserverResult<TData, TError, TVariables, TOnMutateResult>,
|
|
208
|
+
'context'
|
|
209
|
+
>,
|
|
185
210
|
{ mutate: UseMutateFunction<TData, TError, TVariables, TOnMutateResult> }
|
|
186
|
-
>
|
|
187
|
-
mutateAsync: UseMutateAsyncFunction<
|
|
188
|
-
TData,
|
|
189
|
-
TError,
|
|
190
|
-
TVariables,
|
|
191
|
-
TOnMutateResult
|
|
192
|
-
>
|
|
193
|
-
}
|
|
211
|
+
>
|
|
194
212
|
|
|
195
213
|
export type UseMutationResult<
|
|
196
214
|
TData = unknown,
|