feat: Add presence heartbeat for Matrix online status
- matrix-gateway: POST /internal/matrix/presence/online endpoint - usePresenceHeartbeat hook with activity tracking - Auto away after 5 min inactivity - Offline on page close/visibility change - Integrated in MatrixChatRoom component
This commit is contained in:
21
node_modules/@tanstack/query-core/LICENSE
generated
vendored
Normal file
21
node_modules/@tanstack/query-core/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-present Tanner Linsley
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
65
node_modules/@tanstack/query-core/package.json
generated
vendored
Normal file
65
node_modules/@tanstack/query-core/package.json
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "@tanstack/query-core",
|
||||
"version": "5.90.10",
|
||||
"description": "The framework agnostic core that powers TanStack Query",
|
||||
"author": "tannerlinsley",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/TanStack/query.git",
|
||||
"directory": "packages/query-core"
|
||||
},
|
||||
"homepage": "https://tanstack.com/query",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"type": "module",
|
||||
"types": "build/legacy/index.d.ts",
|
||||
"main": "build/legacy/index.cjs",
|
||||
"module": "build/legacy/index.js",
|
||||
"react-native": "src/index.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"@tanstack/custom-condition": "./src/index.ts",
|
||||
"import": {
|
||||
"types": "./build/modern/index.d.ts",
|
||||
"default": "./build/modern/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./build/modern/index.d.cts",
|
||||
"default": "./build/modern/index.cjs"
|
||||
}
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"files": [
|
||||
"build",
|
||||
"src",
|
||||
"!src/__tests__"
|
||||
],
|
||||
"devDependencies": {
|
||||
"npm-run-all2": "^5.0.0",
|
||||
"@tanstack/query-test-utils": "0.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "premove ./build ./coverage ./dist-ts",
|
||||
"compile": "tsc --build",
|
||||
"test:eslint": "eslint --concurrency=auto ./src",
|
||||
"test:types": "npm-run-all --serial test:types:*",
|
||||
"test:types:ts50": "node ../../node_modules/typescript50/lib/tsc.js --build tsconfig.legacy.json",
|
||||
"test:types:ts51": "node ../../node_modules/typescript51/lib/tsc.js --build tsconfig.legacy.json",
|
||||
"test:types:ts52": "node ../../node_modules/typescript52/lib/tsc.js --build tsconfig.legacy.json",
|
||||
"test:types:ts53": "node ../../node_modules/typescript53/lib/tsc.js --build tsconfig.legacy.json",
|
||||
"test:types:ts54": "node ../../node_modules/typescript54/lib/tsc.js --build tsconfig.legacy.json",
|
||||
"test:types:ts55": "node ../../node_modules/typescript55/lib/tsc.js --build tsconfig.legacy.json",
|
||||
"test:types:ts56": "node ../../node_modules/typescript56/lib/tsc.js --build tsconfig.legacy.json",
|
||||
"test:types:ts57": "node ../../node_modules/typescript57/lib/tsc.js --build tsconfig.legacy.json",
|
||||
"test:types:tscurrent": "tsc --build",
|
||||
"test:lib": "vitest",
|
||||
"test:lib:dev": "pnpm run test:lib --watch",
|
||||
"test:build": "publint --strict && attw --pack",
|
||||
"build": "tsup --tsconfig tsconfig.prod.json"
|
||||
}
|
||||
}
|
||||
86
node_modules/@tanstack/query-core/src/focusManager.ts
generated
vendored
Normal file
86
node_modules/@tanstack/query-core/src/focusManager.ts
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
import { Subscribable } from './subscribable'
|
||||
import { isServer } from './utils'
|
||||
|
||||
type Listener = (focused: boolean) => void
|
||||
|
||||
type SetupFn = (
|
||||
setFocused: (focused?: boolean) => void,
|
||||
) => (() => void) | undefined
|
||||
|
||||
export class FocusManager extends Subscribable<Listener> {
|
||||
#focused?: boolean
|
||||
#cleanup?: () => void
|
||||
|
||||
#setup: SetupFn
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.#setup = (onFocus) => {
|
||||
// addEventListener does not exist in React Native, but window does
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!isServer && window.addEventListener) {
|
||||
const listener = () => onFocus()
|
||||
// Listen to visibilitychange
|
||||
window.addEventListener('visibilitychange', listener, false)
|
||||
|
||||
return () => {
|
||||
// Be sure to unsubscribe if a new handler is set
|
||||
window.removeEventListener('visibilitychange', listener)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
protected onSubscribe(): void {
|
||||
if (!this.#cleanup) {
|
||||
this.setEventListener(this.#setup)
|
||||
}
|
||||
}
|
||||
|
||||
protected onUnsubscribe() {
|
||||
if (!this.hasListeners()) {
|
||||
this.#cleanup?.()
|
||||
this.#cleanup = undefined
|
||||
}
|
||||
}
|
||||
|
||||
setEventListener(setup: SetupFn): void {
|
||||
this.#setup = setup
|
||||
this.#cleanup?.()
|
||||
this.#cleanup = setup((focused) => {
|
||||
if (typeof focused === 'boolean') {
|
||||
this.setFocused(focused)
|
||||
} else {
|
||||
this.onFocus()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
setFocused(focused?: boolean): void {
|
||||
const changed = this.#focused !== focused
|
||||
if (changed) {
|
||||
this.#focused = focused
|
||||
this.onFocus()
|
||||
}
|
||||
}
|
||||
|
||||
onFocus(): void {
|
||||
const isFocused = this.isFocused()
|
||||
this.listeners.forEach((listener) => {
|
||||
listener(isFocused)
|
||||
})
|
||||
}
|
||||
|
||||
isFocused(): boolean {
|
||||
if (typeof this.#focused === 'boolean') {
|
||||
return this.#focused
|
||||
}
|
||||
|
||||
// document global can be unavailable in react native
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
return globalThis.document?.visibilityState !== 'hidden'
|
||||
}
|
||||
}
|
||||
|
||||
export const focusManager = new FocusManager()
|
||||
285
node_modules/@tanstack/query-core/src/hydration.ts
generated
vendored
Normal file
285
node_modules/@tanstack/query-core/src/hydration.ts
generated
vendored
Normal file
@@ -0,0 +1,285 @@
|
||||
import { tryResolveSync } from './thenable'
|
||||
import { noop } from './utils'
|
||||
import type {
|
||||
DefaultError,
|
||||
MutationKey,
|
||||
MutationMeta,
|
||||
MutationOptions,
|
||||
MutationScope,
|
||||
QueryKey,
|
||||
QueryMeta,
|
||||
QueryOptions,
|
||||
} from './types'
|
||||
import type { QueryClient } from './queryClient'
|
||||
import type { Query, QueryState } from './query'
|
||||
import type { Mutation, MutationState } from './mutation'
|
||||
|
||||
// TYPES
|
||||
type TransformerFn = (data: any) => any
|
||||
function defaultTransformerFn(data: any): any {
|
||||
return data
|
||||
}
|
||||
|
||||
export interface DehydrateOptions {
|
||||
serializeData?: TransformerFn
|
||||
shouldDehydrateMutation?: (mutation: Mutation) => boolean
|
||||
shouldDehydrateQuery?: (query: Query) => boolean
|
||||
shouldRedactErrors?: (error: unknown) => boolean
|
||||
}
|
||||
|
||||
export interface HydrateOptions {
|
||||
defaultOptions?: {
|
||||
deserializeData?: TransformerFn
|
||||
queries?: QueryOptions
|
||||
mutations?: MutationOptions<unknown, DefaultError, unknown, unknown>
|
||||
}
|
||||
}
|
||||
|
||||
interface DehydratedMutation {
|
||||
mutationKey?: MutationKey
|
||||
state: MutationState
|
||||
meta?: MutationMeta
|
||||
scope?: MutationScope
|
||||
}
|
||||
|
||||
interface DehydratedQuery {
|
||||
queryHash: string
|
||||
queryKey: QueryKey
|
||||
state: QueryState
|
||||
promise?: Promise<unknown>
|
||||
meta?: QueryMeta
|
||||
// This is only optional because older versions of Query might have dehydrated
|
||||
// without it which we need to handle for backwards compatibility.
|
||||
// This should be changed to required in the future.
|
||||
dehydratedAt?: number
|
||||
}
|
||||
|
||||
export interface DehydratedState {
|
||||
mutations: Array<DehydratedMutation>
|
||||
queries: Array<DehydratedQuery>
|
||||
}
|
||||
|
||||
// FUNCTIONS
|
||||
|
||||
function dehydrateMutation(mutation: Mutation): DehydratedMutation {
|
||||
return {
|
||||
mutationKey: mutation.options.mutationKey,
|
||||
state: mutation.state,
|
||||
...(mutation.options.scope && { scope: mutation.options.scope }),
|
||||
...(mutation.meta && { meta: mutation.meta }),
|
||||
}
|
||||
}
|
||||
|
||||
// Most config is not dehydrated but instead meant to configure again when
|
||||
// consuming the de/rehydrated data, typically with useQuery on the client.
|
||||
// Sometimes it might make sense to prefetch data on the server and include
|
||||
// in the html-payload, but not consume it on the initial render.
|
||||
function dehydrateQuery(
|
||||
query: Query,
|
||||
serializeData: TransformerFn,
|
||||
shouldRedactErrors: (error: unknown) => boolean,
|
||||
): DehydratedQuery {
|
||||
const dehydratePromise = () => {
|
||||
const promise = query.promise?.then(serializeData).catch((error) => {
|
||||
if (!shouldRedactErrors(error)) {
|
||||
// Reject original error if it should not be redacted
|
||||
return Promise.reject(error)
|
||||
}
|
||||
// If not in production, log original error before rejecting redacted error
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
console.error(
|
||||
`A query that was dehydrated as pending ended up rejecting. [${query.queryHash}]: ${error}; The error will be redacted in production builds`,
|
||||
)
|
||||
}
|
||||
return Promise.reject(new Error('redacted'))
|
||||
})
|
||||
|
||||
// Avoid unhandled promise rejections
|
||||
// We need the promise we dehydrate to reject to get the correct result into
|
||||
// the query cache, but we also want to avoid unhandled promise rejections
|
||||
// in whatever environment the prefetches are happening in.
|
||||
promise?.catch(noop)
|
||||
|
||||
return promise
|
||||
}
|
||||
|
||||
return {
|
||||
dehydratedAt: Date.now(),
|
||||
state: {
|
||||
...query.state,
|
||||
...(query.state.data !== undefined && {
|
||||
data: serializeData(query.state.data),
|
||||
}),
|
||||
},
|
||||
queryKey: query.queryKey,
|
||||
queryHash: query.queryHash,
|
||||
...(query.state.status === 'pending' && {
|
||||
promise: dehydratePromise(),
|
||||
}),
|
||||
...(query.meta && { meta: query.meta }),
|
||||
}
|
||||
}
|
||||
|
||||
export function defaultShouldDehydrateMutation(mutation: Mutation) {
|
||||
return mutation.state.isPaused
|
||||
}
|
||||
|
||||
export function defaultShouldDehydrateQuery(query: Query) {
|
||||
return query.state.status === 'success'
|
||||
}
|
||||
|
||||
function defaultShouldRedactErrors(_: unknown) {
|
||||
return true
|
||||
}
|
||||
|
||||
export function dehydrate(
|
||||
client: QueryClient,
|
||||
options: DehydrateOptions = {},
|
||||
): DehydratedState {
|
||||
const filterMutation =
|
||||
options.shouldDehydrateMutation ??
|
||||
client.getDefaultOptions().dehydrate?.shouldDehydrateMutation ??
|
||||
defaultShouldDehydrateMutation
|
||||
|
||||
const mutations = client
|
||||
.getMutationCache()
|
||||
.getAll()
|
||||
.flatMap((mutation) =>
|
||||
filterMutation(mutation) ? [dehydrateMutation(mutation)] : [],
|
||||
)
|
||||
|
||||
const filterQuery =
|
||||
options.shouldDehydrateQuery ??
|
||||
client.getDefaultOptions().dehydrate?.shouldDehydrateQuery ??
|
||||
defaultShouldDehydrateQuery
|
||||
|
||||
const shouldRedactErrors =
|
||||
options.shouldRedactErrors ??
|
||||
client.getDefaultOptions().dehydrate?.shouldRedactErrors ??
|
||||
defaultShouldRedactErrors
|
||||
|
||||
const serializeData =
|
||||
options.serializeData ??
|
||||
client.getDefaultOptions().dehydrate?.serializeData ??
|
||||
defaultTransformerFn
|
||||
|
||||
const queries = client
|
||||
.getQueryCache()
|
||||
.getAll()
|
||||
.flatMap((query) =>
|
||||
filterQuery(query)
|
||||
? [dehydrateQuery(query, serializeData, shouldRedactErrors)]
|
||||
: [],
|
||||
)
|
||||
|
||||
return { mutations, queries }
|
||||
}
|
||||
|
||||
export function hydrate(
|
||||
client: QueryClient,
|
||||
dehydratedState: unknown,
|
||||
options?: HydrateOptions,
|
||||
): void {
|
||||
if (typeof dehydratedState !== 'object' || dehydratedState === null) {
|
||||
return
|
||||
}
|
||||
|
||||
const mutationCache = client.getMutationCache()
|
||||
const queryCache = client.getQueryCache()
|
||||
const deserializeData =
|
||||
options?.defaultOptions?.deserializeData ??
|
||||
client.getDefaultOptions().hydrate?.deserializeData ??
|
||||
defaultTransformerFn
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const mutations = (dehydratedState as DehydratedState).mutations || []
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const queries = (dehydratedState as DehydratedState).queries || []
|
||||
|
||||
mutations.forEach(({ state, ...mutationOptions }) => {
|
||||
mutationCache.build(
|
||||
client,
|
||||
{
|
||||
...client.getDefaultOptions().hydrate?.mutations,
|
||||
...options?.defaultOptions?.mutations,
|
||||
...mutationOptions,
|
||||
},
|
||||
state,
|
||||
)
|
||||
})
|
||||
|
||||
queries.forEach(
|
||||
({ queryKey, state, queryHash, meta, promise, dehydratedAt }) => {
|
||||
const syncData = promise ? tryResolveSync(promise) : undefined
|
||||
const rawData = state.data === undefined ? syncData?.data : state.data
|
||||
const data = rawData === undefined ? rawData : deserializeData(rawData)
|
||||
|
||||
let query = queryCache.get(queryHash)
|
||||
const existingQueryIsPending = query?.state.status === 'pending'
|
||||
const existingQueryIsFetching = query?.state.fetchStatus === 'fetching'
|
||||
|
||||
// Do not hydrate if an existing query exists with newer data
|
||||
if (query) {
|
||||
const hasNewerSyncData =
|
||||
syncData &&
|
||||
// We only need this undefined check to handle older dehydration
|
||||
// payloads that might not have dehydratedAt
|
||||
dehydratedAt !== undefined &&
|
||||
dehydratedAt > query.state.dataUpdatedAt
|
||||
if (
|
||||
state.dataUpdatedAt > query.state.dataUpdatedAt ||
|
||||
hasNewerSyncData
|
||||
) {
|
||||
// omit fetchStatus from dehydrated state
|
||||
// so that query stays in its current fetchStatus
|
||||
const { fetchStatus: _ignored, ...serializedState } = state
|
||||
query.setState({
|
||||
...serializedState,
|
||||
data,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// Restore query
|
||||
query = queryCache.build(
|
||||
client,
|
||||
{
|
||||
...client.getDefaultOptions().hydrate?.queries,
|
||||
...options?.defaultOptions?.queries,
|
||||
queryKey,
|
||||
queryHash,
|
||||
meta,
|
||||
},
|
||||
// Reset fetch status to idle to avoid
|
||||
// query being stuck in fetching state upon hydration
|
||||
{
|
||||
...state,
|
||||
data,
|
||||
fetchStatus: 'idle',
|
||||
status: data !== undefined ? 'success' : state.status,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
promise &&
|
||||
!existingQueryIsPending &&
|
||||
!existingQueryIsFetching &&
|
||||
// Only hydrate if dehydration is newer than any existing data,
|
||||
// this is always true for new queries
|
||||
(dehydratedAt === undefined || dehydratedAt > query.state.dataUpdatedAt)
|
||||
) {
|
||||
// This doesn't actually fetch - it just creates a retryer
|
||||
// which will re-use the passed `initialPromise`
|
||||
// Note that we need to call these even when data was synchronously
|
||||
// available, as we still need to set up the retryer
|
||||
query
|
||||
.fetch(undefined, {
|
||||
// RSC transformed promises are not thenable
|
||||
initialPromise: Promise.resolve(promise).then(deserializeData),
|
||||
})
|
||||
// Avoid unhandled promise rejections
|
||||
.catch(noop)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
55
node_modules/@tanstack/query-core/src/index.ts
generated
vendored
Normal file
55
node_modules/@tanstack/query-core/src/index.ts
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
export { focusManager } from './focusManager'
|
||||
export {
|
||||
defaultShouldDehydrateMutation,
|
||||
defaultShouldDehydrateQuery,
|
||||
dehydrate,
|
||||
hydrate,
|
||||
} from './hydration'
|
||||
export { InfiniteQueryObserver } from './infiniteQueryObserver'
|
||||
export { MutationCache } from './mutationCache'
|
||||
export type { MutationCacheNotifyEvent } from './mutationCache'
|
||||
export { MutationObserver } from './mutationObserver'
|
||||
export { defaultScheduler, notifyManager } from './notifyManager'
|
||||
export { onlineManager } from './onlineManager'
|
||||
export { QueriesObserver } from './queriesObserver'
|
||||
export { QueryCache } from './queryCache'
|
||||
export type { QueryCacheNotifyEvent } from './queryCache'
|
||||
export { QueryClient } from './queryClient'
|
||||
export { QueryObserver } from './queryObserver'
|
||||
export { CancelledError, isCancelledError } from './retryer'
|
||||
export {
|
||||
timeoutManager,
|
||||
type ManagedTimerId,
|
||||
type TimeoutCallback,
|
||||
type TimeoutProvider,
|
||||
} from './timeoutManager'
|
||||
export {
|
||||
hashKey,
|
||||
isServer,
|
||||
keepPreviousData,
|
||||
matchMutation,
|
||||
matchQuery,
|
||||
noop,
|
||||
partialMatchKey,
|
||||
replaceEqualDeep,
|
||||
shouldThrowError,
|
||||
skipToken,
|
||||
} from './utils'
|
||||
export type { MutationFilters, QueryFilters, SkipToken, Updater } from './utils'
|
||||
|
||||
export { streamedQuery as experimental_streamedQuery } from './streamedQuery'
|
||||
|
||||
// Types
|
||||
export type {
|
||||
DehydratedState,
|
||||
DehydrateOptions,
|
||||
HydrateOptions,
|
||||
} from './hydration'
|
||||
export { Mutation } from './mutation'
|
||||
export type { MutationState } from './mutation'
|
||||
export type { QueriesObserverOptions } from './queriesObserver'
|
||||
export { Query } from './query'
|
||||
export type { QueryState } from './query'
|
||||
export * from './types'
|
||||
179
node_modules/@tanstack/query-core/src/infiniteQueryBehavior.ts
generated
vendored
Normal file
179
node_modules/@tanstack/query-core/src/infiniteQueryBehavior.ts
generated
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
import { addToEnd, addToStart, ensureQueryFn } from './utils'
|
||||
import type { QueryBehavior } from './query'
|
||||
import type {
|
||||
InfiniteData,
|
||||
InfiniteQueryPageParamsOptions,
|
||||
OmitKeyof,
|
||||
QueryFunctionContext,
|
||||
QueryKey,
|
||||
} from './types'
|
||||
|
||||
export function infiniteQueryBehavior<TQueryFnData, TError, TData, TPageParam>(
|
||||
pages?: number,
|
||||
): QueryBehavior<TQueryFnData, TError, InfiniteData<TData, TPageParam>> {
|
||||
return {
|
||||
onFetch: (context, query) => {
|
||||
const options = context.options as InfiniteQueryPageParamsOptions<TData>
|
||||
const direction = context.fetchOptions?.meta?.fetchMore?.direction
|
||||
const oldPages = context.state.data?.pages || []
|
||||
const oldPageParams = context.state.data?.pageParams || []
|
||||
let result: InfiniteData<unknown> = { pages: [], pageParams: [] }
|
||||
let currentPage = 0
|
||||
|
||||
const fetchFn = async () => {
|
||||
let cancelled = false
|
||||
const addSignalProperty = (object: unknown) => {
|
||||
Object.defineProperty(object, 'signal', {
|
||||
enumerable: true,
|
||||
get: () => {
|
||||
if (context.signal.aborted) {
|
||||
cancelled = true
|
||||
} else {
|
||||
context.signal.addEventListener('abort', () => {
|
||||
cancelled = true
|
||||
})
|
||||
}
|
||||
return context.signal
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const queryFn = ensureQueryFn(context.options, context.fetchOptions)
|
||||
|
||||
// Create function to fetch a page
|
||||
const fetchPage = async (
|
||||
data: InfiniteData<unknown>,
|
||||
param: unknown,
|
||||
previous?: boolean,
|
||||
): Promise<InfiniteData<unknown>> => {
|
||||
if (cancelled) {
|
||||
return Promise.reject()
|
||||
}
|
||||
|
||||
if (param == null && data.pages.length) {
|
||||
return Promise.resolve(data)
|
||||
}
|
||||
|
||||
const createQueryFnContext = () => {
|
||||
const queryFnContext: OmitKeyof<
|
||||
QueryFunctionContext<QueryKey, unknown>,
|
||||
'signal'
|
||||
> = {
|
||||
client: context.client,
|
||||
queryKey: context.queryKey,
|
||||
pageParam: param,
|
||||
direction: previous ? 'backward' : 'forward',
|
||||
meta: context.options.meta,
|
||||
}
|
||||
addSignalProperty(queryFnContext)
|
||||
return queryFnContext as QueryFunctionContext<QueryKey, unknown>
|
||||
}
|
||||
|
||||
const queryFnContext = createQueryFnContext()
|
||||
|
||||
const page = await queryFn(queryFnContext)
|
||||
|
||||
const { maxPages } = context.options
|
||||
const addTo = previous ? addToStart : addToEnd
|
||||
|
||||
return {
|
||||
pages: addTo(data.pages, page, maxPages),
|
||||
pageParams: addTo(data.pageParams, param, maxPages),
|
||||
}
|
||||
}
|
||||
|
||||
// fetch next / previous page?
|
||||
if (direction && oldPages.length) {
|
||||
const previous = direction === 'backward'
|
||||
const pageParamFn = previous ? getPreviousPageParam : getNextPageParam
|
||||
const oldData = {
|
||||
pages: oldPages,
|
||||
pageParams: oldPageParams,
|
||||
}
|
||||
const param = pageParamFn(options, oldData)
|
||||
|
||||
result = await fetchPage(oldData, param, previous)
|
||||
} else {
|
||||
const remainingPages = pages ?? oldPages.length
|
||||
|
||||
// Fetch all pages
|
||||
do {
|
||||
const param =
|
||||
currentPage === 0
|
||||
? (oldPageParams[0] ?? options.initialPageParam)
|
||||
: getNextPageParam(options, result)
|
||||
if (currentPage > 0 && param == null) {
|
||||
break
|
||||
}
|
||||
result = await fetchPage(result, param)
|
||||
currentPage++
|
||||
} while (currentPage < remainingPages)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
if (context.options.persister) {
|
||||
context.fetchFn = () => {
|
||||
return context.options.persister?.(
|
||||
fetchFn as any,
|
||||
{
|
||||
client: context.client,
|
||||
queryKey: context.queryKey,
|
||||
meta: context.options.meta,
|
||||
signal: context.signal,
|
||||
},
|
||||
query,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
context.fetchFn = fetchFn
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function getNextPageParam(
|
||||
options: InfiniteQueryPageParamsOptions<any>,
|
||||
{ pages, pageParams }: InfiniteData<unknown>,
|
||||
): unknown | undefined {
|
||||
const lastIndex = pages.length - 1
|
||||
return pages.length > 0
|
||||
? options.getNextPageParam(
|
||||
pages[lastIndex],
|
||||
pages,
|
||||
pageParams[lastIndex],
|
||||
pageParams,
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
|
||||
function getPreviousPageParam(
|
||||
options: InfiniteQueryPageParamsOptions<any>,
|
||||
{ pages, pageParams }: InfiniteData<unknown>,
|
||||
): unknown | undefined {
|
||||
return pages.length > 0
|
||||
? options.getPreviousPageParam?.(pages[0], pages, pageParams[0], pageParams)
|
||||
: undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is a next page.
|
||||
*/
|
||||
export function hasNextPage(
|
||||
options: InfiniteQueryPageParamsOptions<any, any>,
|
||||
data?: InfiniteData<unknown>,
|
||||
): boolean {
|
||||
if (!data) return false
|
||||
return getNextPageParam(options, data) != null
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is a previous page.
|
||||
*/
|
||||
export function hasPreviousPage(
|
||||
options: InfiniteQueryPageParamsOptions<any, any>,
|
||||
data?: InfiniteData<unknown>,
|
||||
): boolean {
|
||||
if (!data || !options.getPreviousPageParam) return false
|
||||
return getPreviousPageParam(options, data) != null
|
||||
}
|
||||
190
node_modules/@tanstack/query-core/src/infiniteQueryObserver.ts
generated
vendored
Normal file
190
node_modules/@tanstack/query-core/src/infiniteQueryObserver.ts
generated
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
import { QueryObserver } from './queryObserver'
|
||||
import {
|
||||
hasNextPage,
|
||||
hasPreviousPage,
|
||||
infiniteQueryBehavior,
|
||||
} from './infiniteQueryBehavior'
|
||||
import type { Subscribable } from './subscribable'
|
||||
import type {
|
||||
DefaultError,
|
||||
DefaultedInfiniteQueryObserverOptions,
|
||||
FetchNextPageOptions,
|
||||
FetchPreviousPageOptions,
|
||||
InfiniteData,
|
||||
InfiniteQueryObserverBaseResult,
|
||||
InfiniteQueryObserverOptions,
|
||||
InfiniteQueryObserverResult,
|
||||
QueryKey,
|
||||
} from './types'
|
||||
import type { QueryClient } from './queryClient'
|
||||
import type { Query } from './query'
|
||||
|
||||
type InfiniteQueryObserverListener<TData, TError> = (
|
||||
result: InfiniteQueryObserverResult<TData, TError>,
|
||||
) => void
|
||||
|
||||
export class InfiniteQueryObserver<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = InfiniteData<TQueryFnData>,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = unknown,
|
||||
> extends QueryObserver<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
InfiniteData<TQueryFnData, TPageParam>,
|
||||
TQueryKey
|
||||
> {
|
||||
// Type override
|
||||
subscribe!: Subscribable<
|
||||
InfiniteQueryObserverListener<TData, TError>
|
||||
>['subscribe']
|
||||
|
||||
// Type override
|
||||
getCurrentResult!: ReplaceReturnType<
|
||||
QueryObserver<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
InfiniteData<TQueryFnData, TPageParam>,
|
||||
TQueryKey
|
||||
>['getCurrentResult'],
|
||||
InfiniteQueryObserverResult<TData, TError>
|
||||
>
|
||||
|
||||
// Type override
|
||||
protected fetch!: ReplaceReturnType<
|
||||
QueryObserver<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
InfiniteData<TQueryFnData, TPageParam>,
|
||||
TQueryKey
|
||||
>['fetch'],
|
||||
Promise<InfiniteQueryObserverResult<TData, TError>>
|
||||
>
|
||||
|
||||
constructor(
|
||||
client: QueryClient,
|
||||
options: InfiniteQueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>,
|
||||
) {
|
||||
super(client, options)
|
||||
}
|
||||
|
||||
protected bindMethods(): void {
|
||||
super.bindMethods()
|
||||
this.fetchNextPage = this.fetchNextPage.bind(this)
|
||||
this.fetchPreviousPage = this.fetchPreviousPage.bind(this)
|
||||
}
|
||||
|
||||
setOptions(
|
||||
options: InfiniteQueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>,
|
||||
): void {
|
||||
super.setOptions({
|
||||
...options,
|
||||
behavior: infiniteQueryBehavior(),
|
||||
})
|
||||
}
|
||||
|
||||
getOptimisticResult(
|
||||
options: DefaultedInfiniteQueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>,
|
||||
): InfiniteQueryObserverResult<TData, TError> {
|
||||
options.behavior = infiniteQueryBehavior()
|
||||
return super.getOptimisticResult(options) as InfiniteQueryObserverResult<
|
||||
TData,
|
||||
TError
|
||||
>
|
||||
}
|
||||
|
||||
fetchNextPage(
|
||||
options?: FetchNextPageOptions,
|
||||
): Promise<InfiniteQueryObserverResult<TData, TError>> {
|
||||
return this.fetch({
|
||||
...options,
|
||||
meta: {
|
||||
fetchMore: { direction: 'forward' },
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fetchPreviousPage(
|
||||
options?: FetchPreviousPageOptions,
|
||||
): Promise<InfiniteQueryObserverResult<TData, TError>> {
|
||||
return this.fetch({
|
||||
...options,
|
||||
meta: {
|
||||
fetchMore: { direction: 'backward' },
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
protected createResult(
|
||||
query: Query<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
InfiniteData<TQueryFnData, TPageParam>,
|
||||
TQueryKey
|
||||
>,
|
||||
options: InfiniteQueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>,
|
||||
): InfiniteQueryObserverResult<TData, TError> {
|
||||
const { state } = query
|
||||
const parentResult = super.createResult(query, options)
|
||||
|
||||
const { isFetching, isRefetching, isError, isRefetchError } = parentResult
|
||||
const fetchDirection = state.fetchMeta?.fetchMore?.direction
|
||||
|
||||
const isFetchNextPageError = isError && fetchDirection === 'forward'
|
||||
const isFetchingNextPage = isFetching && fetchDirection === 'forward'
|
||||
|
||||
const isFetchPreviousPageError = isError && fetchDirection === 'backward'
|
||||
const isFetchingPreviousPage = isFetching && fetchDirection === 'backward'
|
||||
|
||||
const result: InfiniteQueryObserverBaseResult<TData, TError> = {
|
||||
...parentResult,
|
||||
fetchNextPage: this.fetchNextPage,
|
||||
fetchPreviousPage: this.fetchPreviousPage,
|
||||
hasNextPage: hasNextPage(options, state.data),
|
||||
hasPreviousPage: hasPreviousPage(options, state.data),
|
||||
isFetchNextPageError,
|
||||
isFetchingNextPage,
|
||||
isFetchPreviousPageError,
|
||||
isFetchingPreviousPage,
|
||||
isRefetchError:
|
||||
isRefetchError && !isFetchNextPageError && !isFetchPreviousPageError,
|
||||
isRefetching:
|
||||
isRefetching && !isFetchingNextPage && !isFetchingPreviousPage,
|
||||
}
|
||||
|
||||
return result as InfiniteQueryObserverResult<TData, TError>
|
||||
}
|
||||
}
|
||||
|
||||
type ReplaceReturnType<
|
||||
TFunction extends (...args: Array<any>) => unknown,
|
||||
TReturn,
|
||||
> = (...args: Parameters<TFunction>) => TReturn
|
||||
403
node_modules/@tanstack/query-core/src/mutation.ts
generated
vendored
Normal file
403
node_modules/@tanstack/query-core/src/mutation.ts
generated
vendored
Normal file
@@ -0,0 +1,403 @@
|
||||
import { notifyManager } from './notifyManager'
|
||||
import { Removable } from './removable'
|
||||
import { createRetryer } from './retryer'
|
||||
import type {
|
||||
DefaultError,
|
||||
MutationFunctionContext,
|
||||
MutationMeta,
|
||||
MutationOptions,
|
||||
MutationStatus,
|
||||
} from './types'
|
||||
import type { MutationCache } from './mutationCache'
|
||||
import type { MutationObserver } from './mutationObserver'
|
||||
import type { Retryer } from './retryer'
|
||||
import type { QueryClient } from './queryClient'
|
||||
|
||||
// TYPES
|
||||
|
||||
interface MutationConfig<TData, TError, TVariables, TOnMutateResult> {
|
||||
client: QueryClient
|
||||
mutationId: number
|
||||
mutationCache: MutationCache
|
||||
options: MutationOptions<TData, TError, TVariables, TOnMutateResult>
|
||||
state?: MutationState<TData, TError, TVariables, TOnMutateResult>
|
||||
}
|
||||
|
||||
export interface MutationState<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
TVariables = unknown,
|
||||
TOnMutateResult = unknown,
|
||||
> {
|
||||
context: TOnMutateResult | undefined
|
||||
data: TData | undefined
|
||||
error: TError | null
|
||||
failureCount: number
|
||||
failureReason: TError | null
|
||||
isPaused: boolean
|
||||
status: MutationStatus
|
||||
variables: TVariables | undefined
|
||||
submittedAt: number
|
||||
}
|
||||
|
||||
interface FailedAction<TError> {
|
||||
type: 'failed'
|
||||
failureCount: number
|
||||
error: TError | null
|
||||
}
|
||||
|
||||
interface PendingAction<TVariables, TOnMutateResult> {
|
||||
type: 'pending'
|
||||
isPaused: boolean
|
||||
variables?: TVariables
|
||||
context?: TOnMutateResult
|
||||
}
|
||||
|
||||
interface SuccessAction<TData> {
|
||||
type: 'success'
|
||||
data: TData
|
||||
}
|
||||
|
||||
interface ErrorAction<TError> {
|
||||
type: 'error'
|
||||
error: TError
|
||||
}
|
||||
|
||||
interface PauseAction {
|
||||
type: 'pause'
|
||||
}
|
||||
|
||||
interface ContinueAction {
|
||||
type: 'continue'
|
||||
}
|
||||
|
||||
export type Action<TData, TError, TVariables, TOnMutateResult> =
|
||||
| ContinueAction
|
||||
| ErrorAction<TError>
|
||||
| FailedAction<TError>
|
||||
| PendingAction<TVariables, TOnMutateResult>
|
||||
| PauseAction
|
||||
| SuccessAction<TData>
|
||||
|
||||
// CLASS
|
||||
|
||||
export class Mutation<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
TVariables = unknown,
|
||||
TOnMutateResult = unknown,
|
||||
> extends Removable {
|
||||
state: MutationState<TData, TError, TVariables, TOnMutateResult>
|
||||
options!: MutationOptions<TData, TError, TVariables, TOnMutateResult>
|
||||
readonly mutationId: number
|
||||
|
||||
#client: QueryClient
|
||||
#observers: Array<
|
||||
MutationObserver<TData, TError, TVariables, TOnMutateResult>
|
||||
>
|
||||
#mutationCache: MutationCache
|
||||
#retryer?: Retryer<TData>
|
||||
|
||||
constructor(
|
||||
config: MutationConfig<TData, TError, TVariables, TOnMutateResult>,
|
||||
) {
|
||||
super()
|
||||
|
||||
this.#client = config.client
|
||||
this.mutationId = config.mutationId
|
||||
this.#mutationCache = config.mutationCache
|
||||
this.#observers = []
|
||||
this.state = config.state || getDefaultState()
|
||||
|
||||
this.setOptions(config.options)
|
||||
this.scheduleGc()
|
||||
}
|
||||
|
||||
setOptions(
|
||||
options: MutationOptions<TData, TError, TVariables, TOnMutateResult>,
|
||||
): void {
|
||||
this.options = options
|
||||
|
||||
this.updateGcTime(this.options.gcTime)
|
||||
}
|
||||
|
||||
get meta(): MutationMeta | undefined {
|
||||
return this.options.meta
|
||||
}
|
||||
|
||||
addObserver(observer: MutationObserver<any, any, any, any>): void {
|
||||
if (!this.#observers.includes(observer)) {
|
||||
this.#observers.push(observer)
|
||||
|
||||
// Stop the mutation from being garbage collected
|
||||
this.clearGcTimeout()
|
||||
|
||||
this.#mutationCache.notify({
|
||||
type: 'observerAdded',
|
||||
mutation: this,
|
||||
observer,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
removeObserver(observer: MutationObserver<any, any, any, any>): void {
|
||||
this.#observers = this.#observers.filter((x) => x !== observer)
|
||||
|
||||
this.scheduleGc()
|
||||
|
||||
this.#mutationCache.notify({
|
||||
type: 'observerRemoved',
|
||||
mutation: this,
|
||||
observer,
|
||||
})
|
||||
}
|
||||
|
||||
protected optionalRemove() {
|
||||
if (!this.#observers.length) {
|
||||
if (this.state.status === 'pending') {
|
||||
this.scheduleGc()
|
||||
} else {
|
||||
this.#mutationCache.remove(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
continue(): Promise<unknown> {
|
||||
return (
|
||||
this.#retryer?.continue() ??
|
||||
// continuing a mutation assumes that variables are set, mutation must have been dehydrated before
|
||||
this.execute(this.state.variables!)
|
||||
)
|
||||
}
|
||||
|
||||
async execute(variables: TVariables): Promise<TData> {
|
||||
const onContinue = () => {
|
||||
this.#dispatch({ type: 'continue' })
|
||||
}
|
||||
|
||||
const mutationFnContext = {
|
||||
client: this.#client,
|
||||
meta: this.options.meta,
|
||||
mutationKey: this.options.mutationKey,
|
||||
} satisfies MutationFunctionContext
|
||||
|
||||
this.#retryer = createRetryer({
|
||||
fn: () => {
|
||||
if (!this.options.mutationFn) {
|
||||
return Promise.reject(new Error('No mutationFn found'))
|
||||
}
|
||||
|
||||
return this.options.mutationFn(variables, mutationFnContext)
|
||||
},
|
||||
onFail: (failureCount, error) => {
|
||||
this.#dispatch({ type: 'failed', failureCount, error })
|
||||
},
|
||||
onPause: () => {
|
||||
this.#dispatch({ type: 'pause' })
|
||||
},
|
||||
onContinue,
|
||||
retry: this.options.retry ?? 0,
|
||||
retryDelay: this.options.retryDelay,
|
||||
networkMode: this.options.networkMode,
|
||||
canRun: () => this.#mutationCache.canRun(this),
|
||||
})
|
||||
|
||||
const restored = this.state.status === 'pending'
|
||||
const isPaused = !this.#retryer.canStart()
|
||||
|
||||
try {
|
||||
if (restored) {
|
||||
// Dispatch continue action to unpause restored mutation
|
||||
onContinue()
|
||||
} else {
|
||||
this.#dispatch({ type: 'pending', variables, isPaused })
|
||||
// Notify cache callback
|
||||
await this.#mutationCache.config.onMutate?.(
|
||||
variables,
|
||||
this as Mutation<unknown, unknown, unknown, unknown>,
|
||||
mutationFnContext,
|
||||
)
|
||||
const context = await this.options.onMutate?.(
|
||||
variables,
|
||||
mutationFnContext,
|
||||
)
|
||||
if (context !== this.state.context) {
|
||||
this.#dispatch({
|
||||
type: 'pending',
|
||||
context,
|
||||
variables,
|
||||
isPaused,
|
||||
})
|
||||
}
|
||||
}
|
||||
const data = await this.#retryer.start()
|
||||
|
||||
// Notify cache callback
|
||||
await this.#mutationCache.config.onSuccess?.(
|
||||
data,
|
||||
variables,
|
||||
this.state.context,
|
||||
this as Mutation<unknown, unknown, unknown, unknown>,
|
||||
mutationFnContext,
|
||||
)
|
||||
|
||||
await this.options.onSuccess?.(
|
||||
data,
|
||||
variables,
|
||||
this.state.context!,
|
||||
mutationFnContext,
|
||||
)
|
||||
|
||||
// Notify cache callback
|
||||
await this.#mutationCache.config.onSettled?.(
|
||||
data,
|
||||
null,
|
||||
this.state.variables,
|
||||
this.state.context,
|
||||
this as Mutation<unknown, unknown, unknown, unknown>,
|
||||
mutationFnContext,
|
||||
)
|
||||
|
||||
await this.options.onSettled?.(
|
||||
data,
|
||||
null,
|
||||
variables,
|
||||
this.state.context,
|
||||
mutationFnContext,
|
||||
)
|
||||
|
||||
this.#dispatch({ type: 'success', data })
|
||||
return data
|
||||
} catch (error) {
|
||||
try {
|
||||
// Notify cache callback
|
||||
await this.#mutationCache.config.onError?.(
|
||||
error as any,
|
||||
variables,
|
||||
this.state.context,
|
||||
this as Mutation<unknown, unknown, unknown, unknown>,
|
||||
mutationFnContext,
|
||||
)
|
||||
|
||||
await this.options.onError?.(
|
||||
error as TError,
|
||||
variables,
|
||||
this.state.context,
|
||||
mutationFnContext,
|
||||
)
|
||||
|
||||
// Notify cache callback
|
||||
await this.#mutationCache.config.onSettled?.(
|
||||
undefined,
|
||||
error as any,
|
||||
this.state.variables,
|
||||
this.state.context,
|
||||
this as Mutation<unknown, unknown, unknown, unknown>,
|
||||
mutationFnContext,
|
||||
)
|
||||
|
||||
await this.options.onSettled?.(
|
||||
undefined,
|
||||
error as TError,
|
||||
variables,
|
||||
this.state.context,
|
||||
mutationFnContext,
|
||||
)
|
||||
throw error
|
||||
} finally {
|
||||
this.#dispatch({ type: 'error', error: error as TError })
|
||||
}
|
||||
} finally {
|
||||
this.#mutationCache.runNext(this)
|
||||
}
|
||||
}
|
||||
|
||||
#dispatch(action: Action<TData, TError, TVariables, TOnMutateResult>): void {
|
||||
const reducer = (
|
||||
state: MutationState<TData, TError, TVariables, TOnMutateResult>,
|
||||
): MutationState<TData, TError, TVariables, TOnMutateResult> => {
|
||||
switch (action.type) {
|
||||
case 'failed':
|
||||
return {
|
||||
...state,
|
||||
failureCount: action.failureCount,
|
||||
failureReason: action.error,
|
||||
}
|
||||
case 'pause':
|
||||
return {
|
||||
...state,
|
||||
isPaused: true,
|
||||
}
|
||||
case 'continue':
|
||||
return {
|
||||
...state,
|
||||
isPaused: false,
|
||||
}
|
||||
case 'pending':
|
||||
return {
|
||||
...state,
|
||||
context: action.context,
|
||||
data: undefined,
|
||||
failureCount: 0,
|
||||
failureReason: null,
|
||||
error: null,
|
||||
isPaused: action.isPaused,
|
||||
status: 'pending',
|
||||
variables: action.variables,
|
||||
submittedAt: Date.now(),
|
||||
}
|
||||
case 'success':
|
||||
return {
|
||||
...state,
|
||||
data: action.data,
|
||||
failureCount: 0,
|
||||
failureReason: null,
|
||||
error: null,
|
||||
status: 'success',
|
||||
isPaused: false,
|
||||
}
|
||||
case 'error':
|
||||
return {
|
||||
...state,
|
||||
data: undefined,
|
||||
error: action.error,
|
||||
failureCount: state.failureCount + 1,
|
||||
failureReason: action.error,
|
||||
isPaused: false,
|
||||
status: 'error',
|
||||
}
|
||||
}
|
||||
}
|
||||
this.state = reducer(this.state)
|
||||
|
||||
notifyManager.batch(() => {
|
||||
this.#observers.forEach((observer) => {
|
||||
observer.onMutationUpdate(action)
|
||||
})
|
||||
this.#mutationCache.notify({
|
||||
mutation: this,
|
||||
type: 'updated',
|
||||
action,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function getDefaultState<
|
||||
TData,
|
||||
TError,
|
||||
TVariables,
|
||||
TOnMutateResult,
|
||||
>(): MutationState<TData, TError, TVariables, TOnMutateResult> {
|
||||
return {
|
||||
context: undefined,
|
||||
data: undefined,
|
||||
error: null,
|
||||
failureCount: 0,
|
||||
failureReason: null,
|
||||
isPaused: false,
|
||||
status: 'idle',
|
||||
variables: undefined,
|
||||
submittedAt: 0,
|
||||
}
|
||||
}
|
||||
244
node_modules/@tanstack/query-core/src/mutationCache.ts
generated
vendored
Normal file
244
node_modules/@tanstack/query-core/src/mutationCache.ts
generated
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
import { notifyManager } from './notifyManager'
|
||||
import { Mutation } from './mutation'
|
||||
import { matchMutation, noop } from './utils'
|
||||
import { Subscribable } from './subscribable'
|
||||
import type { MutationObserver } from './mutationObserver'
|
||||
import type {
|
||||
DefaultError,
|
||||
MutationFunctionContext,
|
||||
MutationOptions,
|
||||
NotifyEvent,
|
||||
} from './types'
|
||||
import type { QueryClient } from './queryClient'
|
||||
import type { Action, MutationState } from './mutation'
|
||||
import type { MutationFilters } from './utils'
|
||||
|
||||
// TYPES
|
||||
|
||||
interface MutationCacheConfig {
|
||||
onError?: (
|
||||
error: DefaultError,
|
||||
variables: unknown,
|
||||
onMutateResult: unknown,
|
||||
mutation: Mutation<unknown, unknown, unknown>,
|
||||
context: MutationFunctionContext,
|
||||
) => Promise<unknown> | unknown
|
||||
onSuccess?: (
|
||||
data: unknown,
|
||||
variables: unknown,
|
||||
onMutateResult: unknown,
|
||||
mutation: Mutation<unknown, unknown, unknown>,
|
||||
context: MutationFunctionContext,
|
||||
) => Promise<unknown> | unknown
|
||||
onMutate?: (
|
||||
variables: unknown,
|
||||
mutation: Mutation<unknown, unknown, unknown>,
|
||||
context: MutationFunctionContext,
|
||||
) => Promise<unknown> | unknown
|
||||
onSettled?: (
|
||||
data: unknown | undefined,
|
||||
error: DefaultError | null,
|
||||
variables: unknown,
|
||||
onMutateResult: unknown,
|
||||
mutation: Mutation<unknown, unknown, unknown>,
|
||||
context: MutationFunctionContext,
|
||||
) => Promise<unknown> | unknown
|
||||
}
|
||||
|
||||
interface NotifyEventMutationAdded extends NotifyEvent {
|
||||
type: 'added'
|
||||
mutation: Mutation<any, any, any, any>
|
||||
}
|
||||
interface NotifyEventMutationRemoved extends NotifyEvent {
|
||||
type: 'removed'
|
||||
mutation: Mutation<any, any, any, any>
|
||||
}
|
||||
|
||||
interface NotifyEventMutationObserverAdded extends NotifyEvent {
|
||||
type: 'observerAdded'
|
||||
mutation: Mutation<any, any, any, any>
|
||||
observer: MutationObserver<any, any, any>
|
||||
}
|
||||
|
||||
interface NotifyEventMutationObserverRemoved extends NotifyEvent {
|
||||
type: 'observerRemoved'
|
||||
mutation: Mutation<any, any, any, any>
|
||||
observer: MutationObserver<any, any, any>
|
||||
}
|
||||
|
||||
interface NotifyEventMutationObserverOptionsUpdated extends NotifyEvent {
|
||||
type: 'observerOptionsUpdated'
|
||||
mutation?: Mutation<any, any, any, any>
|
||||
observer: MutationObserver<any, any, any, any>
|
||||
}
|
||||
|
||||
interface NotifyEventMutationUpdated extends NotifyEvent {
|
||||
type: 'updated'
|
||||
mutation: Mutation<any, any, any, any>
|
||||
action: Action<any, any, any, any>
|
||||
}
|
||||
|
||||
export type MutationCacheNotifyEvent =
|
||||
| NotifyEventMutationAdded
|
||||
| NotifyEventMutationRemoved
|
||||
| NotifyEventMutationObserverAdded
|
||||
| NotifyEventMutationObserverRemoved
|
||||
| NotifyEventMutationObserverOptionsUpdated
|
||||
| NotifyEventMutationUpdated
|
||||
|
||||
type MutationCacheListener = (event: MutationCacheNotifyEvent) => void
|
||||
|
||||
// CLASS
|
||||
|
||||
export class MutationCache extends Subscribable<MutationCacheListener> {
|
||||
#mutations: Set<Mutation<any, any, any, any>>
|
||||
#scopes: Map<string, Array<Mutation<any, any, any, any>>>
|
||||
#mutationId: number
|
||||
|
||||
constructor(public config: MutationCacheConfig = {}) {
|
||||
super()
|
||||
this.#mutations = new Set()
|
||||
this.#scopes = new Map()
|
||||
this.#mutationId = 0
|
||||
}
|
||||
|
||||
build<TData, TError, TVariables, TOnMutateResult>(
|
||||
client: QueryClient,
|
||||
options: MutationOptions<TData, TError, TVariables, TOnMutateResult>,
|
||||
state?: MutationState<TData, TError, TVariables, TOnMutateResult>,
|
||||
): Mutation<TData, TError, TVariables, TOnMutateResult> {
|
||||
const mutation = new Mutation({
|
||||
client,
|
||||
mutationCache: this,
|
||||
mutationId: ++this.#mutationId,
|
||||
options: client.defaultMutationOptions(options),
|
||||
state,
|
||||
})
|
||||
|
||||
this.add(mutation)
|
||||
|
||||
return mutation
|
||||
}
|
||||
|
||||
add(mutation: Mutation<any, any, any, any>): void {
|
||||
this.#mutations.add(mutation)
|
||||
const scope = scopeFor(mutation)
|
||||
if (typeof scope === 'string') {
|
||||
const scopedMutations = this.#scopes.get(scope)
|
||||
if (scopedMutations) {
|
||||
scopedMutations.push(mutation)
|
||||
} else {
|
||||
this.#scopes.set(scope, [mutation])
|
||||
}
|
||||
}
|
||||
this.notify({ type: 'added', mutation })
|
||||
}
|
||||
|
||||
remove(mutation: Mutation<any, any, any, any>): void {
|
||||
if (this.#mutations.delete(mutation)) {
|
||||
const scope = scopeFor(mutation)
|
||||
if (typeof scope === 'string') {
|
||||
const scopedMutations = this.#scopes.get(scope)
|
||||
if (scopedMutations) {
|
||||
if (scopedMutations.length > 1) {
|
||||
const index = scopedMutations.indexOf(mutation)
|
||||
if (index !== -1) {
|
||||
scopedMutations.splice(index, 1)
|
||||
}
|
||||
} else if (scopedMutations[0] === mutation) {
|
||||
this.#scopes.delete(scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Currently we notify the removal even if the mutation was already removed.
|
||||
// Consider making this an error or not notifying of the removal depending on the desired semantics.
|
||||
this.notify({ type: 'removed', mutation })
|
||||
}
|
||||
|
||||
canRun(mutation: Mutation<any, any, any, any>): boolean {
|
||||
const scope = scopeFor(mutation)
|
||||
if (typeof scope === 'string') {
|
||||
const mutationsWithSameScope = this.#scopes.get(scope)
|
||||
const firstPendingMutation = mutationsWithSameScope?.find(
|
||||
(m) => m.state.status === 'pending',
|
||||
)
|
||||
// we can run if there is no current pending mutation (start use-case)
|
||||
// or if WE are the first pending mutation (continue use-case)
|
||||
return !firstPendingMutation || firstPendingMutation === mutation
|
||||
} else {
|
||||
// For unscoped mutations there are never any pending mutations in front of the
|
||||
// current mutation
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
runNext(mutation: Mutation<any, any, any, any>): Promise<unknown> {
|
||||
const scope = scopeFor(mutation)
|
||||
if (typeof scope === 'string') {
|
||||
const foundMutation = this.#scopes
|
||||
.get(scope)
|
||||
?.find((m) => m !== mutation && m.state.isPaused)
|
||||
|
||||
return foundMutation?.continue() ?? Promise.resolve()
|
||||
} else {
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
notifyManager.batch(() => {
|
||||
this.#mutations.forEach((mutation) => {
|
||||
this.notify({ type: 'removed', mutation })
|
||||
})
|
||||
this.#mutations.clear()
|
||||
this.#scopes.clear()
|
||||
})
|
||||
}
|
||||
|
||||
getAll(): Array<Mutation> {
|
||||
return Array.from(this.#mutations)
|
||||
}
|
||||
|
||||
find<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
TVariables = any,
|
||||
TOnMutateResult = unknown,
|
||||
>(
|
||||
filters: MutationFilters,
|
||||
): Mutation<TData, TError, TVariables, TOnMutateResult> | undefined {
|
||||
const defaultedFilters = { exact: true, ...filters }
|
||||
|
||||
return this.getAll().find((mutation) =>
|
||||
matchMutation(defaultedFilters, mutation),
|
||||
) as Mutation<TData, TError, TVariables, TOnMutateResult> | undefined
|
||||
}
|
||||
|
||||
findAll(filters: MutationFilters = {}): Array<Mutation> {
|
||||
return this.getAll().filter((mutation) => matchMutation(filters, mutation))
|
||||
}
|
||||
|
||||
notify(event: MutationCacheNotifyEvent) {
|
||||
notifyManager.batch(() => {
|
||||
this.listeners.forEach((listener) => {
|
||||
listener(event)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
resumePausedMutations(): Promise<unknown> {
|
||||
const pausedMutations = this.getAll().filter((x) => x.state.isPaused)
|
||||
|
||||
return notifyManager.batch(() =>
|
||||
Promise.all(
|
||||
pausedMutations.map((mutation) => mutation.continue().catch(noop)),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function scopeFor(mutation: Mutation<any, any, any, any>) {
|
||||
return mutation.options.scope?.id
|
||||
}
|
||||
211
node_modules/@tanstack/query-core/src/mutationObserver.ts
generated
vendored
Normal file
211
node_modules/@tanstack/query-core/src/mutationObserver.ts
generated
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
import { getDefaultState } from './mutation'
|
||||
import { notifyManager } from './notifyManager'
|
||||
import { Subscribable } from './subscribable'
|
||||
import { hashKey, shallowEqualObjects } from './utils'
|
||||
import type { QueryClient } from './queryClient'
|
||||
import type {
|
||||
DefaultError,
|
||||
MutateOptions,
|
||||
MutationFunctionContext,
|
||||
MutationObserverOptions,
|
||||
MutationObserverResult,
|
||||
} from './types'
|
||||
import type { Action, Mutation } from './mutation'
|
||||
|
||||
// TYPES
|
||||
|
||||
type MutationObserverListener<TData, TError, TVariables, TOnMutateResult> = (
|
||||
result: MutationObserverResult<TData, TError, TVariables, TOnMutateResult>,
|
||||
) => void
|
||||
|
||||
// CLASS
|
||||
|
||||
export class MutationObserver<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
TVariables = void,
|
||||
TOnMutateResult = unknown,
|
||||
> extends Subscribable<
|
||||
MutationObserverListener<TData, TError, TVariables, TOnMutateResult>
|
||||
> {
|
||||
options!: MutationObserverOptions<TData, TError, TVariables, TOnMutateResult>
|
||||
|
||||
#client: QueryClient
|
||||
#currentResult: MutationObserverResult<
|
||||
TData,
|
||||
TError,
|
||||
TVariables,
|
||||
TOnMutateResult
|
||||
> = undefined!
|
||||
#currentMutation?: Mutation<TData, TError, TVariables, TOnMutateResult>
|
||||
#mutateOptions?: MutateOptions<TData, TError, TVariables, TOnMutateResult>
|
||||
|
||||
constructor(
|
||||
client: QueryClient,
|
||||
options: MutationObserverOptions<
|
||||
TData,
|
||||
TError,
|
||||
TVariables,
|
||||
TOnMutateResult
|
||||
>,
|
||||
) {
|
||||
super()
|
||||
|
||||
this.#client = client
|
||||
this.setOptions(options)
|
||||
this.bindMethods()
|
||||
this.#updateResult()
|
||||
}
|
||||
|
||||
protected bindMethods(): void {
|
||||
this.mutate = this.mutate.bind(this)
|
||||
this.reset = this.reset.bind(this)
|
||||
}
|
||||
|
||||
setOptions(
|
||||
options: MutationObserverOptions<
|
||||
TData,
|
||||
TError,
|
||||
TVariables,
|
||||
TOnMutateResult
|
||||
>,
|
||||
) {
|
||||
const prevOptions = this.options as
|
||||
| MutationObserverOptions<TData, TError, TVariables, TOnMutateResult>
|
||||
| undefined
|
||||
this.options = this.#client.defaultMutationOptions(options)
|
||||
if (!shallowEqualObjects(this.options, prevOptions)) {
|
||||
this.#client.getMutationCache().notify({
|
||||
type: 'observerOptionsUpdated',
|
||||
mutation: this.#currentMutation,
|
||||
observer: this,
|
||||
})
|
||||
}
|
||||
|
||||
if (
|
||||
prevOptions?.mutationKey &&
|
||||
this.options.mutationKey &&
|
||||
hashKey(prevOptions.mutationKey) !== hashKey(this.options.mutationKey)
|
||||
) {
|
||||
this.reset()
|
||||
} else if (this.#currentMutation?.state.status === 'pending') {
|
||||
this.#currentMutation.setOptions(this.options)
|
||||
}
|
||||
}
|
||||
|
||||
protected onUnsubscribe(): void {
|
||||
if (!this.hasListeners()) {
|
||||
this.#currentMutation?.removeObserver(this)
|
||||
}
|
||||
}
|
||||
|
||||
onMutationUpdate(
|
||||
action: Action<TData, TError, TVariables, TOnMutateResult>,
|
||||
): void {
|
||||
this.#updateResult()
|
||||
|
||||
this.#notify(action)
|
||||
}
|
||||
|
||||
getCurrentResult(): MutationObserverResult<
|
||||
TData,
|
||||
TError,
|
||||
TVariables,
|
||||
TOnMutateResult
|
||||
> {
|
||||
return this.#currentResult
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
// reset needs to remove the observer from the mutation because there is no way to "get it back"
|
||||
// another mutate call will yield a new mutation!
|
||||
this.#currentMutation?.removeObserver(this)
|
||||
this.#currentMutation = undefined
|
||||
this.#updateResult()
|
||||
this.#notify()
|
||||
}
|
||||
|
||||
mutate(
|
||||
variables: TVariables,
|
||||
options?: MutateOptions<TData, TError, TVariables, TOnMutateResult>,
|
||||
): Promise<TData> {
|
||||
this.#mutateOptions = options
|
||||
|
||||
this.#currentMutation?.removeObserver(this)
|
||||
|
||||
this.#currentMutation = this.#client
|
||||
.getMutationCache()
|
||||
.build(this.#client, this.options)
|
||||
|
||||
this.#currentMutation.addObserver(this)
|
||||
|
||||
return this.#currentMutation.execute(variables)
|
||||
}
|
||||
|
||||
#updateResult(): void {
|
||||
const state =
|
||||
this.#currentMutation?.state ??
|
||||
getDefaultState<TData, TError, TVariables, TOnMutateResult>()
|
||||
|
||||
this.#currentResult = {
|
||||
...state,
|
||||
isPending: state.status === 'pending',
|
||||
isSuccess: state.status === 'success',
|
||||
isError: state.status === 'error',
|
||||
isIdle: state.status === 'idle',
|
||||
mutate: this.mutate,
|
||||
reset: this.reset,
|
||||
} as MutationObserverResult<TData, TError, TVariables, TOnMutateResult>
|
||||
}
|
||||
|
||||
#notify(action?: Action<TData, TError, TVariables, TOnMutateResult>): void {
|
||||
notifyManager.batch(() => {
|
||||
// First trigger the mutate callbacks
|
||||
if (this.#mutateOptions && this.hasListeners()) {
|
||||
const variables = this.#currentResult.variables!
|
||||
const onMutateResult = this.#currentResult.context
|
||||
|
||||
const context = {
|
||||
client: this.#client,
|
||||
meta: this.options.meta,
|
||||
mutationKey: this.options.mutationKey,
|
||||
} satisfies MutationFunctionContext
|
||||
|
||||
if (action?.type === 'success') {
|
||||
this.#mutateOptions.onSuccess?.(
|
||||
action.data,
|
||||
variables,
|
||||
onMutateResult,
|
||||
context,
|
||||
)
|
||||
this.#mutateOptions.onSettled?.(
|
||||
action.data,
|
||||
null,
|
||||
variables,
|
||||
onMutateResult,
|
||||
context,
|
||||
)
|
||||
} else if (action?.type === 'error') {
|
||||
this.#mutateOptions.onError?.(
|
||||
action.error,
|
||||
variables,
|
||||
onMutateResult,
|
||||
context,
|
||||
)
|
||||
this.#mutateOptions.onSettled?.(
|
||||
undefined,
|
||||
action.error,
|
||||
variables,
|
||||
onMutateResult,
|
||||
context,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Then trigger the listeners
|
||||
this.listeners.forEach((listener) => {
|
||||
listener(this.#currentResult)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
99
node_modules/@tanstack/query-core/src/notifyManager.ts
generated
vendored
Normal file
99
node_modules/@tanstack/query-core/src/notifyManager.ts
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
// TYPES
|
||||
|
||||
import { systemSetTimeoutZero } from './timeoutManager'
|
||||
|
||||
type NotifyCallback = () => void
|
||||
|
||||
type NotifyFunction = (callback: () => void) => void
|
||||
|
||||
type BatchNotifyFunction = (callback: () => void) => void
|
||||
|
||||
type BatchCallsCallback<T extends Array<unknown>> = (...args: T) => void
|
||||
|
||||
type ScheduleFunction = (callback: () => void) => void
|
||||
|
||||
export const defaultScheduler: ScheduleFunction = systemSetTimeoutZero
|
||||
|
||||
export function createNotifyManager() {
|
||||
let queue: Array<NotifyCallback> = []
|
||||
let transactions = 0
|
||||
let notifyFn: NotifyFunction = (callback) => {
|
||||
callback()
|
||||
}
|
||||
let batchNotifyFn: BatchNotifyFunction = (callback: () => void) => {
|
||||
callback()
|
||||
}
|
||||
let scheduleFn = defaultScheduler
|
||||
|
||||
const schedule = (callback: NotifyCallback): void => {
|
||||
if (transactions) {
|
||||
queue.push(callback)
|
||||
} else {
|
||||
scheduleFn(() => {
|
||||
notifyFn(callback)
|
||||
})
|
||||
}
|
||||
}
|
||||
const flush = (): void => {
|
||||
const originalQueue = queue
|
||||
queue = []
|
||||
if (originalQueue.length) {
|
||||
scheduleFn(() => {
|
||||
batchNotifyFn(() => {
|
||||
originalQueue.forEach((callback) => {
|
||||
notifyFn(callback)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
batch: <T>(callback: () => T): T => {
|
||||
let result
|
||||
transactions++
|
||||
try {
|
||||
result = callback()
|
||||
} finally {
|
||||
transactions--
|
||||
if (!transactions) {
|
||||
flush()
|
||||
}
|
||||
}
|
||||
return result
|
||||
},
|
||||
/**
|
||||
* All calls to the wrapped function will be batched.
|
||||
*/
|
||||
batchCalls: <T extends Array<unknown>>(
|
||||
callback: BatchCallsCallback<T>,
|
||||
): BatchCallsCallback<T> => {
|
||||
return (...args) => {
|
||||
schedule(() => {
|
||||
callback(...args)
|
||||
})
|
||||
}
|
||||
},
|
||||
schedule,
|
||||
/**
|
||||
* Use this method to set a custom notify function.
|
||||
* This can be used to for example wrap notifications with `React.act` while running tests.
|
||||
*/
|
||||
setNotifyFunction: (fn: NotifyFunction) => {
|
||||
notifyFn = fn
|
||||
},
|
||||
/**
|
||||
* Use this method to set a custom function to batch notifications together into a single tick.
|
||||
* By default React Query will use the batch function provided by ReactDOM or React Native.
|
||||
*/
|
||||
setBatchNotifyFunction: (fn: BatchNotifyFunction) => {
|
||||
batchNotifyFn = fn
|
||||
},
|
||||
setScheduler: (fn: ScheduleFunction) => {
|
||||
scheduleFn = fn
|
||||
},
|
||||
} as const
|
||||
}
|
||||
|
||||
// SINGLETON
|
||||
export const notifyManager = createNotifyManager()
|
||||
71
node_modules/@tanstack/query-core/src/onlineManager.ts
generated
vendored
Normal file
71
node_modules/@tanstack/query-core/src/onlineManager.ts
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
import { Subscribable } from './subscribable'
|
||||
import { isServer } from './utils'
|
||||
|
||||
type Listener = (online: boolean) => void
|
||||
type SetupFn = (setOnline: Listener) => (() => void) | undefined
|
||||
|
||||
export class OnlineManager extends Subscribable<Listener> {
|
||||
#online = true
|
||||
#cleanup?: () => void
|
||||
|
||||
#setup: SetupFn
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.#setup = (onOnline) => {
|
||||
// addEventListener does not exist in React Native, but window does
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (!isServer && window.addEventListener) {
|
||||
const onlineListener = () => onOnline(true)
|
||||
const offlineListener = () => onOnline(false)
|
||||
// Listen to online
|
||||
window.addEventListener('online', onlineListener, false)
|
||||
window.addEventListener('offline', offlineListener, false)
|
||||
|
||||
return () => {
|
||||
// Be sure to unsubscribe if a new handler is set
|
||||
window.removeEventListener('online', onlineListener)
|
||||
window.removeEventListener('offline', offlineListener)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
protected onSubscribe(): void {
|
||||
if (!this.#cleanup) {
|
||||
this.setEventListener(this.#setup)
|
||||
}
|
||||
}
|
||||
|
||||
protected onUnsubscribe() {
|
||||
if (!this.hasListeners()) {
|
||||
this.#cleanup?.()
|
||||
this.#cleanup = undefined
|
||||
}
|
||||
}
|
||||
|
||||
setEventListener(setup: SetupFn): void {
|
||||
this.#setup = setup
|
||||
this.#cleanup?.()
|
||||
this.#cleanup = setup(this.setOnline.bind(this))
|
||||
}
|
||||
|
||||
setOnline(online: boolean): void {
|
||||
const changed = this.#online !== online
|
||||
|
||||
if (changed) {
|
||||
this.#online = online
|
||||
this.listeners.forEach((listener) => {
|
||||
listener(online)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
isOnline(): boolean {
|
||||
return this.#online
|
||||
}
|
||||
}
|
||||
|
||||
export const onlineManager = new OnlineManager()
|
||||
291
node_modules/@tanstack/query-core/src/queriesObserver.ts
generated
vendored
Normal file
291
node_modules/@tanstack/query-core/src/queriesObserver.ts
generated
vendored
Normal file
@@ -0,0 +1,291 @@
|
||||
import { notifyManager } from './notifyManager'
|
||||
import { QueryObserver } from './queryObserver'
|
||||
import { Subscribable } from './subscribable'
|
||||
import { replaceEqualDeep, shallowEqualObjects } from './utils'
|
||||
import type {
|
||||
DefaultedQueryObserverOptions,
|
||||
QueryObserverOptions,
|
||||
QueryObserverResult,
|
||||
} from './types'
|
||||
import type { QueryClient } from './queryClient'
|
||||
|
||||
function difference<T>(array1: Array<T>, array2: Array<T>): Array<T> {
|
||||
const excludeSet = new Set(array2)
|
||||
return array1.filter((x) => !excludeSet.has(x))
|
||||
}
|
||||
|
||||
function replaceAt<T>(array: Array<T>, index: number, value: T): Array<T> {
|
||||
const copy = array.slice(0)
|
||||
copy[index] = value
|
||||
return copy
|
||||
}
|
||||
|
||||
type QueriesObserverListener = (result: Array<QueryObserverResult>) => void
|
||||
|
||||
type CombineFn<TCombinedResult> = (
|
||||
result: Array<QueryObserverResult>,
|
||||
) => TCombinedResult
|
||||
|
||||
export interface QueriesObserverOptions<
|
||||
TCombinedResult = Array<QueryObserverResult>,
|
||||
> {
|
||||
combine?: CombineFn<TCombinedResult>
|
||||
}
|
||||
|
||||
export class QueriesObserver<
|
||||
TCombinedResult = Array<QueryObserverResult>,
|
||||
> extends Subscribable<QueriesObserverListener> {
|
||||
#client: QueryClient
|
||||
#result!: Array<QueryObserverResult>
|
||||
#queries: Array<QueryObserverOptions>
|
||||
#options?: QueriesObserverOptions<TCombinedResult>
|
||||
#observers: Array<QueryObserver>
|
||||
#combinedResult?: TCombinedResult
|
||||
#lastCombine?: CombineFn<TCombinedResult>
|
||||
#lastResult?: Array<QueryObserverResult>
|
||||
#observerMatches: Array<QueryObserverMatch> = []
|
||||
|
||||
constructor(
|
||||
client: QueryClient,
|
||||
queries: Array<QueryObserverOptions<any, any, any, any, any>>,
|
||||
options?: QueriesObserverOptions<TCombinedResult>,
|
||||
) {
|
||||
super()
|
||||
|
||||
this.#client = client
|
||||
this.#options = options
|
||||
this.#queries = []
|
||||
this.#observers = []
|
||||
this.#result = []
|
||||
|
||||
this.setQueries(queries)
|
||||
}
|
||||
|
||||
protected onSubscribe(): void {
|
||||
if (this.listeners.size === 1) {
|
||||
this.#observers.forEach((observer) => {
|
||||
observer.subscribe((result) => {
|
||||
this.#onUpdate(observer, result)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
protected onUnsubscribe(): void {
|
||||
if (!this.listeners.size) {
|
||||
this.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this.listeners = new Set()
|
||||
this.#observers.forEach((observer) => {
|
||||
observer.destroy()
|
||||
})
|
||||
}
|
||||
|
||||
setQueries(
|
||||
queries: Array<QueryObserverOptions>,
|
||||
options?: QueriesObserverOptions<TCombinedResult>,
|
||||
): void {
|
||||
this.#queries = queries
|
||||
this.#options = options
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const queryHashes = queries.map(
|
||||
(query) => this.#client.defaultQueryOptions(query).queryHash,
|
||||
)
|
||||
if (new Set(queryHashes).size !== queryHashes.length) {
|
||||
console.warn(
|
||||
'[QueriesObserver]: Duplicate Queries found. This might result in unexpected behavior.',
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
notifyManager.batch(() => {
|
||||
const prevObservers = this.#observers
|
||||
|
||||
const newObserverMatches = this.#findMatchingObservers(this.#queries)
|
||||
this.#observerMatches = newObserverMatches
|
||||
|
||||
// set options for the new observers to notify of changes
|
||||
newObserverMatches.forEach((match) =>
|
||||
match.observer.setOptions(match.defaultedQueryOptions),
|
||||
)
|
||||
|
||||
const newObservers = newObserverMatches.map((match) => match.observer)
|
||||
const newResult = newObservers.map((observer) =>
|
||||
observer.getCurrentResult(),
|
||||
)
|
||||
|
||||
const hasLengthChange = prevObservers.length !== newObservers.length
|
||||
const hasIndexChange = newObservers.some(
|
||||
(observer, index) => observer !== prevObservers[index],
|
||||
)
|
||||
const hasStructuralChange = hasLengthChange || hasIndexChange
|
||||
|
||||
const hasResultChange = hasStructuralChange
|
||||
? true
|
||||
: newResult.some((result, index) => {
|
||||
const prev = this.#result[index]
|
||||
return !prev || !shallowEqualObjects(result, prev)
|
||||
})
|
||||
|
||||
if (!hasStructuralChange && !hasResultChange) return
|
||||
|
||||
if (hasStructuralChange) {
|
||||
this.#observers = newObservers
|
||||
}
|
||||
|
||||
this.#result = newResult
|
||||
|
||||
if (!this.hasListeners()) return
|
||||
|
||||
if (hasStructuralChange) {
|
||||
difference(prevObservers, newObservers).forEach((observer) => {
|
||||
observer.destroy()
|
||||
})
|
||||
difference(newObservers, prevObservers).forEach((observer) => {
|
||||
observer.subscribe((result) => {
|
||||
this.#onUpdate(observer, result)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
this.#notify()
|
||||
})
|
||||
}
|
||||
|
||||
getCurrentResult(): Array<QueryObserverResult> {
|
||||
return this.#result
|
||||
}
|
||||
|
||||
getQueries() {
|
||||
return this.#observers.map((observer) => observer.getCurrentQuery())
|
||||
}
|
||||
|
||||
getObservers() {
|
||||
return this.#observers
|
||||
}
|
||||
|
||||
getOptimisticResult(
|
||||
queries: Array<QueryObserverOptions>,
|
||||
combine: CombineFn<TCombinedResult> | undefined,
|
||||
): [
|
||||
rawResult: Array<QueryObserverResult>,
|
||||
combineResult: (r?: Array<QueryObserverResult>) => TCombinedResult,
|
||||
trackResult: () => Array<QueryObserverResult>,
|
||||
] {
|
||||
const matches = this.#findMatchingObservers(queries)
|
||||
const result = matches.map((match) =>
|
||||
match.observer.getOptimisticResult(match.defaultedQueryOptions),
|
||||
)
|
||||
|
||||
return [
|
||||
result,
|
||||
(r?: Array<QueryObserverResult>) => {
|
||||
return this.#combineResult(r ?? result, combine)
|
||||
},
|
||||
() => {
|
||||
return this.#trackResult(result, matches)
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
#trackResult(
|
||||
result: Array<QueryObserverResult>,
|
||||
matches: Array<QueryObserverMatch>,
|
||||
) {
|
||||
return matches.map((match, index) => {
|
||||
const observerResult = result[index]!
|
||||
return !match.defaultedQueryOptions.notifyOnChangeProps
|
||||
? match.observer.trackResult(observerResult, (accessedProp) => {
|
||||
// track property on all observers to ensure proper (synchronized) tracking (#7000)
|
||||
matches.forEach((m) => {
|
||||
m.observer.trackProp(accessedProp)
|
||||
})
|
||||
})
|
||||
: observerResult
|
||||
})
|
||||
}
|
||||
|
||||
#combineResult(
|
||||
input: Array<QueryObserverResult>,
|
||||
combine: CombineFn<TCombinedResult> | undefined,
|
||||
): TCombinedResult {
|
||||
if (combine) {
|
||||
if (
|
||||
!this.#combinedResult ||
|
||||
this.#result !== this.#lastResult ||
|
||||
combine !== this.#lastCombine
|
||||
) {
|
||||
this.#lastCombine = combine
|
||||
this.#lastResult = this.#result
|
||||
this.#combinedResult = replaceEqualDeep(
|
||||
this.#combinedResult,
|
||||
combine(input),
|
||||
)
|
||||
}
|
||||
|
||||
return this.#combinedResult
|
||||
}
|
||||
return input as any
|
||||
}
|
||||
|
||||
#findMatchingObservers(
|
||||
queries: Array<QueryObserverOptions>,
|
||||
): Array<QueryObserverMatch> {
|
||||
const prevObserversMap = new Map(
|
||||
this.#observers.map((observer) => [observer.options.queryHash, observer]),
|
||||
)
|
||||
|
||||
const observers: Array<QueryObserverMatch> = []
|
||||
|
||||
queries.forEach((options) => {
|
||||
const defaultedOptions = this.#client.defaultQueryOptions(options)
|
||||
const match = prevObserversMap.get(defaultedOptions.queryHash)
|
||||
if (match) {
|
||||
observers.push({
|
||||
defaultedQueryOptions: defaultedOptions,
|
||||
observer: match,
|
||||
})
|
||||
} else {
|
||||
observers.push({
|
||||
defaultedQueryOptions: defaultedOptions,
|
||||
observer: new QueryObserver(this.#client, defaultedOptions),
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return observers
|
||||
}
|
||||
|
||||
#onUpdate(observer: QueryObserver, result: QueryObserverResult): void {
|
||||
const index = this.#observers.indexOf(observer)
|
||||
if (index !== -1) {
|
||||
this.#result = replaceAt(this.#result, index, result)
|
||||
this.#notify()
|
||||
}
|
||||
}
|
||||
|
||||
#notify(): void {
|
||||
if (this.hasListeners()) {
|
||||
const previousResult = this.#combinedResult
|
||||
const newTracked = this.#trackResult(this.#result, this.#observerMatches)
|
||||
const newResult = this.#combineResult(newTracked, this.#options?.combine)
|
||||
|
||||
if (previousResult !== newResult) {
|
||||
notifyManager.batch(() => {
|
||||
this.listeners.forEach((listener) => {
|
||||
listener(this.#result)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type QueryObserverMatch = {
|
||||
defaultedQueryOptions: DefaultedQueryObserverOptions
|
||||
observer: QueryObserver
|
||||
}
|
||||
753
node_modules/@tanstack/query-core/src/query.ts
generated
vendored
Normal file
753
node_modules/@tanstack/query-core/src/query.ts
generated
vendored
Normal file
@@ -0,0 +1,753 @@
|
||||
import {
|
||||
ensureQueryFn,
|
||||
noop,
|
||||
replaceData,
|
||||
resolveEnabled,
|
||||
resolveStaleTime,
|
||||
skipToken,
|
||||
timeUntilStale,
|
||||
} from './utils'
|
||||
import { notifyManager } from './notifyManager'
|
||||
import { CancelledError, canFetch, createRetryer } from './retryer'
|
||||
import { Removable } from './removable'
|
||||
import type { QueryCache } from './queryCache'
|
||||
import type { QueryClient } from './queryClient'
|
||||
import type {
|
||||
CancelOptions,
|
||||
DefaultError,
|
||||
FetchStatus,
|
||||
InitialDataFunction,
|
||||
OmitKeyof,
|
||||
QueryFunctionContext,
|
||||
QueryKey,
|
||||
QueryMeta,
|
||||
QueryOptions,
|
||||
QueryStatus,
|
||||
SetDataOptions,
|
||||
StaleTime,
|
||||
} from './types'
|
||||
import type { QueryObserver } from './queryObserver'
|
||||
import type { Retryer } from './retryer'
|
||||
|
||||
// TYPES
|
||||
|
||||
interface QueryConfig<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
> {
|
||||
client: QueryClient
|
||||
queryKey: TQueryKey
|
||||
queryHash: string
|
||||
options?: QueryOptions<TQueryFnData, TError, TData, TQueryKey>
|
||||
defaultOptions?: QueryOptions<TQueryFnData, TError, TData, TQueryKey>
|
||||
state?: QueryState<TData, TError>
|
||||
}
|
||||
|
||||
export interface QueryState<TData = unknown, TError = DefaultError> {
|
||||
data: TData | undefined
|
||||
dataUpdateCount: number
|
||||
dataUpdatedAt: number
|
||||
error: TError | null
|
||||
errorUpdateCount: number
|
||||
errorUpdatedAt: number
|
||||
fetchFailureCount: number
|
||||
fetchFailureReason: TError | null
|
||||
fetchMeta: FetchMeta | null
|
||||
isInvalidated: boolean
|
||||
status: QueryStatus
|
||||
fetchStatus: FetchStatus
|
||||
}
|
||||
|
||||
export interface FetchContext<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
> {
|
||||
fetchFn: () => unknown | Promise<unknown>
|
||||
fetchOptions?: FetchOptions
|
||||
signal: AbortSignal
|
||||
options: QueryOptions<TQueryFnData, TError, TData, any>
|
||||
client: QueryClient
|
||||
queryKey: TQueryKey
|
||||
state: QueryState<TData, TError>
|
||||
}
|
||||
|
||||
export interface QueryBehavior<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
> {
|
||||
onFetch: (
|
||||
context: FetchContext<TQueryFnData, TError, TData, TQueryKey>,
|
||||
query: Query,
|
||||
) => void
|
||||
}
|
||||
|
||||
export type FetchDirection = 'forward' | 'backward'
|
||||
|
||||
export interface FetchMeta {
|
||||
fetchMore?: { direction: FetchDirection }
|
||||
}
|
||||
|
||||
export interface FetchOptions<TData = unknown> {
|
||||
cancelRefetch?: boolean
|
||||
meta?: FetchMeta
|
||||
initialPromise?: Promise<TData>
|
||||
}
|
||||
|
||||
interface FailedAction<TError> {
|
||||
type: 'failed'
|
||||
failureCount: number
|
||||
error: TError
|
||||
}
|
||||
|
||||
interface FetchAction {
|
||||
type: 'fetch'
|
||||
meta?: FetchMeta
|
||||
}
|
||||
|
||||
interface SuccessAction<TData> {
|
||||
data: TData | undefined
|
||||
type: 'success'
|
||||
dataUpdatedAt?: number
|
||||
manual?: boolean
|
||||
}
|
||||
|
||||
interface ErrorAction<TError> {
|
||||
type: 'error'
|
||||
error: TError
|
||||
}
|
||||
|
||||
interface InvalidateAction {
|
||||
type: 'invalidate'
|
||||
}
|
||||
|
||||
interface PauseAction {
|
||||
type: 'pause'
|
||||
}
|
||||
|
||||
interface ContinueAction {
|
||||
type: 'continue'
|
||||
}
|
||||
|
||||
interface SetStateAction<TData, TError> {
|
||||
type: 'setState'
|
||||
state: Partial<QueryState<TData, TError>>
|
||||
setStateOptions?: SetStateOptions
|
||||
}
|
||||
|
||||
export type Action<TData, TError> =
|
||||
| ContinueAction
|
||||
| ErrorAction<TError>
|
||||
| FailedAction<TError>
|
||||
| FetchAction
|
||||
| InvalidateAction
|
||||
| PauseAction
|
||||
| SetStateAction<TData, TError>
|
||||
| SuccessAction<TData>
|
||||
|
||||
export interface SetStateOptions {
|
||||
meta?: any
|
||||
}
|
||||
|
||||
// CLASS
|
||||
|
||||
export class Query<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
> extends Removable {
|
||||
queryKey: TQueryKey
|
||||
queryHash: string
|
||||
options!: QueryOptions<TQueryFnData, TError, TData, TQueryKey>
|
||||
state: QueryState<TData, TError>
|
||||
|
||||
#initialState: QueryState<TData, TError>
|
||||
#revertState?: QueryState<TData, TError>
|
||||
#cache: QueryCache
|
||||
#client: QueryClient
|
||||
#retryer?: Retryer<TData>
|
||||
observers: Array<QueryObserver<any, any, any, any, any>>
|
||||
#defaultOptions?: QueryOptions<TQueryFnData, TError, TData, TQueryKey>
|
||||
#abortSignalConsumed: boolean
|
||||
|
||||
constructor(config: QueryConfig<TQueryFnData, TError, TData, TQueryKey>) {
|
||||
super()
|
||||
|
||||
this.#abortSignalConsumed = false
|
||||
this.#defaultOptions = config.defaultOptions
|
||||
this.setOptions(config.options)
|
||||
this.observers = []
|
||||
this.#client = config.client
|
||||
this.#cache = this.#client.getQueryCache()
|
||||
this.queryKey = config.queryKey
|
||||
this.queryHash = config.queryHash
|
||||
this.#initialState = getDefaultState(this.options)
|
||||
this.state = config.state ?? this.#initialState
|
||||
this.scheduleGc()
|
||||
}
|
||||
get meta(): QueryMeta | undefined {
|
||||
return this.options.meta
|
||||
}
|
||||
|
||||
get promise(): Promise<TData> | undefined {
|
||||
return this.#retryer?.promise
|
||||
}
|
||||
|
||||
setOptions(
|
||||
options?: QueryOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
): void {
|
||||
this.options = { ...this.#defaultOptions, ...options }
|
||||
|
||||
this.updateGcTime(this.options.gcTime)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (this.state && this.state.data === undefined) {
|
||||
const defaultState = getDefaultState(this.options)
|
||||
if (defaultState.data !== undefined) {
|
||||
this.setState(
|
||||
successState(defaultState.data, defaultState.dataUpdatedAt),
|
||||
)
|
||||
this.#initialState = defaultState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected optionalRemove() {
|
||||
if (!this.observers.length && this.state.fetchStatus === 'idle') {
|
||||
this.#cache.remove(this)
|
||||
}
|
||||
}
|
||||
|
||||
setData(
|
||||
newData: TData,
|
||||
options?: SetDataOptions & { manual: boolean },
|
||||
): TData {
|
||||
const data = replaceData(this.state.data, newData, this.options)
|
||||
|
||||
// Set data and mark it as cached
|
||||
this.#dispatch({
|
||||
data,
|
||||
type: 'success',
|
||||
dataUpdatedAt: options?.updatedAt,
|
||||
manual: options?.manual,
|
||||
})
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
setState(
|
||||
state: Partial<QueryState<TData, TError>>,
|
||||
setStateOptions?: SetStateOptions,
|
||||
): void {
|
||||
this.#dispatch({ type: 'setState', state, setStateOptions })
|
||||
}
|
||||
|
||||
cancel(options?: CancelOptions): Promise<void> {
|
||||
const promise = this.#retryer?.promise
|
||||
this.#retryer?.cancel(options)
|
||||
return promise ? promise.then(noop).catch(noop) : Promise.resolve()
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
super.destroy()
|
||||
|
||||
this.cancel({ silent: true })
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.destroy()
|
||||
this.setState(this.#initialState)
|
||||
}
|
||||
|
||||
isActive(): boolean {
|
||||
return this.observers.some(
|
||||
(observer) => resolveEnabled(observer.options.enabled, this) !== false,
|
||||
)
|
||||
}
|
||||
|
||||
isDisabled(): boolean {
|
||||
if (this.getObserversCount() > 0) {
|
||||
return !this.isActive()
|
||||
}
|
||||
// if a query has no observers, it should still be considered disabled if it never attempted a fetch
|
||||
return (
|
||||
this.options.queryFn === skipToken ||
|
||||
this.state.dataUpdateCount + this.state.errorUpdateCount === 0
|
||||
)
|
||||
}
|
||||
|
||||
isStatic(): boolean {
|
||||
if (this.getObserversCount() > 0) {
|
||||
return this.observers.some(
|
||||
(observer) =>
|
||||
resolveStaleTime(observer.options.staleTime, this) === 'static',
|
||||
)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
isStale(): boolean {
|
||||
// check observers first, their `isStale` has the source of truth
|
||||
// calculated with `isStaleByTime` and it takes `enabled` into account
|
||||
if (this.getObserversCount() > 0) {
|
||||
return this.observers.some(
|
||||
(observer) => observer.getCurrentResult().isStale,
|
||||
)
|
||||
}
|
||||
|
||||
return this.state.data === undefined || this.state.isInvalidated
|
||||
}
|
||||
|
||||
isStaleByTime(staleTime: StaleTime = 0): boolean {
|
||||
// no data is always stale
|
||||
if (this.state.data === undefined) {
|
||||
return true
|
||||
}
|
||||
// static is never stale
|
||||
if (staleTime === 'static') {
|
||||
return false
|
||||
}
|
||||
// if the query is invalidated, it is stale
|
||||
if (this.state.isInvalidated) {
|
||||
return true
|
||||
}
|
||||
|
||||
return !timeUntilStale(this.state.dataUpdatedAt, staleTime)
|
||||
}
|
||||
|
||||
onFocus(): void {
|
||||
const observer = this.observers.find((x) => x.shouldFetchOnWindowFocus())
|
||||
|
||||
observer?.refetch({ cancelRefetch: false })
|
||||
|
||||
// Continue fetch if currently paused
|
||||
this.#retryer?.continue()
|
||||
}
|
||||
|
||||
onOnline(): void {
|
||||
const observer = this.observers.find((x) => x.shouldFetchOnReconnect())
|
||||
|
||||
observer?.refetch({ cancelRefetch: false })
|
||||
|
||||
// Continue fetch if currently paused
|
||||
this.#retryer?.continue()
|
||||
}
|
||||
|
||||
addObserver(observer: QueryObserver<any, any, any, any, any>): void {
|
||||
if (!this.observers.includes(observer)) {
|
||||
this.observers.push(observer)
|
||||
|
||||
// Stop the query from being garbage collected
|
||||
this.clearGcTimeout()
|
||||
|
||||
this.#cache.notify({ type: 'observerAdded', query: this, observer })
|
||||
}
|
||||
}
|
||||
|
||||
removeObserver(observer: QueryObserver<any, any, any, any, any>): void {
|
||||
if (this.observers.includes(observer)) {
|
||||
this.observers = this.observers.filter((x) => x !== observer)
|
||||
|
||||
if (!this.observers.length) {
|
||||
// If the transport layer does not support cancellation
|
||||
// we'll let the query continue so the result can be cached
|
||||
if (this.#retryer) {
|
||||
if (this.#abortSignalConsumed) {
|
||||
this.#retryer.cancel({ revert: true })
|
||||
} else {
|
||||
this.#retryer.cancelRetry()
|
||||
}
|
||||
}
|
||||
|
||||
this.scheduleGc()
|
||||
}
|
||||
|
||||
this.#cache.notify({ type: 'observerRemoved', query: this, observer })
|
||||
}
|
||||
}
|
||||
|
||||
getObserversCount(): number {
|
||||
return this.observers.length
|
||||
}
|
||||
|
||||
invalidate(): void {
|
||||
if (!this.state.isInvalidated) {
|
||||
this.#dispatch({ type: 'invalidate' })
|
||||
}
|
||||
}
|
||||
|
||||
async fetch(
|
||||
options?: QueryOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
fetchOptions?: FetchOptions<TQueryFnData>,
|
||||
): Promise<TData> {
|
||||
if (
|
||||
this.state.fetchStatus !== 'idle' &&
|
||||
// If the promise in the retyer is already rejected, we have to definitely
|
||||
// re-start the fetch; there is a chance that the query is still in a
|
||||
// pending state when that happens
|
||||
this.#retryer?.status() !== 'rejected'
|
||||
) {
|
||||
if (this.state.data !== undefined && fetchOptions?.cancelRefetch) {
|
||||
// Silently cancel current fetch if the user wants to cancel refetch
|
||||
this.cancel({ silent: true })
|
||||
} else if (this.#retryer) {
|
||||
// make sure that retries that were potentially cancelled due to unmounts can continue
|
||||
this.#retryer.continueRetry()
|
||||
// Return current promise if we are already fetching
|
||||
return this.#retryer.promise
|
||||
}
|
||||
}
|
||||
|
||||
// Update config if passed, otherwise the config from the last execution is used
|
||||
if (options) {
|
||||
this.setOptions(options)
|
||||
}
|
||||
|
||||
// Use the options from the first observer with a query function if no function is found.
|
||||
// This can happen when the query is hydrated or created with setQueryData.
|
||||
if (!this.options.queryFn) {
|
||||
const observer = this.observers.find((x) => x.options.queryFn)
|
||||
if (observer) {
|
||||
this.setOptions(observer.options)
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (!Array.isArray(this.options.queryKey)) {
|
||||
console.error(
|
||||
`As of v4, queryKey needs to be an Array. If you are using a string like 'repoData', please change it to an Array, e.g. ['repoData']`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const abortController = new AbortController()
|
||||
|
||||
// Adds an enumerable signal property to the object that
|
||||
// which sets abortSignalConsumed to true when the signal
|
||||
// is read.
|
||||
const addSignalProperty = (object: unknown) => {
|
||||
Object.defineProperty(object, 'signal', {
|
||||
enumerable: true,
|
||||
get: () => {
|
||||
this.#abortSignalConsumed = true
|
||||
return abortController.signal
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Create fetch function
|
||||
const fetchFn = () => {
|
||||
const queryFn = ensureQueryFn(this.options, fetchOptions)
|
||||
|
||||
// Create query function context
|
||||
const createQueryFnContext = (): QueryFunctionContext<TQueryKey> => {
|
||||
const queryFnContext: OmitKeyof<
|
||||
QueryFunctionContext<TQueryKey>,
|
||||
'signal'
|
||||
> = {
|
||||
client: this.#client,
|
||||
queryKey: this.queryKey,
|
||||
meta: this.meta,
|
||||
}
|
||||
addSignalProperty(queryFnContext)
|
||||
return queryFnContext as QueryFunctionContext<TQueryKey>
|
||||
}
|
||||
|
||||
const queryFnContext = createQueryFnContext()
|
||||
|
||||
this.#abortSignalConsumed = false
|
||||
if (this.options.persister) {
|
||||
return this.options.persister(
|
||||
queryFn,
|
||||
queryFnContext,
|
||||
this as unknown as Query,
|
||||
)
|
||||
}
|
||||
|
||||
return queryFn(queryFnContext)
|
||||
}
|
||||
|
||||
// Trigger behavior hook
|
||||
const createFetchContext = (): FetchContext<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey
|
||||
> => {
|
||||
const context: OmitKeyof<
|
||||
FetchContext<TQueryFnData, TError, TData, TQueryKey>,
|
||||
'signal'
|
||||
> = {
|
||||
fetchOptions,
|
||||
options: this.options,
|
||||
queryKey: this.queryKey,
|
||||
client: this.#client,
|
||||
state: this.state,
|
||||
fetchFn,
|
||||
}
|
||||
|
||||
addSignalProperty(context)
|
||||
return context as FetchContext<TQueryFnData, TError, TData, TQueryKey>
|
||||
}
|
||||
|
||||
const context = createFetchContext()
|
||||
|
||||
this.options.behavior?.onFetch(context, this as unknown as Query)
|
||||
|
||||
// Store state in case the current fetch needs to be reverted
|
||||
this.#revertState = this.state
|
||||
|
||||
// Set to fetching state if not already in it
|
||||
if (
|
||||
this.state.fetchStatus === 'idle' ||
|
||||
this.state.fetchMeta !== context.fetchOptions?.meta
|
||||
) {
|
||||
this.#dispatch({ type: 'fetch', meta: context.fetchOptions?.meta })
|
||||
}
|
||||
|
||||
// Try to fetch the data
|
||||
this.#retryer = createRetryer({
|
||||
initialPromise: fetchOptions?.initialPromise as
|
||||
| Promise<TData>
|
||||
| undefined,
|
||||
fn: context.fetchFn as () => Promise<TData>,
|
||||
onCancel: (error) => {
|
||||
if (error instanceof CancelledError && error.revert) {
|
||||
this.setState({
|
||||
...this.#revertState,
|
||||
fetchStatus: 'idle' as const,
|
||||
})
|
||||
}
|
||||
abortController.abort()
|
||||
},
|
||||
onFail: (failureCount, error) => {
|
||||
this.#dispatch({ type: 'failed', failureCount, error })
|
||||
},
|
||||
onPause: () => {
|
||||
this.#dispatch({ type: 'pause' })
|
||||
},
|
||||
onContinue: () => {
|
||||
this.#dispatch({ type: 'continue' })
|
||||
},
|
||||
retry: context.options.retry,
|
||||
retryDelay: context.options.retryDelay,
|
||||
networkMode: context.options.networkMode,
|
||||
canRun: () => true,
|
||||
})
|
||||
|
||||
try {
|
||||
const data = await this.#retryer.start()
|
||||
// this is more of a runtime guard
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (data === undefined) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
console.error(
|
||||
`Query data cannot be undefined. Please make sure to return a value other than undefined from your query function. Affected query key: ${this.queryHash}`,
|
||||
)
|
||||
}
|
||||
throw new Error(`${this.queryHash} data is undefined`)
|
||||
}
|
||||
|
||||
this.setData(data)
|
||||
|
||||
// Notify cache callback
|
||||
this.#cache.config.onSuccess?.(data, this as Query<any, any, any, any>)
|
||||
this.#cache.config.onSettled?.(
|
||||
data,
|
||||
this.state.error as any,
|
||||
this as Query<any, any, any, any>,
|
||||
)
|
||||
return data
|
||||
} catch (error) {
|
||||
if (error instanceof CancelledError) {
|
||||
if (error.silent) {
|
||||
// silent cancellation implies a new fetch is going to be started,
|
||||
// so we piggyback onto that promise
|
||||
return this.#retryer.promise
|
||||
} else if (error.revert) {
|
||||
// transform error into reverted state data
|
||||
// if the initial fetch was cancelled, we have no data, so we have
|
||||
// to get reject with a CancelledError
|
||||
if (this.state.data === undefined) {
|
||||
throw error
|
||||
}
|
||||
return this.state.data
|
||||
}
|
||||
}
|
||||
this.#dispatch({
|
||||
type: 'error',
|
||||
error: error as TError,
|
||||
})
|
||||
|
||||
// Notify cache callback
|
||||
this.#cache.config.onError?.(
|
||||
error as any,
|
||||
this as Query<any, any, any, any>,
|
||||
)
|
||||
this.#cache.config.onSettled?.(
|
||||
this.state.data,
|
||||
error as any,
|
||||
this as Query<any, any, any, any>,
|
||||
)
|
||||
|
||||
throw error // rethrow the error for further handling
|
||||
} finally {
|
||||
// Schedule query gc after fetching
|
||||
this.scheduleGc()
|
||||
}
|
||||
}
|
||||
|
||||
#dispatch(action: Action<TData, TError>): void {
|
||||
const reducer = (
|
||||
state: QueryState<TData, TError>,
|
||||
): QueryState<TData, TError> => {
|
||||
switch (action.type) {
|
||||
case 'failed':
|
||||
return {
|
||||
...state,
|
||||
fetchFailureCount: action.failureCount,
|
||||
fetchFailureReason: action.error,
|
||||
}
|
||||
case 'pause':
|
||||
return {
|
||||
...state,
|
||||
fetchStatus: 'paused',
|
||||
}
|
||||
case 'continue':
|
||||
return {
|
||||
...state,
|
||||
fetchStatus: 'fetching',
|
||||
}
|
||||
case 'fetch':
|
||||
return {
|
||||
...state,
|
||||
...fetchState(state.data, this.options),
|
||||
fetchMeta: action.meta ?? null,
|
||||
}
|
||||
case 'success':
|
||||
const newState = {
|
||||
...state,
|
||||
...successState(action.data, action.dataUpdatedAt),
|
||||
dataUpdateCount: state.dataUpdateCount + 1,
|
||||
...(!action.manual && {
|
||||
fetchStatus: 'idle' as const,
|
||||
fetchFailureCount: 0,
|
||||
fetchFailureReason: null,
|
||||
}),
|
||||
}
|
||||
// If fetching ends successfully, we don't need revertState as a fallback anymore.
|
||||
// For manual updates, capture the state to revert to it in case of a cancellation.
|
||||
this.#revertState = action.manual ? newState : undefined
|
||||
|
||||
return newState
|
||||
case 'error':
|
||||
const error = action.error
|
||||
return {
|
||||
...state,
|
||||
error,
|
||||
errorUpdateCount: state.errorUpdateCount + 1,
|
||||
errorUpdatedAt: Date.now(),
|
||||
fetchFailureCount: state.fetchFailureCount + 1,
|
||||
fetchFailureReason: error,
|
||||
fetchStatus: 'idle',
|
||||
status: 'error',
|
||||
}
|
||||
case 'invalidate':
|
||||
return {
|
||||
...state,
|
||||
isInvalidated: true,
|
||||
}
|
||||
case 'setState':
|
||||
return {
|
||||
...state,
|
||||
...action.state,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.state = reducer(this.state)
|
||||
|
||||
notifyManager.batch(() => {
|
||||
this.observers.forEach((observer) => {
|
||||
observer.onQueryUpdate()
|
||||
})
|
||||
|
||||
this.#cache.notify({ query: this, type: 'updated', action })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function fetchState<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey extends QueryKey,
|
||||
>(
|
||||
data: TData | undefined,
|
||||
options: QueryOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
) {
|
||||
return {
|
||||
fetchFailureCount: 0,
|
||||
fetchFailureReason: null,
|
||||
fetchStatus: canFetch(options.networkMode) ? 'fetching' : 'paused',
|
||||
...(data === undefined &&
|
||||
({
|
||||
error: null,
|
||||
status: 'pending',
|
||||
} as const)),
|
||||
} as const
|
||||
}
|
||||
|
||||
function successState<TData>(data: TData | undefined, dataUpdatedAt?: number) {
|
||||
return {
|
||||
data,
|
||||
dataUpdatedAt: dataUpdatedAt ?? Date.now(),
|
||||
error: null,
|
||||
isInvalidated: false,
|
||||
status: 'success' as const,
|
||||
}
|
||||
}
|
||||
|
||||
function getDefaultState<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey extends QueryKey,
|
||||
>(
|
||||
options: QueryOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
): QueryState<TData, TError> {
|
||||
const data =
|
||||
typeof options.initialData === 'function'
|
||||
? (options.initialData as InitialDataFunction<TData>)()
|
||||
: options.initialData
|
||||
|
||||
const hasData = data !== undefined
|
||||
|
||||
const initialDataUpdatedAt = hasData
|
||||
? typeof options.initialDataUpdatedAt === 'function'
|
||||
? (options.initialDataUpdatedAt as () => number | undefined)()
|
||||
: options.initialDataUpdatedAt
|
||||
: 0
|
||||
|
||||
return {
|
||||
data,
|
||||
dataUpdateCount: 0,
|
||||
dataUpdatedAt: hasData ? (initialDataUpdatedAt ?? Date.now()) : 0,
|
||||
error: null,
|
||||
errorUpdateCount: 0,
|
||||
errorUpdatedAt: 0,
|
||||
fetchFailureCount: 0,
|
||||
fetchFailureReason: null,
|
||||
fetchMeta: null,
|
||||
isInvalidated: false,
|
||||
status: hasData ? 'success' : 'pending',
|
||||
fetchStatus: 'idle',
|
||||
}
|
||||
}
|
||||
223
node_modules/@tanstack/query-core/src/queryCache.ts
generated
vendored
Normal file
223
node_modules/@tanstack/query-core/src/queryCache.ts
generated
vendored
Normal file
@@ -0,0 +1,223 @@
|
||||
import { hashQueryKeyByOptions, matchQuery } from './utils'
|
||||
import { Query } from './query'
|
||||
import { notifyManager } from './notifyManager'
|
||||
import { Subscribable } from './subscribable'
|
||||
import type { QueryFilters } from './utils'
|
||||
import type { Action, QueryState } from './query'
|
||||
import type {
|
||||
DefaultError,
|
||||
NotifyEvent,
|
||||
QueryKey,
|
||||
QueryOptions,
|
||||
WithRequired,
|
||||
} from './types'
|
||||
import type { QueryClient } from './queryClient'
|
||||
import type { QueryObserver } from './queryObserver'
|
||||
|
||||
// TYPES
|
||||
|
||||
interface QueryCacheConfig {
|
||||
onError?: (
|
||||
error: DefaultError,
|
||||
query: Query<unknown, unknown, unknown>,
|
||||
) => void
|
||||
onSuccess?: (data: unknown, query: Query<unknown, unknown, unknown>) => void
|
||||
onSettled?: (
|
||||
data: unknown | undefined,
|
||||
error: DefaultError | null,
|
||||
query: Query<unknown, unknown, unknown>,
|
||||
) => void
|
||||
}
|
||||
|
||||
interface NotifyEventQueryAdded extends NotifyEvent {
|
||||
type: 'added'
|
||||
query: Query<any, any, any, any>
|
||||
}
|
||||
|
||||
interface NotifyEventQueryRemoved extends NotifyEvent {
|
||||
type: 'removed'
|
||||
query: Query<any, any, any, any>
|
||||
}
|
||||
|
||||
interface NotifyEventQueryUpdated extends NotifyEvent {
|
||||
type: 'updated'
|
||||
query: Query<any, any, any, any>
|
||||
action: Action<any, any>
|
||||
}
|
||||
|
||||
interface NotifyEventQueryObserverAdded extends NotifyEvent {
|
||||
type: 'observerAdded'
|
||||
query: Query<any, any, any, any>
|
||||
observer: QueryObserver<any, any, any, any, any>
|
||||
}
|
||||
|
||||
interface NotifyEventQueryObserverRemoved extends NotifyEvent {
|
||||
type: 'observerRemoved'
|
||||
query: Query<any, any, any, any>
|
||||
observer: QueryObserver<any, any, any, any, any>
|
||||
}
|
||||
|
||||
interface NotifyEventQueryObserverResultsUpdated extends NotifyEvent {
|
||||
type: 'observerResultsUpdated'
|
||||
query: Query<any, any, any, any>
|
||||
}
|
||||
|
||||
interface NotifyEventQueryObserverOptionsUpdated extends NotifyEvent {
|
||||
type: 'observerOptionsUpdated'
|
||||
query: Query<any, any, any, any>
|
||||
observer: QueryObserver<any, any, any, any, any>
|
||||
}
|
||||
|
||||
export type QueryCacheNotifyEvent =
|
||||
| NotifyEventQueryAdded
|
||||
| NotifyEventQueryRemoved
|
||||
| NotifyEventQueryUpdated
|
||||
| NotifyEventQueryObserverAdded
|
||||
| NotifyEventQueryObserverRemoved
|
||||
| NotifyEventQueryObserverResultsUpdated
|
||||
| NotifyEventQueryObserverOptionsUpdated
|
||||
|
||||
type QueryCacheListener = (event: QueryCacheNotifyEvent) => void
|
||||
|
||||
export interface QueryStore {
|
||||
has: (queryHash: string) => boolean
|
||||
set: (queryHash: string, query: Query) => void
|
||||
get: (queryHash: string) => Query | undefined
|
||||
delete: (queryHash: string) => void
|
||||
values: () => IterableIterator<Query>
|
||||
}
|
||||
|
||||
// CLASS
|
||||
|
||||
export class QueryCache extends Subscribable<QueryCacheListener> {
|
||||
#queries: QueryStore
|
||||
|
||||
constructor(public config: QueryCacheConfig = {}) {
|
||||
super()
|
||||
this.#queries = new Map<string, Query>()
|
||||
}
|
||||
|
||||
build<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>(
|
||||
client: QueryClient,
|
||||
options: WithRequired<
|
||||
QueryOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
'queryKey'
|
||||
>,
|
||||
state?: QueryState<TData, TError>,
|
||||
): Query<TQueryFnData, TError, TData, TQueryKey> {
|
||||
const queryKey = options.queryKey
|
||||
const queryHash =
|
||||
options.queryHash ?? hashQueryKeyByOptions(queryKey, options)
|
||||
let query = this.get<TQueryFnData, TError, TData, TQueryKey>(queryHash)
|
||||
|
||||
if (!query) {
|
||||
query = new Query({
|
||||
client,
|
||||
queryKey,
|
||||
queryHash,
|
||||
options: client.defaultQueryOptions(options),
|
||||
state,
|
||||
defaultOptions: client.getQueryDefaults(queryKey),
|
||||
})
|
||||
this.add(query)
|
||||
}
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
add(query: Query<any, any, any, any>): void {
|
||||
if (!this.#queries.has(query.queryHash)) {
|
||||
this.#queries.set(query.queryHash, query)
|
||||
|
||||
this.notify({
|
||||
type: 'added',
|
||||
query,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
remove(query: Query<any, any, any, any>): void {
|
||||
const queryInMap = this.#queries.get(query.queryHash)
|
||||
|
||||
if (queryInMap) {
|
||||
query.destroy()
|
||||
|
||||
if (queryInMap === query) {
|
||||
this.#queries.delete(query.queryHash)
|
||||
}
|
||||
|
||||
this.notify({ type: 'removed', query })
|
||||
}
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
notifyManager.batch(() => {
|
||||
this.getAll().forEach((query) => {
|
||||
this.remove(query)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
get<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>(
|
||||
queryHash: string,
|
||||
): Query<TQueryFnData, TError, TData, TQueryKey> | undefined {
|
||||
return this.#queries.get(queryHash) as
|
||||
| Query<TQueryFnData, TError, TData, TQueryKey>
|
||||
| undefined
|
||||
}
|
||||
|
||||
getAll(): Array<Query> {
|
||||
return [...this.#queries.values()]
|
||||
}
|
||||
|
||||
find<TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData>(
|
||||
filters: WithRequired<QueryFilters, 'queryKey'>,
|
||||
): Query<TQueryFnData, TError, TData> | undefined {
|
||||
const defaultedFilters = { exact: true, ...filters }
|
||||
|
||||
return this.getAll().find((query) =>
|
||||
matchQuery(defaultedFilters, query),
|
||||
) as Query<TQueryFnData, TError, TData> | undefined
|
||||
}
|
||||
|
||||
findAll(filters: QueryFilters<any> = {}): Array<Query> {
|
||||
const queries = this.getAll()
|
||||
return Object.keys(filters).length > 0
|
||||
? queries.filter((query) => matchQuery(filters, query))
|
||||
: queries
|
||||
}
|
||||
|
||||
notify(event: QueryCacheNotifyEvent): void {
|
||||
notifyManager.batch(() => {
|
||||
this.listeners.forEach((listener) => {
|
||||
listener(event)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
onFocus(): void {
|
||||
notifyManager.batch(() => {
|
||||
this.getAll().forEach((query) => {
|
||||
query.onFocus()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
onOnline(): void {
|
||||
notifyManager.batch(() => {
|
||||
this.getAll().forEach((query) => {
|
||||
query.onOnline()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
648
node_modules/@tanstack/query-core/src/queryClient.ts
generated
vendored
Normal file
648
node_modules/@tanstack/query-core/src/queryClient.ts
generated
vendored
Normal file
@@ -0,0 +1,648 @@
|
||||
import {
|
||||
functionalUpdate,
|
||||
hashKey,
|
||||
hashQueryKeyByOptions,
|
||||
noop,
|
||||
partialMatchKey,
|
||||
resolveStaleTime,
|
||||
skipToken,
|
||||
} from './utils'
|
||||
import { QueryCache } from './queryCache'
|
||||
import { MutationCache } from './mutationCache'
|
||||
import { focusManager } from './focusManager'
|
||||
import { onlineManager } from './onlineManager'
|
||||
import { notifyManager } from './notifyManager'
|
||||
import { infiniteQueryBehavior } from './infiniteQueryBehavior'
|
||||
import type {
|
||||
CancelOptions,
|
||||
DefaultError,
|
||||
DefaultOptions,
|
||||
DefaultedQueryObserverOptions,
|
||||
EnsureInfiniteQueryDataOptions,
|
||||
EnsureQueryDataOptions,
|
||||
FetchInfiniteQueryOptions,
|
||||
FetchQueryOptions,
|
||||
InferDataFromTag,
|
||||
InferErrorFromTag,
|
||||
InfiniteData,
|
||||
InvalidateOptions,
|
||||
InvalidateQueryFilters,
|
||||
MutationKey,
|
||||
MutationObserverOptions,
|
||||
MutationOptions,
|
||||
NoInfer,
|
||||
OmitKeyof,
|
||||
QueryClientConfig,
|
||||
QueryKey,
|
||||
QueryObserverOptions,
|
||||
QueryOptions,
|
||||
RefetchOptions,
|
||||
RefetchQueryFilters,
|
||||
ResetOptions,
|
||||
SetDataOptions,
|
||||
} from './types'
|
||||
import type { QueryState } from './query'
|
||||
import type { MutationFilters, QueryFilters, Updater } from './utils'
|
||||
|
||||
// TYPES
|
||||
|
||||
interface QueryDefaults {
|
||||
queryKey: QueryKey
|
||||
defaultOptions: OmitKeyof<QueryOptions<any, any, any>, 'queryKey'>
|
||||
}
|
||||
|
||||
interface MutationDefaults {
|
||||
mutationKey: MutationKey
|
||||
defaultOptions: MutationOptions<any, any, any, any>
|
||||
}
|
||||
|
||||
// CLASS
|
||||
|
||||
export class QueryClient {
|
||||
#queryCache: QueryCache
|
||||
#mutationCache: MutationCache
|
||||
#defaultOptions: DefaultOptions
|
||||
#queryDefaults: Map<string, QueryDefaults>
|
||||
#mutationDefaults: Map<string, MutationDefaults>
|
||||
#mountCount: number
|
||||
#unsubscribeFocus?: () => void
|
||||
#unsubscribeOnline?: () => void
|
||||
|
||||
constructor(config: QueryClientConfig = {}) {
|
||||
this.#queryCache = config.queryCache || new QueryCache()
|
||||
this.#mutationCache = config.mutationCache || new MutationCache()
|
||||
this.#defaultOptions = config.defaultOptions || {}
|
||||
this.#queryDefaults = new Map()
|
||||
this.#mutationDefaults = new Map()
|
||||
this.#mountCount = 0
|
||||
}
|
||||
|
||||
mount(): void {
|
||||
this.#mountCount++
|
||||
if (this.#mountCount !== 1) return
|
||||
|
||||
this.#unsubscribeFocus = focusManager.subscribe(async (focused) => {
|
||||
if (focused) {
|
||||
await this.resumePausedMutations()
|
||||
this.#queryCache.onFocus()
|
||||
}
|
||||
})
|
||||
this.#unsubscribeOnline = onlineManager.subscribe(async (online) => {
|
||||
if (online) {
|
||||
await this.resumePausedMutations()
|
||||
this.#queryCache.onOnline()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
unmount(): void {
|
||||
this.#mountCount--
|
||||
if (this.#mountCount !== 0) return
|
||||
|
||||
this.#unsubscribeFocus?.()
|
||||
this.#unsubscribeFocus = undefined
|
||||
|
||||
this.#unsubscribeOnline?.()
|
||||
this.#unsubscribeOnline = undefined
|
||||
}
|
||||
|
||||
isFetching<TQueryFilters extends QueryFilters<any> = QueryFilters>(
|
||||
filters?: TQueryFilters,
|
||||
): number {
|
||||
return this.#queryCache.findAll({ ...filters, fetchStatus: 'fetching' })
|
||||
.length
|
||||
}
|
||||
|
||||
isMutating<
|
||||
TMutationFilters extends MutationFilters<any, any> = MutationFilters,
|
||||
>(filters?: TMutationFilters): number {
|
||||
return this.#mutationCache.findAll({ ...filters, status: 'pending' }).length
|
||||
}
|
||||
|
||||
/**
|
||||
* Imperative (non-reactive) way to retrieve data for a QueryKey.
|
||||
* Should only be used in callbacks or functions where reading the latest data is necessary, e.g. for optimistic updates.
|
||||
*
|
||||
* Hint: Do not use this function inside a component, because it won't receive updates.
|
||||
* Use `useQuery` to create a `QueryObserver` that subscribes to changes.
|
||||
*/
|
||||
getQueryData<
|
||||
TQueryFnData = unknown,
|
||||
TTaggedQueryKey extends QueryKey = QueryKey,
|
||||
TInferredQueryFnData = InferDataFromTag<TQueryFnData, TTaggedQueryKey>,
|
||||
>(queryKey: TTaggedQueryKey): TInferredQueryFnData | undefined {
|
||||
const options = this.defaultQueryOptions({ queryKey })
|
||||
|
||||
return this.#queryCache.get<TInferredQueryFnData>(options.queryHash)?.state
|
||||
.data
|
||||
}
|
||||
|
||||
ensureQueryData<
|
||||
TQueryFnData,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>(
|
||||
options: EnsureQueryDataOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
): Promise<TData> {
|
||||
const defaultedOptions = this.defaultQueryOptions(options)
|
||||
const query = this.#queryCache.build(this, defaultedOptions)
|
||||
const cachedData = query.state.data
|
||||
|
||||
if (cachedData === undefined) {
|
||||
return this.fetchQuery(options)
|
||||
}
|
||||
|
||||
if (
|
||||
options.revalidateIfStale &&
|
||||
query.isStaleByTime(resolveStaleTime(defaultedOptions.staleTime, query))
|
||||
) {
|
||||
void this.prefetchQuery(defaultedOptions)
|
||||
}
|
||||
|
||||
return Promise.resolve(cachedData)
|
||||
}
|
||||
|
||||
getQueriesData<
|
||||
TQueryFnData = unknown,
|
||||
TQueryFilters extends QueryFilters<any> = QueryFilters,
|
||||
>(filters: TQueryFilters): Array<[QueryKey, TQueryFnData | undefined]> {
|
||||
return this.#queryCache.findAll(filters).map(({ queryKey, state }) => {
|
||||
const data = state.data as TQueryFnData | undefined
|
||||
return [queryKey, data]
|
||||
})
|
||||
}
|
||||
|
||||
setQueryData<
|
||||
TQueryFnData = unknown,
|
||||
TTaggedQueryKey extends QueryKey = QueryKey,
|
||||
TInferredQueryFnData = InferDataFromTag<TQueryFnData, TTaggedQueryKey>,
|
||||
>(
|
||||
queryKey: TTaggedQueryKey,
|
||||
updater: Updater<
|
||||
NoInfer<TInferredQueryFnData> | undefined,
|
||||
NoInfer<TInferredQueryFnData> | undefined
|
||||
>,
|
||||
options?: SetDataOptions,
|
||||
): NoInfer<TInferredQueryFnData> | undefined {
|
||||
const defaultedOptions = this.defaultQueryOptions<
|
||||
any,
|
||||
any,
|
||||
unknown,
|
||||
any,
|
||||
QueryKey
|
||||
>({ queryKey })
|
||||
|
||||
const query = this.#queryCache.get<TInferredQueryFnData>(
|
||||
defaultedOptions.queryHash,
|
||||
)
|
||||
const prevData = query?.state.data
|
||||
const data = functionalUpdate(updater, prevData)
|
||||
|
||||
if (data === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return this.#queryCache
|
||||
.build(this, defaultedOptions)
|
||||
.setData(data, { ...options, manual: true })
|
||||
}
|
||||
|
||||
setQueriesData<
|
||||
TQueryFnData,
|
||||
TQueryFilters extends QueryFilters<any> = QueryFilters,
|
||||
>(
|
||||
filters: TQueryFilters,
|
||||
updater: Updater<
|
||||
NoInfer<TQueryFnData> | undefined,
|
||||
NoInfer<TQueryFnData> | undefined
|
||||
>,
|
||||
options?: SetDataOptions,
|
||||
): Array<[QueryKey, TQueryFnData | undefined]> {
|
||||
return notifyManager.batch(() =>
|
||||
this.#queryCache
|
||||
.findAll(filters)
|
||||
.map(({ queryKey }) => [
|
||||
queryKey,
|
||||
this.setQueryData<TQueryFnData>(queryKey, updater, options),
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
getQueryState<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TTaggedQueryKey extends QueryKey = QueryKey,
|
||||
TInferredQueryFnData = InferDataFromTag<TQueryFnData, TTaggedQueryKey>,
|
||||
TInferredError = InferErrorFromTag<TError, TTaggedQueryKey>,
|
||||
>(
|
||||
queryKey: TTaggedQueryKey,
|
||||
): QueryState<TInferredQueryFnData, TInferredError> | undefined {
|
||||
const options = this.defaultQueryOptions({ queryKey })
|
||||
return this.#queryCache.get<TInferredQueryFnData, TInferredError>(
|
||||
options.queryHash,
|
||||
)?.state
|
||||
}
|
||||
|
||||
removeQueries<TTaggedQueryKey extends QueryKey = QueryKey>(
|
||||
filters?: QueryFilters<TTaggedQueryKey>,
|
||||
): void {
|
||||
const queryCache = this.#queryCache
|
||||
notifyManager.batch(() => {
|
||||
queryCache.findAll(filters).forEach((query) => {
|
||||
queryCache.remove(query)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
resetQueries<TTaggedQueryKey extends QueryKey = QueryKey>(
|
||||
filters?: QueryFilters<TTaggedQueryKey>,
|
||||
options?: ResetOptions,
|
||||
): Promise<void> {
|
||||
const queryCache = this.#queryCache
|
||||
|
||||
return notifyManager.batch(() => {
|
||||
queryCache.findAll(filters).forEach((query) => {
|
||||
query.reset()
|
||||
})
|
||||
return this.refetchQueries(
|
||||
{
|
||||
type: 'active',
|
||||
...filters,
|
||||
},
|
||||
options,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
cancelQueries<TTaggedQueryKey extends QueryKey = QueryKey>(
|
||||
filters?: QueryFilters<TTaggedQueryKey>,
|
||||
cancelOptions: CancelOptions = {},
|
||||
): Promise<void> {
|
||||
const defaultedCancelOptions = { revert: true, ...cancelOptions }
|
||||
|
||||
const promises = notifyManager.batch(() =>
|
||||
this.#queryCache
|
||||
.findAll(filters)
|
||||
.map((query) => query.cancel(defaultedCancelOptions)),
|
||||
)
|
||||
|
||||
return Promise.all(promises).then(noop).catch(noop)
|
||||
}
|
||||
|
||||
invalidateQueries<TTaggedQueryKey extends QueryKey = QueryKey>(
|
||||
filters?: InvalidateQueryFilters<TTaggedQueryKey>,
|
||||
options: InvalidateOptions = {},
|
||||
): Promise<void> {
|
||||
return notifyManager.batch(() => {
|
||||
this.#queryCache.findAll(filters).forEach((query) => {
|
||||
query.invalidate()
|
||||
})
|
||||
|
||||
if (filters?.refetchType === 'none') {
|
||||
return Promise.resolve()
|
||||
}
|
||||
return this.refetchQueries(
|
||||
{
|
||||
...filters,
|
||||
type: filters?.refetchType ?? filters?.type ?? 'active',
|
||||
},
|
||||
options,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
refetchQueries<TTaggedQueryKey extends QueryKey = QueryKey>(
|
||||
filters?: RefetchQueryFilters<TTaggedQueryKey>,
|
||||
options: RefetchOptions = {},
|
||||
): Promise<void> {
|
||||
const fetchOptions = {
|
||||
...options,
|
||||
cancelRefetch: options.cancelRefetch ?? true,
|
||||
}
|
||||
const promises = notifyManager.batch(() =>
|
||||
this.#queryCache
|
||||
.findAll(filters)
|
||||
.filter((query) => !query.isDisabled() && !query.isStatic())
|
||||
.map((query) => {
|
||||
let promise = query.fetch(undefined, fetchOptions)
|
||||
if (!fetchOptions.throwOnError) {
|
||||
promise = promise.catch(noop)
|
||||
}
|
||||
return query.state.fetchStatus === 'paused'
|
||||
? Promise.resolve()
|
||||
: promise
|
||||
}),
|
||||
)
|
||||
|
||||
return Promise.all(promises).then(noop)
|
||||
}
|
||||
|
||||
fetchQuery<
|
||||
TQueryFnData,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = never,
|
||||
>(
|
||||
options: FetchQueryOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>,
|
||||
): Promise<TData> {
|
||||
const defaultedOptions = this.defaultQueryOptions(options)
|
||||
|
||||
// https://github.com/tannerlinsley/react-query/issues/652
|
||||
if (defaultedOptions.retry === undefined) {
|
||||
defaultedOptions.retry = false
|
||||
}
|
||||
|
||||
const query = this.#queryCache.build(this, defaultedOptions)
|
||||
|
||||
return query.isStaleByTime(
|
||||
resolveStaleTime(defaultedOptions.staleTime, query),
|
||||
)
|
||||
? query.fetch(defaultedOptions)
|
||||
: Promise.resolve(query.state.data as TData)
|
||||
}
|
||||
|
||||
prefetchQuery<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>(
|
||||
options: FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
): Promise<void> {
|
||||
return this.fetchQuery(options).then(noop).catch(noop)
|
||||
}
|
||||
|
||||
fetchInfiniteQuery<
|
||||
TQueryFnData,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = unknown,
|
||||
>(
|
||||
options: FetchInfiniteQueryOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>,
|
||||
): Promise<InfiniteData<TData, TPageParam>> {
|
||||
options.behavior = infiniteQueryBehavior<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TPageParam
|
||||
>(options.pages)
|
||||
return this.fetchQuery(options as any)
|
||||
}
|
||||
|
||||
prefetchInfiniteQuery<
|
||||
TQueryFnData,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = unknown,
|
||||
>(
|
||||
options: FetchInfiniteQueryOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>,
|
||||
): Promise<void> {
|
||||
return this.fetchInfiniteQuery(options).then(noop).catch(noop)
|
||||
}
|
||||
|
||||
ensureInfiniteQueryData<
|
||||
TQueryFnData,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = unknown,
|
||||
>(
|
||||
options: EnsureInfiniteQueryDataOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>,
|
||||
): Promise<InfiniteData<TData, TPageParam>> {
|
||||
options.behavior = infiniteQueryBehavior<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TPageParam
|
||||
>(options.pages)
|
||||
|
||||
return this.ensureQueryData(options as any)
|
||||
}
|
||||
|
||||
resumePausedMutations(): Promise<unknown> {
|
||||
if (onlineManager.isOnline()) {
|
||||
return this.#mutationCache.resumePausedMutations()
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
getQueryCache(): QueryCache {
|
||||
return this.#queryCache
|
||||
}
|
||||
|
||||
getMutationCache(): MutationCache {
|
||||
return this.#mutationCache
|
||||
}
|
||||
|
||||
getDefaultOptions(): DefaultOptions {
|
||||
return this.#defaultOptions
|
||||
}
|
||||
|
||||
setDefaultOptions(options: DefaultOptions): void {
|
||||
this.#defaultOptions = options
|
||||
}
|
||||
|
||||
setQueryDefaults<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryData = TQueryFnData,
|
||||
>(
|
||||
queryKey: QueryKey,
|
||||
options: Partial<
|
||||
OmitKeyof<
|
||||
QueryObserverOptions<TQueryFnData, TError, TData, TQueryData>,
|
||||
'queryKey'
|
||||
>
|
||||
>,
|
||||
): void {
|
||||
this.#queryDefaults.set(hashKey(queryKey), {
|
||||
queryKey,
|
||||
defaultOptions: options,
|
||||
})
|
||||
}
|
||||
|
||||
getQueryDefaults(
|
||||
queryKey: QueryKey,
|
||||
): OmitKeyof<QueryObserverOptions<any, any, any, any, any>, 'queryKey'> {
|
||||
const defaults = [...this.#queryDefaults.values()]
|
||||
|
||||
const result: OmitKeyof<
|
||||
QueryObserverOptions<any, any, any, any, any>,
|
||||
'queryKey'
|
||||
> = {}
|
||||
|
||||
defaults.forEach((queryDefault) => {
|
||||
if (partialMatchKey(queryKey, queryDefault.queryKey)) {
|
||||
Object.assign(result, queryDefault.defaultOptions)
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
setMutationDefaults<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
TVariables = void,
|
||||
TOnMutateResult = unknown,
|
||||
>(
|
||||
mutationKey: MutationKey,
|
||||
options: OmitKeyof<
|
||||
MutationObserverOptions<TData, TError, TVariables, TOnMutateResult>,
|
||||
'mutationKey'
|
||||
>,
|
||||
): void {
|
||||
this.#mutationDefaults.set(hashKey(mutationKey), {
|
||||
mutationKey,
|
||||
defaultOptions: options,
|
||||
})
|
||||
}
|
||||
|
||||
getMutationDefaults(
|
||||
mutationKey: MutationKey,
|
||||
): OmitKeyof<MutationObserverOptions<any, any, any, any>, 'mutationKey'> {
|
||||
const defaults = [...this.#mutationDefaults.values()]
|
||||
|
||||
const result: OmitKeyof<
|
||||
MutationObserverOptions<any, any, any, any>,
|
||||
'mutationKey'
|
||||
> = {}
|
||||
|
||||
defaults.forEach((queryDefault) => {
|
||||
if (partialMatchKey(mutationKey, queryDefault.mutationKey)) {
|
||||
Object.assign(result, queryDefault.defaultOptions)
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
defaultQueryOptions<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = never,
|
||||
>(
|
||||
options:
|
||||
| QueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>
|
||||
| DefaultedQueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryData,
|
||||
TQueryKey
|
||||
>,
|
||||
): DefaultedQueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryData,
|
||||
TQueryKey
|
||||
> {
|
||||
if (options._defaulted) {
|
||||
return options as DefaultedQueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryData,
|
||||
TQueryKey
|
||||
>
|
||||
}
|
||||
|
||||
const defaultedOptions = {
|
||||
...this.#defaultOptions.queries,
|
||||
...this.getQueryDefaults(options.queryKey),
|
||||
...options,
|
||||
_defaulted: true,
|
||||
}
|
||||
|
||||
if (!defaultedOptions.queryHash) {
|
||||
defaultedOptions.queryHash = hashQueryKeyByOptions(
|
||||
defaultedOptions.queryKey,
|
||||
defaultedOptions,
|
||||
)
|
||||
}
|
||||
|
||||
// dependent default values
|
||||
if (defaultedOptions.refetchOnReconnect === undefined) {
|
||||
defaultedOptions.refetchOnReconnect =
|
||||
defaultedOptions.networkMode !== 'always'
|
||||
}
|
||||
if (defaultedOptions.throwOnError === undefined) {
|
||||
defaultedOptions.throwOnError = !!defaultedOptions.suspense
|
||||
}
|
||||
|
||||
if (!defaultedOptions.networkMode && defaultedOptions.persister) {
|
||||
defaultedOptions.networkMode = 'offlineFirst'
|
||||
}
|
||||
|
||||
if (defaultedOptions.queryFn === skipToken) {
|
||||
defaultedOptions.enabled = false
|
||||
}
|
||||
|
||||
return defaultedOptions as DefaultedQueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryData,
|
||||
TQueryKey
|
||||
>
|
||||
}
|
||||
|
||||
defaultMutationOptions<T extends MutationOptions<any, any, any, any>>(
|
||||
options?: T,
|
||||
): T {
|
||||
if (options?._defaulted) {
|
||||
return options
|
||||
}
|
||||
return {
|
||||
...this.#defaultOptions.mutations,
|
||||
...(options?.mutationKey &&
|
||||
this.getMutationDefaults(options.mutationKey)),
|
||||
...options,
|
||||
_defaulted: true,
|
||||
} as T
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.#queryCache.clear()
|
||||
this.#mutationCache.clear()
|
||||
}
|
||||
}
|
||||
833
node_modules/@tanstack/query-core/src/queryObserver.ts
generated
vendored
Normal file
833
node_modules/@tanstack/query-core/src/queryObserver.ts
generated
vendored
Normal file
@@ -0,0 +1,833 @@
|
||||
import { focusManager } from './focusManager'
|
||||
import { notifyManager } from './notifyManager'
|
||||
import { fetchState } from './query'
|
||||
import { Subscribable } from './subscribable'
|
||||
import { pendingThenable } from './thenable'
|
||||
import {
|
||||
isServer,
|
||||
isValidTimeout,
|
||||
noop,
|
||||
replaceData,
|
||||
resolveEnabled,
|
||||
resolveStaleTime,
|
||||
shallowEqualObjects,
|
||||
timeUntilStale,
|
||||
} from './utils'
|
||||
import { timeoutManager } from './timeoutManager'
|
||||
import type { ManagedTimerId } from './timeoutManager'
|
||||
import type { FetchOptions, Query, QueryState } from './query'
|
||||
import type { QueryClient } from './queryClient'
|
||||
import type { PendingThenable, Thenable } from './thenable'
|
||||
import type {
|
||||
DefaultError,
|
||||
DefaultedQueryObserverOptions,
|
||||
PlaceholderDataFunction,
|
||||
QueryKey,
|
||||
QueryObserverBaseResult,
|
||||
QueryObserverOptions,
|
||||
QueryObserverResult,
|
||||
QueryOptions,
|
||||
RefetchOptions,
|
||||
} from './types'
|
||||
|
||||
type QueryObserverListener<TData, TError> = (
|
||||
result: QueryObserverResult<TData, TError>,
|
||||
) => void
|
||||
|
||||
interface ObserverFetchOptions extends FetchOptions {
|
||||
throwOnError?: boolean
|
||||
}
|
||||
|
||||
export class QueryObserver<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
> extends Subscribable<QueryObserverListener<TData, TError>> {
|
||||
#client: QueryClient
|
||||
#currentQuery: Query<TQueryFnData, TError, TQueryData, TQueryKey> = undefined!
|
||||
#currentQueryInitialState: QueryState<TQueryData, TError> = undefined!
|
||||
#currentResult: QueryObserverResult<TData, TError> = undefined!
|
||||
#currentResultState?: QueryState<TQueryData, TError>
|
||||
#currentResultOptions?: QueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryData,
|
||||
TQueryKey
|
||||
>
|
||||
#currentThenable: Thenable<TData>
|
||||
#selectError: TError | null
|
||||
#selectFn?: (data: TQueryData) => TData
|
||||
#selectResult?: TData
|
||||
// This property keeps track of the last query with defined data.
|
||||
// It will be used to pass the previous data and query to the placeholder function between renders.
|
||||
#lastQueryWithDefinedData?: Query<TQueryFnData, TError, TQueryData, TQueryKey>
|
||||
#staleTimeoutId?: ManagedTimerId
|
||||
#refetchIntervalId?: ManagedTimerId
|
||||
#currentRefetchInterval?: number | false
|
||||
#trackedProps = new Set<keyof QueryObserverResult>()
|
||||
|
||||
constructor(
|
||||
client: QueryClient,
|
||||
public options: QueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryData,
|
||||
TQueryKey
|
||||
>,
|
||||
) {
|
||||
super()
|
||||
|
||||
this.#client = client
|
||||
this.#selectError = null
|
||||
this.#currentThenable = pendingThenable()
|
||||
|
||||
this.bindMethods()
|
||||
this.setOptions(options)
|
||||
}
|
||||
|
||||
protected bindMethods(): void {
|
||||
this.refetch = this.refetch.bind(this)
|
||||
}
|
||||
|
||||
protected onSubscribe(): void {
|
||||
if (this.listeners.size === 1) {
|
||||
this.#currentQuery.addObserver(this)
|
||||
|
||||
if (shouldFetchOnMount(this.#currentQuery, this.options)) {
|
||||
this.#executeFetch()
|
||||
} else {
|
||||
this.updateResult()
|
||||
}
|
||||
|
||||
this.#updateTimers()
|
||||
}
|
||||
}
|
||||
|
||||
protected onUnsubscribe(): void {
|
||||
if (!this.hasListeners()) {
|
||||
this.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
shouldFetchOnReconnect(): boolean {
|
||||
return shouldFetchOn(
|
||||
this.#currentQuery,
|
||||
this.options,
|
||||
this.options.refetchOnReconnect,
|
||||
)
|
||||
}
|
||||
|
||||
shouldFetchOnWindowFocus(): boolean {
|
||||
return shouldFetchOn(
|
||||
this.#currentQuery,
|
||||
this.options,
|
||||
this.options.refetchOnWindowFocus,
|
||||
)
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this.listeners = new Set()
|
||||
this.#clearStaleTimeout()
|
||||
this.#clearRefetchInterval()
|
||||
this.#currentQuery.removeObserver(this)
|
||||
}
|
||||
|
||||
setOptions(
|
||||
options: QueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryData,
|
||||
TQueryKey
|
||||
>,
|
||||
): void {
|
||||
const prevOptions = this.options
|
||||
const prevQuery = this.#currentQuery
|
||||
|
||||
this.options = this.#client.defaultQueryOptions(options)
|
||||
|
||||
if (
|
||||
this.options.enabled !== undefined &&
|
||||
typeof this.options.enabled !== 'boolean' &&
|
||||
typeof this.options.enabled !== 'function' &&
|
||||
typeof resolveEnabled(this.options.enabled, this.#currentQuery) !==
|
||||
'boolean'
|
||||
) {
|
||||
throw new Error(
|
||||
'Expected enabled to be a boolean or a callback that returns a boolean',
|
||||
)
|
||||
}
|
||||
|
||||
this.#updateQuery()
|
||||
this.#currentQuery.setOptions(this.options)
|
||||
|
||||
if (
|
||||
prevOptions._defaulted &&
|
||||
!shallowEqualObjects(this.options, prevOptions)
|
||||
) {
|
||||
this.#client.getQueryCache().notify({
|
||||
type: 'observerOptionsUpdated',
|
||||
query: this.#currentQuery,
|
||||
observer: this,
|
||||
})
|
||||
}
|
||||
|
||||
const mounted = this.hasListeners()
|
||||
|
||||
// Fetch if there are subscribers
|
||||
if (
|
||||
mounted &&
|
||||
shouldFetchOptionally(
|
||||
this.#currentQuery,
|
||||
prevQuery,
|
||||
this.options,
|
||||
prevOptions,
|
||||
)
|
||||
) {
|
||||
this.#executeFetch()
|
||||
}
|
||||
|
||||
// Update result
|
||||
this.updateResult()
|
||||
|
||||
// Update stale interval if needed
|
||||
if (
|
||||
mounted &&
|
||||
(this.#currentQuery !== prevQuery ||
|
||||
resolveEnabled(this.options.enabled, this.#currentQuery) !==
|
||||
resolveEnabled(prevOptions.enabled, this.#currentQuery) ||
|
||||
resolveStaleTime(this.options.staleTime, this.#currentQuery) !==
|
||||
resolveStaleTime(prevOptions.staleTime, this.#currentQuery))
|
||||
) {
|
||||
this.#updateStaleTimeout()
|
||||
}
|
||||
|
||||
const nextRefetchInterval = this.#computeRefetchInterval()
|
||||
|
||||
// Update refetch interval if needed
|
||||
if (
|
||||
mounted &&
|
||||
(this.#currentQuery !== prevQuery ||
|
||||
resolveEnabled(this.options.enabled, this.#currentQuery) !==
|
||||
resolveEnabled(prevOptions.enabled, this.#currentQuery) ||
|
||||
nextRefetchInterval !== this.#currentRefetchInterval)
|
||||
) {
|
||||
this.#updateRefetchInterval(nextRefetchInterval)
|
||||
}
|
||||
}
|
||||
|
||||
getOptimisticResult(
|
||||
options: DefaultedQueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryData,
|
||||
TQueryKey
|
||||
>,
|
||||
): QueryObserverResult<TData, TError> {
|
||||
const query = this.#client.getQueryCache().build(this.#client, options)
|
||||
|
||||
const result = this.createResult(query, options)
|
||||
|
||||
if (shouldAssignObserverCurrentProperties(this, result)) {
|
||||
// this assigns the optimistic result to the current Observer
|
||||
// because if the query function changes, useQuery will be performing
|
||||
// an effect where it would fetch again.
|
||||
// When the fetch finishes, we perform a deep data cloning in order
|
||||
// to reuse objects references. This deep data clone is performed against
|
||||
// the `observer.currentResult.data` property
|
||||
// When QueryKey changes, we refresh the query and get new `optimistic`
|
||||
// result, while we leave the `observer.currentResult`, so when new data
|
||||
// arrives, it finds the old `observer.currentResult` which is related
|
||||
// to the old QueryKey. Which means that currentResult and selectData are
|
||||
// out of sync already.
|
||||
// To solve this, we move the cursor of the currentResult every time
|
||||
// an observer reads an optimistic value.
|
||||
|
||||
// When keeping the previous data, the result doesn't change until new
|
||||
// data arrives.
|
||||
this.#currentResult = result
|
||||
this.#currentResultOptions = this.options
|
||||
this.#currentResultState = this.#currentQuery.state
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
getCurrentResult(): QueryObserverResult<TData, TError> {
|
||||
return this.#currentResult
|
||||
}
|
||||
|
||||
trackResult(
|
||||
result: QueryObserverResult<TData, TError>,
|
||||
onPropTracked?: (key: keyof QueryObserverResult) => void,
|
||||
): QueryObserverResult<TData, TError> {
|
||||
return new Proxy(result, {
|
||||
get: (target, key) => {
|
||||
this.trackProp(key as keyof QueryObserverResult)
|
||||
onPropTracked?.(key as keyof QueryObserverResult)
|
||||
if (key === 'promise') {
|
||||
this.trackProp('data')
|
||||
if (
|
||||
!this.options.experimental_prefetchInRender &&
|
||||
this.#currentThenable.status === 'pending'
|
||||
) {
|
||||
this.#currentThenable.reject(
|
||||
new Error(
|
||||
'experimental_prefetchInRender feature flag is not enabled',
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
return Reflect.get(target, key)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
trackProp(key: keyof QueryObserverResult) {
|
||||
this.#trackedProps.add(key)
|
||||
}
|
||||
|
||||
getCurrentQuery(): Query<TQueryFnData, TError, TQueryData, TQueryKey> {
|
||||
return this.#currentQuery
|
||||
}
|
||||
|
||||
refetch({ ...options }: RefetchOptions = {}): Promise<
|
||||
QueryObserverResult<TData, TError>
|
||||
> {
|
||||
return this.fetch({
|
||||
...options,
|
||||
})
|
||||
}
|
||||
|
||||
fetchOptimistic(
|
||||
options: QueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryData,
|
||||
TQueryKey
|
||||
>,
|
||||
): Promise<QueryObserverResult<TData, TError>> {
|
||||
const defaultedOptions = this.#client.defaultQueryOptions(options)
|
||||
|
||||
const query = this.#client
|
||||
.getQueryCache()
|
||||
.build(this.#client, defaultedOptions)
|
||||
|
||||
return query.fetch().then(() => this.createResult(query, defaultedOptions))
|
||||
}
|
||||
|
||||
protected fetch(
|
||||
fetchOptions: ObserverFetchOptions,
|
||||
): Promise<QueryObserverResult<TData, TError>> {
|
||||
return this.#executeFetch({
|
||||
...fetchOptions,
|
||||
cancelRefetch: fetchOptions.cancelRefetch ?? true,
|
||||
}).then(() => {
|
||||
this.updateResult()
|
||||
return this.#currentResult
|
||||
})
|
||||
}
|
||||
|
||||
#executeFetch(
|
||||
fetchOptions?: Omit<ObserverFetchOptions, 'initialPromise'>,
|
||||
): Promise<TQueryData | undefined> {
|
||||
// Make sure we reference the latest query as the current one might have been removed
|
||||
this.#updateQuery()
|
||||
|
||||
// Fetch
|
||||
let promise: Promise<TQueryData | undefined> = this.#currentQuery.fetch(
|
||||
this.options as QueryOptions<TQueryFnData, TError, TQueryData, TQueryKey>,
|
||||
fetchOptions,
|
||||
)
|
||||
|
||||
if (!fetchOptions?.throwOnError) {
|
||||
promise = promise.catch(noop)
|
||||
}
|
||||
|
||||
return promise
|
||||
}
|
||||
|
||||
#updateStaleTimeout(): void {
|
||||
this.#clearStaleTimeout()
|
||||
const staleTime = resolveStaleTime(
|
||||
this.options.staleTime,
|
||||
this.#currentQuery,
|
||||
)
|
||||
|
||||
if (isServer || this.#currentResult.isStale || !isValidTimeout(staleTime)) {
|
||||
return
|
||||
}
|
||||
|
||||
const time = timeUntilStale(this.#currentResult.dataUpdatedAt, staleTime)
|
||||
|
||||
// The timeout is sometimes triggered 1 ms before the stale time expiration.
|
||||
// To mitigate this issue we always add 1 ms to the timeout.
|
||||
const timeout = time + 1
|
||||
|
||||
this.#staleTimeoutId = timeoutManager.setTimeout(() => {
|
||||
if (!this.#currentResult.isStale) {
|
||||
this.updateResult()
|
||||
}
|
||||
}, timeout)
|
||||
}
|
||||
|
||||
#computeRefetchInterval() {
|
||||
return (
|
||||
(typeof this.options.refetchInterval === 'function'
|
||||
? this.options.refetchInterval(this.#currentQuery)
|
||||
: this.options.refetchInterval) ?? false
|
||||
)
|
||||
}
|
||||
|
||||
#updateRefetchInterval(nextInterval: number | false): void {
|
||||
this.#clearRefetchInterval()
|
||||
|
||||
this.#currentRefetchInterval = nextInterval
|
||||
|
||||
if (
|
||||
isServer ||
|
||||
resolveEnabled(this.options.enabled, this.#currentQuery) === false ||
|
||||
!isValidTimeout(this.#currentRefetchInterval) ||
|
||||
this.#currentRefetchInterval === 0
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
this.#refetchIntervalId = timeoutManager.setInterval(() => {
|
||||
if (
|
||||
this.options.refetchIntervalInBackground ||
|
||||
focusManager.isFocused()
|
||||
) {
|
||||
this.#executeFetch()
|
||||
}
|
||||
}, this.#currentRefetchInterval)
|
||||
}
|
||||
|
||||
#updateTimers(): void {
|
||||
this.#updateStaleTimeout()
|
||||
this.#updateRefetchInterval(this.#computeRefetchInterval())
|
||||
}
|
||||
|
||||
#clearStaleTimeout(): void {
|
||||
if (this.#staleTimeoutId) {
|
||||
timeoutManager.clearTimeout(this.#staleTimeoutId)
|
||||
this.#staleTimeoutId = undefined
|
||||
}
|
||||
}
|
||||
|
||||
#clearRefetchInterval(): void {
|
||||
if (this.#refetchIntervalId) {
|
||||
timeoutManager.clearInterval(this.#refetchIntervalId)
|
||||
this.#refetchIntervalId = undefined
|
||||
}
|
||||
}
|
||||
|
||||
protected createResult(
|
||||
query: Query<TQueryFnData, TError, TQueryData, TQueryKey>,
|
||||
options: QueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryData,
|
||||
TQueryKey
|
||||
>,
|
||||
): QueryObserverResult<TData, TError> {
|
||||
const prevQuery = this.#currentQuery
|
||||
const prevOptions = this.options
|
||||
const prevResult = this.#currentResult as
|
||||
| QueryObserverResult<TData, TError>
|
||||
| undefined
|
||||
const prevResultState = this.#currentResultState
|
||||
const prevResultOptions = this.#currentResultOptions
|
||||
const queryChange = query !== prevQuery
|
||||
const queryInitialState = queryChange
|
||||
? query.state
|
||||
: this.#currentQueryInitialState
|
||||
|
||||
const { state } = query
|
||||
let newState = { ...state }
|
||||
let isPlaceholderData = false
|
||||
let data: TData | undefined
|
||||
|
||||
// Optimistically set result in fetching state if needed
|
||||
if (options._optimisticResults) {
|
||||
const mounted = this.hasListeners()
|
||||
|
||||
const fetchOnMount = !mounted && shouldFetchOnMount(query, options)
|
||||
|
||||
const fetchOptionally =
|
||||
mounted && shouldFetchOptionally(query, prevQuery, options, prevOptions)
|
||||
|
||||
if (fetchOnMount || fetchOptionally) {
|
||||
newState = {
|
||||
...newState,
|
||||
...fetchState(state.data, query.options),
|
||||
}
|
||||
}
|
||||
if (options._optimisticResults === 'isRestoring') {
|
||||
newState.fetchStatus = 'idle'
|
||||
}
|
||||
}
|
||||
|
||||
let { error, errorUpdatedAt, status } = newState
|
||||
|
||||
// Per default, use query data
|
||||
data = newState.data as unknown as TData
|
||||
let skipSelect = false
|
||||
|
||||
// use placeholderData if needed
|
||||
if (
|
||||
options.placeholderData !== undefined &&
|
||||
data === undefined &&
|
||||
status === 'pending'
|
||||
) {
|
||||
let placeholderData
|
||||
|
||||
// Memoize placeholder data
|
||||
if (
|
||||
prevResult?.isPlaceholderData &&
|
||||
options.placeholderData === prevResultOptions?.placeholderData
|
||||
) {
|
||||
placeholderData = prevResult.data
|
||||
// we have to skip select when reading this memoization
|
||||
// because prevResult.data is already "selected"
|
||||
skipSelect = true
|
||||
} else {
|
||||
// compute placeholderData
|
||||
placeholderData =
|
||||
typeof options.placeholderData === 'function'
|
||||
? (
|
||||
options.placeholderData as unknown as PlaceholderDataFunction<TQueryData>
|
||||
)(
|
||||
this.#lastQueryWithDefinedData?.state.data,
|
||||
this.#lastQueryWithDefinedData as any,
|
||||
)
|
||||
: options.placeholderData
|
||||
}
|
||||
|
||||
if (placeholderData !== undefined) {
|
||||
status = 'success'
|
||||
data = replaceData(
|
||||
prevResult?.data,
|
||||
placeholderData as unknown,
|
||||
options,
|
||||
) as TData
|
||||
isPlaceholderData = true
|
||||
}
|
||||
}
|
||||
|
||||
// Select data if needed
|
||||
// this also runs placeholderData through the select function
|
||||
if (options.select && data !== undefined && !skipSelect) {
|
||||
// Memoize select result
|
||||
if (
|
||||
prevResult &&
|
||||
data === prevResultState?.data &&
|
||||
options.select === this.#selectFn
|
||||
) {
|
||||
data = this.#selectResult
|
||||
} else {
|
||||
try {
|
||||
this.#selectFn = options.select
|
||||
data = options.select(data as any)
|
||||
data = replaceData(prevResult?.data, data, options)
|
||||
this.#selectResult = data
|
||||
this.#selectError = null
|
||||
} catch (selectError) {
|
||||
this.#selectError = selectError as TError
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.#selectError) {
|
||||
error = this.#selectError as any
|
||||
data = this.#selectResult
|
||||
errorUpdatedAt = Date.now()
|
||||
status = 'error'
|
||||
}
|
||||
|
||||
const isFetching = newState.fetchStatus === 'fetching'
|
||||
const isPending = status === 'pending'
|
||||
const isError = status === 'error'
|
||||
|
||||
const isLoading = isPending && isFetching
|
||||
const hasData = data !== undefined
|
||||
|
||||
const result: QueryObserverBaseResult<TData, TError> = {
|
||||
status,
|
||||
fetchStatus: newState.fetchStatus,
|
||||
isPending,
|
||||
isSuccess: status === 'success',
|
||||
isError,
|
||||
isInitialLoading: isLoading,
|
||||
isLoading,
|
||||
data,
|
||||
dataUpdatedAt: newState.dataUpdatedAt,
|
||||
error,
|
||||
errorUpdatedAt,
|
||||
failureCount: newState.fetchFailureCount,
|
||||
failureReason: newState.fetchFailureReason,
|
||||
errorUpdateCount: newState.errorUpdateCount,
|
||||
isFetched: newState.dataUpdateCount > 0 || newState.errorUpdateCount > 0,
|
||||
isFetchedAfterMount:
|
||||
newState.dataUpdateCount > queryInitialState.dataUpdateCount ||
|
||||
newState.errorUpdateCount > queryInitialState.errorUpdateCount,
|
||||
isFetching,
|
||||
isRefetching: isFetching && !isPending,
|
||||
isLoadingError: isError && !hasData,
|
||||
isPaused: newState.fetchStatus === 'paused',
|
||||
isPlaceholderData,
|
||||
isRefetchError: isError && hasData,
|
||||
isStale: isStale(query, options),
|
||||
refetch: this.refetch,
|
||||
promise: this.#currentThenable,
|
||||
isEnabled: resolveEnabled(options.enabled, query) !== false,
|
||||
}
|
||||
|
||||
const nextResult = result as QueryObserverResult<TData, TError>
|
||||
|
||||
if (this.options.experimental_prefetchInRender) {
|
||||
const finalizeThenableIfPossible = (thenable: PendingThenable<TData>) => {
|
||||
if (nextResult.status === 'error') {
|
||||
thenable.reject(nextResult.error)
|
||||
} else if (nextResult.data !== undefined) {
|
||||
thenable.resolve(nextResult.data)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new thenable and result promise when the results have changed
|
||||
*/
|
||||
const recreateThenable = () => {
|
||||
const pending =
|
||||
(this.#currentThenable =
|
||||
nextResult.promise =
|
||||
pendingThenable())
|
||||
|
||||
finalizeThenableIfPossible(pending)
|
||||
}
|
||||
|
||||
const prevThenable = this.#currentThenable
|
||||
switch (prevThenable.status) {
|
||||
case 'pending':
|
||||
// Finalize the previous thenable if it was pending
|
||||
// and we are still observing the same query
|
||||
if (query.queryHash === prevQuery.queryHash) {
|
||||
finalizeThenableIfPossible(prevThenable)
|
||||
}
|
||||
break
|
||||
case 'fulfilled':
|
||||
if (
|
||||
nextResult.status === 'error' ||
|
||||
nextResult.data !== prevThenable.value
|
||||
) {
|
||||
recreateThenable()
|
||||
}
|
||||
break
|
||||
case 'rejected':
|
||||
if (
|
||||
nextResult.status !== 'error' ||
|
||||
nextResult.error !== prevThenable.reason
|
||||
) {
|
||||
recreateThenable()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nextResult
|
||||
}
|
||||
|
||||
updateResult(): void {
|
||||
const prevResult = this.#currentResult as
|
||||
| QueryObserverResult<TData, TError>
|
||||
| undefined
|
||||
|
||||
const nextResult = this.createResult(this.#currentQuery, this.options)
|
||||
|
||||
this.#currentResultState = this.#currentQuery.state
|
||||
this.#currentResultOptions = this.options
|
||||
|
||||
if (this.#currentResultState.data !== undefined) {
|
||||
this.#lastQueryWithDefinedData = this.#currentQuery
|
||||
}
|
||||
|
||||
// Only notify and update result if something has changed
|
||||
if (shallowEqualObjects(nextResult, prevResult)) {
|
||||
return
|
||||
}
|
||||
|
||||
this.#currentResult = nextResult
|
||||
|
||||
const shouldNotifyListeners = (): boolean => {
|
||||
if (!prevResult) {
|
||||
return true
|
||||
}
|
||||
|
||||
const { notifyOnChangeProps } = this.options
|
||||
const notifyOnChangePropsValue =
|
||||
typeof notifyOnChangeProps === 'function'
|
||||
? notifyOnChangeProps()
|
||||
: notifyOnChangeProps
|
||||
|
||||
if (
|
||||
notifyOnChangePropsValue === 'all' ||
|
||||
(!notifyOnChangePropsValue && !this.#trackedProps.size)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
const includedProps = new Set(
|
||||
notifyOnChangePropsValue ?? this.#trackedProps,
|
||||
)
|
||||
|
||||
if (this.options.throwOnError) {
|
||||
includedProps.add('error')
|
||||
}
|
||||
|
||||
return Object.keys(this.#currentResult).some((key) => {
|
||||
const typedKey = key as keyof QueryObserverResult
|
||||
const changed = this.#currentResult[typedKey] !== prevResult[typedKey]
|
||||
|
||||
return changed && includedProps.has(typedKey)
|
||||
})
|
||||
}
|
||||
|
||||
this.#notify({ listeners: shouldNotifyListeners() })
|
||||
}
|
||||
|
||||
#updateQuery(): void {
|
||||
const query = this.#client.getQueryCache().build(this.#client, this.options)
|
||||
|
||||
if (query === this.#currentQuery) {
|
||||
return
|
||||
}
|
||||
|
||||
const prevQuery = this.#currentQuery as
|
||||
| Query<TQueryFnData, TError, TQueryData, TQueryKey>
|
||||
| undefined
|
||||
this.#currentQuery = query
|
||||
this.#currentQueryInitialState = query.state
|
||||
|
||||
if (this.hasListeners()) {
|
||||
prevQuery?.removeObserver(this)
|
||||
query.addObserver(this)
|
||||
}
|
||||
}
|
||||
|
||||
onQueryUpdate(): void {
|
||||
this.updateResult()
|
||||
|
||||
if (this.hasListeners()) {
|
||||
this.#updateTimers()
|
||||
}
|
||||
}
|
||||
|
||||
#notify(notifyOptions: { listeners: boolean }): void {
|
||||
notifyManager.batch(() => {
|
||||
// First, trigger the listeners
|
||||
if (notifyOptions.listeners) {
|
||||
this.listeners.forEach((listener) => {
|
||||
listener(this.#currentResult)
|
||||
})
|
||||
}
|
||||
|
||||
// Then the cache listeners
|
||||
this.#client.getQueryCache().notify({
|
||||
query: this.#currentQuery,
|
||||
type: 'observerResultsUpdated',
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function shouldLoadOnMount(
|
||||
query: Query<any, any, any, any>,
|
||||
options: QueryObserverOptions<any, any, any, any>,
|
||||
): boolean {
|
||||
return (
|
||||
resolveEnabled(options.enabled, query) !== false &&
|
||||
query.state.data === undefined &&
|
||||
!(query.state.status === 'error' && options.retryOnMount === false)
|
||||
)
|
||||
}
|
||||
|
||||
function shouldFetchOnMount(
|
||||
query: Query<any, any, any, any>,
|
||||
options: QueryObserverOptions<any, any, any, any, any>,
|
||||
): boolean {
|
||||
return (
|
||||
shouldLoadOnMount(query, options) ||
|
||||
(query.state.data !== undefined &&
|
||||
shouldFetchOn(query, options, options.refetchOnMount))
|
||||
)
|
||||
}
|
||||
|
||||
function shouldFetchOn(
|
||||
query: Query<any, any, any, any>,
|
||||
options: QueryObserverOptions<any, any, any, any, any>,
|
||||
field: (typeof options)['refetchOnMount'] &
|
||||
(typeof options)['refetchOnWindowFocus'] &
|
||||
(typeof options)['refetchOnReconnect'],
|
||||
) {
|
||||
if (
|
||||
resolveEnabled(options.enabled, query) !== false &&
|
||||
resolveStaleTime(options.staleTime, query) !== 'static'
|
||||
) {
|
||||
const value = typeof field === 'function' ? field(query) : field
|
||||
|
||||
return value === 'always' || (value !== false && isStale(query, options))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function shouldFetchOptionally(
|
||||
query: Query<any, any, any, any>,
|
||||
prevQuery: Query<any, any, any, any>,
|
||||
options: QueryObserverOptions<any, any, any, any, any>,
|
||||
prevOptions: QueryObserverOptions<any, any, any, any, any>,
|
||||
): boolean {
|
||||
return (
|
||||
(query !== prevQuery ||
|
||||
resolveEnabled(prevOptions.enabled, query) === false) &&
|
||||
(!options.suspense || query.state.status !== 'error') &&
|
||||
isStale(query, options)
|
||||
)
|
||||
}
|
||||
|
||||
function isStale(
|
||||
query: Query<any, any, any, any>,
|
||||
options: QueryObserverOptions<any, any, any, any, any>,
|
||||
): boolean {
|
||||
return (
|
||||
resolveEnabled(options.enabled, query) !== false &&
|
||||
query.isStaleByTime(resolveStaleTime(options.staleTime, query))
|
||||
)
|
||||
}
|
||||
|
||||
// this function would decide if we will update the observer's 'current'
|
||||
// properties after an optimistic reading via getOptimisticResult
|
||||
function shouldAssignObserverCurrentProperties<
|
||||
TQueryFnData = unknown,
|
||||
TError = unknown,
|
||||
TData = TQueryFnData,
|
||||
TQueryData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>(
|
||||
observer: QueryObserver<TQueryFnData, TError, TData, TQueryData, TQueryKey>,
|
||||
optimisticResult: QueryObserverResult<TData, TError>,
|
||||
) {
|
||||
// if the newly created result isn't what the observer is holding as current,
|
||||
// then we'll need to update the properties as well
|
||||
if (!shallowEqualObjects(observer.getCurrentResult(), optimisticResult)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// basically, just keep previous properties if nothing changed
|
||||
return false
|
||||
}
|
||||
39
node_modules/@tanstack/query-core/src/removable.ts
generated
vendored
Normal file
39
node_modules/@tanstack/query-core/src/removable.ts
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
import { timeoutManager } from './timeoutManager'
|
||||
import { isServer, isValidTimeout } from './utils'
|
||||
import type { ManagedTimerId } from './timeoutManager'
|
||||
|
||||
export abstract class Removable {
|
||||
gcTime!: number
|
||||
#gcTimeout?: ManagedTimerId
|
||||
|
||||
destroy(): void {
|
||||
this.clearGcTimeout()
|
||||
}
|
||||
|
||||
protected scheduleGc(): void {
|
||||
this.clearGcTimeout()
|
||||
|
||||
if (isValidTimeout(this.gcTime)) {
|
||||
this.#gcTimeout = timeoutManager.setTimeout(() => {
|
||||
this.optionalRemove()
|
||||
}, this.gcTime)
|
||||
}
|
||||
}
|
||||
|
||||
protected updateGcTime(newGcTime: number | undefined): void {
|
||||
// Default to 5 minutes (Infinity for server-side) if no gcTime is set
|
||||
this.gcTime = Math.max(
|
||||
this.gcTime || 0,
|
||||
newGcTime ?? (isServer ? Infinity : 5 * 60 * 1000),
|
||||
)
|
||||
}
|
||||
|
||||
protected clearGcTimeout() {
|
||||
if (this.#gcTimeout) {
|
||||
timeoutManager.clearTimeout(this.#gcTimeout)
|
||||
this.#gcTimeout = undefined
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract optionalRemove(): void
|
||||
}
|
||||
228
node_modules/@tanstack/query-core/src/retryer.ts
generated
vendored
Normal file
228
node_modules/@tanstack/query-core/src/retryer.ts
generated
vendored
Normal file
@@ -0,0 +1,228 @@
|
||||
import { focusManager } from './focusManager'
|
||||
import { onlineManager } from './onlineManager'
|
||||
import { pendingThenable } from './thenable'
|
||||
import { isServer, sleep } from './utils'
|
||||
import type { Thenable } from './thenable'
|
||||
import type { CancelOptions, DefaultError, NetworkMode } from './types'
|
||||
|
||||
// TYPES
|
||||
|
||||
interface RetryerConfig<TData = unknown, TError = DefaultError> {
|
||||
fn: () => TData | Promise<TData>
|
||||
initialPromise?: Promise<TData>
|
||||
onCancel?: (error: TError) => void
|
||||
onFail?: (failureCount: number, error: TError) => void
|
||||
onPause?: () => void
|
||||
onContinue?: () => void
|
||||
retry?: RetryValue<TError>
|
||||
retryDelay?: RetryDelayValue<TError>
|
||||
networkMode: NetworkMode | undefined
|
||||
canRun: () => boolean
|
||||
}
|
||||
|
||||
export interface Retryer<TData = unknown> {
|
||||
promise: Promise<TData>
|
||||
cancel: (cancelOptions?: CancelOptions) => void
|
||||
continue: () => Promise<unknown>
|
||||
cancelRetry: () => void
|
||||
continueRetry: () => void
|
||||
canStart: () => boolean
|
||||
start: () => Promise<TData>
|
||||
status: () => 'pending' | 'resolved' | 'rejected'
|
||||
}
|
||||
|
||||
export type RetryValue<TError> = boolean | number | ShouldRetryFunction<TError>
|
||||
|
||||
type ShouldRetryFunction<TError = DefaultError> = (
|
||||
failureCount: number,
|
||||
error: TError,
|
||||
) => boolean
|
||||
|
||||
export type RetryDelayValue<TError> = number | RetryDelayFunction<TError>
|
||||
|
||||
type RetryDelayFunction<TError = DefaultError> = (
|
||||
failureCount: number,
|
||||
error: TError,
|
||||
) => number
|
||||
|
||||
function defaultRetryDelay(failureCount: number) {
|
||||
return Math.min(1000 * 2 ** failureCount, 30000)
|
||||
}
|
||||
|
||||
export function canFetch(networkMode: NetworkMode | undefined): boolean {
|
||||
return (networkMode ?? 'online') === 'online'
|
||||
? onlineManager.isOnline()
|
||||
: true
|
||||
}
|
||||
|
||||
export class CancelledError extends Error {
|
||||
revert?: boolean
|
||||
silent?: boolean
|
||||
constructor(options?: CancelOptions) {
|
||||
super('CancelledError')
|
||||
this.revert = options?.revert
|
||||
this.silent = options?.silent
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use instanceof `CancelledError` instead.
|
||||
*/
|
||||
export function isCancelledError(value: any): value is CancelledError {
|
||||
return value instanceof CancelledError
|
||||
}
|
||||
|
||||
export function createRetryer<TData = unknown, TError = DefaultError>(
|
||||
config: RetryerConfig<TData, TError>,
|
||||
): Retryer<TData> {
|
||||
let isRetryCancelled = false
|
||||
let failureCount = 0
|
||||
let continueFn: ((value?: unknown) => void) | undefined
|
||||
|
||||
const thenable = pendingThenable<TData>()
|
||||
|
||||
const isResolved = () =>
|
||||
(thenable.status as Thenable<TData>['status']) !== 'pending'
|
||||
|
||||
const cancel = (cancelOptions?: CancelOptions): void => {
|
||||
if (!isResolved()) {
|
||||
const error = new CancelledError(cancelOptions) as TError
|
||||
reject(error)
|
||||
|
||||
config.onCancel?.(error)
|
||||
}
|
||||
}
|
||||
const cancelRetry = () => {
|
||||
isRetryCancelled = true
|
||||
}
|
||||
|
||||
const continueRetry = () => {
|
||||
isRetryCancelled = false
|
||||
}
|
||||
|
||||
const canContinue = () =>
|
||||
focusManager.isFocused() &&
|
||||
(config.networkMode === 'always' || onlineManager.isOnline()) &&
|
||||
config.canRun()
|
||||
|
||||
const canStart = () => canFetch(config.networkMode) && config.canRun()
|
||||
|
||||
const resolve = (value: any) => {
|
||||
if (!isResolved()) {
|
||||
continueFn?.()
|
||||
thenable.resolve(value)
|
||||
}
|
||||
}
|
||||
|
||||
const reject = (value: any) => {
|
||||
if (!isResolved()) {
|
||||
continueFn?.()
|
||||
thenable.reject(value)
|
||||
}
|
||||
}
|
||||
|
||||
const pause = () => {
|
||||
return new Promise((continueResolve) => {
|
||||
continueFn = (value) => {
|
||||
if (isResolved() || canContinue()) {
|
||||
continueResolve(value)
|
||||
}
|
||||
}
|
||||
config.onPause?.()
|
||||
}).then(() => {
|
||||
continueFn = undefined
|
||||
if (!isResolved()) {
|
||||
config.onContinue?.()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Create loop function
|
||||
const run = () => {
|
||||
// Do nothing if already resolved
|
||||
if (isResolved()) {
|
||||
return
|
||||
}
|
||||
|
||||
let promiseOrValue: any
|
||||
|
||||
// we can re-use config.initialPromise on the first call of run()
|
||||
const initialPromise =
|
||||
failureCount === 0 ? config.initialPromise : undefined
|
||||
|
||||
// Execute query
|
||||
try {
|
||||
promiseOrValue = initialPromise ?? config.fn()
|
||||
} catch (error) {
|
||||
promiseOrValue = Promise.reject(error)
|
||||
}
|
||||
|
||||
Promise.resolve(promiseOrValue)
|
||||
.then(resolve)
|
||||
.catch((error) => {
|
||||
// Stop if the fetch is already resolved
|
||||
if (isResolved()) {
|
||||
return
|
||||
}
|
||||
|
||||
// Do we need to retry the request?
|
||||
const retry = config.retry ?? (isServer ? 0 : 3)
|
||||
const retryDelay = config.retryDelay ?? defaultRetryDelay
|
||||
const delay =
|
||||
typeof retryDelay === 'function'
|
||||
? retryDelay(failureCount, error)
|
||||
: retryDelay
|
||||
const shouldRetry =
|
||||
retry === true ||
|
||||
(typeof retry === 'number' && failureCount < retry) ||
|
||||
(typeof retry === 'function' && retry(failureCount, error))
|
||||
|
||||
if (isRetryCancelled || !shouldRetry) {
|
||||
// We are done if the query does not need to be retried
|
||||
reject(error)
|
||||
return
|
||||
}
|
||||
|
||||
failureCount++
|
||||
|
||||
// Notify on fail
|
||||
config.onFail?.(failureCount, error)
|
||||
|
||||
// Delay
|
||||
sleep(delay)
|
||||
// Pause if the document is not visible or when the device is offline
|
||||
.then(() => {
|
||||
return canContinue() ? undefined : pause()
|
||||
})
|
||||
.then(() => {
|
||||
if (isRetryCancelled) {
|
||||
reject(error)
|
||||
} else {
|
||||
run()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
promise: thenable,
|
||||
status: () => thenable.status,
|
||||
cancel,
|
||||
continue: () => {
|
||||
continueFn?.()
|
||||
return thenable
|
||||
},
|
||||
cancelRetry,
|
||||
continueRetry,
|
||||
canStart,
|
||||
start: () => {
|
||||
// Start loop
|
||||
if (canStart()) {
|
||||
run()
|
||||
} else {
|
||||
pause().then(run)
|
||||
}
|
||||
return thenable
|
||||
},
|
||||
}
|
||||
}
|
||||
99
node_modules/@tanstack/query-core/src/streamedQuery.ts
generated
vendored
Normal file
99
node_modules/@tanstack/query-core/src/streamedQuery.ts
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
import { addToEnd } from './utils'
|
||||
import type { QueryFunction, QueryFunctionContext, QueryKey } from './types'
|
||||
|
||||
type BaseStreamedQueryParams<TQueryFnData, TQueryKey extends QueryKey> = {
|
||||
streamFn: (
|
||||
context: QueryFunctionContext<TQueryKey>,
|
||||
) => AsyncIterable<TQueryFnData> | Promise<AsyncIterable<TQueryFnData>>
|
||||
refetchMode?: 'append' | 'reset' | 'replace'
|
||||
}
|
||||
|
||||
type SimpleStreamedQueryParams<
|
||||
TQueryFnData,
|
||||
TQueryKey extends QueryKey,
|
||||
> = BaseStreamedQueryParams<TQueryFnData, TQueryKey> & {
|
||||
reducer?: never
|
||||
initialValue?: never
|
||||
}
|
||||
|
||||
type ReducibleStreamedQueryParams<
|
||||
TQueryFnData,
|
||||
TData,
|
||||
TQueryKey extends QueryKey,
|
||||
> = BaseStreamedQueryParams<TQueryFnData, TQueryKey> & {
|
||||
reducer: (acc: TData, chunk: TQueryFnData) => TData
|
||||
initialValue: TData
|
||||
}
|
||||
|
||||
type StreamedQueryParams<TQueryFnData, TData, TQueryKey extends QueryKey> =
|
||||
| SimpleStreamedQueryParams<TQueryFnData, TQueryKey>
|
||||
| ReducibleStreamedQueryParams<TQueryFnData, TData, TQueryKey>
|
||||
|
||||
/**
|
||||
* This is a helper function to create a query function that streams data from an AsyncIterable.
|
||||
* Data will be an Array of all the chunks received.
|
||||
* The query will be in a 'pending' state until the first chunk of data is received, but will go to 'success' after that.
|
||||
* The query will stay in fetchStatus 'fetching' until the stream ends.
|
||||
* @param queryFn - The function that returns an AsyncIterable to stream data from.
|
||||
* @param refetchMode - Defines how re-fetches are handled.
|
||||
* Defaults to `'reset'`, erases all data and puts the query back into `pending` state.
|
||||
* Set to `'append'` to append new data to the existing data.
|
||||
* Set to `'replace'` to write all data to the cache once the stream ends.
|
||||
* @param reducer - A function to reduce the streamed chunks into the final data.
|
||||
* Defaults to a function that appends chunks to the end of the array.
|
||||
* @param initialValue - Initial value to be used while the first chunk is being fetched, and returned if the stream yields no values.
|
||||
*/
|
||||
export function streamedQuery<
|
||||
TQueryFnData = unknown,
|
||||
TData = Array<TQueryFnData>,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>({
|
||||
streamFn,
|
||||
refetchMode = 'reset',
|
||||
reducer = (items, chunk) =>
|
||||
addToEnd(items as Array<TQueryFnData>, chunk) as TData,
|
||||
initialValue = [] as TData,
|
||||
}: StreamedQueryParams<TQueryFnData, TData, TQueryKey>): QueryFunction<
|
||||
TData,
|
||||
TQueryKey
|
||||
> {
|
||||
return async (context) => {
|
||||
const query = context.client
|
||||
.getQueryCache()
|
||||
.find({ queryKey: context.queryKey, exact: true })
|
||||
const isRefetch = !!query && query.state.data !== undefined
|
||||
if (isRefetch && refetchMode === 'reset') {
|
||||
query.setState({
|
||||
status: 'pending',
|
||||
data: undefined,
|
||||
error: null,
|
||||
fetchStatus: 'fetching',
|
||||
})
|
||||
}
|
||||
|
||||
let result = initialValue
|
||||
|
||||
const stream = await streamFn(context)
|
||||
|
||||
for await (const chunk of stream) {
|
||||
if (context.signal.aborted) {
|
||||
break
|
||||
}
|
||||
|
||||
// don't append to the cache directly when replace-refetching
|
||||
if (!isRefetch || refetchMode !== 'replace') {
|
||||
context.client.setQueryData<TData>(context.queryKey, (prev) =>
|
||||
reducer(prev === undefined ? initialValue : prev, chunk),
|
||||
)
|
||||
}
|
||||
result = reducer(result, chunk)
|
||||
}
|
||||
|
||||
// finalize result: replace-refetching needs to write to the cache
|
||||
if (isRefetch && refetchMode === 'replace' && !context.signal.aborted) {
|
||||
context.client.setQueryData<TData>(context.queryKey, result)
|
||||
}
|
||||
|
||||
return context.client.getQueryData(context.queryKey) ?? initialValue
|
||||
}
|
||||
}
|
||||
30
node_modules/@tanstack/query-core/src/subscribable.ts
generated
vendored
Normal file
30
node_modules/@tanstack/query-core/src/subscribable.ts
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
export class Subscribable<TListener extends Function> {
|
||||
protected listeners = new Set<TListener>()
|
||||
|
||||
constructor() {
|
||||
this.subscribe = this.subscribe.bind(this)
|
||||
}
|
||||
|
||||
subscribe(listener: TListener): () => void {
|
||||
this.listeners.add(listener)
|
||||
|
||||
this.onSubscribe()
|
||||
|
||||
return () => {
|
||||
this.listeners.delete(listener)
|
||||
this.onUnsubscribe()
|
||||
}
|
||||
}
|
||||
|
||||
hasListeners(): boolean {
|
||||
return this.listeners.size > 0
|
||||
}
|
||||
|
||||
protected onSubscribe(): void {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
protected onUnsubscribe(): void {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
111
node_modules/@tanstack/query-core/src/thenable.ts
generated
vendored
Normal file
111
node_modules/@tanstack/query-core/src/thenable.ts
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Thenable types which matches React's types for promises
|
||||
*
|
||||
* React seemingly uses `.status`, `.value` and `.reason` properties on a promises to optimistically unwrap data from promises
|
||||
*
|
||||
* @see https://github.com/facebook/react/blob/main/packages/shared/ReactTypes.js#L112-L138
|
||||
* @see https://github.com/facebook/react/blob/4f604941569d2e8947ce1460a0b2997e835f37b9/packages/react-debug-tools/src/ReactDebugHooks.js#L224-L227
|
||||
*/
|
||||
|
||||
import { noop } from './utils'
|
||||
|
||||
interface Fulfilled<T> {
|
||||
status: 'fulfilled'
|
||||
value: T
|
||||
}
|
||||
interface Rejected {
|
||||
status: 'rejected'
|
||||
reason: unknown
|
||||
}
|
||||
interface Pending<T> {
|
||||
status: 'pending'
|
||||
|
||||
/**
|
||||
* Resolve the promise with a value.
|
||||
* Will remove the `resolve` and `reject` properties from the promise.
|
||||
*/
|
||||
resolve: (value: T) => void
|
||||
/**
|
||||
* Reject the promise with a reason.
|
||||
* Will remove the `resolve` and `reject` properties from the promise.
|
||||
*/
|
||||
reject: (reason: unknown) => void
|
||||
}
|
||||
|
||||
export type FulfilledThenable<T> = Promise<T> & Fulfilled<T>
|
||||
export type RejectedThenable<T> = Promise<T> & Rejected
|
||||
export type PendingThenable<T> = Promise<T> & Pending<T>
|
||||
|
||||
export type Thenable<T> =
|
||||
| FulfilledThenable<T>
|
||||
| RejectedThenable<T>
|
||||
| PendingThenable<T>
|
||||
|
||||
export function pendingThenable<T>(): PendingThenable<T> {
|
||||
let resolve: Pending<T>['resolve']
|
||||
let reject: Pending<T>['reject']
|
||||
// this could use `Promise.withResolvers()` in the future
|
||||
const thenable = new Promise((_resolve, _reject) => {
|
||||
resolve = _resolve
|
||||
reject = _reject
|
||||
}) as PendingThenable<T>
|
||||
|
||||
thenable.status = 'pending'
|
||||
thenable.catch(() => {
|
||||
// prevent unhandled rejection errors
|
||||
})
|
||||
|
||||
function finalize(data: Fulfilled<T> | Rejected) {
|
||||
Object.assign(thenable, data)
|
||||
|
||||
// clear pending props props to avoid calling them twice
|
||||
delete (thenable as Partial<PendingThenable<T>>).resolve
|
||||
delete (thenable as Partial<PendingThenable<T>>).reject
|
||||
}
|
||||
|
||||
thenable.resolve = (value) => {
|
||||
finalize({
|
||||
status: 'fulfilled',
|
||||
value,
|
||||
})
|
||||
|
||||
resolve(value)
|
||||
}
|
||||
thenable.reject = (reason) => {
|
||||
finalize({
|
||||
status: 'rejected',
|
||||
reason,
|
||||
})
|
||||
|
||||
reject(reason)
|
||||
}
|
||||
|
||||
return thenable
|
||||
}
|
||||
|
||||
/**
|
||||
* This function takes a Promise-like input and detects whether the data
|
||||
* is synchronously available or not.
|
||||
*
|
||||
* It does not inspect .status, .value or .reason properties of the promise,
|
||||
* as those are not always available, and the .status of React's promises
|
||||
* should not be considered part of the public API.
|
||||
*/
|
||||
export function tryResolveSync(promise: Promise<unknown> | Thenable<unknown>) {
|
||||
let data: unknown
|
||||
|
||||
promise
|
||||
.then((result) => {
|
||||
data = result
|
||||
return result
|
||||
}, noop)
|
||||
// .catch can be unavailable on certain kinds of thenable's
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
?.catch(noop)
|
||||
|
||||
if (data !== undefined) {
|
||||
return { data }
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
135
node_modules/@tanstack/query-core/src/timeoutManager.ts
generated
vendored
Normal file
135
node_modules/@tanstack/query-core/src/timeoutManager.ts
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* {@link TimeoutManager} does not support passing arguments to the callback.
|
||||
*
|
||||
* `(_: void)` is the argument type inferred by TypeScript's default typings for
|
||||
* `setTimeout(cb, number)`.
|
||||
* If we don't accept a single void argument, then
|
||||
* `new Promise(resolve => timeoutManager.setTimeout(resolve, N))` is a type error.
|
||||
*/
|
||||
export type TimeoutCallback = (_: void) => void
|
||||
|
||||
/**
|
||||
* Wrapping `setTimeout` is awkward from a typing perspective because platform
|
||||
* typings may extend the return type of `setTimeout`. For example, NodeJS
|
||||
* typings add `NodeJS.Timeout`; but a non-default `timeoutManager` may not be
|
||||
* able to return such a type.
|
||||
*/
|
||||
export type ManagedTimerId = number | { [Symbol.toPrimitive]: () => number }
|
||||
|
||||
/**
|
||||
* Backend for timer functions.
|
||||
*/
|
||||
export type TimeoutProvider<TTimerId extends ManagedTimerId = ManagedTimerId> =
|
||||
{
|
||||
readonly setTimeout: (callback: TimeoutCallback, delay: number) => TTimerId
|
||||
readonly clearTimeout: (timeoutId: TTimerId | undefined) => void
|
||||
|
||||
readonly setInterval: (callback: TimeoutCallback, delay: number) => TTimerId
|
||||
readonly clearInterval: (intervalId: TTimerId | undefined) => void
|
||||
}
|
||||
|
||||
export const defaultTimeoutProvider: TimeoutProvider<
|
||||
ReturnType<typeof setTimeout>
|
||||
> = {
|
||||
// We need the wrapper function syntax below instead of direct references to
|
||||
// global setTimeout etc.
|
||||
//
|
||||
// BAD: `setTimeout: setTimeout`
|
||||
// GOOD: `setTimeout: (cb, delay) => setTimeout(cb, delay)`
|
||||
//
|
||||
// If we use direct references here, then anything that wants to spy on or
|
||||
// replace the global setTimeout (like tests) won't work since we'll already
|
||||
// have a hard reference to the original implementation at the time when this
|
||||
// file was imported.
|
||||
setTimeout: (callback, delay) => setTimeout(callback, delay),
|
||||
clearTimeout: (timeoutId) => clearTimeout(timeoutId),
|
||||
|
||||
setInterval: (callback, delay) => setInterval(callback, delay),
|
||||
clearInterval: (intervalId) => clearInterval(intervalId),
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows customization of how timeouts are created.
|
||||
*
|
||||
* @tanstack/query-core makes liberal use of timeouts to implement `staleTime`
|
||||
* and `gcTime`. The default TimeoutManager provider uses the platform's global
|
||||
* `setTimeout` implementation, which is known to have scalability issues with
|
||||
* thousands of timeouts on the event loop.
|
||||
*
|
||||
* If you hit this limitation, consider providing a custom TimeoutProvider that
|
||||
* coalesces timeouts.
|
||||
*/
|
||||
export class TimeoutManager implements Omit<TimeoutProvider, 'name'> {
|
||||
// We cannot have TimeoutManager<T> as we must instantiate it with a concrete
|
||||
// type at app boot; and if we leave that type, then any new timer provider
|
||||
// would need to support ReturnType<typeof setTimeout>, which is infeasible.
|
||||
//
|
||||
// We settle for type safety for the TimeoutProvider type, and accept that
|
||||
// this class is unsafe internally to allow for extension.
|
||||
#provider: TimeoutProvider<any> = defaultTimeoutProvider
|
||||
#providerCalled = false
|
||||
|
||||
setTimeoutProvider<TTimerId extends ManagedTimerId>(
|
||||
provider: TimeoutProvider<TTimerId>,
|
||||
): void {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (this.#providerCalled && provider !== this.#provider) {
|
||||
// After changing providers, `clearTimeout` will not work as expected for
|
||||
// timeouts from the previous provider.
|
||||
//
|
||||
// Since they may allocate the same timeout ID, clearTimeout may cancel an
|
||||
// arbitrary different timeout, or unexpected no-op.
|
||||
//
|
||||
// We could protect against this by mixing the timeout ID bits
|
||||
// deterministically with some per-provider bits.
|
||||
//
|
||||
// We could internally queue `setTimeout` calls to `TimeoutManager` until
|
||||
// some API call to set the initial provider.
|
||||
console.error(
|
||||
`[timeoutManager]: Switching provider after calls to previous provider might result in unexpected behavior.`,
|
||||
{ previous: this.#provider, provider },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
this.#provider = provider
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
this.#providerCalled = false
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(callback: TimeoutCallback, delay: number): ManagedTimerId {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
this.#providerCalled = true
|
||||
}
|
||||
return this.#provider.setTimeout(callback, delay)
|
||||
}
|
||||
|
||||
clearTimeout(timeoutId: ManagedTimerId | undefined): void {
|
||||
this.#provider.clearTimeout(timeoutId)
|
||||
}
|
||||
|
||||
setInterval(callback: TimeoutCallback, delay: number): ManagedTimerId {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
this.#providerCalled = true
|
||||
}
|
||||
return this.#provider.setInterval(callback, delay)
|
||||
}
|
||||
|
||||
clearInterval(intervalId: ManagedTimerId | undefined): void {
|
||||
this.#provider.clearInterval(intervalId)
|
||||
}
|
||||
}
|
||||
|
||||
export const timeoutManager = new TimeoutManager()
|
||||
|
||||
/**
|
||||
* In many cases code wants to delay to the next event loop tick; this is not
|
||||
* mediated by {@link timeoutManager}.
|
||||
*
|
||||
* This function is provided to make auditing the `tanstack/query-core` for
|
||||
* incorrect use of system `setTimeout` easier.
|
||||
*/
|
||||
export function systemSetTimeoutZero(callback: TimeoutCallback): void {
|
||||
setTimeout(callback, 0)
|
||||
}
|
||||
1387
node_modules/@tanstack/query-core/src/types.ts
generated
vendored
Normal file
1387
node_modules/@tanstack/query-core/src/types.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
467
node_modules/@tanstack/query-core/src/utils.ts
generated
vendored
Normal file
467
node_modules/@tanstack/query-core/src/utils.ts
generated
vendored
Normal file
@@ -0,0 +1,467 @@
|
||||
import { timeoutManager } from './timeoutManager'
|
||||
import type {
|
||||
DefaultError,
|
||||
Enabled,
|
||||
FetchStatus,
|
||||
MutationKey,
|
||||
MutationStatus,
|
||||
QueryFunction,
|
||||
QueryKey,
|
||||
QueryOptions,
|
||||
StaleTime,
|
||||
StaleTimeFunction,
|
||||
} from './types'
|
||||
import type { Mutation } from './mutation'
|
||||
import type { FetchOptions, Query } from './query'
|
||||
|
||||
// TYPES
|
||||
|
||||
type DropLast<T extends ReadonlyArray<unknown>> = T extends readonly [
|
||||
...infer R,
|
||||
unknown,
|
||||
]
|
||||
? readonly [...R]
|
||||
: never
|
||||
|
||||
type TuplePrefixes<T extends ReadonlyArray<unknown>> = T extends readonly []
|
||||
? readonly []
|
||||
: TuplePrefixes<DropLast<T>> | T
|
||||
|
||||
export interface QueryFilters<TQueryKey extends QueryKey = QueryKey> {
|
||||
/**
|
||||
* Filter to active queries, inactive queries or all queries
|
||||
*/
|
||||
type?: QueryTypeFilter
|
||||
/**
|
||||
* Match query key exactly
|
||||
*/
|
||||
exact?: boolean
|
||||
/**
|
||||
* Include queries matching this predicate function
|
||||
*/
|
||||
predicate?: (query: Query) => boolean
|
||||
/**
|
||||
* Include queries matching this query key
|
||||
*/
|
||||
queryKey?: TQueryKey | TuplePrefixes<TQueryKey>
|
||||
/**
|
||||
* Include or exclude stale queries
|
||||
*/
|
||||
stale?: boolean
|
||||
/**
|
||||
* Include queries matching their fetchStatus
|
||||
*/
|
||||
fetchStatus?: FetchStatus
|
||||
}
|
||||
|
||||
export interface MutationFilters<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
TVariables = unknown,
|
||||
TOnMutateResult = unknown,
|
||||
> {
|
||||
/**
|
||||
* Match mutation key exactly
|
||||
*/
|
||||
exact?: boolean
|
||||
/**
|
||||
* Include mutations matching this predicate function
|
||||
*/
|
||||
predicate?: (
|
||||
mutation: Mutation<TData, TError, TVariables, TOnMutateResult>,
|
||||
) => boolean
|
||||
/**
|
||||
* Include mutations matching this mutation key
|
||||
*/
|
||||
mutationKey?: TuplePrefixes<MutationKey>
|
||||
/**
|
||||
* Filter by mutation status
|
||||
*/
|
||||
status?: MutationStatus
|
||||
}
|
||||
|
||||
export type Updater<TInput, TOutput> = TOutput | ((input: TInput) => TOutput)
|
||||
|
||||
export type QueryTypeFilter = 'all' | 'active' | 'inactive'
|
||||
|
||||
// UTILS
|
||||
|
||||
export const isServer = typeof window === 'undefined' || 'Deno' in globalThis
|
||||
|
||||
export function noop(): void
|
||||
export function noop(): undefined
|
||||
export function noop() {}
|
||||
|
||||
export function functionalUpdate<TInput, TOutput>(
|
||||
updater: Updater<TInput, TOutput>,
|
||||
input: TInput,
|
||||
): TOutput {
|
||||
return typeof updater === 'function'
|
||||
? (updater as (_: TInput) => TOutput)(input)
|
||||
: updater
|
||||
}
|
||||
|
||||
export function isValidTimeout(value: unknown): value is number {
|
||||
return typeof value === 'number' && value >= 0 && value !== Infinity
|
||||
}
|
||||
|
||||
export function timeUntilStale(updatedAt: number, staleTime?: number): number {
|
||||
return Math.max(updatedAt + (staleTime || 0) - Date.now(), 0)
|
||||
}
|
||||
|
||||
export function resolveStaleTime<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>(
|
||||
staleTime:
|
||||
| undefined
|
||||
| StaleTimeFunction<TQueryFnData, TError, TData, TQueryKey>,
|
||||
query: Query<TQueryFnData, TError, TData, TQueryKey>,
|
||||
): StaleTime | undefined {
|
||||
return typeof staleTime === 'function' ? staleTime(query) : staleTime
|
||||
}
|
||||
|
||||
export function resolveEnabled<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>(
|
||||
enabled: undefined | Enabled<TQueryFnData, TError, TData, TQueryKey>,
|
||||
query: Query<TQueryFnData, TError, TData, TQueryKey>,
|
||||
): boolean | undefined {
|
||||
return typeof enabled === 'function' ? enabled(query) : enabled
|
||||
}
|
||||
|
||||
export function matchQuery(
|
||||
filters: QueryFilters,
|
||||
query: Query<any, any, any, any>,
|
||||
): boolean {
|
||||
const {
|
||||
type = 'all',
|
||||
exact,
|
||||
fetchStatus,
|
||||
predicate,
|
||||
queryKey,
|
||||
stale,
|
||||
} = filters
|
||||
|
||||
if (queryKey) {
|
||||
if (exact) {
|
||||
if (query.queryHash !== hashQueryKeyByOptions(queryKey, query.options)) {
|
||||
return false
|
||||
}
|
||||
} else if (!partialMatchKey(query.queryKey, queryKey)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if (type !== 'all') {
|
||||
const isActive = query.isActive()
|
||||
if (type === 'active' && !isActive) {
|
||||
return false
|
||||
}
|
||||
if (type === 'inactive' && isActive) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof stale === 'boolean' && query.isStale() !== stale) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (fetchStatus && fetchStatus !== query.state.fetchStatus) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (predicate && !predicate(query)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export function matchMutation(
|
||||
filters: MutationFilters,
|
||||
mutation: Mutation<any, any>,
|
||||
): boolean {
|
||||
const { exact, status, predicate, mutationKey } = filters
|
||||
if (mutationKey) {
|
||||
if (!mutation.options.mutationKey) {
|
||||
return false
|
||||
}
|
||||
if (exact) {
|
||||
if (hashKey(mutation.options.mutationKey) !== hashKey(mutationKey)) {
|
||||
return false
|
||||
}
|
||||
} else if (!partialMatchKey(mutation.options.mutationKey, mutationKey)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if (status && mutation.state.status !== status) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (predicate && !predicate(mutation)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export function hashQueryKeyByOptions<TQueryKey extends QueryKey = QueryKey>(
|
||||
queryKey: TQueryKey,
|
||||
options?: Pick<QueryOptions<any, any, any, any>, 'queryKeyHashFn'>,
|
||||
): string {
|
||||
const hashFn = options?.queryKeyHashFn || hashKey
|
||||
return hashFn(queryKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* Default query & mutation keys hash function.
|
||||
* Hashes the value into a stable hash.
|
||||
*/
|
||||
export function hashKey(queryKey: QueryKey | MutationKey): string {
|
||||
return JSON.stringify(queryKey, (_, val) =>
|
||||
isPlainObject(val)
|
||||
? Object.keys(val)
|
||||
.sort()
|
||||
.reduce((result, key) => {
|
||||
result[key] = val[key]
|
||||
return result
|
||||
}, {} as any)
|
||||
: val,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if key `b` partially matches with key `a`.
|
||||
*/
|
||||
export function partialMatchKey(a: QueryKey, b: QueryKey): boolean
|
||||
export function partialMatchKey(a: any, b: any): boolean {
|
||||
if (a === b) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (typeof a !== typeof b) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (a && b && typeof a === 'object' && typeof b === 'object') {
|
||||
return Object.keys(b).every((key) => partialMatchKey(a[key], b[key]))
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const hasOwn = Object.prototype.hasOwnProperty
|
||||
|
||||
/**
|
||||
* This function returns `a` if `b` is deeply equal.
|
||||
* If not, it will replace any deeply equal children of `b` with those of `a`.
|
||||
* This can be used for structural sharing between JSON values for example.
|
||||
*/
|
||||
export function replaceEqualDeep<T>(a: unknown, b: T): T
|
||||
export function replaceEqualDeep(a: any, b: any): any {
|
||||
if (a === b) {
|
||||
return a
|
||||
}
|
||||
|
||||
const array = isPlainArray(a) && isPlainArray(b)
|
||||
|
||||
if (!array && !(isPlainObject(a) && isPlainObject(b))) return b
|
||||
|
||||
const aItems = array ? a : Object.keys(a)
|
||||
const aSize = aItems.length
|
||||
const bItems = array ? b : Object.keys(b)
|
||||
const bSize = bItems.length
|
||||
const copy: any = array ? new Array(bSize) : {}
|
||||
|
||||
let equalItems = 0
|
||||
|
||||
for (let i = 0; i < bSize; i++) {
|
||||
const key: any = array ? i : bItems[i]
|
||||
const aItem = a[key]
|
||||
const bItem = b[key]
|
||||
|
||||
if (aItem === bItem) {
|
||||
copy[key] = aItem
|
||||
if (array ? i < aSize : hasOwn.call(a, key)) equalItems++
|
||||
continue
|
||||
}
|
||||
|
||||
if (
|
||||
aItem === null ||
|
||||
bItem === null ||
|
||||
typeof aItem !== 'object' ||
|
||||
typeof bItem !== 'object'
|
||||
) {
|
||||
copy[key] = bItem
|
||||
continue
|
||||
}
|
||||
|
||||
const v = replaceEqualDeep(aItem, bItem)
|
||||
copy[key] = v
|
||||
if (v === aItem) equalItems++
|
||||
}
|
||||
|
||||
return aSize === bSize && equalItems === aSize ? a : copy
|
||||
}
|
||||
|
||||
/**
|
||||
* Shallow compare objects.
|
||||
*/
|
||||
export function shallowEqualObjects<T extends Record<string, any>>(
|
||||
a: T,
|
||||
b: T | undefined,
|
||||
): boolean {
|
||||
if (!b || Object.keys(a).length !== Object.keys(b).length) {
|
||||
return false
|
||||
}
|
||||
|
||||
for (const key in a) {
|
||||
if (a[key] !== b[key]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export function isPlainArray(value: unknown): value is Array<unknown> {
|
||||
return Array.isArray(value) && value.length === Object.keys(value).length
|
||||
}
|
||||
|
||||
// Copied from: https://github.com/jonschlinkert/is-plain-object
|
||||
export function isPlainObject(o: any): o is Record<PropertyKey, unknown> {
|
||||
if (!hasObjectPrototype(o)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// If has no constructor
|
||||
const ctor = o.constructor
|
||||
if (ctor === undefined) {
|
||||
return true
|
||||
}
|
||||
|
||||
// If has modified prototype
|
||||
const prot = ctor.prototype
|
||||
if (!hasObjectPrototype(prot)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// If constructor does not have an Object-specific method
|
||||
if (!prot.hasOwnProperty('isPrototypeOf')) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Handles Objects created by Object.create(<arbitrary prototype>)
|
||||
if (Object.getPrototypeOf(o) !== Object.prototype) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Most likely a plain Object
|
||||
return true
|
||||
}
|
||||
|
||||
function hasObjectPrototype(o: any): boolean {
|
||||
return Object.prototype.toString.call(o) === '[object Object]'
|
||||
}
|
||||
|
||||
export function sleep(timeout: number): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
timeoutManager.setTimeout(resolve, timeout)
|
||||
})
|
||||
}
|
||||
|
||||
export function replaceData<
|
||||
TData,
|
||||
TOptions extends QueryOptions<any, any, any, any>,
|
||||
>(prevData: TData | undefined, data: TData, options: TOptions): TData {
|
||||
if (typeof options.structuralSharing === 'function') {
|
||||
return options.structuralSharing(prevData, data) as TData
|
||||
} else if (options.structuralSharing !== false) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
try {
|
||||
return replaceEqualDeep(prevData, data)
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Structural sharing requires data to be JSON serializable. To fix this, turn off structuralSharing or return JSON-serializable data from your queryFn. [${options.queryHash}]: ${error}`,
|
||||
)
|
||||
|
||||
// Prevent the replaceEqualDeep from being called again down below.
|
||||
throw error
|
||||
}
|
||||
}
|
||||
// Structurally share data between prev and new data if needed
|
||||
return replaceEqualDeep(prevData, data)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
export function keepPreviousData<T>(
|
||||
previousData: T | undefined,
|
||||
): T | undefined {
|
||||
return previousData
|
||||
}
|
||||
|
||||
export function addToEnd<T>(items: Array<T>, item: T, max = 0): Array<T> {
|
||||
const newItems = [...items, item]
|
||||
return max && newItems.length > max ? newItems.slice(1) : newItems
|
||||
}
|
||||
|
||||
export function addToStart<T>(items: Array<T>, item: T, max = 0): Array<T> {
|
||||
const newItems = [item, ...items]
|
||||
return max && newItems.length > max ? newItems.slice(0, -1) : newItems
|
||||
}
|
||||
|
||||
export const skipToken = Symbol()
|
||||
export type SkipToken = typeof skipToken
|
||||
|
||||
export function ensureQueryFn<
|
||||
TQueryFnData = unknown,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>(
|
||||
options: {
|
||||
queryFn?: QueryFunction<TQueryFnData, TQueryKey> | SkipToken
|
||||
queryHash?: string
|
||||
},
|
||||
fetchOptions?: FetchOptions<TQueryFnData>,
|
||||
): QueryFunction<TQueryFnData, TQueryKey> {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (options.queryFn === skipToken) {
|
||||
console.error(
|
||||
`Attempted to invoke queryFn when set to skipToken. This is likely a configuration error. Query hash: '${options.queryHash}'`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// if we attempt to retry a fetch that was triggered from an initialPromise
|
||||
// when we don't have a queryFn yet, we can't retry, so we just return the already rejected initialPromise
|
||||
// if an observer has already mounted, we will be able to retry with that queryFn
|
||||
if (!options.queryFn && fetchOptions?.initialPromise) {
|
||||
return () => fetchOptions.initialPromise!
|
||||
}
|
||||
|
||||
if (!options.queryFn || options.queryFn === skipToken) {
|
||||
return () =>
|
||||
Promise.reject(new Error(`Missing queryFn: '${options.queryHash}'`))
|
||||
}
|
||||
|
||||
return options.queryFn
|
||||
}
|
||||
|
||||
export function shouldThrowError<T extends (...args: Array<any>) => boolean>(
|
||||
throwOnError: boolean | T | undefined,
|
||||
params: Parameters<T>,
|
||||
): boolean {
|
||||
// Allow throwOnError function to override throwing behavior on a per-error basis
|
||||
if (typeof throwOnError === 'function') {
|
||||
return throwOnError(...params)
|
||||
}
|
||||
|
||||
return !!throwOnError
|
||||
}
|
||||
21
node_modules/@tanstack/react-query/LICENSE
generated
vendored
Normal file
21
node_modules/@tanstack/react-query/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-present Tanner Linsley
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
48
node_modules/@tanstack/react-query/README.md
generated
vendored
Normal file
48
node_modules/@tanstack/react-query/README.md
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<img src="https://static.scarf.sh/a.png?x-pxid=be2d8a11-9712-4c1d-9963-580b2d4fb133" />
|
||||
|
||||

|
||||
|
||||
Hooks for fetching, caching and updating asynchronous data in React
|
||||
|
||||
<a href="https://twitter.com/intent/tweet?button_hashtag=TanStack" target="\_parent">
|
||||
<img alt="#TanStack" src="https://img.shields.io/twitter/url?color=%2308a0e9&label=%23TanStack&style=social&url=https%3A%2F%2Ftwitter.com%2Fintent%2Ftweet%3Fbutton_hashtag%3DTanStack">
|
||||
</a><a href="https://discord.com/invite/WrRKjPJ" target="\_parent">
|
||||
<img alt="" src="https://img.shields.io/badge/Discord-TanStack-%235865F2" />
|
||||
</a><a href="https://github.com/TanStack/query/actions?query=workflow%3A%22react-query+tests%22">
|
||||
<img src="https://github.com/TanStack/query/workflows/react-query%20tests/badge.svg" />
|
||||
</a><a href="https://www.npmjs.com/package/@tanstack/query-core" target="\_parent">
|
||||
<img alt="" src="https://img.shields.io/npm/dm/@tanstack/query-core.svg" />
|
||||
</a><a href="https://bundlejs.com/?q=%40tanstack%2Freact-query&config=%7B%22esbuild%22%3A%7B%22external%22%3A%5B%22react%22%2C%22react-dom%22%5D%7D%7D&badge=" target="\_parent">
|
||||
<img alt="" src="https://deno.bundlejs.com/?q=@tanstack/react-query&config={%22esbuild%22:{%22external%22:[%22react%22,%22react-dom%22]}}&badge=detailed" />
|
||||
</a><a href="#badge">
|
||||
<img alt="semantic-release" src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg">
|
||||
</a><a href="https://github.com/TanStack/query/discussions">
|
||||
<img alt="Join the discussion on Github" src="https://img.shields.io/badge/Github%20Discussions%20%26%20Support-Chat%20now!-blue" />
|
||||
</a><a href="https://bestofjs.org/projects/tanstack-query"><img alt="Best of JS" src="https://img.shields.io/endpoint?url=https://bestofjs-serverless.now.sh/api/project-badge?fullName=TanStack%2Fquery%26since=daily" /></a><a href="https://github.com/TanStack/query/" target="\_parent">
|
||||
<img alt="" src="https://img.shields.io/github/stars/TanStack/query.svg?style=social&label=Star" />
|
||||
</a><a href="https://twitter.com/tannerlinsley" target="\_parent">
|
||||
<img alt="" src="https://img.shields.io/twitter/follow/tannerlinsley.svg?style=social&label=Follow" />
|
||||
</a> <a href="https://gitpod.io/from-referrer/">
|
||||
<img src="https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod" alt="Gitpod Ready-to-Code"/>
|
||||
</a>
|
||||
|
||||
Enjoy this library? Try the entire [TanStack](https://tanstack.com)! [TanStack Table](https://github.com/TanStack/table), [TanStack Router](https://github.com/tanstack/router), [TanStack Virtual](https://github.com/tanstack/virtual), [React Charts](https://github.com/TanStack/react-charts), [React Ranger](https://github.com/TanStack/ranger)
|
||||
|
||||
## Visit [tanstack.com/query](https://tanstack.com/query) for docs, guides, API and more!
|
||||
|
||||
## Quick Features
|
||||
|
||||
- Transport/protocol/backend agnostic data fetching (REST, GraphQL, promises, whatever!)
|
||||
- Auto Caching + Refetching (stale-while-revalidate, Window Refocus, Polling/Realtime)
|
||||
- Parallel + Dependent Queries
|
||||
- Mutations + Reactive Query Refetching
|
||||
- Multi-layer Cache + Automatic Garbage Collection
|
||||
- Paginated + Cursor-based Queries
|
||||
- Load-More + Infinite Scroll Queries w/ Scroll Recovery
|
||||
- Request Cancellation
|
||||
- [React Suspense](https://react.dev/reference/react/Suspense) + Fetch-As-You-Render Query Prefetching
|
||||
- Dedicated Devtools
|
||||
|
||||
### [Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
|
||||
|
||||
<!-- Use the force, Luke -->
|
||||
87
node_modules/@tanstack/react-query/package.json
generated
vendored
Normal file
87
node_modules/@tanstack/react-query/package.json
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"name": "@tanstack/react-query",
|
||||
"version": "5.90.10",
|
||||
"description": "Hooks for managing, caching and syncing asynchronous and remote data in React",
|
||||
"author": "tannerlinsley",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/TanStack/query.git",
|
||||
"directory": "packages/react-query"
|
||||
},
|
||||
"homepage": "https://tanstack.com/query",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"type": "module",
|
||||
"types": "build/legacy/index.d.ts",
|
||||
"main": "build/legacy/index.cjs",
|
||||
"module": "build/legacy/index.js",
|
||||
"react-native": "src/index.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"@tanstack/custom-condition": "./src/index.ts",
|
||||
"import": {
|
||||
"types": "./build/modern/index.d.ts",
|
||||
"default": "./build/modern/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./build/modern/index.d.cts",
|
||||
"default": "./build/modern/index.cjs"
|
||||
}
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"files": [
|
||||
"build",
|
||||
"src",
|
||||
"!src/__tests__",
|
||||
"!build/codemods/node_modules",
|
||||
"!build/codemods/vite.config.ts",
|
||||
"!build/codemods/**/__testfixtures__",
|
||||
"!build/codemods/**/__tests__"
|
||||
],
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.90.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/react": "^16.1.0",
|
||||
"@testing-library/react-render-stream": "^2.0.0",
|
||||
"@types/react": "^19.0.1",
|
||||
"@types/react-dom": "^19.0.2",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"cpy-cli": "^5.0.0",
|
||||
"npm-run-all2": "^5.0.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-error-boundary": "^4.1.2",
|
||||
"@tanstack/query-persist-client-core": "5.91.9",
|
||||
"@tanstack/query-test-utils": "0.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18 || ^19"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "premove ./build ./coverage ./dist-ts",
|
||||
"compile": "tsc --build",
|
||||
"test:eslint": "eslint --concurrency=auto ./src",
|
||||
"test:types": "npm-run-all --serial test:types:*",
|
||||
"test:types:ts50": "node ../../node_modules/typescript50/lib/tsc.js --build tsconfig.legacy.json",
|
||||
"test:types:ts51": "node ../../node_modules/typescript51/lib/tsc.js --build tsconfig.legacy.json",
|
||||
"test:types:ts52": "node ../../node_modules/typescript52/lib/tsc.js --build tsconfig.legacy.json",
|
||||
"test:types:ts53": "node ../../node_modules/typescript53/lib/tsc.js --build tsconfig.legacy.json",
|
||||
"test:types:ts54": "node ../../node_modules/typescript54/lib/tsc.js --build tsconfig.legacy.json",
|
||||
"test:types:ts55": "node ../../node_modules/typescript55/lib/tsc.js --build tsconfig.legacy.json",
|
||||
"test:types:ts56": "node ../../node_modules/typescript56/lib/tsc.js --build tsconfig.legacy.json",
|
||||
"test:types:ts57": "node ../../node_modules/typescript57/lib/tsc.js --build tsconfig.legacy.json",
|
||||
"test:types:tscurrent": "tsc --build",
|
||||
"test:lib": "vitest",
|
||||
"test:lib:dev": "pnpm run test:lib --watch",
|
||||
"test:build": "publint --strict && attw --pack",
|
||||
"build": "pnpm build:tsup && pnpm build:codemods",
|
||||
"build:tsup": "tsup --tsconfig tsconfig.prod.json",
|
||||
"build:codemods": "cpy ../query-codemods/* ./build/codemods"
|
||||
}
|
||||
}
|
||||
111
node_modules/@tanstack/react-query/src/HydrationBoundary.tsx
generated
vendored
Normal file
111
node_modules/@tanstack/react-query/src/HydrationBoundary.tsx
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
'use client'
|
||||
import * as React from 'react'
|
||||
|
||||
import { hydrate } from '@tanstack/query-core'
|
||||
import { useQueryClient } from './QueryClientProvider'
|
||||
import type {
|
||||
DehydratedState,
|
||||
HydrateOptions,
|
||||
OmitKeyof,
|
||||
QueryClient,
|
||||
} from '@tanstack/query-core'
|
||||
|
||||
export interface HydrationBoundaryProps {
|
||||
state: DehydratedState | null | undefined
|
||||
options?: OmitKeyof<HydrateOptions, 'defaultOptions'> & {
|
||||
defaultOptions?: OmitKeyof<
|
||||
Exclude<HydrateOptions['defaultOptions'], undefined>,
|
||||
'mutations'
|
||||
>
|
||||
}
|
||||
children?: React.ReactNode
|
||||
queryClient?: QueryClient
|
||||
}
|
||||
|
||||
export const HydrationBoundary = ({
|
||||
children,
|
||||
options = {},
|
||||
state,
|
||||
queryClient,
|
||||
}: HydrationBoundaryProps) => {
|
||||
const client = useQueryClient(queryClient)
|
||||
|
||||
const optionsRef = React.useRef(options)
|
||||
React.useEffect(() => {
|
||||
optionsRef.current = options
|
||||
})
|
||||
|
||||
// This useMemo is for performance reasons only, everything inside it must
|
||||
// be safe to run in every render and code here should be read as "in render".
|
||||
//
|
||||
// This code needs to happen during the render phase, because after initial
|
||||
// SSR, hydration needs to happen _before_ children render. Also, if hydrating
|
||||
// during a transition, we want to hydrate as much as is safe in render so
|
||||
// we can prerender as much as possible.
|
||||
//
|
||||
// For any queries that already exist in the cache, we want to hold back on
|
||||
// hydrating until _after_ the render phase. The reason for this is that during
|
||||
// transitions, we don't want the existing queries and observers to update to
|
||||
// the new data on the current page, only _after_ the transition is committed.
|
||||
// If the transition is aborted, we will have hydrated any _new_ queries, but
|
||||
// we throw away the fresh data for any existing ones to avoid unexpectedly
|
||||
// updating the UI.
|
||||
const hydrationQueue: DehydratedState['queries'] | undefined =
|
||||
React.useMemo(() => {
|
||||
if (state) {
|
||||
if (typeof state !== 'object') {
|
||||
return
|
||||
}
|
||||
|
||||
const queryCache = client.getQueryCache()
|
||||
// State is supplied from the outside and we might as well fail
|
||||
// gracefully if it has the wrong shape, so while we type `queries`
|
||||
// as required, we still provide a fallback.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const queries = state.queries || []
|
||||
|
||||
const newQueries: DehydratedState['queries'] = []
|
||||
const existingQueries: DehydratedState['queries'] = []
|
||||
for (const dehydratedQuery of queries) {
|
||||
const existingQuery = queryCache.get(dehydratedQuery.queryHash)
|
||||
|
||||
if (!existingQuery) {
|
||||
newQueries.push(dehydratedQuery)
|
||||
} else {
|
||||
const hydrationIsNewer =
|
||||
dehydratedQuery.state.dataUpdatedAt >
|
||||
existingQuery.state.dataUpdatedAt ||
|
||||
(dehydratedQuery.promise &&
|
||||
existingQuery.state.status !== 'pending' &&
|
||||
existingQuery.state.fetchStatus !== 'fetching' &&
|
||||
dehydratedQuery.dehydratedAt !== undefined &&
|
||||
dehydratedQuery.dehydratedAt >
|
||||
existingQuery.state.dataUpdatedAt)
|
||||
|
||||
if (hydrationIsNewer) {
|
||||
existingQueries.push(dehydratedQuery)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newQueries.length > 0) {
|
||||
// It's actually fine to call this with queries/state that already exists
|
||||
// in the cache, or is older. hydrate() is idempotent for queries.
|
||||
// eslint-disable-next-line react-hooks/refs
|
||||
hydrate(client, { queries: newQueries }, optionsRef.current)
|
||||
}
|
||||
if (existingQueries.length > 0) {
|
||||
return existingQueries
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}, [client, state])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (hydrationQueue) {
|
||||
hydrate(client, { queries: hydrationQueue }, optionsRef.current)
|
||||
}
|
||||
}, [client, hydrationQueue])
|
||||
|
||||
return children as React.ReactElement
|
||||
}
|
||||
7
node_modules/@tanstack/react-query/src/IsRestoringProvider.ts
generated
vendored
Normal file
7
node_modules/@tanstack/react-query/src/IsRestoringProvider.ts
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
'use client'
|
||||
import * as React from 'react'
|
||||
|
||||
const IsRestoringContext = React.createContext(false)
|
||||
|
||||
export const useIsRestoring = () => React.useContext(IsRestoringContext)
|
||||
export const IsRestoringProvider = IsRestoringContext.Provider
|
||||
45
node_modules/@tanstack/react-query/src/QueryClientProvider.tsx
generated
vendored
Normal file
45
node_modules/@tanstack/react-query/src/QueryClientProvider.tsx
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
'use client'
|
||||
import * as React from 'react'
|
||||
|
||||
import type { QueryClient } from '@tanstack/query-core'
|
||||
|
||||
export const QueryClientContext = React.createContext<QueryClient | undefined>(
|
||||
undefined,
|
||||
)
|
||||
|
||||
export const useQueryClient = (queryClient?: QueryClient) => {
|
||||
const client = React.useContext(QueryClientContext)
|
||||
|
||||
if (queryClient) {
|
||||
return queryClient
|
||||
}
|
||||
|
||||
if (!client) {
|
||||
throw new Error('No QueryClient set, use QueryClientProvider to set one')
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
export type QueryClientProviderProps = {
|
||||
client: QueryClient
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
export const QueryClientProvider = ({
|
||||
client,
|
||||
children,
|
||||
}: QueryClientProviderProps): React.JSX.Element => {
|
||||
React.useEffect(() => {
|
||||
client.mount()
|
||||
return () => {
|
||||
client.unmount()
|
||||
}
|
||||
}, [client])
|
||||
|
||||
return (
|
||||
<QueryClientContext.Provider value={client}>
|
||||
{children}
|
||||
</QueryClientContext.Provider>
|
||||
)
|
||||
}
|
||||
56
node_modules/@tanstack/react-query/src/QueryErrorResetBoundary.tsx
generated
vendored
Normal file
56
node_modules/@tanstack/react-query/src/QueryErrorResetBoundary.tsx
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
'use client'
|
||||
import * as React from 'react'
|
||||
|
||||
// CONTEXT
|
||||
export type QueryErrorResetFunction = () => void
|
||||
export type QueryErrorIsResetFunction = () => boolean
|
||||
export type QueryErrorClearResetFunction = () => void
|
||||
|
||||
export interface QueryErrorResetBoundaryValue {
|
||||
clearReset: QueryErrorClearResetFunction
|
||||
isReset: QueryErrorIsResetFunction
|
||||
reset: QueryErrorResetFunction
|
||||
}
|
||||
|
||||
function createValue(): QueryErrorResetBoundaryValue {
|
||||
let isReset = false
|
||||
return {
|
||||
clearReset: () => {
|
||||
isReset = false
|
||||
},
|
||||
reset: () => {
|
||||
isReset = true
|
||||
},
|
||||
isReset: () => {
|
||||
return isReset
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const QueryErrorResetBoundaryContext = React.createContext(createValue())
|
||||
|
||||
// HOOK
|
||||
|
||||
export const useQueryErrorResetBoundary = () =>
|
||||
React.useContext(QueryErrorResetBoundaryContext)
|
||||
|
||||
// COMPONENT
|
||||
|
||||
export type QueryErrorResetBoundaryFunction = (
|
||||
value: QueryErrorResetBoundaryValue,
|
||||
) => React.ReactNode
|
||||
|
||||
export interface QueryErrorResetBoundaryProps {
|
||||
children: QueryErrorResetBoundaryFunction | React.ReactNode
|
||||
}
|
||||
|
||||
export const QueryErrorResetBoundary = ({
|
||||
children,
|
||||
}: QueryErrorResetBoundaryProps) => {
|
||||
const [value] = React.useState(() => createValue())
|
||||
return (
|
||||
<QueryErrorResetBoundaryContext.Provider value={value}>
|
||||
{typeof children === 'function' ? children(value) : children}
|
||||
</QueryErrorResetBoundaryContext.Provider>
|
||||
)
|
||||
}
|
||||
76
node_modules/@tanstack/react-query/src/errorBoundaryUtils.ts
generated
vendored
Normal file
76
node_modules/@tanstack/react-query/src/errorBoundaryUtils.ts
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
'use client'
|
||||
import * as React from 'react'
|
||||
import { shouldThrowError } from '@tanstack/query-core'
|
||||
import type {
|
||||
DefaultedQueryObserverOptions,
|
||||
Query,
|
||||
QueryKey,
|
||||
QueryObserverResult,
|
||||
ThrowOnError,
|
||||
} from '@tanstack/query-core'
|
||||
import type { QueryErrorResetBoundaryValue } from './QueryErrorResetBoundary'
|
||||
|
||||
export const ensurePreventErrorBoundaryRetry = <
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryData,
|
||||
TQueryKey extends QueryKey,
|
||||
>(
|
||||
options: DefaultedQueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryData,
|
||||
TQueryKey
|
||||
>,
|
||||
errorResetBoundary: QueryErrorResetBoundaryValue,
|
||||
) => {
|
||||
if (
|
||||
options.suspense ||
|
||||
options.throwOnError ||
|
||||
options.experimental_prefetchInRender
|
||||
) {
|
||||
// Prevent retrying failed query if the error boundary has not been reset yet
|
||||
if (!errorResetBoundary.isReset()) {
|
||||
options.retryOnMount = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const useClearResetErrorBoundary = (
|
||||
errorResetBoundary: QueryErrorResetBoundaryValue,
|
||||
) => {
|
||||
React.useEffect(() => {
|
||||
errorResetBoundary.clearReset()
|
||||
}, [errorResetBoundary])
|
||||
}
|
||||
|
||||
export const getHasError = <
|
||||
TData,
|
||||
TError,
|
||||
TQueryFnData,
|
||||
TQueryData,
|
||||
TQueryKey extends QueryKey,
|
||||
>({
|
||||
result,
|
||||
errorResetBoundary,
|
||||
throwOnError,
|
||||
query,
|
||||
suspense,
|
||||
}: {
|
||||
result: QueryObserverResult<TData, TError>
|
||||
errorResetBoundary: QueryErrorResetBoundaryValue
|
||||
throwOnError: ThrowOnError<TQueryFnData, TError, TQueryData, TQueryKey>
|
||||
query: Query<TQueryFnData, TError, TQueryData, TQueryKey> | undefined
|
||||
suspense: boolean | undefined
|
||||
}) => {
|
||||
return (
|
||||
result.isError &&
|
||||
!errorResetBoundary.isReset() &&
|
||||
!result.isFetching &&
|
||||
query &&
|
||||
((suspense && result.data === undefined) ||
|
||||
shouldThrowError(throwOnError, [result.error, query]))
|
||||
)
|
||||
}
|
||||
56
node_modules/@tanstack/react-query/src/index.ts
generated
vendored
Normal file
56
node_modules/@tanstack/react-query/src/index.ts
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
// Re-export core
|
||||
export * from '@tanstack/query-core'
|
||||
|
||||
// React Query
|
||||
export * from './types'
|
||||
export { useQueries } from './useQueries'
|
||||
export type { QueriesResults, QueriesOptions } from './useQueries'
|
||||
export { useQuery } from './useQuery'
|
||||
export { useSuspenseQuery } from './useSuspenseQuery'
|
||||
export { useSuspenseInfiniteQuery } from './useSuspenseInfiniteQuery'
|
||||
export { useSuspenseQueries } from './useSuspenseQueries'
|
||||
export type {
|
||||
SuspenseQueriesResults,
|
||||
SuspenseQueriesOptions,
|
||||
} from './useSuspenseQueries'
|
||||
export { usePrefetchQuery } from './usePrefetchQuery'
|
||||
export { usePrefetchInfiniteQuery } from './usePrefetchInfiniteQuery'
|
||||
export { queryOptions } from './queryOptions'
|
||||
export type {
|
||||
DefinedInitialDataOptions,
|
||||
UndefinedInitialDataOptions,
|
||||
UnusedSkipTokenOptions,
|
||||
} from './queryOptions'
|
||||
export { infiniteQueryOptions } from './infiniteQueryOptions'
|
||||
export type {
|
||||
DefinedInitialDataInfiniteOptions,
|
||||
UndefinedInitialDataInfiniteOptions,
|
||||
UnusedSkipTokenInfiniteOptions,
|
||||
} from './infiniteQueryOptions'
|
||||
export {
|
||||
QueryClientContext,
|
||||
QueryClientProvider,
|
||||
useQueryClient,
|
||||
} from './QueryClientProvider'
|
||||
export type { QueryClientProviderProps } from './QueryClientProvider'
|
||||
export type { QueryErrorResetBoundaryProps } from './QueryErrorResetBoundary'
|
||||
export { HydrationBoundary } from './HydrationBoundary'
|
||||
export type { HydrationBoundaryProps } from './HydrationBoundary'
|
||||
export type {
|
||||
QueryErrorClearResetFunction,
|
||||
QueryErrorIsResetFunction,
|
||||
QueryErrorResetBoundaryFunction,
|
||||
QueryErrorResetFunction,
|
||||
} from './QueryErrorResetBoundary'
|
||||
export {
|
||||
QueryErrorResetBoundary,
|
||||
useQueryErrorResetBoundary,
|
||||
} from './QueryErrorResetBoundary'
|
||||
export { useIsFetching } from './useIsFetching'
|
||||
export { useIsMutating, useMutationState } from './useMutationState'
|
||||
export { useMutation } from './useMutation'
|
||||
export { mutationOptions } from './mutationOptions'
|
||||
export { useInfiniteQuery } from './useInfiniteQuery'
|
||||
export { useIsRestoring, IsRestoringProvider } from './IsRestoringProvider'
|
||||
149
node_modules/@tanstack/react-query/src/infiniteQueryOptions.ts
generated
vendored
Normal file
149
node_modules/@tanstack/react-query/src/infiniteQueryOptions.ts
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
import type {
|
||||
DataTag,
|
||||
DefaultError,
|
||||
InfiniteData,
|
||||
InitialDataFunction,
|
||||
NonUndefinedGuard,
|
||||
OmitKeyof,
|
||||
QueryKey,
|
||||
SkipToken,
|
||||
} from '@tanstack/query-core'
|
||||
import type { UseInfiniteQueryOptions } from './types'
|
||||
|
||||
export type UndefinedInitialDataInfiniteOptions<
|
||||
TQueryFnData,
|
||||
TError = DefaultError,
|
||||
TData = InfiniteData<TQueryFnData>,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = unknown,
|
||||
> = UseInfiniteQueryOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
> & {
|
||||
initialData?:
|
||||
| undefined
|
||||
| NonUndefinedGuard<InfiniteData<TQueryFnData, TPageParam>>
|
||||
| InitialDataFunction<
|
||||
NonUndefinedGuard<InfiniteData<TQueryFnData, TPageParam>>
|
||||
>
|
||||
}
|
||||
|
||||
export type UnusedSkipTokenInfiniteOptions<
|
||||
TQueryFnData,
|
||||
TError = DefaultError,
|
||||
TData = InfiniteData<TQueryFnData>,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = unknown,
|
||||
> = OmitKeyof<
|
||||
UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>,
|
||||
'queryFn'
|
||||
> & {
|
||||
queryFn?: Exclude<
|
||||
UseInfiniteQueryOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>['queryFn'],
|
||||
SkipToken | undefined
|
||||
>
|
||||
}
|
||||
|
||||
export type DefinedInitialDataInfiniteOptions<
|
||||
TQueryFnData,
|
||||
TError = DefaultError,
|
||||
TData = InfiniteData<TQueryFnData>,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = unknown,
|
||||
> = UseInfiniteQueryOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
> & {
|
||||
initialData:
|
||||
| NonUndefinedGuard<InfiniteData<TQueryFnData, TPageParam>>
|
||||
| (() => NonUndefinedGuard<InfiniteData<TQueryFnData, TPageParam>>)
|
||||
| undefined
|
||||
}
|
||||
|
||||
export function infiniteQueryOptions<
|
||||
TQueryFnData,
|
||||
TError = DefaultError,
|
||||
TData = InfiniteData<TQueryFnData>,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = unknown,
|
||||
>(
|
||||
options: DefinedInitialDataInfiniteOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>,
|
||||
): DefinedInitialDataInfiniteOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
> & {
|
||||
queryKey: DataTag<TQueryKey, InfiniteData<TQueryFnData>, TError>
|
||||
}
|
||||
|
||||
export function infiniteQueryOptions<
|
||||
TQueryFnData,
|
||||
TError = DefaultError,
|
||||
TData = InfiniteData<TQueryFnData>,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = unknown,
|
||||
>(
|
||||
options: UnusedSkipTokenInfiniteOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>,
|
||||
): UnusedSkipTokenInfiniteOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
> & {
|
||||
queryKey: DataTag<TQueryKey, InfiniteData<TQueryFnData>, TError>
|
||||
}
|
||||
|
||||
export function infiniteQueryOptions<
|
||||
TQueryFnData,
|
||||
TError = DefaultError,
|
||||
TData = InfiniteData<TQueryFnData>,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = unknown,
|
||||
>(
|
||||
options: UndefinedInitialDataInfiniteOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>,
|
||||
): UndefinedInitialDataInfiniteOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
> & {
|
||||
queryKey: DataTag<TQueryKey, InfiniteData<TQueryFnData>, TError>
|
||||
}
|
||||
|
||||
export function infiniteQueryOptions(options: unknown) {
|
||||
return options
|
||||
}
|
||||
41
node_modules/@tanstack/react-query/src/mutationOptions.ts
generated
vendored
Normal file
41
node_modules/@tanstack/react-query/src/mutationOptions.ts
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
import type { DefaultError, WithRequired } from '@tanstack/query-core'
|
||||
import type { UseMutationOptions } from './types'
|
||||
|
||||
export function mutationOptions<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
TVariables = void,
|
||||
TOnMutateResult = unknown,
|
||||
>(
|
||||
options: WithRequired<
|
||||
UseMutationOptions<TData, TError, TVariables, TOnMutateResult>,
|
||||
'mutationKey'
|
||||
>,
|
||||
): WithRequired<
|
||||
UseMutationOptions<TData, TError, TVariables, TOnMutateResult>,
|
||||
'mutationKey'
|
||||
>
|
||||
export function mutationOptions<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
TVariables = void,
|
||||
TOnMutateResult = unknown,
|
||||
>(
|
||||
options: Omit<
|
||||
UseMutationOptions<TData, TError, TVariables, TOnMutateResult>,
|
||||
'mutationKey'
|
||||
>,
|
||||
): Omit<
|
||||
UseMutationOptions<TData, TError, TVariables, TOnMutateResult>,
|
||||
'mutationKey'
|
||||
>
|
||||
export function mutationOptions<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
TVariables = void,
|
||||
TOnMutateResult = unknown,
|
||||
>(
|
||||
options: UseMutationOptions<TData, TError, TVariables, TOnMutateResult>,
|
||||
): UseMutationOptions<TData, TError, TVariables, TOnMutateResult> {
|
||||
return options
|
||||
}
|
||||
87
node_modules/@tanstack/react-query/src/queryOptions.ts
generated
vendored
Normal file
87
node_modules/@tanstack/react-query/src/queryOptions.ts
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
import type {
|
||||
DataTag,
|
||||
DefaultError,
|
||||
InitialDataFunction,
|
||||
NonUndefinedGuard,
|
||||
OmitKeyof,
|
||||
QueryFunction,
|
||||
QueryKey,
|
||||
SkipToken,
|
||||
} from '@tanstack/query-core'
|
||||
import type { UseQueryOptions } from './types'
|
||||
|
||||
export type UndefinedInitialDataOptions<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
> = UseQueryOptions<TQueryFnData, TError, TData, TQueryKey> & {
|
||||
initialData?:
|
||||
| undefined
|
||||
| InitialDataFunction<NonUndefinedGuard<TQueryFnData>>
|
||||
| NonUndefinedGuard<TQueryFnData>
|
||||
}
|
||||
|
||||
export type UnusedSkipTokenOptions<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
> = OmitKeyof<
|
||||
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
'queryFn'
|
||||
> & {
|
||||
queryFn?: Exclude<
|
||||
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>['queryFn'],
|
||||
SkipToken | undefined
|
||||
>
|
||||
}
|
||||
|
||||
export type DefinedInitialDataOptions<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
> = Omit<UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>, 'queryFn'> & {
|
||||
initialData:
|
||||
| NonUndefinedGuard<TQueryFnData>
|
||||
| (() => NonUndefinedGuard<TQueryFnData>)
|
||||
queryFn?: QueryFunction<TQueryFnData, TQueryKey>
|
||||
}
|
||||
|
||||
export function queryOptions<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>(
|
||||
options: DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
): DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey> & {
|
||||
queryKey: DataTag<TQueryKey, TQueryFnData, TError>
|
||||
}
|
||||
|
||||
export function queryOptions<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>(
|
||||
options: UnusedSkipTokenOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
): UnusedSkipTokenOptions<TQueryFnData, TError, TData, TQueryKey> & {
|
||||
queryKey: DataTag<TQueryKey, TQueryFnData, TError>
|
||||
}
|
||||
|
||||
export function queryOptions<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>(
|
||||
options: UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
): UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey> & {
|
||||
queryKey: DataTag<TQueryKey, TQueryFnData, TError>
|
||||
}
|
||||
|
||||
export function queryOptions(options: unknown) {
|
||||
return options
|
||||
}
|
||||
80
node_modules/@tanstack/react-query/src/suspense.ts
generated
vendored
Normal file
80
node_modules/@tanstack/react-query/src/suspense.ts
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
import type {
|
||||
DefaultError,
|
||||
DefaultedQueryObserverOptions,
|
||||
Query,
|
||||
QueryKey,
|
||||
QueryObserver,
|
||||
QueryObserverResult,
|
||||
} from '@tanstack/query-core'
|
||||
import type { QueryErrorResetBoundaryValue } from './QueryErrorResetBoundary'
|
||||
|
||||
export const defaultThrowOnError = <
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>(
|
||||
_error: TError,
|
||||
query: Query<TQueryFnData, TError, TData, TQueryKey>,
|
||||
) => query.state.data === undefined
|
||||
|
||||
export const ensureSuspenseTimers = (
|
||||
defaultedOptions: DefaultedQueryObserverOptions<any, any, any, any, any>,
|
||||
) => {
|
||||
if (defaultedOptions.suspense) {
|
||||
// Handle staleTime to ensure minimum 1000ms in Suspense mode
|
||||
// This prevents unnecessary refetching when components remount after suspending
|
||||
const MIN_SUSPENSE_TIME_MS = 1000
|
||||
|
||||
const clamp = (value: number | 'static' | undefined) =>
|
||||
value === 'static'
|
||||
? value
|
||||
: Math.max(value ?? MIN_SUSPENSE_TIME_MS, MIN_SUSPENSE_TIME_MS)
|
||||
|
||||
const originalStaleTime = defaultedOptions.staleTime
|
||||
defaultedOptions.staleTime =
|
||||
typeof originalStaleTime === 'function'
|
||||
? (...args) => clamp(originalStaleTime(...args))
|
||||
: clamp(originalStaleTime)
|
||||
|
||||
if (typeof defaultedOptions.gcTime === 'number') {
|
||||
defaultedOptions.gcTime = Math.max(
|
||||
defaultedOptions.gcTime,
|
||||
MIN_SUSPENSE_TIME_MS,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const willFetch = (
|
||||
result: QueryObserverResult<any, any>,
|
||||
isRestoring: boolean,
|
||||
) => result.isLoading && result.isFetching && !isRestoring
|
||||
|
||||
export const shouldSuspend = (
|
||||
defaultedOptions:
|
||||
| DefaultedQueryObserverOptions<any, any, any, any, any>
|
||||
| undefined,
|
||||
result: QueryObserverResult<any, any>,
|
||||
) => defaultedOptions?.suspense && result.isPending
|
||||
|
||||
export const fetchOptimistic = <
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryData,
|
||||
TQueryKey extends QueryKey,
|
||||
>(
|
||||
defaultedOptions: DefaultedQueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryData,
|
||||
TQueryKey
|
||||
>,
|
||||
observer: QueryObserver<TQueryFnData, TError, TData, TQueryData, TQueryKey>,
|
||||
errorResetBoundary: QueryErrorResetBoundaryValue,
|
||||
) =>
|
||||
observer.fetchOptimistic(defaultedOptions).catch(() => {
|
||||
errorResetBoundary.clearReset()
|
||||
})
|
||||
242
node_modules/@tanstack/react-query/src/types.ts
generated
vendored
Normal file
242
node_modules/@tanstack/react-query/src/types.ts
generated
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
import type {
|
||||
DefaultError,
|
||||
DefinedInfiniteQueryObserverResult,
|
||||
DefinedQueryObserverResult,
|
||||
DistributiveOmit,
|
||||
FetchQueryOptions,
|
||||
InfiniteQueryObserverOptions,
|
||||
InfiniteQueryObserverResult,
|
||||
MutateFunction,
|
||||
MutationObserverOptions,
|
||||
MutationObserverResult,
|
||||
OmitKeyof,
|
||||
Override,
|
||||
QueryKey,
|
||||
QueryObserverOptions,
|
||||
QueryObserverResult,
|
||||
SkipToken,
|
||||
} from '@tanstack/query-core'
|
||||
|
||||
export type AnyUseBaseQueryOptions = UseBaseQueryOptions<
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
any
|
||||
>
|
||||
export interface UseBaseQueryOptions<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
> extends QueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryData,
|
||||
TQueryKey
|
||||
> {
|
||||
/**
|
||||
* Set this to `false` to unsubscribe this observer from updates to the query cache.
|
||||
* Defaults to `true`.
|
||||
*/
|
||||
subscribed?: boolean
|
||||
}
|
||||
|
||||
export interface UsePrefetchQueryOptions<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
> extends OmitKeyof<
|
||||
FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
'queryFn'
|
||||
> {
|
||||
queryFn?: Exclude<
|
||||
FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>['queryFn'],
|
||||
SkipToken
|
||||
>
|
||||
}
|
||||
|
||||
export type AnyUseQueryOptions = UseQueryOptions<any, any, any, any>
|
||||
export interface UseQueryOptions<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
> extends OmitKeyof<
|
||||
UseBaseQueryOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>,
|
||||
'suspense'
|
||||
> {}
|
||||
|
||||
export type AnyUseSuspenseQueryOptions = UseSuspenseQueryOptions<
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
any
|
||||
>
|
||||
export interface UseSuspenseQueryOptions<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
> extends OmitKeyof<
|
||||
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
'queryFn' | 'enabled' | 'throwOnError' | 'placeholderData'
|
||||
> {
|
||||
queryFn?: Exclude<
|
||||
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>['queryFn'],
|
||||
SkipToken
|
||||
>
|
||||
}
|
||||
|
||||
export type AnyUseInfiniteQueryOptions = UseInfiniteQueryOptions<
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
any,
|
||||
any
|
||||
>
|
||||
export interface UseInfiniteQueryOptions<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = unknown,
|
||||
> extends OmitKeyof<
|
||||
InfiniteQueryObserverOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>,
|
||||
'suspense'
|
||||
> {
|
||||
/**
|
||||
* Set this to `false` to unsubscribe this observer from updates to the query cache.
|
||||
* Defaults to `true`.
|
||||
*/
|
||||
subscribed?: boolean
|
||||
}
|
||||
|
||||
export type AnyUseSuspenseInfiniteQueryOptions =
|
||||
UseSuspenseInfiniteQueryOptions<any, any, any, any, any>
|
||||
export interface UseSuspenseInfiniteQueryOptions<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = unknown,
|
||||
> extends OmitKeyof<
|
||||
UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>,
|
||||
'queryFn' | 'enabled' | 'throwOnError' | 'placeholderData'
|
||||
> {
|
||||
queryFn?: Exclude<
|
||||
UseInfiniteQueryOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>['queryFn'],
|
||||
SkipToken
|
||||
>
|
||||
}
|
||||
|
||||
export type UseBaseQueryResult<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
> = QueryObserverResult<TData, TError>
|
||||
|
||||
export type UseQueryResult<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
> = UseBaseQueryResult<TData, TError>
|
||||
|
||||
export type UseSuspenseQueryResult<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
> = DistributiveOmit<
|
||||
DefinedQueryObserverResult<TData, TError>,
|
||||
'isPlaceholderData' | 'promise'
|
||||
>
|
||||
|
||||
export type DefinedUseQueryResult<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
> = DefinedQueryObserverResult<TData, TError>
|
||||
|
||||
export type UseInfiniteQueryResult<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
> = InfiniteQueryObserverResult<TData, TError>
|
||||
|
||||
export type DefinedUseInfiniteQueryResult<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
> = DefinedInfiniteQueryObserverResult<TData, TError>
|
||||
|
||||
export type UseSuspenseInfiniteQueryResult<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
> = OmitKeyof<
|
||||
DefinedInfiniteQueryObserverResult<TData, TError>,
|
||||
'isPlaceholderData' | 'promise'
|
||||
>
|
||||
|
||||
export type AnyUseMutationOptions = UseMutationOptions<any, any, any, any>
|
||||
export interface UseMutationOptions<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
TVariables = void,
|
||||
TOnMutateResult = unknown,
|
||||
> extends OmitKeyof<
|
||||
MutationObserverOptions<TData, TError, TVariables, TOnMutateResult>,
|
||||
'_defaulted'
|
||||
> {}
|
||||
|
||||
export type UseMutateFunction<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
TVariables = void,
|
||||
TOnMutateResult = unknown,
|
||||
> = (
|
||||
...args: Parameters<
|
||||
MutateFunction<TData, TError, TVariables, TOnMutateResult>
|
||||
>
|
||||
) => void
|
||||
|
||||
export type UseMutateAsyncFunction<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
TVariables = void,
|
||||
TOnMutateResult = unknown,
|
||||
> = MutateFunction<TData, TError, TVariables, TOnMutateResult>
|
||||
|
||||
export type UseBaseMutationResult<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
TVariables = unknown,
|
||||
TOnMutateResult = unknown,
|
||||
> = Override<
|
||||
MutationObserverResult<TData, TError, TVariables, TOnMutateResult>,
|
||||
{ mutate: UseMutateFunction<TData, TError, TVariables, TOnMutateResult> }
|
||||
> & {
|
||||
mutateAsync: UseMutateAsyncFunction<
|
||||
TData,
|
||||
TError,
|
||||
TVariables,
|
||||
TOnMutateResult
|
||||
>
|
||||
}
|
||||
|
||||
export type UseMutationResult<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
TVariables = unknown,
|
||||
TOnMutateResult = unknown,
|
||||
> = UseBaseMutationResult<TData, TError, TVariables, TOnMutateResult>
|
||||
170
node_modules/@tanstack/react-query/src/useBaseQuery.ts
generated
vendored
Normal file
170
node_modules/@tanstack/react-query/src/useBaseQuery.ts
generated
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
'use client'
|
||||
import * as React from 'react'
|
||||
|
||||
import { isServer, noop, notifyManager } from '@tanstack/query-core'
|
||||
import { useQueryClient } from './QueryClientProvider'
|
||||
import { useQueryErrorResetBoundary } from './QueryErrorResetBoundary'
|
||||
import {
|
||||
ensurePreventErrorBoundaryRetry,
|
||||
getHasError,
|
||||
useClearResetErrorBoundary,
|
||||
} from './errorBoundaryUtils'
|
||||
import { useIsRestoring } from './IsRestoringProvider'
|
||||
import {
|
||||
ensureSuspenseTimers,
|
||||
fetchOptimistic,
|
||||
shouldSuspend,
|
||||
willFetch,
|
||||
} from './suspense'
|
||||
import type {
|
||||
QueryClient,
|
||||
QueryKey,
|
||||
QueryObserver,
|
||||
QueryObserverResult,
|
||||
} from '@tanstack/query-core'
|
||||
import type { UseBaseQueryOptions } from './types'
|
||||
|
||||
export function useBaseQuery<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryData,
|
||||
TQueryKey extends QueryKey,
|
||||
>(
|
||||
options: UseBaseQueryOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryData,
|
||||
TQueryKey
|
||||
>,
|
||||
Observer: typeof QueryObserver,
|
||||
queryClient?: QueryClient,
|
||||
): QueryObserverResult<TData, TError> {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (typeof options !== 'object' || Array.isArray(options)) {
|
||||
throw new Error(
|
||||
'Bad argument type. Starting with v5, only the "Object" form is allowed when calling query related functions. Please use the error stack to find the culprit call. More info here: https://tanstack.com/query/latest/docs/react/guides/migrating-to-v5#supports-a-single-signature-one-object',
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const isRestoring = useIsRestoring()
|
||||
const errorResetBoundary = useQueryErrorResetBoundary()
|
||||
const client = useQueryClient(queryClient)
|
||||
const defaultedOptions = client.defaultQueryOptions(options)
|
||||
|
||||
;(client.getDefaultOptions().queries as any)?._experimental_beforeQuery?.(
|
||||
defaultedOptions,
|
||||
)
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (!defaultedOptions.queryFn) {
|
||||
console.error(
|
||||
`[${defaultedOptions.queryHash}]: No queryFn was passed as an option, and no default queryFn was found. The queryFn parameter is only optional when using a default queryFn. More info here: https://tanstack.com/query/latest/docs/framework/react/guides/default-query-function`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure results are optimistically set in fetching state before subscribing or updating options
|
||||
defaultedOptions._optimisticResults = isRestoring
|
||||
? 'isRestoring'
|
||||
: 'optimistic'
|
||||
|
||||
ensureSuspenseTimers(defaultedOptions)
|
||||
ensurePreventErrorBoundaryRetry(defaultedOptions, errorResetBoundary)
|
||||
|
||||
useClearResetErrorBoundary(errorResetBoundary)
|
||||
|
||||
// this needs to be invoked before creating the Observer because that can create a cache entry
|
||||
const isNewCacheEntry = !client
|
||||
.getQueryCache()
|
||||
.get(defaultedOptions.queryHash)
|
||||
|
||||
const [observer] = React.useState(
|
||||
() =>
|
||||
new Observer<TQueryFnData, TError, TData, TQueryData, TQueryKey>(
|
||||
client,
|
||||
defaultedOptions,
|
||||
),
|
||||
)
|
||||
|
||||
// note: this must be called before useSyncExternalStore
|
||||
const result = observer.getOptimisticResult(defaultedOptions)
|
||||
|
||||
const shouldSubscribe = !isRestoring && options.subscribed !== false
|
||||
React.useSyncExternalStore(
|
||||
React.useCallback(
|
||||
(onStoreChange) => {
|
||||
const unsubscribe = shouldSubscribe
|
||||
? observer.subscribe(notifyManager.batchCalls(onStoreChange))
|
||||
: noop
|
||||
|
||||
// Update result to make sure we did not miss any query updates
|
||||
// between creating the observer and subscribing to it.
|
||||
observer.updateResult()
|
||||
|
||||
return unsubscribe
|
||||
},
|
||||
[observer, shouldSubscribe],
|
||||
),
|
||||
() => observer.getCurrentResult(),
|
||||
() => observer.getCurrentResult(),
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
observer.setOptions(defaultedOptions)
|
||||
}, [defaultedOptions, observer])
|
||||
|
||||
// Handle suspense
|
||||
if (shouldSuspend(defaultedOptions, result)) {
|
||||
throw fetchOptimistic(defaultedOptions, observer, errorResetBoundary)
|
||||
}
|
||||
|
||||
// Handle error boundary
|
||||
if (
|
||||
getHasError({
|
||||
result,
|
||||
errorResetBoundary,
|
||||
throwOnError: defaultedOptions.throwOnError,
|
||||
query: client
|
||||
.getQueryCache()
|
||||
.get<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TQueryData,
|
||||
TQueryKey
|
||||
>(defaultedOptions.queryHash),
|
||||
suspense: defaultedOptions.suspense,
|
||||
})
|
||||
) {
|
||||
throw result.error
|
||||
}
|
||||
|
||||
;(client.getDefaultOptions().queries as any)?._experimental_afterQuery?.(
|
||||
defaultedOptions,
|
||||
result,
|
||||
)
|
||||
|
||||
if (
|
||||
defaultedOptions.experimental_prefetchInRender &&
|
||||
!isServer &&
|
||||
willFetch(result, isRestoring)
|
||||
) {
|
||||
const promise = isNewCacheEntry
|
||||
? // Fetch immediately on render in order to ensure `.promise` is resolved even if the component is unmounted
|
||||
fetchOptimistic(defaultedOptions, observer, errorResetBoundary)
|
||||
: // subscribe to the "cache promise" so that we can finalize the currentThenable once data comes in
|
||||
client.getQueryCache().get(defaultedOptions.queryHash)?.promise
|
||||
|
||||
promise?.catch(noop).finally(() => {
|
||||
// `.updateResult()` will trigger `.#currentThenable` to finalize
|
||||
observer.updateResult()
|
||||
})
|
||||
}
|
||||
|
||||
// Handle result property usage tracking
|
||||
return !defaultedOptions.notifyOnChangeProps
|
||||
? observer.trackResult(result)
|
||||
: result
|
||||
}
|
||||
81
node_modules/@tanstack/react-query/src/useInfiniteQuery.ts
generated
vendored
Normal file
81
node_modules/@tanstack/react-query/src/useInfiniteQuery.ts
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
'use client'
|
||||
import { InfiniteQueryObserver } from '@tanstack/query-core'
|
||||
import { useBaseQuery } from './useBaseQuery'
|
||||
import type {
|
||||
DefaultError,
|
||||
InfiniteData,
|
||||
QueryClient,
|
||||
QueryKey,
|
||||
QueryObserver,
|
||||
} from '@tanstack/query-core'
|
||||
import type {
|
||||
DefinedUseInfiniteQueryResult,
|
||||
UseInfiniteQueryOptions,
|
||||
UseInfiniteQueryResult,
|
||||
} from './types'
|
||||
import type {
|
||||
DefinedInitialDataInfiniteOptions,
|
||||
UndefinedInitialDataInfiniteOptions,
|
||||
} from './infiniteQueryOptions'
|
||||
|
||||
export function useInfiniteQuery<
|
||||
TQueryFnData,
|
||||
TError = DefaultError,
|
||||
TData = InfiniteData<TQueryFnData>,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = unknown,
|
||||
>(
|
||||
options: DefinedInitialDataInfiniteOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>,
|
||||
queryClient?: QueryClient,
|
||||
): DefinedUseInfiniteQueryResult<TData, TError>
|
||||
|
||||
export function useInfiniteQuery<
|
||||
TQueryFnData,
|
||||
TError = DefaultError,
|
||||
TData = InfiniteData<TQueryFnData>,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = unknown,
|
||||
>(
|
||||
options: UndefinedInitialDataInfiniteOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>,
|
||||
queryClient?: QueryClient,
|
||||
): UseInfiniteQueryResult<TData, TError>
|
||||
|
||||
export function useInfiniteQuery<
|
||||
TQueryFnData,
|
||||
TError = DefaultError,
|
||||
TData = InfiniteData<TQueryFnData>,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = unknown,
|
||||
>(
|
||||
options: UseInfiniteQueryOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>,
|
||||
queryClient?: QueryClient,
|
||||
): UseInfiniteQueryResult<TData, TError>
|
||||
|
||||
export function useInfiniteQuery(
|
||||
options: UseInfiniteQueryOptions,
|
||||
queryClient?: QueryClient,
|
||||
) {
|
||||
return useBaseQuery(
|
||||
options,
|
||||
InfiniteQueryObserver as typeof QueryObserver,
|
||||
queryClient,
|
||||
)
|
||||
}
|
||||
24
node_modules/@tanstack/react-query/src/useIsFetching.ts
generated
vendored
Normal file
24
node_modules/@tanstack/react-query/src/useIsFetching.ts
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
'use client'
|
||||
import * as React from 'react'
|
||||
import { notifyManager } from '@tanstack/query-core'
|
||||
|
||||
import { useQueryClient } from './QueryClientProvider'
|
||||
import type { QueryClient, QueryFilters } from '@tanstack/query-core'
|
||||
|
||||
export function useIsFetching(
|
||||
filters?: QueryFilters,
|
||||
queryClient?: QueryClient,
|
||||
): number {
|
||||
const client = useQueryClient(queryClient)
|
||||
const queryCache = client.getQueryCache()
|
||||
|
||||
return React.useSyncExternalStore(
|
||||
React.useCallback(
|
||||
(onStoreChange) =>
|
||||
queryCache.subscribe(notifyManager.batchCalls(onStoreChange)),
|
||||
[queryCache],
|
||||
),
|
||||
() => client.isFetching(filters),
|
||||
() => client.isFetching(filters),
|
||||
)
|
||||
}
|
||||
69
node_modules/@tanstack/react-query/src/useMutation.ts
generated
vendored
Normal file
69
node_modules/@tanstack/react-query/src/useMutation.ts
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
'use client'
|
||||
import * as React from 'react'
|
||||
import {
|
||||
MutationObserver,
|
||||
noop,
|
||||
notifyManager,
|
||||
shouldThrowError,
|
||||
} from '@tanstack/query-core'
|
||||
import { useQueryClient } from './QueryClientProvider'
|
||||
import type {
|
||||
UseMutateFunction,
|
||||
UseMutationOptions,
|
||||
UseMutationResult,
|
||||
} from './types'
|
||||
import type { DefaultError, QueryClient } from '@tanstack/query-core'
|
||||
|
||||
// HOOK
|
||||
|
||||
export function useMutation<
|
||||
TData = unknown,
|
||||
TError = DefaultError,
|
||||
TVariables = void,
|
||||
TOnMutateResult = unknown,
|
||||
>(
|
||||
options: UseMutationOptions<TData, TError, TVariables, TOnMutateResult>,
|
||||
queryClient?: QueryClient,
|
||||
): UseMutationResult<TData, TError, TVariables, TOnMutateResult> {
|
||||
const client = useQueryClient(queryClient)
|
||||
|
||||
const [observer] = React.useState(
|
||||
() =>
|
||||
new MutationObserver<TData, TError, TVariables, TOnMutateResult>(
|
||||
client,
|
||||
options,
|
||||
),
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
observer.setOptions(options)
|
||||
}, [observer, options])
|
||||
|
||||
const result = React.useSyncExternalStore(
|
||||
React.useCallback(
|
||||
(onStoreChange) =>
|
||||
observer.subscribe(notifyManager.batchCalls(onStoreChange)),
|
||||
[observer],
|
||||
),
|
||||
() => observer.getCurrentResult(),
|
||||
() => observer.getCurrentResult(),
|
||||
)
|
||||
|
||||
const mutate = React.useCallback<
|
||||
UseMutateFunction<TData, TError, TVariables, TOnMutateResult>
|
||||
>(
|
||||
(variables, mutateOptions) => {
|
||||
observer.mutate(variables, mutateOptions).catch(noop)
|
||||
},
|
||||
[observer],
|
||||
)
|
||||
|
||||
if (
|
||||
result.error &&
|
||||
shouldThrowError(observer.options.throwOnError, [result.error])
|
||||
) {
|
||||
throw result.error
|
||||
}
|
||||
|
||||
return { ...result, mutate, mutateAsync: result.mutate }
|
||||
}
|
||||
75
node_modules/@tanstack/react-query/src/useMutationState.ts
generated
vendored
Normal file
75
node_modules/@tanstack/react-query/src/useMutationState.ts
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
'use client'
|
||||
import * as React from 'react'
|
||||
|
||||
import { notifyManager, replaceEqualDeep } from '@tanstack/query-core'
|
||||
import { useQueryClient } from './QueryClientProvider'
|
||||
import type {
|
||||
Mutation,
|
||||
MutationCache,
|
||||
MutationFilters,
|
||||
MutationState,
|
||||
QueryClient,
|
||||
} from '@tanstack/query-core'
|
||||
|
||||
export function useIsMutating(
|
||||
filters?: MutationFilters,
|
||||
queryClient?: QueryClient,
|
||||
): number {
|
||||
const client = useQueryClient(queryClient)
|
||||
return useMutationState(
|
||||
{ filters: { ...filters, status: 'pending' } },
|
||||
client,
|
||||
).length
|
||||
}
|
||||
|
||||
type MutationStateOptions<TResult = MutationState> = {
|
||||
filters?: MutationFilters
|
||||
select?: (mutation: Mutation) => TResult
|
||||
}
|
||||
|
||||
function getResult<TResult = MutationState>(
|
||||
mutationCache: MutationCache,
|
||||
options: MutationStateOptions<TResult>,
|
||||
): Array<TResult> {
|
||||
return mutationCache
|
||||
.findAll(options.filters)
|
||||
.map(
|
||||
(mutation): TResult =>
|
||||
(options.select ? options.select(mutation) : mutation.state) as TResult,
|
||||
)
|
||||
}
|
||||
|
||||
export function useMutationState<TResult = MutationState>(
|
||||
options: MutationStateOptions<TResult> = {},
|
||||
queryClient?: QueryClient,
|
||||
): Array<TResult> {
|
||||
const mutationCache = useQueryClient(queryClient).getMutationCache()
|
||||
const optionsRef = React.useRef(options)
|
||||
const result = React.useRef<Array<TResult>>(null)
|
||||
if (result.current === null) {
|
||||
result.current = getResult(mutationCache, options)
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
optionsRef.current = options
|
||||
})
|
||||
|
||||
return React.useSyncExternalStore(
|
||||
React.useCallback(
|
||||
(onStoreChange) =>
|
||||
mutationCache.subscribe(() => {
|
||||
const nextResult = replaceEqualDeep(
|
||||
result.current,
|
||||
getResult(mutationCache, optionsRef.current),
|
||||
)
|
||||
if (result.current !== nextResult) {
|
||||
result.current = nextResult
|
||||
notifyManager.schedule(onStoreChange)
|
||||
}
|
||||
}),
|
||||
[mutationCache],
|
||||
),
|
||||
() => result.current,
|
||||
() => result.current,
|
||||
)!
|
||||
}
|
||||
30
node_modules/@tanstack/react-query/src/usePrefetchInfiniteQuery.tsx
generated
vendored
Normal file
30
node_modules/@tanstack/react-query/src/usePrefetchInfiniteQuery.tsx
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
import { useQueryClient } from './QueryClientProvider'
|
||||
import type {
|
||||
DefaultError,
|
||||
FetchInfiniteQueryOptions,
|
||||
QueryClient,
|
||||
QueryKey,
|
||||
} from '@tanstack/query-core'
|
||||
|
||||
export function usePrefetchInfiniteQuery<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = unknown,
|
||||
>(
|
||||
options: FetchInfiniteQueryOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>,
|
||||
queryClient?: QueryClient,
|
||||
) {
|
||||
const client = useQueryClient(queryClient)
|
||||
|
||||
if (!client.getQueryState(options.queryKey)) {
|
||||
client.prefetchInfiniteQuery(options)
|
||||
}
|
||||
}
|
||||
19
node_modules/@tanstack/react-query/src/usePrefetchQuery.tsx
generated
vendored
Normal file
19
node_modules/@tanstack/react-query/src/usePrefetchQuery.tsx
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
import { useQueryClient } from './QueryClientProvider'
|
||||
import type { DefaultError, QueryClient, QueryKey } from '@tanstack/query-core'
|
||||
import type { UsePrefetchQueryOptions } from './types'
|
||||
|
||||
export function usePrefetchQuery<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>(
|
||||
options: UsePrefetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
queryClient?: QueryClient,
|
||||
) {
|
||||
const client = useQueryClient(queryClient)
|
||||
|
||||
if (!client.getQueryState(options.queryKey)) {
|
||||
client.prefetchQuery(options)
|
||||
}
|
||||
}
|
||||
332
node_modules/@tanstack/react-query/src/useQueries.ts
generated
vendored
Normal file
332
node_modules/@tanstack/react-query/src/useQueries.ts
generated
vendored
Normal file
@@ -0,0 +1,332 @@
|
||||
'use client'
|
||||
import * as React from 'react'
|
||||
|
||||
import {
|
||||
QueriesObserver,
|
||||
QueryObserver,
|
||||
noop,
|
||||
notifyManager,
|
||||
} from '@tanstack/query-core'
|
||||
import { useQueryClient } from './QueryClientProvider'
|
||||
import { useIsRestoring } from './IsRestoringProvider'
|
||||
import { useQueryErrorResetBoundary } from './QueryErrorResetBoundary'
|
||||
import {
|
||||
ensurePreventErrorBoundaryRetry,
|
||||
getHasError,
|
||||
useClearResetErrorBoundary,
|
||||
} from './errorBoundaryUtils'
|
||||
import {
|
||||
ensureSuspenseTimers,
|
||||
fetchOptimistic,
|
||||
shouldSuspend,
|
||||
willFetch,
|
||||
} from './suspense'
|
||||
import type {
|
||||
DefinedUseQueryResult,
|
||||
UseQueryOptions,
|
||||
UseQueryResult,
|
||||
} from './types'
|
||||
import type {
|
||||
DefaultError,
|
||||
OmitKeyof,
|
||||
QueriesObserverOptions,
|
||||
QueriesPlaceholderDataFunction,
|
||||
QueryClient,
|
||||
QueryFunction,
|
||||
QueryKey,
|
||||
QueryObserverOptions,
|
||||
ThrowOnError,
|
||||
} from '@tanstack/query-core'
|
||||
|
||||
// This defines the `UseQueryOptions` that are accepted in `QueriesOptions` & `GetOptions`.
|
||||
// `placeholderData` function always gets undefined passed
|
||||
type UseQueryOptionsForUseQueries<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
> = OmitKeyof<
|
||||
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
'placeholderData' | 'subscribed'
|
||||
> & {
|
||||
placeholderData?: TQueryFnData | QueriesPlaceholderDataFunction<TQueryFnData>
|
||||
}
|
||||
|
||||
// Avoid TS depth-limit error in case of large array literal
|
||||
type MAXIMUM_DEPTH = 20
|
||||
|
||||
// Widen the type of the symbol to enable type inference even if skipToken is not immutable.
|
||||
type SkipTokenForUseQueries = symbol
|
||||
|
||||
type GetUseQueryOptionsForUseQueries<T> =
|
||||
// Part 1: responsible for applying explicit type parameter to function arguments, if object { queryFnData: TQueryFnData, error: TError, data: TData }
|
||||
T extends {
|
||||
queryFnData: infer TQueryFnData
|
||||
error?: infer TError
|
||||
data: infer TData
|
||||
}
|
||||
? UseQueryOptionsForUseQueries<TQueryFnData, TError, TData>
|
||||
: T extends { queryFnData: infer TQueryFnData; error?: infer TError }
|
||||
? UseQueryOptionsForUseQueries<TQueryFnData, TError>
|
||||
: T extends { data: infer TData; error?: infer TError }
|
||||
? UseQueryOptionsForUseQueries<unknown, TError, TData>
|
||||
: // Part 2: responsible for applying explicit type parameter to function arguments, if tuple [TQueryFnData, TError, TData]
|
||||
T extends [infer TQueryFnData, infer TError, infer TData]
|
||||
? UseQueryOptionsForUseQueries<TQueryFnData, TError, TData>
|
||||
: T extends [infer TQueryFnData, infer TError]
|
||||
? UseQueryOptionsForUseQueries<TQueryFnData, TError>
|
||||
: T extends [infer TQueryFnData]
|
||||
? UseQueryOptionsForUseQueries<TQueryFnData>
|
||||
: // Part 3: responsible for inferring and enforcing type if no explicit parameter was provided
|
||||
T extends {
|
||||
queryFn?:
|
||||
| QueryFunction<infer TQueryFnData, infer TQueryKey>
|
||||
| SkipTokenForUseQueries
|
||||
select?: (data: any) => infer TData
|
||||
throwOnError?: ThrowOnError<any, infer TError, any, any>
|
||||
}
|
||||
? UseQueryOptionsForUseQueries<
|
||||
TQueryFnData,
|
||||
unknown extends TError ? DefaultError : TError,
|
||||
unknown extends TData ? TQueryFnData : TData,
|
||||
TQueryKey
|
||||
>
|
||||
: // Fallback
|
||||
UseQueryOptionsForUseQueries
|
||||
|
||||
// A defined initialData setting should return a DefinedUseQueryResult rather than UseQueryResult
|
||||
type GetDefinedOrUndefinedQueryResult<T, TData, TError = unknown> = T extends {
|
||||
initialData?: infer TInitialData
|
||||
}
|
||||
? unknown extends TInitialData
|
||||
? UseQueryResult<TData, TError>
|
||||
: TInitialData extends TData
|
||||
? DefinedUseQueryResult<TData, TError>
|
||||
: TInitialData extends () => infer TInitialDataResult
|
||||
? unknown extends TInitialDataResult
|
||||
? UseQueryResult<TData, TError>
|
||||
: TInitialDataResult extends TData
|
||||
? DefinedUseQueryResult<TData, TError>
|
||||
: UseQueryResult<TData, TError>
|
||||
: UseQueryResult<TData, TError>
|
||||
: UseQueryResult<TData, TError>
|
||||
|
||||
type GetUseQueryResult<T> =
|
||||
// Part 1: responsible for mapping explicit type parameter to function result, if object
|
||||
T extends { queryFnData: any; error?: infer TError; data: infer TData }
|
||||
? GetDefinedOrUndefinedQueryResult<T, TData, TError>
|
||||
: T extends { queryFnData: infer TQueryFnData; error?: infer TError }
|
||||
? GetDefinedOrUndefinedQueryResult<T, TQueryFnData, TError>
|
||||
: T extends { data: infer TData; error?: infer TError }
|
||||
? GetDefinedOrUndefinedQueryResult<T, TData, TError>
|
||||
: // Part 2: responsible for mapping explicit type parameter to function result, if tuple
|
||||
T extends [any, infer TError, infer TData]
|
||||
? GetDefinedOrUndefinedQueryResult<T, TData, TError>
|
||||
: T extends [infer TQueryFnData, infer TError]
|
||||
? GetDefinedOrUndefinedQueryResult<T, TQueryFnData, TError>
|
||||
: T extends [infer TQueryFnData]
|
||||
? GetDefinedOrUndefinedQueryResult<T, TQueryFnData>
|
||||
: // Part 3: responsible for mapping inferred type to results, if no explicit parameter was provided
|
||||
T extends {
|
||||
queryFn?:
|
||||
| QueryFunction<infer TQueryFnData, any>
|
||||
| SkipTokenForUseQueries
|
||||
select?: (data: any) => infer TData
|
||||
throwOnError?: ThrowOnError<any, infer TError, any, any>
|
||||
}
|
||||
? GetDefinedOrUndefinedQueryResult<
|
||||
T,
|
||||
unknown extends TData ? TQueryFnData : TData,
|
||||
unknown extends TError ? DefaultError : TError
|
||||
>
|
||||
: // Fallback
|
||||
UseQueryResult
|
||||
|
||||
/**
|
||||
* QueriesOptions reducer recursively unwraps function arguments to infer/enforce type param
|
||||
*/
|
||||
export type QueriesOptions<
|
||||
T extends Array<any>,
|
||||
TResults extends Array<any> = [],
|
||||
TDepth extends ReadonlyArray<number> = [],
|
||||
> = TDepth['length'] extends MAXIMUM_DEPTH
|
||||
? Array<UseQueryOptionsForUseQueries>
|
||||
: T extends []
|
||||
? []
|
||||
: T extends [infer Head]
|
||||
? [...TResults, GetUseQueryOptionsForUseQueries<Head>]
|
||||
: T extends [infer Head, ...infer Tails]
|
||||
? QueriesOptions<
|
||||
[...Tails],
|
||||
[...TResults, GetUseQueryOptionsForUseQueries<Head>],
|
||||
[...TDepth, 1]
|
||||
>
|
||||
: ReadonlyArray<unknown> extends T
|
||||
? T
|
||||
: // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogenous type!
|
||||
// use this to infer the param types in the case of Array.map() argument
|
||||
T extends Array<
|
||||
UseQueryOptionsForUseQueries<
|
||||
infer TQueryFnData,
|
||||
infer TError,
|
||||
infer TData,
|
||||
infer TQueryKey
|
||||
>
|
||||
>
|
||||
? Array<
|
||||
UseQueryOptionsForUseQueries<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey
|
||||
>
|
||||
>
|
||||
: // Fallback
|
||||
Array<UseQueryOptionsForUseQueries>
|
||||
|
||||
/**
|
||||
* QueriesResults reducer recursively maps type param to results
|
||||
*/
|
||||
export type QueriesResults<
|
||||
T extends Array<any>,
|
||||
TResults extends Array<any> = [],
|
||||
TDepth extends ReadonlyArray<number> = [],
|
||||
> = TDepth['length'] extends MAXIMUM_DEPTH
|
||||
? Array<UseQueryResult>
|
||||
: T extends []
|
||||
? []
|
||||
: T extends [infer Head]
|
||||
? [...TResults, GetUseQueryResult<Head>]
|
||||
: T extends [infer Head, ...infer Tails]
|
||||
? QueriesResults<
|
||||
[...Tails],
|
||||
[...TResults, GetUseQueryResult<Head>],
|
||||
[...TDepth, 1]
|
||||
>
|
||||
: { [K in keyof T]: GetUseQueryResult<T[K]> }
|
||||
|
||||
export function useQueries<
|
||||
T extends Array<any>,
|
||||
TCombinedResult = QueriesResults<T>,
|
||||
>(
|
||||
{
|
||||
queries,
|
||||
...options
|
||||
}: {
|
||||
queries:
|
||||
| readonly [...QueriesOptions<T>]
|
||||
| readonly [...{ [K in keyof T]: GetUseQueryOptionsForUseQueries<T[K]> }]
|
||||
combine?: (result: QueriesResults<T>) => TCombinedResult
|
||||
subscribed?: boolean
|
||||
},
|
||||
queryClient?: QueryClient,
|
||||
): TCombinedResult {
|
||||
const client = useQueryClient(queryClient)
|
||||
const isRestoring = useIsRestoring()
|
||||
const errorResetBoundary = useQueryErrorResetBoundary()
|
||||
|
||||
const defaultedQueries = React.useMemo(
|
||||
() =>
|
||||
queries.map((opts) => {
|
||||
const defaultedOptions = client.defaultQueryOptions(
|
||||
opts as QueryObserverOptions,
|
||||
)
|
||||
|
||||
// Make sure the results are already in fetching state before subscribing or updating options
|
||||
defaultedOptions._optimisticResults = isRestoring
|
||||
? 'isRestoring'
|
||||
: 'optimistic'
|
||||
|
||||
return defaultedOptions
|
||||
}),
|
||||
[queries, client, isRestoring],
|
||||
)
|
||||
|
||||
defaultedQueries.forEach((query) => {
|
||||
ensureSuspenseTimers(query)
|
||||
ensurePreventErrorBoundaryRetry(query, errorResetBoundary)
|
||||
})
|
||||
|
||||
useClearResetErrorBoundary(errorResetBoundary)
|
||||
|
||||
const [observer] = React.useState(
|
||||
() =>
|
||||
new QueriesObserver<TCombinedResult>(
|
||||
client,
|
||||
defaultedQueries,
|
||||
options as QueriesObserverOptions<TCombinedResult>,
|
||||
),
|
||||
)
|
||||
|
||||
// note: this must be called before useSyncExternalStore
|
||||
const [optimisticResult, getCombinedResult, trackResult] =
|
||||
observer.getOptimisticResult(
|
||||
defaultedQueries,
|
||||
(options as QueriesObserverOptions<TCombinedResult>).combine,
|
||||
)
|
||||
|
||||
const shouldSubscribe = !isRestoring && options.subscribed !== false
|
||||
React.useSyncExternalStore(
|
||||
React.useCallback(
|
||||
(onStoreChange) =>
|
||||
shouldSubscribe
|
||||
? observer.subscribe(notifyManager.batchCalls(onStoreChange))
|
||||
: noop,
|
||||
[observer, shouldSubscribe],
|
||||
),
|
||||
() => observer.getCurrentResult(),
|
||||
() => observer.getCurrentResult(),
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
observer.setQueries(
|
||||
defaultedQueries,
|
||||
options as QueriesObserverOptions<TCombinedResult>,
|
||||
)
|
||||
}, [defaultedQueries, options, observer])
|
||||
|
||||
const shouldAtLeastOneSuspend = optimisticResult.some((result, index) =>
|
||||
shouldSuspend(defaultedQueries[index], result),
|
||||
)
|
||||
|
||||
const suspensePromises = shouldAtLeastOneSuspend
|
||||
? optimisticResult.flatMap((result, index) => {
|
||||
const opts = defaultedQueries[index]
|
||||
|
||||
if (opts) {
|
||||
const queryObserver = new QueryObserver(client, opts)
|
||||
if (shouldSuspend(opts, result)) {
|
||||
return fetchOptimistic(opts, queryObserver, errorResetBoundary)
|
||||
} else if (willFetch(result, isRestoring)) {
|
||||
void fetchOptimistic(opts, queryObserver, errorResetBoundary)
|
||||
}
|
||||
}
|
||||
return []
|
||||
})
|
||||
: []
|
||||
|
||||
if (suspensePromises.length > 0) {
|
||||
throw Promise.all(suspensePromises)
|
||||
}
|
||||
const firstSingleResultWhichShouldThrow = optimisticResult.find(
|
||||
(result, index) => {
|
||||
const query = defaultedQueries[index]
|
||||
return (
|
||||
query &&
|
||||
getHasError({
|
||||
result,
|
||||
errorResetBoundary,
|
||||
throwOnError: query.throwOnError,
|
||||
query: client.getQueryCache().get(query.queryHash),
|
||||
suspense: query.suspense,
|
||||
})
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
if (firstSingleResultWhichShouldThrow?.error) {
|
||||
throw firstSingleResultWhichShouldThrow.error
|
||||
}
|
||||
|
||||
return getCombinedResult(trackResult())
|
||||
}
|
||||
52
node_modules/@tanstack/react-query/src/useQuery.ts
generated
vendored
Normal file
52
node_modules/@tanstack/react-query/src/useQuery.ts
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
'use client'
|
||||
import { QueryObserver } from '@tanstack/query-core'
|
||||
import { useBaseQuery } from './useBaseQuery'
|
||||
import type {
|
||||
DefaultError,
|
||||
NoInfer,
|
||||
QueryClient,
|
||||
QueryKey,
|
||||
} from '@tanstack/query-core'
|
||||
import type {
|
||||
DefinedUseQueryResult,
|
||||
UseQueryOptions,
|
||||
UseQueryResult,
|
||||
} from './types'
|
||||
import type {
|
||||
DefinedInitialDataOptions,
|
||||
UndefinedInitialDataOptions,
|
||||
} from './queryOptions'
|
||||
|
||||
export function useQuery<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>(
|
||||
options: DefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
queryClient?: QueryClient,
|
||||
): DefinedUseQueryResult<NoInfer<TData>, TError>
|
||||
|
||||
export function useQuery<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>(
|
||||
options: UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
queryClient?: QueryClient,
|
||||
): UseQueryResult<NoInfer<TData>, TError>
|
||||
|
||||
export function useQuery<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>(
|
||||
options: UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
queryClient?: QueryClient,
|
||||
): UseQueryResult<NoInfer<TData>, TError>
|
||||
|
||||
export function useQuery(options: UseQueryOptions, queryClient?: QueryClient) {
|
||||
return useBaseQuery(options, QueryObserver, queryClient)
|
||||
}
|
||||
50
node_modules/@tanstack/react-query/src/useSuspenseInfiniteQuery.ts
generated
vendored
Normal file
50
node_modules/@tanstack/react-query/src/useSuspenseInfiniteQuery.ts
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
'use client'
|
||||
import { InfiniteQueryObserver, skipToken } from '@tanstack/query-core'
|
||||
import { useBaseQuery } from './useBaseQuery'
|
||||
import { defaultThrowOnError } from './suspense'
|
||||
import type {
|
||||
DefaultError,
|
||||
InfiniteData,
|
||||
InfiniteQueryObserverSuccessResult,
|
||||
QueryClient,
|
||||
QueryKey,
|
||||
QueryObserver,
|
||||
} from '@tanstack/query-core'
|
||||
import type {
|
||||
UseSuspenseInfiniteQueryOptions,
|
||||
UseSuspenseInfiniteQueryResult,
|
||||
} from './types'
|
||||
|
||||
export function useSuspenseInfiniteQuery<
|
||||
TQueryFnData,
|
||||
TError = DefaultError,
|
||||
TData = InfiniteData<TQueryFnData>,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
TPageParam = unknown,
|
||||
>(
|
||||
options: UseSuspenseInfiniteQueryOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey,
|
||||
TPageParam
|
||||
>,
|
||||
queryClient?: QueryClient,
|
||||
): UseSuspenseInfiniteQueryResult<TData, TError> {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if ((options.queryFn as any) === skipToken) {
|
||||
console.error('skipToken is not allowed for useSuspenseInfiniteQuery')
|
||||
}
|
||||
}
|
||||
|
||||
return useBaseQuery(
|
||||
{
|
||||
...options,
|
||||
enabled: true,
|
||||
suspense: true,
|
||||
throwOnError: defaultThrowOnError,
|
||||
},
|
||||
InfiniteQueryObserver as typeof QueryObserver,
|
||||
queryClient,
|
||||
) as InfiniteQueryObserverSuccessResult<TData, TError>
|
||||
}
|
||||
211
node_modules/@tanstack/react-query/src/useSuspenseQueries.ts
generated
vendored
Normal file
211
node_modules/@tanstack/react-query/src/useSuspenseQueries.ts
generated
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
'use client'
|
||||
import { skipToken } from '@tanstack/query-core'
|
||||
import { useQueries } from './useQueries'
|
||||
import { defaultThrowOnError } from './suspense'
|
||||
import type { UseSuspenseQueryOptions, UseSuspenseQueryResult } from './types'
|
||||
import type {
|
||||
DefaultError,
|
||||
QueryClient,
|
||||
QueryFunction,
|
||||
ThrowOnError,
|
||||
} from '@tanstack/query-core'
|
||||
|
||||
// Avoid TS depth-limit error in case of large array literal
|
||||
type MAXIMUM_DEPTH = 20
|
||||
|
||||
// Widen the type of the symbol to enable type inference even if skipToken is not immutable.
|
||||
type SkipTokenForUseQueries = symbol
|
||||
|
||||
type GetUseSuspenseQueryOptions<T> =
|
||||
// Part 1: responsible for applying explicit type parameter to function arguments, if object { queryFnData: TQueryFnData, error: TError, data: TData }
|
||||
T extends {
|
||||
queryFnData: infer TQueryFnData
|
||||
error?: infer TError
|
||||
data: infer TData
|
||||
}
|
||||
? UseSuspenseQueryOptions<TQueryFnData, TError, TData>
|
||||
: T extends { queryFnData: infer TQueryFnData; error?: infer TError }
|
||||
? UseSuspenseQueryOptions<TQueryFnData, TError>
|
||||
: T extends { data: infer TData; error?: infer TError }
|
||||
? UseSuspenseQueryOptions<unknown, TError, TData>
|
||||
: // Part 2: responsible for applying explicit type parameter to function arguments, if tuple [TQueryFnData, TError, TData]
|
||||
T extends [infer TQueryFnData, infer TError, infer TData]
|
||||
? UseSuspenseQueryOptions<TQueryFnData, TError, TData>
|
||||
: T extends [infer TQueryFnData, infer TError]
|
||||
? UseSuspenseQueryOptions<TQueryFnData, TError>
|
||||
: T extends [infer TQueryFnData]
|
||||
? UseSuspenseQueryOptions<TQueryFnData>
|
||||
: // Part 3: responsible for inferring and enforcing type if no explicit parameter was provided
|
||||
T extends {
|
||||
queryFn?:
|
||||
| QueryFunction<infer TQueryFnData, infer TQueryKey>
|
||||
| SkipTokenForUseQueries
|
||||
select?: (data: any) => infer TData
|
||||
throwOnError?: ThrowOnError<any, infer TError, any, any>
|
||||
}
|
||||
? UseSuspenseQueryOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TData,
|
||||
TQueryKey
|
||||
>
|
||||
: T extends {
|
||||
queryFn?:
|
||||
| QueryFunction<infer TQueryFnData, infer TQueryKey>
|
||||
| SkipTokenForUseQueries
|
||||
throwOnError?: ThrowOnError<any, infer TError, any, any>
|
||||
}
|
||||
? UseSuspenseQueryOptions<
|
||||
TQueryFnData,
|
||||
TError,
|
||||
TQueryFnData,
|
||||
TQueryKey
|
||||
>
|
||||
: // Fallback
|
||||
UseSuspenseQueryOptions
|
||||
|
||||
type GetUseSuspenseQueryResult<T> =
|
||||
// Part 1: responsible for mapping explicit type parameter to function result, if object
|
||||
T extends { queryFnData: any; error?: infer TError; data: infer TData }
|
||||
? UseSuspenseQueryResult<TData, TError>
|
||||
: T extends { queryFnData: infer TQueryFnData; error?: infer TError }
|
||||
? UseSuspenseQueryResult<TQueryFnData, TError>
|
||||
: T extends { data: infer TData; error?: infer TError }
|
||||
? UseSuspenseQueryResult<TData, TError>
|
||||
: // Part 2: responsible for mapping explicit type parameter to function result, if tuple
|
||||
T extends [any, infer TError, infer TData]
|
||||
? UseSuspenseQueryResult<TData, TError>
|
||||
: T extends [infer TQueryFnData, infer TError]
|
||||
? UseSuspenseQueryResult<TQueryFnData, TError>
|
||||
: T extends [infer TQueryFnData]
|
||||
? UseSuspenseQueryResult<TQueryFnData>
|
||||
: // Part 3: responsible for mapping inferred type to results, if no explicit parameter was provided
|
||||
T extends {
|
||||
queryFn?:
|
||||
| QueryFunction<infer TQueryFnData, any>
|
||||
| SkipTokenForUseQueries
|
||||
select?: (data: any) => infer TData
|
||||
throwOnError?: ThrowOnError<any, infer TError, any, any>
|
||||
}
|
||||
? UseSuspenseQueryResult<
|
||||
unknown extends TData ? TQueryFnData : TData,
|
||||
unknown extends TError ? DefaultError : TError
|
||||
>
|
||||
: T extends {
|
||||
queryFn?:
|
||||
| QueryFunction<infer TQueryFnData, any>
|
||||
| SkipTokenForUseQueries
|
||||
throwOnError?: ThrowOnError<any, infer TError, any, any>
|
||||
}
|
||||
? UseSuspenseQueryResult<
|
||||
TQueryFnData,
|
||||
unknown extends TError ? DefaultError : TError
|
||||
>
|
||||
: // Fallback
|
||||
UseSuspenseQueryResult
|
||||
|
||||
/**
|
||||
* SuspenseQueriesOptions reducer recursively unwraps function arguments to infer/enforce type param
|
||||
*/
|
||||
export type SuspenseQueriesOptions<
|
||||
T extends Array<any>,
|
||||
TResults extends Array<any> = [],
|
||||
TDepth extends ReadonlyArray<number> = [],
|
||||
> = TDepth['length'] extends MAXIMUM_DEPTH
|
||||
? Array<UseSuspenseQueryOptions>
|
||||
: T extends []
|
||||
? []
|
||||
: T extends [infer Head]
|
||||
? [...TResults, GetUseSuspenseQueryOptions<Head>]
|
||||
: T extends [infer Head, ...infer Tails]
|
||||
? SuspenseQueriesOptions<
|
||||
[...Tails],
|
||||
[...TResults, GetUseSuspenseQueryOptions<Head>],
|
||||
[...TDepth, 1]
|
||||
>
|
||||
: Array<unknown> extends T
|
||||
? T
|
||||
: // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogenous type!
|
||||
// use this to infer the param types in the case of Array.map() argument
|
||||
T extends Array<
|
||||
UseSuspenseQueryOptions<
|
||||
infer TQueryFnData,
|
||||
infer TError,
|
||||
infer TData,
|
||||
infer TQueryKey
|
||||
>
|
||||
>
|
||||
? Array<
|
||||
UseSuspenseQueryOptions<TQueryFnData, TError, TData, TQueryKey>
|
||||
>
|
||||
: // Fallback
|
||||
Array<UseSuspenseQueryOptions>
|
||||
|
||||
/**
|
||||
* SuspenseQueriesResults reducer recursively maps type param to results
|
||||
*/
|
||||
export type SuspenseQueriesResults<
|
||||
T extends Array<any>,
|
||||
TResults extends Array<any> = [],
|
||||
TDepth extends ReadonlyArray<number> = [],
|
||||
> = TDepth['length'] extends MAXIMUM_DEPTH
|
||||
? Array<UseSuspenseQueryResult>
|
||||
: T extends []
|
||||
? []
|
||||
: T extends [infer Head]
|
||||
? [...TResults, GetUseSuspenseQueryResult<Head>]
|
||||
: T extends [infer Head, ...infer Tails]
|
||||
? SuspenseQueriesResults<
|
||||
[...Tails],
|
||||
[...TResults, GetUseSuspenseQueryResult<Head>],
|
||||
[...TDepth, 1]
|
||||
>
|
||||
: { [K in keyof T]: GetUseSuspenseQueryResult<T[K]> }
|
||||
|
||||
export function useSuspenseQueries<
|
||||
T extends Array<any>,
|
||||
TCombinedResult = SuspenseQueriesResults<T>,
|
||||
>(
|
||||
options: {
|
||||
queries:
|
||||
| readonly [...SuspenseQueriesOptions<T>]
|
||||
| readonly [...{ [K in keyof T]: GetUseSuspenseQueryOptions<T[K]> }]
|
||||
combine?: (result: SuspenseQueriesResults<T>) => TCombinedResult
|
||||
},
|
||||
queryClient?: QueryClient,
|
||||
): TCombinedResult
|
||||
|
||||
export function useSuspenseQueries<
|
||||
T extends Array<any>,
|
||||
TCombinedResult = SuspenseQueriesResults<T>,
|
||||
>(
|
||||
options: {
|
||||
queries: readonly [...SuspenseQueriesOptions<T>]
|
||||
combine?: (result: SuspenseQueriesResults<T>) => TCombinedResult
|
||||
},
|
||||
queryClient?: QueryClient,
|
||||
): TCombinedResult
|
||||
|
||||
export function useSuspenseQueries(options: any, queryClient?: QueryClient) {
|
||||
return useQueries(
|
||||
{
|
||||
...options,
|
||||
queries: options.queries.map((query: any) => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (query.queryFn === skipToken) {
|
||||
console.error('skipToken is not allowed for useSuspenseQueries')
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...query,
|
||||
suspense: true,
|
||||
throwOnError: defaultThrowOnError,
|
||||
enabled: true,
|
||||
placeholderData: undefined,
|
||||
}
|
||||
}),
|
||||
},
|
||||
queryClient,
|
||||
)
|
||||
}
|
||||
34
node_modules/@tanstack/react-query/src/useSuspenseQuery.ts
generated
vendored
Normal file
34
node_modules/@tanstack/react-query/src/useSuspenseQuery.ts
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
'use client'
|
||||
import { QueryObserver, skipToken } from '@tanstack/query-core'
|
||||
import { useBaseQuery } from './useBaseQuery'
|
||||
import { defaultThrowOnError } from './suspense'
|
||||
import type { UseSuspenseQueryOptions, UseSuspenseQueryResult } from './types'
|
||||
import type { DefaultError, QueryClient, QueryKey } from '@tanstack/query-core'
|
||||
|
||||
export function useSuspenseQuery<
|
||||
TQueryFnData = unknown,
|
||||
TError = DefaultError,
|
||||
TData = TQueryFnData,
|
||||
TQueryKey extends QueryKey = QueryKey,
|
||||
>(
|
||||
options: UseSuspenseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
|
||||
queryClient?: QueryClient,
|
||||
): UseSuspenseQueryResult<TData, TError> {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if ((options.queryFn as any) === skipToken) {
|
||||
console.error('skipToken is not allowed for useSuspenseQuery')
|
||||
}
|
||||
}
|
||||
|
||||
return useBaseQuery(
|
||||
{
|
||||
...options,
|
||||
enabled: true,
|
||||
suspense: true,
|
||||
throwOnError: defaultThrowOnError,
|
||||
placeholderData: undefined,
|
||||
},
|
||||
QueryObserver,
|
||||
queryClient,
|
||||
) as UseSuspenseQueryResult<TData, TError>
|
||||
}
|
||||
Reference in New Issue
Block a user