From e618736a63b20090928e20e5debd7fea06e2203f Mon Sep 17 00:00:00 2001 From: TkDodo Date: Sat, 28 Feb 2026 10:33:09 +0100 Subject: [PATCH 1/7] environmentManager --- docs/config.json | 4 ++ docs/reference/environmentManager.md | 53 +++++++++++++++++++ packages/preact-query/src/__tests__/utils.tsx | 16 ++---- packages/preact-query/src/useBaseQuery.ts | 8 ++- .../src/__tests__/environmentManager.test.tsx | 25 +++++++++ packages/query-core/src/__tests__/utils.ts | 17 ++---- packages/query-core/src/environmentManager.ts | 37 +++++++++++++ packages/query-core/src/focusManager.ts | 3 +- packages/query-core/src/index.ts | 1 + packages/query-core/src/onlineManager.ts | 3 +- packages/query-core/src/queryObserver.ts | 10 ++-- packages/query-core/src/removable.ts | 5 +- packages/query-core/src/retryer.ts | 5 +- packages/query-core/src/utils.ts | 3 ++ packages/react-query/src/__tests__/utils.tsx | 16 ++---- packages/react-query/src/useBaseQuery.ts | 8 ++- .../src/__tests__/queryClient.test.ts | 10 +++- 17 files changed, 171 insertions(+), 53 deletions(-) create mode 100644 docs/reference/environmentManager.md create mode 100644 packages/query-core/src/__tests__/environmentManager.test.tsx create mode 100644 packages/query-core/src/environmentManager.ts diff --git a/docs/config.json b/docs/config.json index 5ff8e8f2407..8666e2fabe7 100644 --- a/docs/config.json +++ b/docs/config.json @@ -909,6 +909,10 @@ "label": "onlineManager", "to": "reference/onlineManager" }, + { + "label": "environmentManager", + "to": "reference/environmentManager" + }, { "label": "notifyManager", "to": "reference/notifyManager" diff --git a/docs/reference/environmentManager.md b/docs/reference/environmentManager.md new file mode 100644 index 00000000000..d3f7f2dbcca --- /dev/null +++ b/docs/reference/environmentManager.md @@ -0,0 +1,53 @@ +--- +id: EnvironmentManager +title: environmentManager +--- + +The `environmentManager` manages how TanStack Query detects whether the current runtime should be treated as server-side. + +By default, it uses the same server detection as the exported `isServer` utility from query-core. + +Use this manager to override server detection globally for runtimes that are not traditional browser/server environments (for example, extension workers). + +Its available methods are: + +- [`isServer`](#environmentmanagerisserver) +- [`setIsServer`](#environmentmanagersetisserver) + +## `environmentManager.isServer` + +Returns whether the current runtime is treated as a server environment. + +```tsx +import { environmentManager } from '@tanstack/react-query' + +const server = environmentManager.isServer() +``` + +## `environmentManager.setIsServer` + +Overrides the server check globally. + +```tsx +import { environmentManager } from '@tanstack/react-query' + +// Static override +environmentManager.setIsServer(false) + +// Dynamic override +environmentManager.setIsServer(() => { + return typeof window === 'undefined' && !('chrome' in globalThis) +}) +``` + +**Options** + +- `isServerValue: boolean | (() => boolean)` + +To restore the default behavior, set the function back to query-core's `isServer` utility: + +```tsx +import { environmentManager, isServer } from '@tanstack/react-query' + +environmentManager.setIsServer(() => isServer) +``` diff --git a/packages/preact-query/src/__tests__/utils.tsx b/packages/preact-query/src/__tests__/utils.tsx index d4f1dba05e9..618338db70e 100644 --- a/packages/preact-query/src/__tests__/utils.tsx +++ b/packages/preact-query/src/__tests__/utils.tsx @@ -1,4 +1,4 @@ -import * as utils from '@tanstack/query-core' +import { environmentManager, isServer } from '@tanstack/query-core' import { act, render } from '@testing-library/preact' import type { ComponentChildren, VNode } from 'preact' import { useEffect, useState } from 'preact/hooks' @@ -58,17 +58,9 @@ export function setActTimeout(fn: () => void, ms?: number) { }, ms) } -// This monkey-patches the isServer-value from utils, -// so that we can pretend to be in a server environment -export function setIsServer(isServer: boolean) { - const original = utils.isServer - Object.defineProperty(utils, 'isServer', { - get: () => isServer, - }) - +export function setIsServer(value: boolean) { + environmentManager.setIsServer(value) return () => { - Object.defineProperty(utils, 'isServer', { - get: () => original, - }) + environmentManager.setIsServer(() => isServer) } } diff --git a/packages/preact-query/src/useBaseQuery.ts b/packages/preact-query/src/useBaseQuery.ts index 84d75679162..54db9672232 100644 --- a/packages/preact-query/src/useBaseQuery.ts +++ b/packages/preact-query/src/useBaseQuery.ts @@ -1,4 +1,8 @@ -import { isServer, noop, notifyManager } from '@tanstack/query-core' +import { + environmentManager, + noop, + notifyManager, +} from '@tanstack/query-core' import type { QueryClient, QueryKey, @@ -147,7 +151,7 @@ export function useBaseQuery< if ( defaultedOptions.experimental_prefetchInRender && - !isServer && + !environmentManager.isServer() && willFetch(result, isRestoring) ) { const promise = isNewCacheEntry diff --git a/packages/query-core/src/__tests__/environmentManager.test.tsx b/packages/query-core/src/__tests__/environmentManager.test.tsx new file mode 100644 index 00000000000..a54dc8af84e --- /dev/null +++ b/packages/query-core/src/__tests__/environmentManager.test.tsx @@ -0,0 +1,25 @@ +import { afterEach, describe, expect, test } from 'vitest' +import { environmentManager, isServer } from '..' + +describe('environmentManager', () => { + afterEach(() => { + environmentManager.setIsServer(() => isServer) + }) + + test('should use the default isServer detection', () => { + expect(environmentManager.isServer()).toBe(isServer) + }) + + test('should allow overriding isServer globally', () => { + environmentManager.setIsServer(true) + expect(environmentManager.isServer()).toBe(true) + + environmentManager.setIsServer(false) + expect(environmentManager.isServer()).toBe(false) + }) + + test('should allow overriding isServer with a function', () => { + environmentManager.setIsServer(() => true) + expect(environmentManager.isServer()).toBe(true) + }) +}) diff --git a/packages/query-core/src/__tests__/utils.ts b/packages/query-core/src/__tests__/utils.ts index f9ef89d7650..3e92182b277 100644 --- a/packages/query-core/src/__tests__/utils.ts +++ b/packages/query-core/src/__tests__/utils.ts @@ -1,6 +1,5 @@ import { vi } from 'vitest' -import { onlineManager } from '..' -import * as utils from '../utils' +import { environmentManager, isServer, onlineManager } from '..' import type { MockInstance } from 'vitest' import type { MutationOptions, QueryClient } from '..' @@ -21,17 +20,9 @@ export function executeMutation( .execute(variables) } -// This monkey-patches the isServer-value from utils, -// so that we can pretend to be in a server environment -export function setIsServer(isServer: boolean) { - const original = utils.isServer - Object.defineProperty(utils, 'isServer', { - get: () => isServer, - }) - +export function setIsServer(value: boolean) { + environmentManager.setIsServer(value) return () => { - Object.defineProperty(utils, 'isServer', { - get: () => original, - }) + environmentManager.setIsServer(() => isServer) } } diff --git a/packages/query-core/src/environmentManager.ts b/packages/query-core/src/environmentManager.ts new file mode 100644 index 00000000000..febf592cbac --- /dev/null +++ b/packages/query-core/src/environmentManager.ts @@ -0,0 +1,37 @@ +import { isServer } from './utils' + +type IsServerValue = boolean | (() => boolean) + +/** + * Manages environment detection used by TanStack Query internals. + */ +export class EnvironmentManager { + #isServer: () => boolean + + constructor() { + this.#isServer = () => isServer + } + + /** + * Returns whether the current runtime should be treated as a server environment. + */ + isServer(): boolean { + return this.#isServer() + } + + /** + * Overrides the server check globally. + * Accepts either a boolean value or a function that returns a boolean. + */ + setIsServer(isServerValue: IsServerValue): void { + this.#isServer = + typeof isServerValue === 'function' + ? isServerValue + : () => isServerValue + } +} + +/** + * Global manager instance for environment detection. + */ +export const environmentManager = new EnvironmentManager() diff --git a/packages/query-core/src/focusManager.ts b/packages/query-core/src/focusManager.ts index cb0d8598713..21be2612d3a 100644 --- a/packages/query-core/src/focusManager.ts +++ b/packages/query-core/src/focusManager.ts @@ -1,5 +1,4 @@ import { Subscribable } from './subscribable' -import { isServer } from './utils' type Listener = (focused: boolean) => void @@ -18,7 +17,7 @@ export class FocusManager extends Subscribable { 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) { + if (typeof window !== 'undefined' && window.addEventListener) { const listener = () => onFocus() // Listen to visibilitychange window.addEventListener('visibilitychange', listener, false) diff --git a/packages/query-core/src/index.ts b/packages/query-core/src/index.ts index a7763cf6483..a4267aabc97 100644 --- a/packages/query-core/src/index.ts +++ b/packages/query-core/src/index.ts @@ -1,6 +1,7 @@ /* istanbul ignore file */ export { focusManager } from './focusManager' +export { environmentManager } from './environmentManager' export { defaultShouldDehydrateMutation, defaultShouldDehydrateQuery, diff --git a/packages/query-core/src/onlineManager.ts b/packages/query-core/src/onlineManager.ts index daf77d5a4c2..6c59a14b908 100644 --- a/packages/query-core/src/onlineManager.ts +++ b/packages/query-core/src/onlineManager.ts @@ -1,5 +1,4 @@ import { Subscribable } from './subscribable' -import { isServer } from './utils' type Listener = (online: boolean) => void type SetupFn = (setOnline: Listener) => (() => void) | undefined @@ -15,7 +14,7 @@ export class OnlineManager extends Subscribable { 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) { + if (typeof window !== 'undefined' && window.addEventListener) { const onlineListener = () => onOnline(true) const offlineListener = () => onOnline(false) // Listen to online diff --git a/packages/query-core/src/queryObserver.ts b/packages/query-core/src/queryObserver.ts index 463407a0737..d6b3817b82b 100644 --- a/packages/query-core/src/queryObserver.ts +++ b/packages/query-core/src/queryObserver.ts @@ -1,10 +1,10 @@ import { focusManager } from './focusManager' +import { environmentManager } from './environmentManager' import { notifyManager } from './notifyManager' import { fetchState } from './query' import { Subscribable } from './subscribable' import { pendingThenable } from './thenable' import { - isServer, isValidTimeout, noop, replaceData, @@ -358,7 +358,11 @@ export class QueryObserver< this.#currentQuery, ) - if (isServer || this.#currentResult.isStale || !isValidTimeout(staleTime)) { + if ( + environmentManager.isServer() || + this.#currentResult.isStale || + !isValidTimeout(staleTime) + ) { return } @@ -389,7 +393,7 @@ export class QueryObserver< this.#currentRefetchInterval = nextInterval if ( - isServer || + environmentManager.isServer() || resolveEnabled(this.options.enabled, this.#currentQuery) === false || !isValidTimeout(this.#currentRefetchInterval) || this.#currentRefetchInterval === 0 diff --git a/packages/query-core/src/removable.ts b/packages/query-core/src/removable.ts index 8642ab36ec2..68545f74383 100644 --- a/packages/query-core/src/removable.ts +++ b/packages/query-core/src/removable.ts @@ -1,5 +1,6 @@ import { timeoutManager } from './timeoutManager' -import { isServer, isValidTimeout } from './utils' +import { environmentManager } from './environmentManager' +import { isValidTimeout } from './utils' import type { ManagedTimerId } from './timeoutManager' export abstract class Removable { @@ -24,7 +25,7 @@ export abstract class Removable { // 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), + newGcTime ?? (environmentManager.isServer() ? Infinity : 5 * 60 * 1000), ) } diff --git a/packages/query-core/src/retryer.ts b/packages/query-core/src/retryer.ts index f4ada851c9f..0b1bb1833a1 100644 --- a/packages/query-core/src/retryer.ts +++ b/packages/query-core/src/retryer.ts @@ -1,7 +1,8 @@ import { focusManager } from './focusManager' import { onlineManager } from './onlineManager' import { pendingThenable } from './thenable' -import { isServer, sleep } from './utils' +import { environmentManager } from './environmentManager' +import { sleep } from './utils' import type { Thenable } from './thenable' import type { CancelOptions, DefaultError, NetworkMode } from './types' @@ -166,7 +167,7 @@ export function createRetryer( } // Do we need to retry the request? - const retry = config.retry ?? (isServer ? 0 : 3) + const retry = config.retry ?? (environmentManager.isServer() ? 0 : 3) const retryDelay = config.retryDelay ?? defaultRetryDelay const delay = typeof retryDelay === 'function' diff --git a/packages/query-core/src/utils.ts b/packages/query-core/src/utils.ts index 27d44bc98b3..b29e8ded456 100644 --- a/packages/query-core/src/utils.ts +++ b/packages/query-core/src/utils.ts @@ -86,6 +86,9 @@ export type QueryTypeFilter = 'all' | 'active' | 'inactive' // UTILS +/** @deprecated + * use `environmentManager.isServer()` instead. + */ export const isServer = typeof window === 'undefined' || 'Deno' in globalThis export function noop(): void diff --git a/packages/react-query/src/__tests__/utils.tsx b/packages/react-query/src/__tests__/utils.tsx index 3734c1caf67..c45caa27ef1 100644 --- a/packages/react-query/src/__tests__/utils.tsx +++ b/packages/react-query/src/__tests__/utils.tsx @@ -1,7 +1,7 @@ import { vi } from 'vitest' import * as React from 'react' import { act, render } from '@testing-library/react' -import * as utils from '@tanstack/query-core' +import { environmentManager, isServer } from '@tanstack/query-core' import { QueryClientProvider, onlineManager } from '..' import type { QueryClient } from '..' import type { MockInstance } from 'vitest' @@ -56,17 +56,9 @@ export function setActTimeout(fn: () => void, ms?: number) { }, ms) } -// This monkey-patches the isServer-value from utils, -// so that we can pretend to be in a server environment -export function setIsServer(isServer: boolean) { - const original = utils.isServer - Object.defineProperty(utils, 'isServer', { - get: () => isServer, - }) - +export function setIsServer(value: boolean) { + environmentManager.setIsServer(value) return () => { - Object.defineProperty(utils, 'isServer', { - get: () => original, - }) + environmentManager.setIsServer(() => isServer) } } diff --git a/packages/react-query/src/useBaseQuery.ts b/packages/react-query/src/useBaseQuery.ts index 2a151fe1137..6107dd8d502 100644 --- a/packages/react-query/src/useBaseQuery.ts +++ b/packages/react-query/src/useBaseQuery.ts @@ -1,7 +1,11 @@ 'use client' import * as React from 'react' -import { isServer, noop, notifyManager } from '@tanstack/query-core' +import { + environmentManager, + noop, + notifyManager, +} from '@tanstack/query-core' import { useQueryClient } from './QueryClientProvider' import { useQueryErrorResetBoundary } from './QueryErrorResetBoundary' import { @@ -148,7 +152,7 @@ export function useBaseQuery< if ( defaultedOptions.experimental_prefetchInRender && - !isServer && + !environmentManager.isServer() && willFetch(result, isRestoring) ) { const promise = isNewCacheEntry diff --git a/packages/vue-query/src/__tests__/queryClient.test.ts b/packages/vue-query/src/__tests__/queryClient.test.ts index c97be2db192..395e27bc599 100644 --- a/packages/vue-query/src/__tests__/queryClient.test.ts +++ b/packages/vue-query/src/__tests__/queryClient.test.ts @@ -4,6 +4,14 @@ import { QueryClient as QueryClientOrigin } from '@tanstack/query-core' import { QueryClient } from '../queryClient' import { infiniteQueryOptions } from '../infiniteQueryOptions' +type QueryClientPrototypeMethod = { + [TKey in keyof typeof QueryClientOrigin.prototype]: (typeof QueryClientOrigin.prototype)[TKey] extends ( + ...args: Array + ) => any + ? TKey + : never +}[keyof typeof QueryClientOrigin.prototype] + vi.mock('@tanstack/query-core', async () => { const actual = await vi.importActual<{ QueryClient: typeof QueryClientOrigin @@ -12,7 +20,7 @@ vi.mock('@tanstack/query-core', async () => { // Get the prototype methods dynamically const prototypeMethods = Object.getOwnPropertyNames( actual.QueryClient.prototype, - ).filter((prop): prop is keyof typeof actual.QueryClient.prototype => { + ).filter((prop): prop is QueryClientPrototypeMethod => { const descriptor = Object.getOwnPropertyDescriptor( actual.QueryClient.prototype, prop, From b79a80b2d8d4de03699860ac189508a9eaf729df Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 28 Feb 2026 09:34:45 +0000 Subject: [PATCH 2/7] ci: apply automated fixes --- packages/preact-query/src/useBaseQuery.ts | 6 +----- packages/query-core/src/environmentManager.ts | 4 +--- packages/react-query/src/useBaseQuery.ts | 6 +----- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/packages/preact-query/src/useBaseQuery.ts b/packages/preact-query/src/useBaseQuery.ts index 54db9672232..05ce46e7f6e 100644 --- a/packages/preact-query/src/useBaseQuery.ts +++ b/packages/preact-query/src/useBaseQuery.ts @@ -1,8 +1,4 @@ -import { - environmentManager, - noop, - notifyManager, -} from '@tanstack/query-core' +import { environmentManager, noop, notifyManager } from '@tanstack/query-core' import type { QueryClient, QueryKey, diff --git a/packages/query-core/src/environmentManager.ts b/packages/query-core/src/environmentManager.ts index febf592cbac..3e8e58f29a9 100644 --- a/packages/query-core/src/environmentManager.ts +++ b/packages/query-core/src/environmentManager.ts @@ -25,9 +25,7 @@ export class EnvironmentManager { */ setIsServer(isServerValue: IsServerValue): void { this.#isServer = - typeof isServerValue === 'function' - ? isServerValue - : () => isServerValue + typeof isServerValue === 'function' ? isServerValue : () => isServerValue } } diff --git a/packages/react-query/src/useBaseQuery.ts b/packages/react-query/src/useBaseQuery.ts index 6107dd8d502..a88f7d40fbf 100644 --- a/packages/react-query/src/useBaseQuery.ts +++ b/packages/react-query/src/useBaseQuery.ts @@ -1,11 +1,7 @@ 'use client' import * as React from 'react' -import { - environmentManager, - noop, - notifyManager, -} from '@tanstack/query-core' +import { environmentManager, noop, notifyManager } from '@tanstack/query-core' import { useQueryClient } from './QueryClientProvider' import { useQueryErrorResetBoundary } from './QueryErrorResetBoundary' import { From eda59de521084d54252154a2d7cf0e27330b349e Mon Sep 17 00:00:00 2001 From: TkDodo Date: Sat, 28 Feb 2026 10:42:32 +0100 Subject: [PATCH 3/7] ref: environmentManager --- docs/reference/environmentManager.md | 7 +-- packages/preact-query/src/__tests__/utils.tsx | 2 +- .../src/__tests__/environmentManager.test.tsx | 4 +- packages/query-core/src/__tests__/utils.ts | 2 +- packages/query-core/src/environmentManager.ts | 44 +++++++------------ packages/react-query/src/__tests__/utils.tsx | 2 +- 6 files changed, 24 insertions(+), 37 deletions(-) diff --git a/docs/reference/environmentManager.md b/docs/reference/environmentManager.md index d3f7f2dbcca..2295a764636 100644 --- a/docs/reference/environmentManager.md +++ b/docs/reference/environmentManager.md @@ -31,10 +31,7 @@ Overrides the server check globally. ```tsx import { environmentManager } from '@tanstack/react-query' -// Static override -environmentManager.setIsServer(false) - -// Dynamic override +// Override environmentManager.setIsServer(() => { return typeof window === 'undefined' && !('chrome' in globalThis) }) @@ -42,7 +39,7 @@ environmentManager.setIsServer(() => { **Options** -- `isServerValue: boolean | (() => boolean)` +- `isServerValue: () => boolean` To restore the default behavior, set the function back to query-core's `isServer` utility: diff --git a/packages/preact-query/src/__tests__/utils.tsx b/packages/preact-query/src/__tests__/utils.tsx index 618338db70e..b37adc318ba 100644 --- a/packages/preact-query/src/__tests__/utils.tsx +++ b/packages/preact-query/src/__tests__/utils.tsx @@ -59,7 +59,7 @@ export function setActTimeout(fn: () => void, ms?: number) { } export function setIsServer(value: boolean) { - environmentManager.setIsServer(value) + environmentManager.setIsServer(() => value) return () => { environmentManager.setIsServer(() => isServer) } diff --git a/packages/query-core/src/__tests__/environmentManager.test.tsx b/packages/query-core/src/__tests__/environmentManager.test.tsx index a54dc8af84e..f7123f1d4ef 100644 --- a/packages/query-core/src/__tests__/environmentManager.test.tsx +++ b/packages/query-core/src/__tests__/environmentManager.test.tsx @@ -11,10 +11,10 @@ describe('environmentManager', () => { }) test('should allow overriding isServer globally', () => { - environmentManager.setIsServer(true) + environmentManager.setIsServer(() => true) expect(environmentManager.isServer()).toBe(true) - environmentManager.setIsServer(false) + environmentManager.setIsServer(() => false) expect(environmentManager.isServer()).toBe(false) }) diff --git a/packages/query-core/src/__tests__/utils.ts b/packages/query-core/src/__tests__/utils.ts index 3e92182b277..6396d341ead 100644 --- a/packages/query-core/src/__tests__/utils.ts +++ b/packages/query-core/src/__tests__/utils.ts @@ -21,7 +21,7 @@ export function executeMutation( } export function setIsServer(value: boolean) { - environmentManager.setIsServer(value) + environmentManager.setIsServer(() => value) return () => { environmentManager.setIsServer(() => isServer) } diff --git a/packages/query-core/src/environmentManager.ts b/packages/query-core/src/environmentManager.ts index 3e8e58f29a9..9da5caafd04 100644 --- a/packages/query-core/src/environmentManager.ts +++ b/packages/query-core/src/environmentManager.ts @@ -1,35 +1,25 @@ import { isServer } from './utils' -type IsServerValue = boolean | (() => boolean) +export type IsServerValue = () => boolean /** * Manages environment detection used by TanStack Query internals. */ -export class EnvironmentManager { - #isServer: () => boolean +export const environmentManager = (() => { + let isServerFn: IsServerValue = () => isServer - constructor() { - this.#isServer = () => isServer + return { + /** + * Returns whether the current runtime should be treated as a server environment. + */ + isServer(): boolean { + return isServerFn() + }, + /** + * Overrides the server check globally. + */ + setIsServer(isServerValue: IsServerValue): void { + isServerFn = isServerValue + }, } - - /** - * Returns whether the current runtime should be treated as a server environment. - */ - isServer(): boolean { - return this.#isServer() - } - - /** - * Overrides the server check globally. - * Accepts either a boolean value or a function that returns a boolean. - */ - setIsServer(isServerValue: IsServerValue): void { - this.#isServer = - typeof isServerValue === 'function' ? isServerValue : () => isServerValue - } -} - -/** - * Global manager instance for environment detection. - */ -export const environmentManager = new EnvironmentManager() +})() diff --git a/packages/react-query/src/__tests__/utils.tsx b/packages/react-query/src/__tests__/utils.tsx index c45caa27ef1..ae8e2fb23f4 100644 --- a/packages/react-query/src/__tests__/utils.tsx +++ b/packages/react-query/src/__tests__/utils.tsx @@ -57,7 +57,7 @@ export function setActTimeout(fn: () => void, ms?: number) { } export function setIsServer(value: boolean) { - environmentManager.setIsServer(value) + environmentManager.setIsServer(() => value) return () => { environmentManager.setIsServer(() => isServer) } From 9ee18dff609bc6d5b812f13285601ee2d26f1605 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Sat, 28 Feb 2026 10:58:34 +0100 Subject: [PATCH 4/7] fix: adapt tests to not using isServer anymore --- .../query-core/src/__tests__/focusManager.test.tsx | 14 +++++++++----- .../src/__tests__/onlineManager.test.tsx | 14 +++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/query-core/src/__tests__/focusManager.test.tsx b/packages/query-core/src/__tests__/focusManager.test.tsx index 3783361fb04..508e2b4c0fc 100644 --- a/packages/query-core/src/__tests__/focusManager.test.tsx +++ b/packages/query-core/src/__tests__/focusManager.test.tsx @@ -1,6 +1,5 @@ import { afterEach, beforeEach, describe, expect, it, test, vi } from 'vitest' import { FocusManager } from '../focusManager' -import { setIsServer } from './utils' describe('focusManager', () => { let focusManager: FocusManager @@ -55,17 +54,22 @@ describe('focusManager', () => { }) test('cleanup (removeEventListener) should not be called if window is not defined', () => { - const restoreIsServer = setIsServer(true) - + const windowSpy = vi.spyOn(globalThis, 'window', 'get') + windowSpy.mockImplementation( + () => undefined as unknown as Window & typeof globalThis, + ) const removeEventListenerSpy = vi.spyOn(globalThis, 'removeEventListener') - const unsubscribe = focusManager.subscribe(() => undefined) + const subscribe = () => focusManager.subscribe(() => undefined) + + expect(subscribe).not.toThrow() + const unsubscribe = subscribe() unsubscribe() expect(removeEventListenerSpy).not.toHaveBeenCalled() - restoreIsServer() + windowSpy.mockRestore() }) test('cleanup (removeEventListener) should not be called if window.addEventListener is not defined', () => { diff --git a/packages/query-core/src/__tests__/onlineManager.test.tsx b/packages/query-core/src/__tests__/onlineManager.test.tsx index 20a9439de9f..1a5245bfc80 100644 --- a/packages/query-core/src/__tests__/onlineManager.test.tsx +++ b/packages/query-core/src/__tests__/onlineManager.test.tsx @@ -1,6 +1,5 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest' import { OnlineManager } from '../onlineManager' -import { setIsServer } from './utils' describe('onlineManager', () => { let onlineManager: OnlineManager @@ -64,17 +63,22 @@ describe('onlineManager', () => { }) test('cleanup (removeEventListener) should not be called if window is not defined', () => { - const restoreIsServer = setIsServer(true) - + const windowSpy = vi.spyOn(globalThis, 'window', 'get') + windowSpy.mockImplementation( + () => undefined as unknown as Window & typeof globalThis, + ) const removeEventListenerSpy = vi.spyOn(globalThis, 'removeEventListener') - const unsubscribe = onlineManager.subscribe(() => undefined) + const subscribe = () => onlineManager.subscribe(() => undefined) + + expect(subscribe).not.toThrow() + const unsubscribe = subscribe() unsubscribe() expect(removeEventListenerSpy).not.toHaveBeenCalled() - restoreIsServer() + windowSpy.mockRestore() }) test('cleanup (removeEventListener) should not be called if window.addEventListener is not defined', () => { From 4960da2a906a8642a442eb72a119c177751e3f01 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Sat, 28 Feb 2026 16:56:05 +0100 Subject: [PATCH 5/7] ref: revert to main --- packages/vue-query/src/__tests__/queryClient.test.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/vue-query/src/__tests__/queryClient.test.ts b/packages/vue-query/src/__tests__/queryClient.test.ts index 395e27bc599..c97be2db192 100644 --- a/packages/vue-query/src/__tests__/queryClient.test.ts +++ b/packages/vue-query/src/__tests__/queryClient.test.ts @@ -4,14 +4,6 @@ import { QueryClient as QueryClientOrigin } from '@tanstack/query-core' import { QueryClient } from '../queryClient' import { infiniteQueryOptions } from '../infiniteQueryOptions' -type QueryClientPrototypeMethod = { - [TKey in keyof typeof QueryClientOrigin.prototype]: (typeof QueryClientOrigin.prototype)[TKey] extends ( - ...args: Array - ) => any - ? TKey - : never -}[keyof typeof QueryClientOrigin.prototype] - vi.mock('@tanstack/query-core', async () => { const actual = await vi.importActual<{ QueryClient: typeof QueryClientOrigin @@ -20,7 +12,7 @@ vi.mock('@tanstack/query-core', async () => { // Get the prototype methods dynamically const prototypeMethods = Object.getOwnPropertyNames( actual.QueryClient.prototype, - ).filter((prop): prop is QueryClientPrototypeMethod => { + ).filter((prop): prop is keyof typeof actual.QueryClient.prototype => { const descriptor = Object.getOwnPropertyDescriptor( actual.QueryClient.prototype, prop, From 4206f7919e5e625b99f13e117d8a8fcb5f161467 Mon Sep 17 00:00:00 2001 From: TkDodo Date: Sat, 28 Feb 2026 16:58:19 +0100 Subject: [PATCH 6/7] make tests better --- .../src/__tests__/environmentManager.test.tsx | 6 +++++- .../query-core/src/__tests__/focusManager.test.tsx | 10 +++++++--- .../query-core/src/__tests__/onlineManager.test.tsx | 6 ++---- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/query-core/src/__tests__/environmentManager.test.tsx b/packages/query-core/src/__tests__/environmentManager.test.tsx index f7123f1d4ef..8d428d7be0c 100644 --- a/packages/query-core/src/__tests__/environmentManager.test.tsx +++ b/packages/query-core/src/__tests__/environmentManager.test.tsx @@ -19,7 +19,11 @@ describe('environmentManager', () => { }) test('should allow overriding isServer with a function', () => { - environmentManager.setIsServer(() => true) + let server = true + environmentManager.setIsServer(() => server) expect(environmentManager.isServer()).toBe(true) + + server = false + expect(environmentManager.isServer()).toBe(false) }) }) diff --git a/packages/query-core/src/__tests__/focusManager.test.tsx b/packages/query-core/src/__tests__/focusManager.test.tsx index 508e2b4c0fc..5900b9b2025 100644 --- a/packages/query-core/src/__tests__/focusManager.test.tsx +++ b/packages/query-core/src/__tests__/focusManager.test.tsx @@ -61,11 +61,15 @@ describe('focusManager', () => { const removeEventListenerSpy = vi.spyOn(globalThis, 'removeEventListener') const subscribe = () => focusManager.subscribe(() => undefined) + let firstUnsubscribe: (() => void) | undefined - expect(subscribe).not.toThrow() - const unsubscribe = subscribe() + expect(() => { + firstUnsubscribe = subscribe() + }).not.toThrow() + const secondUnsubscribe = subscribe() - unsubscribe() + firstUnsubscribe?.() + secondUnsubscribe() expect(removeEventListenerSpy).not.toHaveBeenCalled() diff --git a/packages/query-core/src/__tests__/onlineManager.test.tsx b/packages/query-core/src/__tests__/onlineManager.test.tsx index 1a5245bfc80..79fbb697ee6 100644 --- a/packages/query-core/src/__tests__/onlineManager.test.tsx +++ b/packages/query-core/src/__tests__/onlineManager.test.tsx @@ -69,10 +69,8 @@ describe('onlineManager', () => { ) const removeEventListenerSpy = vi.spyOn(globalThis, 'removeEventListener') - const subscribe = () => onlineManager.subscribe(() => undefined) - - expect(subscribe).not.toThrow() - const unsubscribe = subscribe() + const unsubscribe = onlineManager.subscribe(() => undefined) + expect(unsubscribe).toBeInstanceOf(Function) unsubscribe() From 6c78f60ef7678aa48d22de12a737e709f2785c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Dorfmeister=20=F0=9F=94=AE?= Date: Mon, 2 Mar 2026 16:01:04 +0100 Subject: [PATCH 7/7] Create moody-mails-deliver.md --- .changeset/moody-mails-deliver.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/moody-mails-deliver.md diff --git a/.changeset/moody-mails-deliver.md b/.changeset/moody-mails-deliver.md new file mode 100644 index 00000000000..c7fd8e99295 --- /dev/null +++ b/.changeset/moody-mails-deliver.md @@ -0,0 +1,7 @@ +--- +"@tanstack/preact-query": minor +"@tanstack/query-core": minor +"@tanstack/react-query": minor +--- + +feat: environmentManager