UNPKG

@tanstack/query-core

Version:

The framework agnostic core that powers TanStack Query

1 lines • 40.3 kB
{"version":3,"file":"queryObserver.cjs","names":["Subscribable","resolveQueryValue","shallowEqualObjects","noop","isServerEnvironment","isValidTimeout","timeUntilStale","timeoutManager","focusManager","fetchState","replaceData"],"sources":["../../src/queryObserver.ts"],"sourcesContent":["import { focusManager } from './focusManager'\nimport { isServer as isServerEnvironment } from './environmentManager'\nimport { notifyManager } from './notifyManager'\nimport { fetchState } from './query'\nimport { Subscribable } from './subscribable'\nimport {\n isValidTimeout,\n noop,\n replaceData,\n resolveQueryValue,\n shallowEqualObjects,\n timeUntilStale,\n} from './utils'\nimport { timeoutManager } from './timeoutManager'\nimport type { ManagedTimerId } from './timeoutManager'\nimport type { FetchOptions, Query, QueryState } from './query'\nimport type { QueryClient } from './queryClient'\nimport type {\n DefaultError,\n DefaultedQueryObserverOptions,\n PlaceholderDataFunction,\n QueryKey,\n QueryObserverBaseResult,\n QueryObserverOptions,\n QueryObserverResult,\n RefetchOptions,\n} from './types'\n\ntype QueryObserverListener<TData, TError> = (\n result: QueryObserverResult<TData, TError>,\n) => void\n\ninterface ObserverFetchOptions extends FetchOptions {\n throwOnError?: boolean\n}\n\n/**\n * A `QueryObserver` watches a single query in the `QueryCache` and computes a\n * `QueryObserverResult` from its state, recomputing and notifying subscribers\n * whenever the underlying query (or the observer's options) changes. It is\n * the primitive that framework adapters (e.g. `useQuery`) build their hooks\n * on top of, but it can also be used directly to observe and switch between\n * queries outside of any framework.\n * @example\n * ```ts\n * const observer = new QueryObserver(queryClient, {\n * queryKey: ['posts'],\n * queryFn: fetchPosts,\n * })\n *\n * const unsubscribe = observer.subscribe((result) => {\n * console.log(result.data)\n * })\n * ```\n */\nexport class QueryObserver<\n TQueryFnData = unknown,\n TError = DefaultError,\n TData = TQueryFnData,\n TQueryData = TQueryFnData,\n TQueryKey extends QueryKey = QueryKey,\n> extends Subscribable<QueryObserverListener<TData, TError>> {\n #client: QueryClient\n #currentQuery: Query<TQueryFnData, TError, TQueryData, TQueryKey> = undefined!\n #currentQueryInitialState: QueryState<TQueryData, TError> = undefined!\n #currentResult: QueryObserverResult<TData, TError> = undefined!\n #currentResultState?: QueryState<TQueryData, TError>\n #currentResultOptions?: QueryObserverOptions<\n TQueryFnData,\n TError,\n TData,\n TQueryData,\n TQueryKey\n >\n #selectError: TError | null\n #selectFn?: (data: TQueryData) => TData\n #selectResult?: TData\n // This property keeps track of the last query with defined data.\n // It will be used to pass the previous data and query to the placeholder function between renders.\n #lastQueryWithDefinedData?: Query<TQueryFnData, TError, TQueryData, TQueryKey>\n #staleTimeoutId?: ManagedTimerId\n #refetchIntervalId?: ManagedTimerId\n #currentRefetchInterval?: number | false\n #trackedProps = new Set<keyof QueryObserverResult>()\n\n constructor(\n client: QueryClient,\n public options: QueryObserverOptions<\n TQueryFnData,\n TError,\n TData,\n TQueryData,\n TQueryKey\n >,\n ) {\n super()\n\n this.#client = client\n this.#selectError = null\n\n this.bindMethods()\n this.setOptions(options)\n }\n\n /**\n * Binds the methods of the result (`refetch`) to the observer, so they keep working when destructured\n * from it. Subclasses override it to bind their own result methods as well.\n */\n protected bindMethods(): void {\n this.refetch = this.refetch.bind(this)\n }\n\n protected override onSubscribe(): void {\n if (this.listeners.size === 1) {\n this.#currentQuery.addObserver(this)\n\n if (shouldFetchOnMount(this.#currentQuery, this.options)) {\n this.#executeFetch()\n } else {\n this.updateResult()\n }\n\n this.#updateTimers()\n }\n }\n\n protected override onUnsubscribe(): void {\n if (!this.hasListeners()) {\n this.destroy()\n }\n }\n\n /**\n * Returns whether the observed query is currently stale and configured\n * (via the `refetchOnReconnect` option) to refetch when the network\n * reconnects.\n * @returns `true` if the observer should refetch the query on reconnect.\n */\n shouldFetchOnReconnect(): boolean {\n return shouldFetchOn(\n this.#currentQuery,\n this.options,\n this.options.refetchOnReconnect,\n )\n }\n\n /**\n * Returns whether the observed query is currently stale and configured\n * (via the `refetchOnWindowFocus` option) to refetch when the window\n * regains focus.\n * @returns `true` if the observer should refetch the query on window focus.\n */\n shouldFetchOnWindowFocus(): boolean {\n return shouldFetchOn(\n this.#currentQuery,\n this.options,\n this.options.refetchOnWindowFocus,\n )\n }\n\n /**\n * Stops observing the current query: clears all listeners, cancels the\n * stale and refetch-interval timers, and removes this observer from the\n * query it was observing.\n */\n destroy(): void {\n this.listeners = new Set()\n this.#clearStaleTimeout()\n this.#clearRefetchInterval()\n this.#currentQuery.removeObserver(this)\n }\n\n /**\n * Updates the observer's options. This will re-resolve the query being\n * observed (switching to a different query if the `queryKey` changed),\n * trigger a fetch if the new options require one and the observer has\n * subscribers, recompute the current result, and reschedule the stale and\n * refetch-interval timers as needed.\n * @param options - The new observer options. They are defaulted with {@link\n * QueryClient#defaultQueryOptions} before being applied.\n * @throws {Error} If `enabled` is neither a boolean nor a function.\n * @example\n * ```ts\n * observer.setOptions({ queryKey: ['posts', 1], queryFn: () => fetchPost(1) })\n * // later: switch to a different query, reusing the same observer\n * observer.setOptions({ queryKey: ['posts', 2], queryFn: () => fetchPost(2) })\n * ```\n */\n setOptions(\n options: QueryObserverOptions<\n TQueryFnData,\n TError,\n TData,\n TQueryData,\n TQueryKey\n >,\n ): void {\n const prevOptions = this.options\n const prevQuery = this.#currentQuery\n\n this.options = this.#client.defaultQueryOptions(options)\n\n if (\n this.options.enabled !== undefined &&\n typeof this.options.enabled !== 'boolean' &&\n typeof this.options.enabled !== 'function' &&\n typeof resolveQueryValue(this.options.enabled, this.#currentQuery) !==\n 'boolean'\n ) {\n throw new Error(\n 'Expected enabled to be a boolean or a callback that returns a boolean',\n )\n }\n\n this.#updateQuery()\n this.#currentQuery.setOptions(this.options)\n\n if (\n prevOptions._defaulted &&\n !shallowEqualObjects(this.options, prevOptions)\n ) {\n this.#client.getQueryCache().notify({\n type: 'observerOptionsUpdated',\n query: this.#currentQuery,\n observer: this,\n })\n }\n\n const mounted = this.hasListeners()\n\n // Fetch if there are subscribers\n if (\n mounted &&\n shouldFetchOptionally(\n this.#currentQuery,\n prevQuery,\n this.options,\n prevOptions,\n )\n ) {\n this.#executeFetch()\n }\n\n // Update result\n this.updateResult()\n\n // Update stale interval if needed\n if (\n mounted &&\n (this.#currentQuery !== prevQuery ||\n resolveQueryValue(this.options.enabled, this.#currentQuery) !==\n resolveQueryValue(prevOptions.enabled, this.#currentQuery) ||\n resolveQueryValue(this.options.staleTime, this.#currentQuery) !==\n resolveQueryValue(prevOptions.staleTime, this.#currentQuery))\n ) {\n this.#updateStaleTimeout()\n }\n\n const nextRefetchInterval = this.#computeRefetchInterval()\n\n // Update refetch interval if needed\n if (\n mounted &&\n (this.#currentQuery !== prevQuery ||\n resolveQueryValue(this.options.enabled, this.#currentQuery) !==\n resolveQueryValue(prevOptions.enabled, this.#currentQuery) ||\n nextRefetchInterval !== this.#currentRefetchInterval)\n ) {\n this.#updateRefetchInterval(nextRefetchInterval)\n }\n }\n\n /**\n * Computes the result the observer would produce for the given (already-defaulted) options\n * right now, building the underlying `Query` if it doesn't exist yet, without waiting for a\n * subscription callback. Called by framework adapters on every render (e.g. `useQuery`) so the\n * returned value is available synchronously, ahead of `setOptions` triggering an actual fetch.\n * @param options - The defaulted observer options to compute the result for.\n * @returns The result for the given options.\n */\n getOptimisticResult(\n options: DefaultedQueryObserverOptions<\n TQueryFnData,\n TError,\n TData,\n TQueryData,\n TQueryKey\n >,\n ): QueryObserverResult<TData, TError> {\n const query = this.#client.getQueryCache().build(this.#client, options)\n\n const result = this.createResult(query, options)\n\n if (!shallowEqualObjects(this.getCurrentResult(), result)) {\n // this assigns the optimistic result to the current Observer\n // because if the query function changes, useQuery will be performing\n // an effect where it would fetch again.\n // When the fetch finishes, we perform a deep data cloning in order\n // to reuse objects references. This deep data clone is performed against\n // the `observer.currentResult.data` property\n // When QueryKey changes, we refresh the query and get new `optimistic`\n // result, while we leave the `observer.currentResult`, so when new data\n // arrives, it finds the old `observer.currentResult` which is related\n // to the old QueryKey. Which means that currentResult and selectData are\n // out of sync already.\n // To solve this, we move the cursor of the currentResult every time\n // an observer reads an optimistic value.\n\n // When keeping the previous data, the result doesn't change until new\n // data arrives.\n this.#currentResult = result\n this.#currentResultOptions = this.options\n this.#currentResultState = this.#currentQuery.state\n }\n return result\n }\n\n /**\n * Returns the most recently computed `QueryObserverResult` for the\n * observed query. This is a point-in-time read; to be notified of updates\n * as they happen, subscribe to the observer instead (its inherited\n * `subscribe` method).\n * @returns The observer's latest result.\n * @example\n * ```ts\n * const result = observer.getCurrentResult()\n * console.log(result.status, result.data)\n * ```\n */\n getCurrentResult(): QueryObserverResult<TData, TError> {\n return this.#currentResult\n }\n\n /**\n * Wraps a `QueryObserverResult` in a `Proxy` that records which properties are read, via\n * {@link QueryObserver#trackProp} (and an optional `onPropTracked` callback). Used by framework\n * adapters when `notifyOnChangeProps` is not set, to implement its default \"only re-render on\n * properties you actually read\" behavior.\n * @param result - The result to wrap.\n * @param onPropTracked - Called with the name of each property that is read.\n * @returns A proxy of `result` that tracks property reads.\n */\n trackResult(\n result: QueryObserverResult<TData, TError>,\n onPropTracked?: (key: keyof QueryObserverResult) => void,\n ): QueryObserverResult<TData, TError> {\n return new Proxy(result, {\n get: (target, key) => {\n this.trackProp(key as keyof QueryObserverResult)\n onPropTracked?.(key as keyof QueryObserverResult)\n return Reflect.get(target, key)\n },\n })\n }\n\n /**\n * Records that the given `QueryObserverResult` property was read, so a subsequent update only\n * notifies this observer if a tracked property actually changed. Normally called indirectly via\n * {@link QueryObserver#trackResult}'s proxy; exposed directly for adapters that track property\n * access themselves (e.g. through their own reactivity system) instead of via the proxy.\n * @param key - The name of the property that was read.\n */\n trackProp(key: keyof QueryObserverResult) {\n this.#trackedProps.add(key)\n }\n\n /**\n * Returns the `Query` instance this observer is currently observing.\n * @returns The observed query.\n */\n getCurrentQuery(): Query<TQueryFnData, TError, TQueryData, TQueryKey> {\n return this.#currentQuery\n }\n\n /**\n * Refetches the observed query and returns a promise that resolves with\n * the resulting `QueryObserverResult`.\n * @param options - Set `cancelRefetch` to `false` to keep a running fetch instead of cancelling\n * it, and `throwOnError` to `true` to reject when the refetch fails.\n * @returns A promise that resolves with the result after the refetch.\n * @example\n * ```ts\n * const result = await observer.refetch({ cancelRefetch: false })\n * console.log(result.data)\n * ```\n */\n refetch({ ...options }: RefetchOptions = {}): Promise<\n QueryObserverResult<TData, TError>\n > {\n return this.fetch({\n ...options,\n })\n }\n\n /**\n * Fetches a query defined by the given options without affecting this\n * observer's own tracked query or result, and returns a promise that\n * resolves with the `QueryObserverResult` for that fetch. This is useful\n * for prefetching data that another observer (e.g. a query about to be\n * navigated to) will need, ahead of time.\n * @param options - The observer options of the query to fetch.\n * @returns A promise that resolves with the result for the fetched query.\n * @example\n * ```ts\n * const result = await observer.fetchOptimistic({\n * queryKey: ['posts', 2],\n * queryFn: () => fetchPost(2),\n * })\n * console.log(result.data)\n * ```\n */\n fetchOptimistic(\n options: QueryObserverOptions<\n TQueryFnData,\n TError,\n TData,\n TQueryData,\n TQueryKey\n >,\n ): Promise<QueryObserverResult<TData, TError>> {\n const defaultedOptions = this.#client.defaultQueryOptions(options)\n\n const query = this.#client\n .getQueryCache()\n .build(this.#client, defaultedOptions)\n\n let unsubscribe = () => {}\n let resolveEarly:\n | ((result: QueryObserverResult<TData, TError>) => void)\n | undefined\n\n const cachePromise = new Promise<QueryObserverResult<TData, TError>>(\n (resolve) => {\n resolveEarly = resolve\n unsubscribe = this.#client.getQueryCache().subscribe((event) => {\n if (\n event.type === 'updated' &&\n event.query.queryHash === query.queryHash &&\n query.state.data !== undefined\n ) {\n unsubscribe()\n resolve(this.createResult(query, defaultedOptions))\n }\n })\n },\n )\n\n return Promise.race([\n query\n .fetch(defaultedOptions)\n .then(() => {\n const result = this.createResult(query, defaultedOptions)\n // Settle the subscriber promise so both branches always settle.\n // This value is ignored by Promise.race since the fetch branch already won.\n resolveEarly?.(result)\n return result\n })\n .finally(() => {\n unsubscribe()\n }),\n cachePromise,\n ])\n }\n\n /**\n * Fetches the observed query and updates the result once the fetch settles. Used by `refetch` and,\n * in `InfiniteQueryObserver`, to fetch more pages.\n * @param fetchOptions - Options for this fetch. `cancelRefetch` defaults to `true`.\n * @returns A promise that resolves with the updated result.\n */\n protected fetch(\n fetchOptions: ObserverFetchOptions,\n ): Promise<QueryObserverResult<TData, TError>> {\n return this.#executeFetch({\n ...fetchOptions,\n cancelRefetch: fetchOptions.cancelRefetch ?? true,\n }).then(() => {\n this.updateResult()\n return this.#currentResult\n })\n }\n\n #executeFetch(\n fetchOptions?: Omit<ObserverFetchOptions, 'initialPromise'>,\n ): Promise<TQueryData | undefined> {\n // Make sure we reference the latest query as the current one might have been removed\n this.#updateQuery()\n\n // Fetch\n let promise: Promise<TQueryData | undefined> = this.#currentQuery.fetch(\n this.options,\n fetchOptions,\n )\n\n if (!fetchOptions?.throwOnError) {\n promise = promise.catch(noop)\n }\n\n return promise\n }\n\n #shouldScheduleTimer(timeout: unknown): timeout is number {\n return (\n !isServerEnvironment() &&\n resolveQueryValue(this.options.enabled, this.#currentQuery) !== false &&\n isValidTimeout(timeout)\n )\n }\n\n #updateStaleTimeout(): void {\n this.#clearStaleTimeout()\n const staleTime = resolveQueryValue(\n this.options.staleTime,\n this.#currentQuery,\n )\n\n if (this.#currentResult.isStale || !this.#shouldScheduleTimer(staleTime)) {\n return\n }\n\n const time = timeUntilStale(this.#currentResult.dataUpdatedAt, staleTime)\n\n // The timeout is sometimes triggered 1 ms before the stale time expiration.\n // To mitigate this issue we always add 1 ms to the timeout.\n const timeout = time + 1\n\n this.#staleTimeoutId = timeoutManager.setTimeout(() => {\n if (!this.#currentResult.isStale) {\n this.updateResult()\n }\n }, timeout)\n }\n\n #computeRefetchInterval() {\n return (\n resolveQueryValue(this.options.refetchInterval, this.#currentQuery) ??\n false\n )\n }\n\n #updateRefetchInterval(nextInterval: number | false): void {\n this.#clearRefetchInterval()\n\n this.#currentRefetchInterval = nextInterval\n\n if (\n this.#currentRefetchInterval === 0 ||\n !this.#shouldScheduleTimer(this.#currentRefetchInterval)\n ) {\n return\n }\n\n this.#refetchIntervalId = timeoutManager.setInterval(() => {\n if (\n this.options.refetchIntervalInBackground ||\n focusManager.isFocused()\n ) {\n this.#executeFetch()\n }\n }, this.#currentRefetchInterval)\n }\n\n #updateTimers(): void {\n this.#updateStaleTimeout()\n this.#updateRefetchInterval(this.#computeRefetchInterval())\n }\n\n #clearStaleTimeout(): void {\n if (this.#staleTimeoutId !== undefined) {\n timeoutManager.clearTimeout(this.#staleTimeoutId)\n this.#staleTimeoutId = undefined\n }\n }\n\n #clearRefetchInterval(): void {\n if (this.#refetchIntervalId !== undefined) {\n timeoutManager.clearInterval(this.#refetchIntervalId)\n this.#refetchIntervalId = undefined\n }\n }\n\n /**\n * Computes the result for a query and options from the query's state, applying `select`,\n * `placeholderData`, and the derived flags. Subclasses override it to add their own fields.\n * @param query - The query to compute the result for.\n * @param options - The observer options to compute the result with.\n * @returns The computed result.\n */\n protected createResult(\n query: Query<TQueryFnData, TError, TQueryData, TQueryKey>,\n options: QueryObserverOptions<\n TQueryFnData,\n TError,\n TData,\n TQueryData,\n TQueryKey\n >,\n ): QueryObserverResult<TData, TError> {\n const prevQuery = this.#currentQuery\n const prevOptions = this.options\n const prevResult = this.#currentResult as\n | QueryObserverResult<TData, TError>\n | undefined\n const prevResultState = this.#currentResultState\n const prevResultOptions = this.#currentResultOptions\n const queryChange = query !== prevQuery\n const queryInitialState = queryChange\n ? query.state\n : this.#currentQueryInitialState\n\n const { state } = query\n let newState = { ...state }\n let isPlaceholderData = false\n let data: TData | undefined\n\n // Optimistically set result in fetching state if needed\n if (options._optimisticResults) {\n const mounted = this.hasListeners()\n\n const fetchOnMount = !mounted && shouldFetchOnMount(query, options)\n\n const fetchOptionally =\n mounted && shouldFetchOptionally(query, prevQuery, options, prevOptions)\n\n if (fetchOnMount || fetchOptionally) {\n newState = {\n ...newState,\n ...fetchState(state.data, query.options),\n }\n }\n if (options._optimisticResults === 'isRestoring') {\n newState.fetchStatus = 'idle'\n }\n }\n\n let { error, errorUpdatedAt, status } = newState\n\n // Per default, use query data\n data = newState.data as unknown as TData\n let skipSelect = false\n\n // use placeholderData if needed\n if (\n options.placeholderData !== undefined &&\n data === undefined &&\n status === 'pending'\n ) {\n let placeholderData\n\n // Memoize placeholder data\n if (\n prevResult?.isPlaceholderData &&\n options.placeholderData === prevResultOptions?.placeholderData\n ) {\n placeholderData = prevResult.data\n // we have to skip select when reading this memoization\n // because prevResult.data is already \"selected\"\n skipSelect = true\n } else {\n // compute placeholderData\n placeholderData =\n typeof options.placeholderData === 'function'\n ? (\n options.placeholderData as unknown as PlaceholderDataFunction<TQueryData>\n )(\n this.#lastQueryWithDefinedData?.state.data,\n this.#lastQueryWithDefinedData as any,\n )\n : options.placeholderData\n }\n\n if (placeholderData !== undefined) {\n status = 'success'\n data = replaceData(\n prevResult?.data,\n placeholderData as unknown,\n options,\n ) as TData\n isPlaceholderData = true\n }\n }\n\n // Select data if needed\n // this also runs placeholderData through the select function\n if (options.select && data !== undefined && !skipSelect) {\n // Memoize select result\n if (\n prevResult &&\n data === prevResultState?.data &&\n options.select === this.#selectFn\n ) {\n data = this.#selectResult\n } else {\n try {\n this.#selectFn = options.select\n data = options.select(data as any)\n data = replaceData(prevResult?.data, data, options)\n this.#selectResult = data\n this.#selectError = null\n } catch (selectError) {\n this.#selectError = selectError as TError\n }\n }\n } else if (data === undefined) {\n // a stored select error belongs to previously selected data; once that\n // data is gone (query switch or reset), it must not leak into this result\n this.#selectError = null\n }\n\n if (this.#selectError) {\n error = this.#selectError\n data = this.#selectResult\n errorUpdatedAt = Date.now()\n status = 'error'\n isPlaceholderData = false\n }\n\n const isFetching = newState.fetchStatus === 'fetching'\n const isPending = status === 'pending'\n const isError = status === 'error'\n\n const isLoading = isPending && isFetching\n const hasData = data !== undefined\n\n const result: QueryObserverBaseResult<TData, TError> = {\n status,\n fetchStatus: newState.fetchStatus,\n isPending,\n isSuccess: status === 'success',\n isError,\n isInitialLoading: isLoading,\n isLoading,\n data,\n dataUpdatedAt: newState.dataUpdatedAt,\n error,\n errorUpdatedAt,\n failureCount: newState.fetchFailureCount,\n failureReason: newState.fetchFailureReason,\n errorUpdateCount: newState.errorUpdateCount,\n isFetched: query.isFetched(),\n isFetchedAfterMount:\n newState.dataUpdateCount > queryInitialState.dataUpdateCount ||\n newState.errorUpdateCount > queryInitialState.errorUpdateCount,\n isFetching,\n isRefetching: isFetching && !isPending,\n isLoadingError: isError && !hasData,\n isPaused: newState.fetchStatus === 'paused',\n isPlaceholderData,\n isRefetchError: isError && hasData,\n isStale: isStale(query, options),\n refetch: this.refetch,\n isEnabled: resolveQueryValue(options.enabled, query) !== false,\n }\n\n const nextResult = result as QueryObserverResult<TData, TError>\n\n return nextResult\n }\n\n /**\n * Recomputes and stores the current result from the current query/options, notifying listeners\n * if it changed. Framework adapters call this right after subscribing to make sure no query\n * update was missed in the gap between creating the observer and subscribing to it.\n */\n updateResult(): void {\n const prevResult = this.#currentResult as\n | QueryObserverResult<TData, TError>\n | undefined\n\n const nextResult = this.createResult(this.#currentQuery, this.options)\n\n this.#currentResultState = this.#currentQuery.state\n this.#currentResultOptions = this.options\n\n if (this.#currentResultState.data !== undefined) {\n this.#lastQueryWithDefinedData = this.#currentQuery\n }\n\n // Only notify and update result if something has changed\n if (shallowEqualObjects(nextResult, prevResult)) {\n return\n }\n\n this.#currentResult = nextResult\n\n const shouldNotifyListeners = (): boolean => {\n if (!prevResult) {\n return true\n }\n\n const { notifyOnChangeProps } = this.options\n const notifyOnChangePropsValue =\n typeof notifyOnChangeProps === 'function'\n ? notifyOnChangeProps()\n : notifyOnChangeProps\n\n if (\n notifyOnChangePropsValue === 'all' ||\n (!notifyOnChangePropsValue && !this.#trackedProps.size)\n ) {\n return true\n }\n\n const includedProps = new Set(\n notifyOnChangePropsValue ?? this.#trackedProps,\n )\n\n if (this.options.throwOnError) {\n includedProps.add('error')\n }\n\n return Object.keys(this.#currentResult).some((key) => {\n const typedKey = key as keyof QueryObserverResult\n const changed = this.#currentResult[typedKey] !== prevResult[typedKey]\n\n return changed && includedProps.has(typedKey)\n })\n }\n\n const notifyListeners = shouldNotifyListeners()\n\n notifyManager.batch(() => {\n // First, trigger the listeners\n if (notifyListeners) {\n this.listeners.forEach((listener) => {\n listener(this.#currentResult)\n })\n }\n\n // Then the cache listeners\n this.#client.getQueryCache().notify({\n query: this.#currentQuery,\n type: 'observerResultsUpdated',\n })\n })\n }\n\n #updateQuery(): void {\n const query = this.#client.getQueryCache().build(this.#client, this.options)\n\n if (query === this.#currentQuery) {\n return\n }\n\n const prevQuery = this.#currentQuery as\n | Query<TQueryFnData, TError, TQueryData, TQueryKey>\n | undefined\n this.#currentQuery = query\n this.#currentQueryInitialState = query.state\n\n if (this.hasListeners()) {\n prevQuery?.removeObserver(this)\n query.addObserver(this)\n }\n }\n\n /**\n * Updates the observer's result when the observed query changes, and reschedules its timers if it\n * has listeners.\n * @internal\n */\n onQueryUpdate(): void {\n this.updateResult()\n\n if (this.hasListeners()) {\n this.#updateTimers()\n }\n }\n}\n\n/**\n * Checks whether a query has no data yet and should do its initial load when an observer mounts.\n * @param query - The query to check.\n * @param options - The observer options.\n * @returns `true` if the query is enabled, has no data, and isn't in an error state with\n * `retryOnMount: false`.\n */\nfunction shouldLoadOnMount(\n query: Query<any, any, any, any>,\n options: QueryObserverOptions<any, any, any, any>,\n): boolean {\n return (\n resolveQueryValue(options.enabled, query) !== false &&\n query.state.data === undefined &&\n !(\n query.state.status === 'error' &&\n resolveQueryValue(options.retryOnMount, query) === false\n )\n )\n}\n\n/**\n * Checks whether a query should fetch when an observer mounts: either for its initial load, or\n * because it has data and `refetchOnMount` asks for a refetch.\n * @param query - The query to check.\n * @param options - The observer options.\n * @returns `true` if the query should fetch on mount.\n */\nfunction shouldFetchOnMount(\n query: Query<any, any, any, any>,\n options: QueryObserverOptions<any, any, any, any, any>,\n): boolean {\n return (\n shouldLoadOnMount(query, options) ||\n (query.state.data !== undefined &&\n shouldFetchOn(query, options, options.refetchOnMount))\n )\n}\n\n/**\n * Checks whether a query should fetch for a `refetchOnMount`, `refetchOnWindowFocus`, or\n * `refetchOnReconnect` option.\n * @param query - The query to check.\n * @param options - The observer options.\n * @param field - The value of the option to check.\n * @returns `true` if the query is enabled, not static, and the option is `'always'`, or is not\n * `false` and the query is stale.\n */\nfunction shouldFetchOn(\n query: Query<any, any, any, any>,\n options: QueryObserverOptions<any, any, any, any, any>,\n field: (typeof options)['refetchOnMount'] &\n (typeof options)['refetchOnWindowFocus'] &\n (typeof options)['refetchOnReconnect'],\n) {\n if (\n resolveQueryValue(options.enabled, query) !== false &&\n resolveQueryValue(options.staleTime, query) !== 'static'\n ) {\n const value = resolveQueryValue(field, query)\n\n return value === 'always' || (value !== false && isStale(query, options))\n }\n return false\n}\n\n/**\n * Checks whether a query should fetch after the observer's options change.\n * @param query - The query observed with the new options.\n * @param prevQuery - The query observed with the previous options.\n * @param options - The new observer options.\n * @param prevOptions - The previous observer options.\n * @returns `true` if the query changed or was disabled before, it isn't a suspense query in an\n * error state, and it is stale.\n */\nfunction shouldFetchOptionally(\n query: Query<any, any, any, any>,\n prevQuery: Query<any, any, any, any>,\n options: QueryObserverOptions<any, any, any, any, any>,\n prevOptions: QueryObserverOptions<any, any, any, any, any>,\n): boolean {\n return (\n (query !== prevQuery ||\n resolveQueryValue(prevOptions.enabled, query) === false) &&\n (!options.suspense || query.state.status !== 'error') &&\n isStale(query, options)\n )\n}\n\n/**\n * Checks whether a query is stale for the given observer options.\n * @param query - The query to check.\n * @param options - The observer options, whose `enabled` and `staleTime` are used.\n * @returns `true` if the query is enabled and its data is stale for `staleTime`.\n */\nfunction isStale(\n query: Query<any, any, any, any>,\n options: QueryObserverOptions<any, any, any, any, any>,\n): boolean {\n return (\n resolveQueryValue(options.enabled, query) !== false &&\n query.isStaleByTime(resolveQueryValue(options.staleTime, query))\n )\n}\n"],"mappings":"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"}