import { isAsyncThunkAction , isFulfilled } from '@reduxjs/toolkit' ; import type { AnyAction } from 'redux' ; import type { ThunkDispatch } from 'redux-thunk' ; import type { BaseQueryFn , BaseQueryMeta } from '../../baseQueryTypes' ; import { DefinitionType } from '../../endpointDefinitions' ; import type { RootState } from '../apiState' ; import type { MutationResultSelectorResult , QueryResultSelectorResult , } from '../buildSelectors' ; import { getMutationCacheKey } from '../buildSlice' ; import type { PatchCollection , Recipe } from '../buildThunks' ; import type { PromiseWithKnownReason , SubMiddlewareApi , SubMiddlewareBuilder , } from './types' ; export type ReferenceCacheLifecycle = never ; declare module '../../endpointDefinitions' { export interface QueryBaseLifecycleApi < QueryArg , BaseQuery extends BaseQueryFn , ResultType , ReducerPath extends string = string > extends LifecycleApi < ReducerPath > { /**
         * Gets the current value of this cache entry.
         */ getCacheEntry ( ) : QueryResultSelectorResult < { type : DefinitionType . query ; } & BaseEndpointDefinition < QueryArg , BaseQuery , ResultType > > ; /**
         * Updates the current cache entry value.
         * For documentation see `api.util.updateQueryData`.
         */ updateCachedData ( updateRecipe ) : PatchCollection ; } export interface MutationBaseLifecycleApi < QueryArg , BaseQuery extends BaseQueryFn , ResultType , ReducerPath extends string = string > extends LifecycleApi < ReducerPath > { /**
         * Gets the current value of this cache entry.
         */ getCacheEntry ( ) : MutationResultSelectorResult < { type : DefinitionType . mutation ; } & BaseEndpointDefinition < QueryArg , BaseQuery , ResultType > > ; } export interface LifecycleApi < ReducerPath extends string = string > { /**
         * The dispatch method for the store
         */ dispatch : ThunkDispatch < any , any , AnyAction > ; /**
         * A method to get the current state
         */ getState ( ) : RootState < any , any , ReducerPath > ; /**
         * `extra` as provided as `thunk.extraArgument` to the `configureStore` `getDefaultMiddleware` option.
         */ extra : unknown ; /**
         * A unique ID generated for the mutation
         */ requestId : string ; } export interface CacheLifecyclePromises < ResultType = unknown , MetaType = unknown > { /**
         * Promise that will resolve with the first value for this cache key.
         * This allows you to `await` until an actual value is in cache.
         *
         * If the cache entry is removed from the cache before any value has ever
         * been resolved, this Promise will reject with
         * `new Error('Promise never resolved before cacheEntryRemoved.')`
         * to prevent memory leaks.
         * You can just re-throw that error (or not handle it at all) -
         * it will be caught outside of `cacheEntryAdded`.
         *
         * If you don't interact with this promise, it will not throw.
         */ cacheDataLoaded : PromiseWithKnownReason < { /**
             * The (transformed) query result.
             */ data : ResultType ; /**
             * The `meta` returned by the `baseQuery`
             */ meta : MetaType ; } , typeof neverResolvedError > ; /**
         * Promise that allows you to wait for the point in time when the cache entry
         * has been removed from the cache, by not being used/subscribed to any more
         * in the application for too long or by dispatching `api.util.resetApiState`.
         */ cacheEntryRemoved : Promise < void > ; } export interface QueryCacheLifecycleApi < QueryArg , BaseQuery extends BaseQueryFn , ResultType , ReducerPath extends string = string > extends QueryBaseLifecycleApi < QueryArg , BaseQuery , ResultType , ReducerPath > , CacheLifecyclePromises < ResultType , BaseQueryMeta < BaseQuery > > { } export interface MutationCacheLifecycleApi < QueryArg , BaseQuery extends BaseQueryFn , ResultType , ReducerPath extends string = string > extends MutationBaseLifecycleApi < QueryArg , BaseQuery , ResultType , ReducerPath > , CacheLifecyclePromises < ResultType , BaseQueryMeta < BaseQuery > > { } interface QueryExtraOptions < TagTypes extends string , ResultType , QueryArg , BaseQuery extends BaseQueryFn , ReducerPath extends string = string > { onCacheEntryAdded ? ( arg , api ) : Promise < void > | void ; } interface MutationExtraOptions < TagTypes extends string , ResultType , QueryArg , BaseQuery extends BaseQueryFn , ReducerPath extends string = string > { onCacheEntryAdded ? ( arg , api ) : Promise < void > | void ; } } const neverResolvedError = new Error ( 'Promise never resolved before cacheEntryRemoved.' ) as Error & { message : 'Promise never resolved before cacheEntryRemoved.' ; } ; export const build = ( { api , reducerPath , context , queryThunk , mutationThunk , } ) => { const isQueryThunk = isAsyncThunkAction ( queryThunk ) ; const isMutationThunk = isAsyncThunkAction ( mutationThunk ) ; const isFullfilledThunk = isFulfilled ( queryThunk , mutationThunk ) ; return ( mwApi ) => { type CacheLifecycle = { valueResolved ? ( value ) : unknown ; cacheEntryRemoved ( ) : void ; } ; const lifecycleMap = { } ; return ( next ) => ( action ) : any => { const stateBefore = mwApi . getState ( ) ; const result = next ( action ) ; const cacheKey = getCacheKey ( action ) ; if ( queryThunk . pending . match ( action ) ) { const oldState = stateBefore [ reducerPath ] . queries [ cacheKey ] ; const state = mwApi . getState ( ) [ reducerPath ] . queries [ cacheKey ] ; if ( ! oldState && state ) { handleNewKey ( action . meta . arg . endpointName , action . meta . arg . originalArgs , cacheKey , mwApi , action . meta . requestId ) ; } } else if ( mutationThunk . pending . match ( action ) ) { const state = mwApi . getState ( ) [ reducerPath ] . mutations [ cacheKey ] ; if ( state ) { handleNewKey ( action . meta . arg . endpointName , action . meta . arg . originalArgs , cacheKey , mwApi , action . meta . requestId ) ; } } else if ( isFullfilledThunk ( action ) ) { const lifecycle = lifecycleMap [ cacheKey ] ; if ( lifecycle ?. valueResolved ) { lifecycle . valueResolved ( { data : action . payload , meta : action . meta . baseQueryMeta , } ) ; delete lifecycle . valueResolved ; } } else if ( api . internalActions . removeQueryResult . match ( action ) || api . internalActions . removeMutationResult . match ( action ) ) { const lifecycle = lifecycleMap [ cacheKey ] ; if ( lifecycle ) { delete lifecycleMap [ cacheKey ] ; lifecycle . cacheEntryRemoved ( ) ; } } else if ( api . util . resetApiState . match ( action ) ) { for ( const [ cacheKey , lifecycle ] of Object . entries ( lifecycleMap ) ) { delete lifecycleMap [ cacheKey ] ; lifecycle . cacheEntryRemoved ( ) ; } } return result ; } ; function getCacheKey ( action ) { if ( isQueryThunk ( action ) ) return action . meta . arg . queryCacheKey ; if ( isMutationThunk ( action ) ) return action . meta . requestId ; if ( api . internalActions . removeQueryResult . match ( action ) ) return action . payload . queryCacheKey ; if ( api . internalActions . removeMutationResult . match ( action ) ) return getMutationCacheKey ( action . payload ) ; return '' ; } function handleNewKey ( endpointName , originalArgs , queryCacheKey , mwApi , requestId ) { const endpointDefinition = context . endpointDefinitions [ endpointName ] ; const onCacheEntryAdded = endpointDefinition ?. onCacheEntryAdded ; if ( ! onCacheEntryAdded ) return ; let lifecycle = { } as CacheLifecycle ; const cacheEntryRemoved = new Promise < void > ( ( resolve ) => { lifecycle . cacheEntryRemoved = resolve ; } ) ; const cacheDataLoaded = Promise . race ( [ new Promise < { data : unknown ; meta : unknown ; } > ( ( resolve ) => { lifecycle . valueResolved = resolve ; } ) , cacheEntryRemoved . then ( ( ) => { throw neverResolvedError ; } ) , ] ) ; cacheDataLoaded . catch ( ( ) => { } ) ; lifecycleMap [ queryCacheKey ] = lifecycle ; const selector = ( api . endpoints [ endpointName ] as any ) . select ( endpointDefinition . type === DefinitionType . query ? originalArgs : queryCacheKey ) ; const extra = mwApi . dispatch ( ( _ , __ , extra ) => extra ) ; const lifecycleApi = { ... mwApi , getCacheEntry : ( ) => selector ( mwApi . getState ( ) ) , requestId , extra , updateCachedData : ( endpointDefinition . type === DefinitionType . query ? ( updateRecipe ) => mwApi . dispatch ( api . util . updateQueryData ( endpointName as never , originalArgs , updateRecipe ) ) : undefined ) as any , cacheDataLoaded , cacheEntryRemoved , } ; const runningHandler = onCacheEntryAdded ( originalArgs , lifecycleApi ) ; Promise . resolve ( runningHandler ) . catch ( ( e ) => { if ( e === neverResolvedError ) return ; throw e ; } ) ; } } ; } ;