import type { QueryFilters } from './utils' import { hashQueryKeyByOptions, matchQuery, parseFilterArgs } from './utils' import type { Action, QueryState } from './query' import { Query } from './query' import type { NotifyEvent, QueryKey, QueryOptions } from './types' import { notifyManager } from './notifyManager' import type { QueryClient } from './queryClient' import { Subscribable } from './subscribable' import type { QueryObserver } from './queryObserver' // TYPES interface QueryCacheConfig { onError?: (error: unknown, query: Query) => void onSuccess?: (data: unknown, query: Query) => void onSettled?: ( data: unknown | undefined, error: unknown | null, query: Query, ) => void } interface QueryHashMap { [hash: string]: Query } interface NotifyEventQueryAdded extends NotifyEvent { type: 'added' query: Query } interface NotifyEventQueryRemoved extends NotifyEvent { type: 'removed' query: Query } interface NotifyEventQueryUpdated extends NotifyEvent { type: 'updated' query: Query action: Action } interface NotifyEventQueryObserverAdded extends NotifyEvent { type: 'observerAdded' query: Query observer: QueryObserver } interface NotifyEventQueryObserverRemoved extends NotifyEvent { type: 'observerRemoved' query: Query observer: QueryObserver } interface NotifyEventQueryObserverResultsUpdated extends NotifyEvent { type: 'observerResultsUpdated' query: Query } interface NotifyEventQueryObserverOptionsUpdated extends NotifyEvent { type: 'observerOptionsUpdated' query: Query observer: QueryObserver } type QueryCacheNotifyEvent = | NotifyEventQueryAdded | NotifyEventQueryRemoved | NotifyEventQueryUpdated | NotifyEventQueryObserverAdded | NotifyEventQueryObserverRemoved | NotifyEventQueryObserverResultsUpdated | NotifyEventQueryObserverOptionsUpdated type QueryCacheListener = (event: QueryCacheNotifyEvent) => void // CLASS export class QueryCache extends Subscribable { config: QueryCacheConfig private queries: Query[] private queriesMap: QueryHashMap constructor(config?: QueryCacheConfig) { super() this.config = config || {} this.queries = [] this.queriesMap = {} } build( client: QueryClient, options: QueryOptions, state?: QueryState, ): Query { const queryKey = options.queryKey! const queryHash = options.queryHash ?? hashQueryKeyByOptions(queryKey, options) let query = this.get(queryHash) if (!query) { query = new Query({ cache: this, logger: client.getLogger(), queryKey, queryHash, options: client.defaultQueryOptions(options), state, defaultOptions: client.getQueryDefaults(queryKey), }) this.add(query) } return query } add(query: Query): void { if (!this.queriesMap[query.queryHash]) { this.queriesMap[query.queryHash] = query this.queries.push(query) this.notify({ type: 'added', query, }) } } remove(query: Query): void { const queryInMap = this.queriesMap[query.queryHash] if (queryInMap) { query.destroy() this.queries = this.queries.filter((x) => x !== query) if (queryInMap === query) { delete this.queriesMap[query.queryHash] } this.notify({ type: 'removed', query }) } } clear(): void { notifyManager.batch(() => { this.queries.forEach((query) => { this.remove(query) }) }) } get< TQueryFnData = unknown, TError = unknown, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( queryHash: string, ): Query | undefined { return this.queriesMap[queryHash] } getAll(): Query[] { return this.queries } find( arg1: QueryKey, arg2?: QueryFilters, ): Query | undefined { const [filters] = parseFilterArgs(arg1, arg2) if (typeof filters.exact === 'undefined') { filters.exact = true } return this.queries.find((query) => matchQuery(filters, query)) } findAll(queryKey?: QueryKey, filters?: QueryFilters): Query[] findAll(filters?: QueryFilters): Query[] findAll(arg1?: QueryKey | QueryFilters, arg2?: QueryFilters): Query[] findAll(arg1?: QueryKey | QueryFilters, arg2?: QueryFilters): Query[] { const [filters] = parseFilterArgs(arg1, arg2) return Object.keys(filters).length > 0 ? this.queries.filter((query) => matchQuery(filters, query)) : this.queries } notify(event: QueryCacheNotifyEvent) { notifyManager.batch(() => { this.listeners.forEach(({ listener }) => { listener(event) }) }) } onFocus(): void { notifyManager.batch(() => { this.queries.forEach((query) => { query.onFocus() }) }) } onOnline(): void { notifyManager.batch(() => { this.queries.forEach((query) => { query.onOnline() }) }) } }