@depup/reduxjs__toolkit 2.12.0-depup.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +33 -0
- package/changes.json +18 -0
- package/dist/cjs/index.js +6 -0
- package/dist/cjs/redux-toolkit.development.cjs +2406 -0
- package/dist/cjs/redux-toolkit.development.cjs.map +1 -0
- package/dist/cjs/redux-toolkit.production.min.cjs +3 -0
- package/dist/cjs/redux-toolkit.production.min.cjs.map +1 -0
- package/dist/index.d.mts +2665 -0
- package/dist/index.d.ts +2665 -0
- package/dist/query/cjs/index.js +6 -0
- package/dist/query/cjs/rtk-query.development.cjs +3092 -0
- package/dist/query/cjs/rtk-query.development.cjs.map +1 -0
- package/dist/query/cjs/rtk-query.production.min.cjs +2 -0
- package/dist/query/cjs/rtk-query.production.min.cjs.map +1 -0
- package/dist/query/index.d.mts +3066 -0
- package/dist/query/index.d.ts +3066 -0
- package/dist/query/react/cjs/index.js +6 -0
- package/dist/query/react/cjs/rtk-query-react.development.cjs +748 -0
- package/dist/query/react/cjs/rtk-query-react.development.cjs.map +1 -0
- package/dist/query/react/cjs/rtk-query-react.production.min.cjs +2 -0
- package/dist/query/react/cjs/rtk-query-react.production.min.cjs.map +1 -0
- package/dist/query/react/index.d.mts +1009 -0
- package/dist/query/react/index.d.ts +1009 -0
- package/dist/query/react/rtk-query-react.browser.mjs +2 -0
- package/dist/query/react/rtk-query-react.browser.mjs.map +1 -0
- package/dist/query/react/rtk-query-react.legacy-esm.js +740 -0
- package/dist/query/react/rtk-query-react.legacy-esm.js.map +1 -0
- package/dist/query/react/rtk-query-react.modern.mjs +705 -0
- package/dist/query/react/rtk-query-react.modern.mjs.map +1 -0
- package/dist/query/rtk-query.browser.mjs +2 -0
- package/dist/query/rtk-query.browser.mjs.map +1 -0
- package/dist/query/rtk-query.legacy-esm.js +3117 -0
- package/dist/query/rtk-query.legacy-esm.js.map +1 -0
- package/dist/query/rtk-query.modern.mjs +3052 -0
- package/dist/query/rtk-query.modern.mjs.map +1 -0
- package/dist/react/cjs/index.js +6 -0
- package/dist/react/cjs/redux-toolkit-react.development.cjs +55 -0
- package/dist/react/cjs/redux-toolkit-react.development.cjs.map +1 -0
- package/dist/react/cjs/redux-toolkit-react.production.min.cjs +2 -0
- package/dist/react/cjs/redux-toolkit-react.production.min.cjs.map +1 -0
- package/dist/react/index.d.mts +22 -0
- package/dist/react/index.d.ts +22 -0
- package/dist/react/redux-toolkit-react.browser.mjs +2 -0
- package/dist/react/redux-toolkit-react.browser.mjs.map +1 -0
- package/dist/react/redux-toolkit-react.legacy-esm.js +47 -0
- package/dist/react/redux-toolkit-react.legacy-esm.js.map +1 -0
- package/dist/react/redux-toolkit-react.modern.mjs +28 -0
- package/dist/react/redux-toolkit-react.modern.mjs.map +1 -0
- package/dist/redux-toolkit.browser.mjs +3 -0
- package/dist/redux-toolkit.browser.mjs.map +1 -0
- package/dist/redux-toolkit.legacy-esm.js +2351 -0
- package/dist/redux-toolkit.legacy-esm.js.map +1 -0
- package/dist/redux-toolkit.modern.mjs +2330 -0
- package/dist/redux-toolkit.modern.mjs.map +1 -0
- package/dist/uncheckedindexed.ts +16 -0
- package/package.json +310 -0
- package/query/package.json +13 -0
- package/query/react/package.json +13 -0
- package/react/package.json +13 -0
- package/skills/build-modern-redux-apps/modern-redux/SKILL.md +304 -0
- package/skills/build-modern-redux-apps/modern-redux/references/store-lifetime.md +53 -0
- package/skills/build-modern-redux-apps/redux-dataflow/SKILL.md +264 -0
- package/skills/evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps/SKILL.md +269 -0
- package/skills/evolve-and-diagnose-redux-apps/migrate-to-modern-redux/SKILL.md +226 -0
- package/skills/manage-server-data/adopt-rtk-query/SKILL.md +385 -0
- package/skills/manage-server-data/adopt-rtk-query/references/endpoint-lifecycle.md +36 -0
- package/skills/model-redux-state/build-slices-and-selectors/SKILL.md +364 -0
- package/skills/model-redux-state/build-slices-and-selectors/references/slice-patterns.md +59 -0
- package/skills/model-redux-state/design-state-ownership/SKILL.md +322 -0
- package/skills/model-redux-state/design-state-ownership/references/state-ownership.md +28 -0
- package/skills/orchestrate-side-effects/handle-side-effects/SKILL.md +271 -0
- package/skills/orchestrate-side-effects/handle-side-effects/references/listener-workflows.md +34 -0
- package/src/actionCreatorInvariantMiddleware.ts +34 -0
- package/src/autoBatchEnhancer.ts +146 -0
- package/src/combineSlices.ts +487 -0
- package/src/configureStore.ts +248 -0
- package/src/createAction.ts +324 -0
- package/src/createAsyncThunk.ts +791 -0
- package/src/createDraftSafeSelector.ts +30 -0
- package/src/createReducer.ts +217 -0
- package/src/createSlice.ts +1079 -0
- package/src/devtoolsExtension.ts +241 -0
- package/src/dynamicMiddleware/index.ts +93 -0
- package/src/dynamicMiddleware/react/index.ts +101 -0
- package/src/dynamicMiddleware/types.ts +82 -0
- package/src/entities/create_adapter.ts +47 -0
- package/src/entities/entity_state.ts +38 -0
- package/src/entities/index.ts +8 -0
- package/src/entities/models.ts +198 -0
- package/src/entities/sorted_state_adapter.ts +266 -0
- package/src/entities/state_adapter.ts +58 -0
- package/src/entities/state_selectors.ts +73 -0
- package/src/entities/unsorted_state_adapter.ts +204 -0
- package/src/entities/utils.ts +68 -0
- package/src/formatProdErrorMessage.ts +13 -0
- package/src/getDefaultEnhancers.ts +31 -0
- package/src/getDefaultMiddleware.ts +113 -0
- package/src/immerImports.ts +7 -0
- package/src/immutableStateInvariantMiddleware.ts +274 -0
- package/src/index.ts +213 -0
- package/src/listenerMiddleware/exceptions.ts +20 -0
- package/src/listenerMiddleware/index.ts +562 -0
- package/src/listenerMiddleware/task.ts +100 -0
- package/src/listenerMiddleware/types.ts +886 -0
- package/src/listenerMiddleware/utils.ts +30 -0
- package/src/mapBuilders.ts +298 -0
- package/src/matchers.ts +365 -0
- package/src/nanoid.ts +20 -0
- package/src/query/HandledError.ts +6 -0
- package/src/query/apiTypes.ts +120 -0
- package/src/query/baseQueryTypes.ts +101 -0
- package/src/query/core/apiState.ts +377 -0
- package/src/query/core/buildInitiate.ts +596 -0
- package/src/query/core/buildMiddleware/batchActions.ts +207 -0
- package/src/query/core/buildMiddleware/cacheCollection.ts +204 -0
- package/src/query/core/buildMiddleware/cacheLifecycle.ts +379 -0
- package/src/query/core/buildMiddleware/devMiddleware.ts +34 -0
- package/src/query/core/buildMiddleware/index.ts +162 -0
- package/src/query/core/buildMiddleware/invalidationByTags.ts +139 -0
- package/src/query/core/buildMiddleware/polling.ts +187 -0
- package/src/query/core/buildMiddleware/queryLifecycle.ts +508 -0
- package/src/query/core/buildMiddleware/types.ts +173 -0
- package/src/query/core/buildMiddleware/windowEventHandling.ts +64 -0
- package/src/query/core/buildSelectors.ts +413 -0
- package/src/query/core/buildSlice.ts +735 -0
- package/src/query/core/buildThunks.ts +1124 -0
- package/src/query/core/index.ts +58 -0
- package/src/query/core/module.ts +727 -0
- package/src/query/core/rtkImports.ts +24 -0
- package/src/query/core/setupListeners.ts +118 -0
- package/src/query/createApi.ts +506 -0
- package/src/query/defaultSerializeQueryArgs.ts +52 -0
- package/src/query/endpointDefinitions.ts +1717 -0
- package/src/query/fakeBaseQuery.ts +21 -0
- package/src/query/fetchBaseQuery.ts +385 -0
- package/src/query/index.ts +105 -0
- package/src/query/react/ApiProvider.tsx +69 -0
- package/src/query/react/buildHooks.ts +2297 -0
- package/src/query/react/constants.ts +2 -0
- package/src/query/react/index.ts +45 -0
- package/src/query/react/module.ts +275 -0
- package/src/query/react/namedHooks.ts +59 -0
- package/src/query/react/reactImports.ts +10 -0
- package/src/query/react/reactReduxImports.ts +1 -0
- package/src/query/react/rtkqImports.ts +8 -0
- package/src/query/react/useSerializedStableValue.ts +17 -0
- package/src/query/react/useShallowStableValue.ts +13 -0
- package/src/query/retry.ts +233 -0
- package/src/query/standardSchema.ts +35 -0
- package/src/query/tsHelpers.ts +48 -0
- package/src/query/utils/capitalize.ts +3 -0
- package/src/query/utils/copyWithStructuralSharing.ts +27 -0
- package/src/query/utils/countObjectKeys.ts +14 -0
- package/src/query/utils/filterMap.ts +27 -0
- package/src/query/utils/getCurrent.ts +6 -0
- package/src/query/utils/getOrInsert.ts +41 -0
- package/src/query/utils/immerImports.ts +9 -0
- package/src/query/utils/index.ts +11 -0
- package/src/query/utils/isAbsoluteUrl.ts +9 -0
- package/src/query/utils/isDocumentVisible.ts +12 -0
- package/src/query/utils/isNotNullish.ts +7 -0
- package/src/query/utils/isOnline.ts +12 -0
- package/src/query/utils/isValidUrl.ts +9 -0
- package/src/query/utils/joinUrls.ts +26 -0
- package/src/query/utils/signals.ts +33 -0
- package/src/react/index.ts +7 -0
- package/src/reduxImports.ts +8 -0
- package/src/reselectImports.ts +1 -0
- package/src/serializableStateInvariantMiddleware.ts +285 -0
- package/src/tsHelpers.ts +214 -0
- package/src/uncheckedindexed.ts +16 -0
- package/src/utils.ts +125 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import type { ThunkDispatch, UnknownAction } from '@reduxjs/toolkit'
|
|
2
|
+
import type {
|
|
3
|
+
BaseQueryFn,
|
|
4
|
+
BaseQueryMeta,
|
|
5
|
+
BaseQueryResult,
|
|
6
|
+
} from '../../baseQueryTypes'
|
|
7
|
+
import type {
|
|
8
|
+
BaseEndpointDefinition,
|
|
9
|
+
DefinitionType,
|
|
10
|
+
} from '../../endpointDefinitions'
|
|
11
|
+
import { isAnyQueryDefinition } from '../../endpointDefinitions'
|
|
12
|
+
import type { QueryCacheKey, RootState } from '../apiState'
|
|
13
|
+
import type {
|
|
14
|
+
MutationResultSelectorResult,
|
|
15
|
+
QueryResultSelectorResult,
|
|
16
|
+
} from '../buildSelectors'
|
|
17
|
+
import { getMutationCacheKey } from '../buildSlice'
|
|
18
|
+
import type { PatchCollection, Recipe } from '../buildThunks'
|
|
19
|
+
import { isAsyncThunkAction, isFulfilled } from '../rtkImports'
|
|
20
|
+
import type {
|
|
21
|
+
ApiMiddlewareInternalHandler,
|
|
22
|
+
InternalHandlerBuilder,
|
|
23
|
+
PromiseWithKnownReason,
|
|
24
|
+
SubMiddlewareApi,
|
|
25
|
+
} from './types'
|
|
26
|
+
import { getEndpointDefinition } from '@internal/query/apiTypes'
|
|
27
|
+
|
|
28
|
+
export type ReferenceCacheLifecycle = never
|
|
29
|
+
|
|
30
|
+
export interface QueryBaseLifecycleApi<
|
|
31
|
+
QueryArg,
|
|
32
|
+
BaseQuery extends BaseQueryFn,
|
|
33
|
+
ResultType,
|
|
34
|
+
ReducerPath extends string = string,
|
|
35
|
+
> extends LifecycleApi<ReducerPath> {
|
|
36
|
+
/**
|
|
37
|
+
* Gets the current value of this cache entry.
|
|
38
|
+
*/
|
|
39
|
+
getCacheEntry(): QueryResultSelectorResult<
|
|
40
|
+
{ type: DefinitionType.query } & BaseEndpointDefinition<
|
|
41
|
+
QueryArg,
|
|
42
|
+
BaseQuery,
|
|
43
|
+
ResultType,
|
|
44
|
+
BaseQueryResult<BaseQuery>
|
|
45
|
+
>
|
|
46
|
+
>
|
|
47
|
+
/**
|
|
48
|
+
* Updates the current cache entry value.
|
|
49
|
+
* For documentation see `api.util.updateQueryData`.
|
|
50
|
+
*/
|
|
51
|
+
updateCachedData(updateRecipe: Recipe<ResultType>): PatchCollection
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type MutationBaseLifecycleApi<
|
|
55
|
+
QueryArg,
|
|
56
|
+
BaseQuery extends BaseQueryFn,
|
|
57
|
+
ResultType,
|
|
58
|
+
ReducerPath extends string = string,
|
|
59
|
+
> = LifecycleApi<ReducerPath> & {
|
|
60
|
+
/**
|
|
61
|
+
* Gets the current value of this cache entry.
|
|
62
|
+
*/
|
|
63
|
+
getCacheEntry(): MutationResultSelectorResult<
|
|
64
|
+
{ type: DefinitionType.mutation } & BaseEndpointDefinition<
|
|
65
|
+
QueryArg,
|
|
66
|
+
BaseQuery,
|
|
67
|
+
ResultType,
|
|
68
|
+
BaseQueryResult<BaseQuery>
|
|
69
|
+
>
|
|
70
|
+
>
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
type LifecycleApi<ReducerPath extends string = string> = {
|
|
74
|
+
/**
|
|
75
|
+
* The dispatch method for the store
|
|
76
|
+
*/
|
|
77
|
+
dispatch: ThunkDispatch<any, any, UnknownAction>
|
|
78
|
+
/**
|
|
79
|
+
* A method to get the current state
|
|
80
|
+
*/
|
|
81
|
+
getState(): RootState<any, any, ReducerPath>
|
|
82
|
+
/**
|
|
83
|
+
* `extra` as provided as `thunk.extraArgument` to the `configureStore` `getDefaultMiddleware` option.
|
|
84
|
+
*/
|
|
85
|
+
extra: unknown
|
|
86
|
+
/**
|
|
87
|
+
* A unique ID generated for the mutation
|
|
88
|
+
*/
|
|
89
|
+
requestId: string
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
type CacheLifecyclePromises<ResultType = unknown, MetaType = unknown> = {
|
|
93
|
+
/**
|
|
94
|
+
* Promise that will resolve with the first value for this cache key.
|
|
95
|
+
* This allows you to `await` until an actual value is in cache.
|
|
96
|
+
*
|
|
97
|
+
* If the cache entry is removed from the cache before any value has ever
|
|
98
|
+
* been resolved, this Promise will reject with
|
|
99
|
+
* `new Error('Promise never resolved before cacheEntryRemoved.')`
|
|
100
|
+
* to prevent memory leaks.
|
|
101
|
+
* You can just re-throw that error (or not handle it at all) -
|
|
102
|
+
* it will be caught outside of `cacheEntryAdded`.
|
|
103
|
+
*
|
|
104
|
+
* If you don't interact with this promise, it will not throw.
|
|
105
|
+
*/
|
|
106
|
+
cacheDataLoaded: PromiseWithKnownReason<
|
|
107
|
+
{
|
|
108
|
+
/**
|
|
109
|
+
* The (transformed) query result.
|
|
110
|
+
*/
|
|
111
|
+
data: ResultType
|
|
112
|
+
/**
|
|
113
|
+
* The `meta` returned by the `baseQuery`
|
|
114
|
+
*/
|
|
115
|
+
meta: MetaType
|
|
116
|
+
},
|
|
117
|
+
typeof neverResolvedError
|
|
118
|
+
>
|
|
119
|
+
/**
|
|
120
|
+
* Promise that allows you to wait for the point in time when the cache entry
|
|
121
|
+
* has been removed from the cache, by not being used/subscribed to any more
|
|
122
|
+
* in the application for too long or by dispatching `api.util.resetApiState`.
|
|
123
|
+
*/
|
|
124
|
+
cacheEntryRemoved: Promise<void>
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface QueryCacheLifecycleApi<
|
|
128
|
+
QueryArg,
|
|
129
|
+
BaseQuery extends BaseQueryFn,
|
|
130
|
+
ResultType,
|
|
131
|
+
ReducerPath extends string = string,
|
|
132
|
+
> extends QueryBaseLifecycleApi<QueryArg, BaseQuery, ResultType, ReducerPath>,
|
|
133
|
+
CacheLifecyclePromises<ResultType, BaseQueryMeta<BaseQuery>> {}
|
|
134
|
+
|
|
135
|
+
export type MutationCacheLifecycleApi<
|
|
136
|
+
QueryArg,
|
|
137
|
+
BaseQuery extends BaseQueryFn,
|
|
138
|
+
ResultType,
|
|
139
|
+
ReducerPath extends string = string,
|
|
140
|
+
> = MutationBaseLifecycleApi<QueryArg, BaseQuery, ResultType, ReducerPath> &
|
|
141
|
+
CacheLifecyclePromises<ResultType, BaseQueryMeta<BaseQuery>>
|
|
142
|
+
|
|
143
|
+
export type CacheLifecycleQueryExtraOptions<
|
|
144
|
+
ResultType,
|
|
145
|
+
QueryArg,
|
|
146
|
+
BaseQuery extends BaseQueryFn,
|
|
147
|
+
ReducerPath extends string = string,
|
|
148
|
+
> = {
|
|
149
|
+
onCacheEntryAdded?(
|
|
150
|
+
arg: QueryArg,
|
|
151
|
+
api: QueryCacheLifecycleApi<QueryArg, BaseQuery, ResultType, ReducerPath>,
|
|
152
|
+
): Promise<void> | void
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export type CacheLifecycleInfiniteQueryExtraOptions<
|
|
156
|
+
ResultType,
|
|
157
|
+
QueryArg,
|
|
158
|
+
BaseQuery extends BaseQueryFn,
|
|
159
|
+
ReducerPath extends string = string,
|
|
160
|
+
> = CacheLifecycleQueryExtraOptions<
|
|
161
|
+
ResultType,
|
|
162
|
+
QueryArg,
|
|
163
|
+
BaseQuery,
|
|
164
|
+
ReducerPath
|
|
165
|
+
>
|
|
166
|
+
|
|
167
|
+
export type CacheLifecycleMutationExtraOptions<
|
|
168
|
+
ResultType,
|
|
169
|
+
QueryArg,
|
|
170
|
+
BaseQuery extends BaseQueryFn,
|
|
171
|
+
ReducerPath extends string = string,
|
|
172
|
+
> = {
|
|
173
|
+
onCacheEntryAdded?(
|
|
174
|
+
arg: QueryArg,
|
|
175
|
+
api: MutationCacheLifecycleApi<
|
|
176
|
+
QueryArg,
|
|
177
|
+
BaseQuery,
|
|
178
|
+
ResultType,
|
|
179
|
+
ReducerPath
|
|
180
|
+
>,
|
|
181
|
+
): Promise<void> | void
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const neverResolvedError = new Error(
|
|
185
|
+
'Promise never resolved before cacheEntryRemoved.',
|
|
186
|
+
) as Error & {
|
|
187
|
+
message: 'Promise never resolved before cacheEntryRemoved.'
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export const buildCacheLifecycleHandler: InternalHandlerBuilder = ({
|
|
191
|
+
api,
|
|
192
|
+
reducerPath,
|
|
193
|
+
context,
|
|
194
|
+
queryThunk,
|
|
195
|
+
mutationThunk,
|
|
196
|
+
internalState,
|
|
197
|
+
selectors: { selectQueryEntry, selectApiState },
|
|
198
|
+
}) => {
|
|
199
|
+
const isQueryThunk = isAsyncThunkAction(queryThunk)
|
|
200
|
+
const isMutationThunk = isAsyncThunkAction(mutationThunk)
|
|
201
|
+
const isFulfilledThunk = isFulfilled(queryThunk, mutationThunk)
|
|
202
|
+
|
|
203
|
+
type CacheLifecycle = {
|
|
204
|
+
valueResolved?(value: { data: unknown; meta: unknown }): unknown
|
|
205
|
+
cacheEntryRemoved(): void
|
|
206
|
+
}
|
|
207
|
+
const lifecycleMap: Record<string, CacheLifecycle> = {}
|
|
208
|
+
|
|
209
|
+
const { removeQueryResult, removeMutationResult, cacheEntriesUpserted } =
|
|
210
|
+
api.internalActions
|
|
211
|
+
|
|
212
|
+
function resolveLifecycleEntry(
|
|
213
|
+
cacheKey: string,
|
|
214
|
+
data: unknown,
|
|
215
|
+
meta: unknown,
|
|
216
|
+
) {
|
|
217
|
+
const lifecycle = lifecycleMap[cacheKey]
|
|
218
|
+
|
|
219
|
+
if (lifecycle?.valueResolved) {
|
|
220
|
+
lifecycle.valueResolved({
|
|
221
|
+
data,
|
|
222
|
+
meta,
|
|
223
|
+
})
|
|
224
|
+
delete lifecycle.valueResolved
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function removeLifecycleEntry(cacheKey: string) {
|
|
229
|
+
const lifecycle = lifecycleMap[cacheKey]
|
|
230
|
+
if (lifecycle) {
|
|
231
|
+
delete lifecycleMap[cacheKey]
|
|
232
|
+
lifecycle.cacheEntryRemoved()
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function getActionMetaFields(
|
|
237
|
+
action:
|
|
238
|
+
| ReturnType<typeof queryThunk.pending>
|
|
239
|
+
| ReturnType<typeof mutationThunk.pending>,
|
|
240
|
+
) {
|
|
241
|
+
const { arg, requestId } = action.meta
|
|
242
|
+
const { endpointName, originalArgs } = arg
|
|
243
|
+
return [endpointName, originalArgs, requestId] as const
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const handler: ApiMiddlewareInternalHandler = (
|
|
247
|
+
action,
|
|
248
|
+
mwApi,
|
|
249
|
+
stateBefore,
|
|
250
|
+
) => {
|
|
251
|
+
const cacheKey = getCacheKey(action) as QueryCacheKey
|
|
252
|
+
|
|
253
|
+
function checkForNewCacheKey(
|
|
254
|
+
endpointName: string,
|
|
255
|
+
cacheKey: QueryCacheKey,
|
|
256
|
+
requestId: string,
|
|
257
|
+
originalArgs: unknown,
|
|
258
|
+
) {
|
|
259
|
+
const oldEntry = selectQueryEntry(stateBefore, cacheKey)
|
|
260
|
+
const newEntry = selectQueryEntry(mwApi.getState(), cacheKey)
|
|
261
|
+
if (!oldEntry && newEntry) {
|
|
262
|
+
handleNewKey(endpointName, originalArgs, cacheKey, mwApi, requestId)
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (queryThunk.pending.match(action)) {
|
|
267
|
+
const [endpointName, originalArgs, requestId] =
|
|
268
|
+
getActionMetaFields(action)
|
|
269
|
+
checkForNewCacheKey(endpointName, cacheKey, requestId, originalArgs)
|
|
270
|
+
} else if (cacheEntriesUpserted.match(action)) {
|
|
271
|
+
for (const { queryDescription, value } of action.payload) {
|
|
272
|
+
const { endpointName, originalArgs, queryCacheKey } = queryDescription
|
|
273
|
+
checkForNewCacheKey(
|
|
274
|
+
endpointName,
|
|
275
|
+
queryCacheKey,
|
|
276
|
+
action.meta.requestId,
|
|
277
|
+
originalArgs,
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
resolveLifecycleEntry(queryCacheKey, value, {})
|
|
281
|
+
}
|
|
282
|
+
} else if (mutationThunk.pending.match(action)) {
|
|
283
|
+
const state = mwApi.getState()[reducerPath].mutations[cacheKey]
|
|
284
|
+
if (state) {
|
|
285
|
+
const [endpointName, originalArgs, requestId] =
|
|
286
|
+
getActionMetaFields(action)
|
|
287
|
+
handleNewKey(endpointName, originalArgs, cacheKey, mwApi, requestId)
|
|
288
|
+
}
|
|
289
|
+
} else if (isFulfilledThunk(action)) {
|
|
290
|
+
resolveLifecycleEntry(cacheKey, action.payload, action.meta.baseQueryMeta)
|
|
291
|
+
} else if (
|
|
292
|
+
removeQueryResult.match(action) ||
|
|
293
|
+
removeMutationResult.match(action)
|
|
294
|
+
) {
|
|
295
|
+
removeLifecycleEntry(cacheKey)
|
|
296
|
+
} else if (api.util.resetApiState.match(action)) {
|
|
297
|
+
for (const cacheKey of Object.keys(lifecycleMap)) {
|
|
298
|
+
removeLifecycleEntry(cacheKey)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function getCacheKey(action: any) {
|
|
304
|
+
if (isQueryThunk(action)) return action.meta.arg.queryCacheKey
|
|
305
|
+
if (isMutationThunk(action)) {
|
|
306
|
+
return action.meta.arg.fixedCacheKey ?? action.meta.requestId
|
|
307
|
+
}
|
|
308
|
+
if (removeQueryResult.match(action)) return action.payload.queryCacheKey
|
|
309
|
+
if (removeMutationResult.match(action))
|
|
310
|
+
return getMutationCacheKey(action.payload)
|
|
311
|
+
return ''
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function handleNewKey(
|
|
315
|
+
endpointName: string,
|
|
316
|
+
originalArgs: any,
|
|
317
|
+
queryCacheKey: string,
|
|
318
|
+
mwApi: SubMiddlewareApi,
|
|
319
|
+
requestId: string,
|
|
320
|
+
) {
|
|
321
|
+
const endpointDefinition = getEndpointDefinition(context, endpointName)
|
|
322
|
+
const onCacheEntryAdded = endpointDefinition?.onCacheEntryAdded
|
|
323
|
+
if (!onCacheEntryAdded) return
|
|
324
|
+
|
|
325
|
+
const lifecycle = {} as CacheLifecycle
|
|
326
|
+
|
|
327
|
+
const cacheEntryRemoved = new Promise<void>((resolve) => {
|
|
328
|
+
lifecycle.cacheEntryRemoved = resolve
|
|
329
|
+
})
|
|
330
|
+
const cacheDataLoaded: PromiseWithKnownReason<
|
|
331
|
+
{ data: unknown; meta: unknown },
|
|
332
|
+
typeof neverResolvedError
|
|
333
|
+
> = Promise.race([
|
|
334
|
+
new Promise<{ data: unknown; meta: unknown }>((resolve) => {
|
|
335
|
+
lifecycle.valueResolved = resolve
|
|
336
|
+
}),
|
|
337
|
+
cacheEntryRemoved.then(() => {
|
|
338
|
+
throw neverResolvedError
|
|
339
|
+
}),
|
|
340
|
+
])
|
|
341
|
+
// prevent uncaught promise rejections from happening.
|
|
342
|
+
// if the original promise is used in any way, that will create a new promise that will throw again
|
|
343
|
+
cacheDataLoaded.catch(() => {})
|
|
344
|
+
lifecycleMap[queryCacheKey] = lifecycle
|
|
345
|
+
const selector = (api.endpoints[endpointName] as any).select(
|
|
346
|
+
isAnyQueryDefinition(endpointDefinition) ? originalArgs : queryCacheKey,
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
const extra = mwApi.dispatch((_, __, extra) => extra)
|
|
350
|
+
const lifecycleApi = {
|
|
351
|
+
...mwApi,
|
|
352
|
+
getCacheEntry: () => selector(mwApi.getState()),
|
|
353
|
+
requestId,
|
|
354
|
+
extra,
|
|
355
|
+
updateCachedData: (isAnyQueryDefinition(endpointDefinition)
|
|
356
|
+
? (updateRecipe: Recipe<any>) =>
|
|
357
|
+
mwApi.dispatch(
|
|
358
|
+
api.util.updateQueryData(
|
|
359
|
+
endpointName as never,
|
|
360
|
+
originalArgs as never,
|
|
361
|
+
updateRecipe,
|
|
362
|
+
),
|
|
363
|
+
)
|
|
364
|
+
: undefined) as any,
|
|
365
|
+
|
|
366
|
+
cacheDataLoaded,
|
|
367
|
+
cacheEntryRemoved,
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const runningHandler = onCacheEntryAdded(originalArgs, lifecycleApi as any)
|
|
371
|
+
// if a `neverResolvedError` was thrown, but not handled in the running handler, do not let it leak out further
|
|
372
|
+
Promise.resolve(runningHandler).catch((e) => {
|
|
373
|
+
if (e === neverResolvedError) return
|
|
374
|
+
throw e
|
|
375
|
+
})
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return handler
|
|
379
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { InternalHandlerBuilder } from './types'
|
|
2
|
+
|
|
3
|
+
export const buildDevCheckHandler: InternalHandlerBuilder = ({
|
|
4
|
+
api,
|
|
5
|
+
context: { apiUid },
|
|
6
|
+
reducerPath,
|
|
7
|
+
}) => {
|
|
8
|
+
return (action, mwApi) => {
|
|
9
|
+
if (api.util.resetApiState.match(action)) {
|
|
10
|
+
// dispatch after api reset
|
|
11
|
+
mwApi.dispatch(api.internalActions.middlewareRegistered(apiUid))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (
|
|
15
|
+
typeof process !== 'undefined' &&
|
|
16
|
+
process.env.NODE_ENV === 'development'
|
|
17
|
+
) {
|
|
18
|
+
if (
|
|
19
|
+
api.internalActions.middlewareRegistered.match(action) &&
|
|
20
|
+
action.payload === apiUid &&
|
|
21
|
+
mwApi.getState()[reducerPath]?.config?.middlewareRegistered ===
|
|
22
|
+
'conflict'
|
|
23
|
+
) {
|
|
24
|
+
console.warn(`There is a mismatch between slice and middleware for the reducerPath "${reducerPath}".
|
|
25
|
+
You can only have one api per reducer path, this will lead to crashes in various situations!${
|
|
26
|
+
reducerPath === 'api'
|
|
27
|
+
? `
|
|
28
|
+
If you have multiple apis, you *have* to specify the reducerPath option when using createApi!`
|
|
29
|
+
: ''
|
|
30
|
+
}`)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Action,
|
|
3
|
+
Middleware,
|
|
4
|
+
ThunkDispatch,
|
|
5
|
+
UnknownAction,
|
|
6
|
+
} from '@reduxjs/toolkit'
|
|
7
|
+
import type {
|
|
8
|
+
EndpointDefinitions,
|
|
9
|
+
FullTagDescription,
|
|
10
|
+
} from '../../endpointDefinitions'
|
|
11
|
+
import type { QueryStatus, QuerySubState, RootState } from '../apiState'
|
|
12
|
+
import type { QueryThunkArg } from '../buildThunks'
|
|
13
|
+
import { createAction, isAction } from '../rtkImports'
|
|
14
|
+
import { buildBatchedActionsHandler } from './batchActions'
|
|
15
|
+
import { buildCacheCollectionHandler } from './cacheCollection'
|
|
16
|
+
import { buildCacheLifecycleHandler } from './cacheLifecycle'
|
|
17
|
+
import { buildDevCheckHandler } from './devMiddleware'
|
|
18
|
+
import { buildInvalidationByTagsHandler } from './invalidationByTags'
|
|
19
|
+
import { buildPollingHandler } from './polling'
|
|
20
|
+
import { buildQueryLifecycleHandler } from './queryLifecycle'
|
|
21
|
+
import type {
|
|
22
|
+
BuildMiddlewareInput,
|
|
23
|
+
InternalHandlerBuilder,
|
|
24
|
+
InternalMiddlewareState,
|
|
25
|
+
} from './types'
|
|
26
|
+
import { buildWindowEventHandler } from './windowEventHandling'
|
|
27
|
+
import type { ApiEndpointQuery } from '../module'
|
|
28
|
+
export type { ReferenceCacheCollection } from './cacheCollection'
|
|
29
|
+
export type {
|
|
30
|
+
MutationCacheLifecycleApi,
|
|
31
|
+
QueryCacheLifecycleApi,
|
|
32
|
+
ReferenceCacheLifecycle,
|
|
33
|
+
} from './cacheLifecycle'
|
|
34
|
+
export type {
|
|
35
|
+
MutationLifecycleApi,
|
|
36
|
+
QueryLifecycleApi,
|
|
37
|
+
ReferenceQueryLifecycle,
|
|
38
|
+
TypedMutationOnQueryStarted,
|
|
39
|
+
TypedQueryOnQueryStarted,
|
|
40
|
+
} from './queryLifecycle'
|
|
41
|
+
export type { SubscriptionSelectors } from './types'
|
|
42
|
+
|
|
43
|
+
export function buildMiddleware<
|
|
44
|
+
Definitions extends EndpointDefinitions,
|
|
45
|
+
ReducerPath extends string,
|
|
46
|
+
TagTypes extends string,
|
|
47
|
+
>(input: BuildMiddlewareInput<Definitions, ReducerPath, TagTypes>) {
|
|
48
|
+
const { reducerPath, queryThunk, api, context, getInternalState } = input
|
|
49
|
+
const { apiUid } = context
|
|
50
|
+
|
|
51
|
+
const actions = {
|
|
52
|
+
invalidateTags: createAction<
|
|
53
|
+
Array<TagTypes | FullTagDescription<TagTypes> | null | undefined>
|
|
54
|
+
>(`${reducerPath}/invalidateTags`),
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const isThisApiSliceAction = (action: Action) =>
|
|
58
|
+
action.type.startsWith(`${reducerPath}/`)
|
|
59
|
+
|
|
60
|
+
const handlerBuilders: InternalHandlerBuilder[] = [
|
|
61
|
+
buildDevCheckHandler,
|
|
62
|
+
buildCacheCollectionHandler,
|
|
63
|
+
buildInvalidationByTagsHandler,
|
|
64
|
+
buildPollingHandler,
|
|
65
|
+
buildCacheLifecycleHandler,
|
|
66
|
+
buildQueryLifecycleHandler,
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
const middleware: Middleware<
|
|
70
|
+
{},
|
|
71
|
+
RootState<Definitions, string, ReducerPath>,
|
|
72
|
+
ThunkDispatch<any, any, UnknownAction>
|
|
73
|
+
> = (mwApi) => {
|
|
74
|
+
let initialized = false
|
|
75
|
+
|
|
76
|
+
const internalState = getInternalState(mwApi.dispatch)
|
|
77
|
+
|
|
78
|
+
const builderArgs = {
|
|
79
|
+
...(input as any as BuildMiddlewareInput<
|
|
80
|
+
EndpointDefinitions,
|
|
81
|
+
string,
|
|
82
|
+
string
|
|
83
|
+
>),
|
|
84
|
+
internalState,
|
|
85
|
+
refetchQuery,
|
|
86
|
+
isThisApiSliceAction,
|
|
87
|
+
mwApi,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const handlers = handlerBuilders.map((build) => build(builderArgs))
|
|
91
|
+
|
|
92
|
+
const batchedActionsHandler = buildBatchedActionsHandler(builderArgs)
|
|
93
|
+
const windowEventsHandler = buildWindowEventHandler(builderArgs)
|
|
94
|
+
|
|
95
|
+
return (next) => {
|
|
96
|
+
return (action) => {
|
|
97
|
+
if (!isAction(action)) {
|
|
98
|
+
return next(action)
|
|
99
|
+
}
|
|
100
|
+
if (!initialized) {
|
|
101
|
+
initialized = true
|
|
102
|
+
// dispatch before any other action
|
|
103
|
+
mwApi.dispatch(api.internalActions.middlewareRegistered(apiUid))
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const mwApiWithNext = { ...mwApi, next }
|
|
107
|
+
|
|
108
|
+
const stateBefore = mwApi.getState()
|
|
109
|
+
|
|
110
|
+
const [actionShouldContinue, internalProbeResult] =
|
|
111
|
+
batchedActionsHandler(action, mwApiWithNext, stateBefore)
|
|
112
|
+
|
|
113
|
+
let res: any
|
|
114
|
+
|
|
115
|
+
if (actionShouldContinue) {
|
|
116
|
+
res = next(action)
|
|
117
|
+
} else {
|
|
118
|
+
res = internalProbeResult
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!!mwApi.getState()[reducerPath]) {
|
|
122
|
+
// Only run these checks if the middleware is registered okay
|
|
123
|
+
|
|
124
|
+
// This looks for actions that aren't specific to the API slice
|
|
125
|
+
windowEventsHandler(action, mwApiWithNext, stateBefore)
|
|
126
|
+
|
|
127
|
+
if (
|
|
128
|
+
isThisApiSliceAction(action) ||
|
|
129
|
+
context.hasRehydrationInfo(action)
|
|
130
|
+
) {
|
|
131
|
+
// Only run these additional checks if the actions are part of the API slice,
|
|
132
|
+
// or the action has hydration-related data
|
|
133
|
+
for (const handler of handlers) {
|
|
134
|
+
handler(action, mwApiWithNext, stateBefore)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return res
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return { middleware, actions }
|
|
145
|
+
|
|
146
|
+
function refetchQuery(
|
|
147
|
+
querySubState: Exclude<
|
|
148
|
+
QuerySubState<any>,
|
|
149
|
+
{ status: QueryStatus.uninitialized }
|
|
150
|
+
>,
|
|
151
|
+
) {
|
|
152
|
+
return (
|
|
153
|
+
input.api.endpoints[querySubState.endpointName] as ApiEndpointQuery<
|
|
154
|
+
any,
|
|
155
|
+
any
|
|
156
|
+
>
|
|
157
|
+
).initiate(querySubState.originalArgs as any, {
|
|
158
|
+
subscribe: false,
|
|
159
|
+
forceRefetch: true,
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isAnyOf,
|
|
3
|
+
isFulfilled,
|
|
4
|
+
isRejected,
|
|
5
|
+
isRejectedWithValue,
|
|
6
|
+
} from '../rtkImports'
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
EndpointDefinitions,
|
|
10
|
+
FullTagDescription,
|
|
11
|
+
} from '../../endpointDefinitions'
|
|
12
|
+
import { calculateProvidedBy } from '../../endpointDefinitions'
|
|
13
|
+
import type { CombinedState, QueryCacheKey } from '../apiState'
|
|
14
|
+
import { QueryStatus, STATUS_UNINITIALIZED } from '../apiState'
|
|
15
|
+
import { calculateProvidedByThunk } from '../buildThunks'
|
|
16
|
+
import type {
|
|
17
|
+
SubMiddlewareApi,
|
|
18
|
+
InternalHandlerBuilder,
|
|
19
|
+
ApiMiddlewareInternalHandler,
|
|
20
|
+
} from './types'
|
|
21
|
+
import { getOrInsertComputed, createNewMap } from '../../utils/getOrInsert'
|
|
22
|
+
|
|
23
|
+
export const buildInvalidationByTagsHandler: InternalHandlerBuilder = ({
|
|
24
|
+
reducerPath,
|
|
25
|
+
context,
|
|
26
|
+
context: { endpointDefinitions },
|
|
27
|
+
mutationThunk,
|
|
28
|
+
queryThunk,
|
|
29
|
+
api,
|
|
30
|
+
assertTagType,
|
|
31
|
+
refetchQuery,
|
|
32
|
+
internalState,
|
|
33
|
+
}) => {
|
|
34
|
+
const { removeQueryResult } = api.internalActions
|
|
35
|
+
const isThunkActionWithTags = isAnyOf(
|
|
36
|
+
isFulfilled(mutationThunk),
|
|
37
|
+
isRejectedWithValue(mutationThunk),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
const isQueryEnd = isAnyOf(
|
|
41
|
+
isFulfilled(queryThunk, mutationThunk),
|
|
42
|
+
isRejected(queryThunk, mutationThunk),
|
|
43
|
+
)
|
|
44
|
+
let pendingTagInvalidations: FullTagDescription<string>[] = []
|
|
45
|
+
// Track via counter so we can avoid iterating over state every time
|
|
46
|
+
let pendingRequestCount = 0
|
|
47
|
+
|
|
48
|
+
const handler: ApiMiddlewareInternalHandler = (action, mwApi) => {
|
|
49
|
+
if (
|
|
50
|
+
queryThunk.pending.match(action) ||
|
|
51
|
+
mutationThunk.pending.match(action)
|
|
52
|
+
) {
|
|
53
|
+
pendingRequestCount++
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (isQueryEnd(action)) {
|
|
57
|
+
pendingRequestCount = Math.max(0, pendingRequestCount - 1)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (isThunkActionWithTags(action)) {
|
|
61
|
+
invalidateTags(
|
|
62
|
+
calculateProvidedByThunk(
|
|
63
|
+
action,
|
|
64
|
+
'invalidatesTags',
|
|
65
|
+
endpointDefinitions,
|
|
66
|
+
assertTagType,
|
|
67
|
+
),
|
|
68
|
+
mwApi,
|
|
69
|
+
)
|
|
70
|
+
} else if (isQueryEnd(action)) {
|
|
71
|
+
invalidateTags([], mwApi)
|
|
72
|
+
} else if (api.util.invalidateTags.match(action)) {
|
|
73
|
+
invalidateTags(
|
|
74
|
+
calculateProvidedBy(
|
|
75
|
+
action.payload,
|
|
76
|
+
undefined,
|
|
77
|
+
undefined,
|
|
78
|
+
undefined,
|
|
79
|
+
undefined,
|
|
80
|
+
assertTagType,
|
|
81
|
+
),
|
|
82
|
+
mwApi,
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function hasPendingRequests() {
|
|
88
|
+
return pendingRequestCount > 0
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function invalidateTags(
|
|
92
|
+
newTags: readonly FullTagDescription<string>[],
|
|
93
|
+
mwApi: SubMiddlewareApi,
|
|
94
|
+
) {
|
|
95
|
+
const rootState = mwApi.getState()
|
|
96
|
+
const state = rootState[reducerPath]
|
|
97
|
+
|
|
98
|
+
pendingTagInvalidations.push(...newTags)
|
|
99
|
+
|
|
100
|
+
if (
|
|
101
|
+
state.config.invalidationBehavior === 'delayed' &&
|
|
102
|
+
hasPendingRequests()
|
|
103
|
+
) {
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const tags = pendingTagInvalidations
|
|
108
|
+
pendingTagInvalidations = []
|
|
109
|
+
if (tags.length === 0) return
|
|
110
|
+
|
|
111
|
+
const toInvalidate = api.util.selectInvalidatedBy(rootState, tags)
|
|
112
|
+
|
|
113
|
+
context.batch(() => {
|
|
114
|
+
const valuesArray = Array.from(toInvalidate.values())
|
|
115
|
+
for (const { queryCacheKey } of valuesArray) {
|
|
116
|
+
const querySubState = state.queries[queryCacheKey]
|
|
117
|
+
const subscriptionSubState = getOrInsertComputed(
|
|
118
|
+
internalState.currentSubscriptions,
|
|
119
|
+
queryCacheKey,
|
|
120
|
+
createNewMap,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
if (querySubState) {
|
|
124
|
+
if (subscriptionSubState.size === 0) {
|
|
125
|
+
mwApi.dispatch(
|
|
126
|
+
removeQueryResult({
|
|
127
|
+
queryCacheKey: queryCacheKey as QueryCacheKey,
|
|
128
|
+
}),
|
|
129
|
+
)
|
|
130
|
+
} else if (querySubState.status !== STATUS_UNINITIALIZED) {
|
|
131
|
+
mwApi.dispatch(refetchQuery(querySubState))
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return handler
|
|
139
|
+
}
|