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, ) => void onSuccess?: (data: unknown, query: Query) => void onSettled?: ( data: unknown | undefined, error: DefaultError | null, query: Query, ) => void } 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 } 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 } // CLASS export class QueryCache extends Subscribable { #queries: QueryStore constructor(public config: QueryCacheConfig = {}) { super() this.#queries = new Map() } build< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( client: QueryClient, options: WithRequired< QueryOptions, 'queryKey' >, state?: QueryState, ): Query { const queryKey = options.queryKey const queryHash = options.queryHash ?? hashQueryKeyByOptions(queryKey, options) let query = this.get(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): void { if (!this.#queries.has(query.queryHash)) { this.#queries.set(query.queryHash, query) this.notify({ type: 'added', query, }) } } remove(query: Query): 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 | undefined { return this.#queries.get(queryHash) as | Query | undefined } getAll(): Array { return [...this.#queries.values()] } find( filters: WithRequired, ): Query | undefined { const defaultedFilters = { exact: true, ...filters } return this.getAll().find((query) => matchQuery(defaultedFilters, query), ) as Query | undefined } findAll(filters: QueryFilters = {}): Array { 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() }) }) } }