chore: Remove Dockerfile and docker-compose.yml, update README and migration guide for backend log checks
Some checks failed
Documentation / build-docs (push) Has been cancelled
Tests / test (macos-latest, 3.11) (push) Has been cancelled
Tests / test (macos-latest, 3.12) (push) Has been cancelled
Tests / test (macos-latest, 3.13) (push) Has been cancelled
Tests / test (macos-latest, 3.14) (push) Has been cancelled
Tests / test (ubuntu-latest, 3.11) (push) Has been cancelled
Tests / test (ubuntu-latest, 3.12) (push) Has been cancelled
Tests / test (ubuntu-latest, 3.13) (push) Has been cancelled
Tests / test (ubuntu-latest, 3.14) (push) Has been cancelled

This commit is contained in:
kfox
2026-01-02 22:46:03 -05:00
parent 6834655e03
commit 3c6770acf5
134 changed files with 3025 additions and 18054 deletions

View File

@@ -1,12 +1,12 @@
{
"name": "@tanstack/query-core",
"version": "5.90.12",
"version": "5.90.16",
"description": "The framework agnostic core that powers TanStack Query",
"author": "tannerlinsley",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/TanStack/query.git",
"url": "git+https://github.com/TanStack/query.git",
"directory": "packages/query-core"
},
"homepage": "https://tanstack.com/query",

View File

@@ -1,4 +1,9 @@
import { addToEnd, addToStart, ensureQueryFn } from './utils'
import {
addConsumeAwareSignal,
addToEnd,
addToStart,
ensureQueryFn,
} from './utils'
import type { QueryBehavior } from './query'
import type {
InfiniteData,
@@ -23,19 +28,11 @@ export function infiniteQueryBehavior<TQueryFnData, TError, TData, TPageParam>(
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
},
})
addConsumeAwareSignal(
object,
() => context.signal,
() => (cancelled = true),
)
}
const queryFn = ensureQueryFn(context.options, context.fetchOptions)

View File

@@ -278,14 +278,22 @@ export class Mutation<
this as Mutation<unknown, unknown, unknown, unknown>,
mutationFnContext,
)
} catch (e) {
void Promise.reject(e)
}
try {
await this.options.onError?.(
error as TError,
variables,
this.state.context,
mutationFnContext,
)
} catch (e) {
void Promise.reject(e)
}
try {
// Notify cache callback
await this.#mutationCache.config.onSettled?.(
undefined,
@@ -295,7 +303,11 @@ export class Mutation<
this as Mutation<unknown, unknown, unknown, unknown>,
mutationFnContext,
)
} catch (e) {
void Promise.reject(e)
}
try {
await this.options.onSettled?.(
undefined,
error as TError,
@@ -303,10 +315,12 @@ export class Mutation<
this.state.context,
mutationFnContext,
)
throw error
} finally {
this.#dispatch({ type: 'error', error: error as TError })
} catch (e) {
void Promise.reject(e)
}
this.#dispatch({ type: 'error', error: error as TError })
throw error
} finally {
this.#mutationCache.runNext(this)
}

View File

@@ -172,33 +172,49 @@ export class MutationObserver<
} satisfies MutationFunctionContext
if (action?.type === 'success') {
this.#mutateOptions.onSuccess?.(
action.data,
variables,
onMutateResult,
context,
)
this.#mutateOptions.onSettled?.(
action.data,
null,
variables,
onMutateResult,
context,
)
try {
this.#mutateOptions.onSuccess?.(
action.data,
variables,
onMutateResult,
context,
)
} catch (e) {
void Promise.reject(e)
}
try {
this.#mutateOptions.onSettled?.(
action.data,
null,
variables,
onMutateResult,
context,
)
} catch (e) {
void Promise.reject(e)
}
} else if (action?.type === 'error') {
this.#mutateOptions.onError?.(
action.error,
variables,
onMutateResult,
context,
)
this.#mutateOptions.onSettled?.(
undefined,
action.error,
variables,
onMutateResult,
context,
)
try {
this.#mutateOptions.onError?.(
action.error,
variables,
onMutateResult,
context,
)
} catch (e) {
void Promise.reject(e)
}
try {
this.#mutateOptions.onSettled?.(
undefined,
action.error,
variables,
onMutateResult,
context,
)
} catch (e) {
void Promise.reject(e)
}
}
}

View File

@@ -106,7 +106,6 @@ export class QueriesObserver<
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) =>
@@ -134,6 +133,7 @@ export class QueriesObserver<
if (!hasStructuralChange && !hasResultChange) return
if (hasStructuralChange) {
this.#observerMatches = newObserverMatches
this.#observers = newObservers
}

View File

@@ -658,6 +658,9 @@ export class Query<
fetchFailureReason: error,
fetchStatus: 'idle',
status: 'error',
// flag existing data as invalidated if we get a background error
// note that "no data" always means stale so we can set unconditionally here
isInvalidated: true,
}
case 'invalidate':
return {

View File

@@ -1,5 +1,10 @@
import { addToEnd } from './utils'
import type { QueryFunction, QueryFunctionContext, QueryKey } from './types'
import { addConsumeAwareSignal, addToEnd } from './utils'
import type {
OmitKeyof,
QueryFunction,
QueryFunctionContext,
QueryKey,
} from './types'
type BaseStreamedQueryParams<TQueryFnData, TQueryKey extends QueryKey> = {
streamFn: (
@@ -73,24 +78,42 @@ export function streamedQuery<
let result = initialValue
const stream = await streamFn(context)
let cancelled: boolean = false as boolean
const streamFnContext = addConsumeAwareSignal<
OmitKeyof<typeof context, 'signal'>
>(
{
client: context.client,
meta: context.meta,
queryKey: context.queryKey,
pageParam: context.pageParam,
direction: context.direction,
},
() => context.signal,
() => (cancelled = true),
)
const stream = await streamFn(streamFnContext)
const isReplaceRefetch = isRefetch && refetchMode === 'replace'
for await (const chunk of stream) {
if (context.signal.aborted) {
if (cancelled) {
break
}
// don't append to the cache directly when replace-refetching
if (!isRefetch || refetchMode !== 'replace') {
if (isReplaceRefetch) {
// don't append to the cache directly when replace-refetching
result = reducer(result, chunk)
} else {
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) {
if (isReplaceRefetch && !cancelled) {
context.client.setQueryData<TData>(context.queryKey, result)
}

View File

@@ -317,9 +317,9 @@ export interface QueryObserverOptions<
TQueryKey extends QueryKey = QueryKey,
TPageParam = never,
> extends WithRequired<
QueryOptions<TQueryFnData, TError, TQueryData, TQueryKey, TPageParam>,
'queryKey'
> {
QueryOptions<TQueryFnData, TError, TQueryData, TQueryKey, TPageParam>,
'queryKey'
> {
/**
* Set this to `false` or a function that returns `false` to disable automatic refetching when the query mounts or changes query keys.
* To refetch the query, use the `refetch` method returned from the `useQuery` instance.
@@ -461,7 +461,9 @@ export interface InfiniteQueryObserverOptions<
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
> extends QueryObserverOptions<
>
extends
QueryObserverOptions<
TQueryFnData,
TError,
TData,
@@ -495,9 +497,9 @@ export interface FetchQueryOptions<
TQueryKey extends QueryKey = QueryKey,
TPageParam = never,
> extends WithRequired<
QueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>,
'queryKey'
> {
QueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>,
'queryKey'
> {
initialPageParam?: never
/**
* The time in milliseconds after data is considered stale.
@@ -513,12 +515,12 @@ export interface EnsureQueryDataOptions<
TQueryKey extends QueryKey = QueryKey,
TPageParam = never,
> extends FetchQueryOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
> {
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
> {
revalidateIfStale?: boolean
}
@@ -579,13 +581,15 @@ export interface RefetchOptions extends ResultOptions {
cancelRefetch?: boolean
}
export interface InvalidateQueryFilters<TQueryKey extends QueryKey = QueryKey>
extends QueryFilters<TQueryKey> {
export interface InvalidateQueryFilters<
TQueryKey extends QueryKey = QueryKey,
> extends QueryFilters<TQueryKey> {
refetchType?: QueryTypeFilter | 'none'
}
export interface RefetchQueryFilters<TQueryKey extends QueryKey = QueryKey>
extends QueryFilters<TQueryKey> {}
export interface RefetchQueryFilters<
TQueryKey extends QueryKey = QueryKey,
> extends QueryFilters<TQueryKey> {}
export interface InvalidateOptions extends RefetchOptions {}
export interface ResetOptions extends RefetchOptions {}
@@ -1259,11 +1263,11 @@ export interface MutationObserverIdleResult<
TVariables = void,
TOnMutateResult = unknown,
> extends MutationObserverBaseResult<
TData,
TError,
TVariables,
TOnMutateResult
> {
TData,
TError,
TVariables,
TOnMutateResult
> {
data: undefined
variables: undefined
error: null
@@ -1280,11 +1284,11 @@ export interface MutationObserverLoadingResult<
TVariables = void,
TOnMutateResult = unknown,
> extends MutationObserverBaseResult<
TData,
TError,
TVariables,
TOnMutateResult
> {
TData,
TError,
TVariables,
TOnMutateResult
> {
data: undefined
variables: TVariables
error: null
@@ -1301,11 +1305,11 @@ export interface MutationObserverErrorResult<
TVariables = void,
TOnMutateResult = unknown,
> extends MutationObserverBaseResult<
TData,
TError,
TVariables,
TOnMutateResult
> {
TData,
TError,
TVariables,
TOnMutateResult
> {
data: undefined
error: TError
variables: TVariables
@@ -1322,11 +1326,11 @@ export interface MutationObserverSuccessResult<
TVariables = void,
TOnMutateResult = unknown,
> extends MutationObserverBaseResult<
TData,
TError,
TVariables,
TOnMutateResult
> {
TData,
TError,
TVariables,
TOnMutateResult
> {
data: TData
error: null
variables: TVariables

View File

@@ -465,3 +465,33 @@ export function shouldThrowError<T extends (...args: Array<any>) => boolean>(
return !!throwOnError
}
export function addConsumeAwareSignal<T>(
object: T,
getSignal: () => AbortSignal,
onCancelled: VoidFunction,
): T & { signal: AbortSignal } {
let consumed = false
let signal: AbortSignal | undefined
Object.defineProperty(object, 'signal', {
enumerable: true,
get: () => {
signal ??= getSignal()
if (consumed) {
return signal
}
consumed = true
if (signal.aborted) {
onCancelled()
} else {
signal.addEventListener('abort', onCancelled, { once: true })
}
return signal
},
})
return object as T & { signal: AbortSignal }
}

View File

@@ -1,12 +1,12 @@
{
"name": "@tanstack/react-query",
"version": "5.90.12",
"version": "5.90.16",
"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",
"url": "git+https://github.com/TanStack/query.git",
"directory": "packages/react-query"
},
"homepage": "https://tanstack.com/query",
@@ -44,7 +44,7 @@
"!build/codemods/**/__tests__"
],
"dependencies": {
"@tanstack/query-core": "5.90.12"
"@tanstack/query-core": "5.90.16"
},
"devDependencies": {
"@testing-library/react": "^16.1.0",
@@ -57,7 +57,7 @@
"react": "^19.2.1",
"react-dom": "^19.2.1",
"react-error-boundary": "^4.1.2",
"@tanstack/query-persist-client-core": "5.91.11",
"@tanstack/query-persist-client-core": "5.91.15",
"@tanstack/query-test-utils": "0.0.0"
},
"peerDependencies": {

View File

@@ -25,11 +25,17 @@ export const ensurePreventErrorBoundaryRetry = <
TQueryKey
>,
errorResetBoundary: QueryErrorResetBoundaryValue,
query: Query<TQueryFnData, TError, TQueryData, TQueryKey> | undefined,
) => {
const throwOnError =
query?.state.error && typeof options.throwOnError === 'function'
? shouldThrowError(options.throwOnError, [query.state.error, query])
: options.throwOnError
if (
options.suspense ||
options.throwOnError ||
options.experimental_prefetchInRender
options.experimental_prefetchInRender ||
throwOnError
) {
// Prevent retrying failed query if the error boundary has not been reset yet
if (!errorResetBoundary.isReset()) {

View File

@@ -33,12 +33,12 @@ export interface UseBaseQueryOptions<
TQueryData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
> extends QueryObserverOptions<
TQueryFnData,
TError,
TData,
TQueryData,
TQueryKey
> {
TQueryFnData,
TError,
TData,
TQueryData,
TQueryKey
> {
/**
* Set this to `false` to unsubscribe this observer from updates to the query cache.
* Defaults to `true`.
@@ -52,9 +52,9 @@ export interface UsePrefetchQueryOptions<
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
> extends OmitKeyof<
FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
'queryFn'
> {
FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
'queryFn'
> {
queryFn?: Exclude<
FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>['queryFn'],
SkipToken
@@ -68,9 +68,9 @@ export interface UseQueryOptions<
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
> extends OmitKeyof<
UseBaseQueryOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>,
'suspense'
> {}
UseBaseQueryOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>,
'suspense'
> {}
export type AnyUseSuspenseQueryOptions = UseSuspenseQueryOptions<
any,
@@ -84,9 +84,9 @@ export interface UseSuspenseQueryOptions<
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
> extends OmitKeyof<
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
'queryFn' | 'enabled' | 'throwOnError' | 'placeholderData'
> {
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
'queryFn' | 'enabled' | 'throwOnError' | 'placeholderData'
> {
queryFn?: Exclude<
UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>['queryFn'],
SkipToken
@@ -107,15 +107,15 @@ export interface UseInfiniteQueryOptions<
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
> extends OmitKeyof<
InfiniteQueryObserverOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
>,
'suspense'
> {
InfiniteQueryObserverOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
>,
'suspense'
> {
/**
* Set this to `false` to unsubscribe this observer from updates to the query cache.
* Defaults to `true`.
@@ -132,9 +132,9 @@ export interface UseSuspenseInfiniteQueryOptions<
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
> extends OmitKeyof<
UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>,
'queryFn' | 'enabled' | 'throwOnError' | 'placeholderData'
> {
UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>,
'queryFn' | 'enabled' | 'throwOnError' | 'placeholderData'
> {
queryFn?: Exclude<
UseInfiniteQueryOptions<
TQueryFnData,
@@ -195,9 +195,9 @@ export interface UseMutationOptions<
TVariables = void,
TOnMutateResult = unknown,
> extends OmitKeyof<
MutationObserverOptions<TData, TError, TVariables, TOnMutateResult>,
'_defaulted'
> {}
MutationObserverOptions<TData, TError, TVariables, TOnMutateResult>,
'_defaulted'
> {}
export type UseMutateFunction<
TData = unknown,

View File

@@ -53,11 +53,19 @@ export function useBaseQuery<
const errorResetBoundary = useQueryErrorResetBoundary()
const client = useQueryClient(queryClient)
const defaultedOptions = client.defaultQueryOptions(options)
;(client.getDefaultOptions().queries as any)?._experimental_beforeQuery?.(
defaultedOptions,
)
const query = client
.getQueryCache()
.get<
TQueryFnData,
TError,
TQueryData,
TQueryKey
>(defaultedOptions.queryHash)
if (process.env.NODE_ENV !== 'production') {
if (!defaultedOptions.queryFn) {
console.error(
@@ -72,8 +80,7 @@ export function useBaseQuery<
: 'optimistic'
ensureSuspenseTimers(defaultedOptions)
ensurePreventErrorBoundaryRetry(defaultedOptions, errorResetBoundary)
ensurePreventErrorBoundaryRetry(defaultedOptions, errorResetBoundary, query)
useClearResetErrorBoundary(errorResetBoundary)
// this needs to be invoked before creating the Observer because that can create a cache entry
@@ -127,14 +134,7 @@ export function useBaseQuery<
result,
errorResetBoundary,
throwOnError: defaultedOptions.throwOnError,
query: client
.getQueryCache()
.get<
TQueryFnData,
TError,
TQueryData,
TQueryKey
>(defaultedOptions.queryHash),
query,
suspense: defaultedOptions.suspense,
})
) {
@@ -155,7 +155,7 @@ export function useBaseQuery<
? // 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
query?.promise
promise?.catch(noop).finally(() => {
// `.updateResult()` will trigger `.#currentThenable` to finalize

View File

@@ -242,9 +242,10 @@ export function useQueries<
[queries, client, isRestoring],
)
defaultedQueries.forEach((query) => {
ensureSuspenseTimers(query)
ensurePreventErrorBoundaryRetry(query, errorResetBoundary)
defaultedQueries.forEach((queryOptions) => {
ensureSuspenseTimers(queryOptions)
const query = client.getQueryCache().get(queryOptions.queryHash)
ensurePreventErrorBoundaryRetry(queryOptions, errorResetBoundary, query)
})
useClearResetErrorBoundary(errorResetBoundary)