@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.
Files changed (173) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +33 -0
  3. package/changes.json +18 -0
  4. package/dist/cjs/index.js +6 -0
  5. package/dist/cjs/redux-toolkit.development.cjs +2406 -0
  6. package/dist/cjs/redux-toolkit.development.cjs.map +1 -0
  7. package/dist/cjs/redux-toolkit.production.min.cjs +3 -0
  8. package/dist/cjs/redux-toolkit.production.min.cjs.map +1 -0
  9. package/dist/index.d.mts +2665 -0
  10. package/dist/index.d.ts +2665 -0
  11. package/dist/query/cjs/index.js +6 -0
  12. package/dist/query/cjs/rtk-query.development.cjs +3092 -0
  13. package/dist/query/cjs/rtk-query.development.cjs.map +1 -0
  14. package/dist/query/cjs/rtk-query.production.min.cjs +2 -0
  15. package/dist/query/cjs/rtk-query.production.min.cjs.map +1 -0
  16. package/dist/query/index.d.mts +3066 -0
  17. package/dist/query/index.d.ts +3066 -0
  18. package/dist/query/react/cjs/index.js +6 -0
  19. package/dist/query/react/cjs/rtk-query-react.development.cjs +748 -0
  20. package/dist/query/react/cjs/rtk-query-react.development.cjs.map +1 -0
  21. package/dist/query/react/cjs/rtk-query-react.production.min.cjs +2 -0
  22. package/dist/query/react/cjs/rtk-query-react.production.min.cjs.map +1 -0
  23. package/dist/query/react/index.d.mts +1009 -0
  24. package/dist/query/react/index.d.ts +1009 -0
  25. package/dist/query/react/rtk-query-react.browser.mjs +2 -0
  26. package/dist/query/react/rtk-query-react.browser.mjs.map +1 -0
  27. package/dist/query/react/rtk-query-react.legacy-esm.js +740 -0
  28. package/dist/query/react/rtk-query-react.legacy-esm.js.map +1 -0
  29. package/dist/query/react/rtk-query-react.modern.mjs +705 -0
  30. package/dist/query/react/rtk-query-react.modern.mjs.map +1 -0
  31. package/dist/query/rtk-query.browser.mjs +2 -0
  32. package/dist/query/rtk-query.browser.mjs.map +1 -0
  33. package/dist/query/rtk-query.legacy-esm.js +3117 -0
  34. package/dist/query/rtk-query.legacy-esm.js.map +1 -0
  35. package/dist/query/rtk-query.modern.mjs +3052 -0
  36. package/dist/query/rtk-query.modern.mjs.map +1 -0
  37. package/dist/react/cjs/index.js +6 -0
  38. package/dist/react/cjs/redux-toolkit-react.development.cjs +55 -0
  39. package/dist/react/cjs/redux-toolkit-react.development.cjs.map +1 -0
  40. package/dist/react/cjs/redux-toolkit-react.production.min.cjs +2 -0
  41. package/dist/react/cjs/redux-toolkit-react.production.min.cjs.map +1 -0
  42. package/dist/react/index.d.mts +22 -0
  43. package/dist/react/index.d.ts +22 -0
  44. package/dist/react/redux-toolkit-react.browser.mjs +2 -0
  45. package/dist/react/redux-toolkit-react.browser.mjs.map +1 -0
  46. package/dist/react/redux-toolkit-react.legacy-esm.js +47 -0
  47. package/dist/react/redux-toolkit-react.legacy-esm.js.map +1 -0
  48. package/dist/react/redux-toolkit-react.modern.mjs +28 -0
  49. package/dist/react/redux-toolkit-react.modern.mjs.map +1 -0
  50. package/dist/redux-toolkit.browser.mjs +3 -0
  51. package/dist/redux-toolkit.browser.mjs.map +1 -0
  52. package/dist/redux-toolkit.legacy-esm.js +2351 -0
  53. package/dist/redux-toolkit.legacy-esm.js.map +1 -0
  54. package/dist/redux-toolkit.modern.mjs +2330 -0
  55. package/dist/redux-toolkit.modern.mjs.map +1 -0
  56. package/dist/uncheckedindexed.ts +16 -0
  57. package/package.json +310 -0
  58. package/query/package.json +13 -0
  59. package/query/react/package.json +13 -0
  60. package/react/package.json +13 -0
  61. package/skills/build-modern-redux-apps/modern-redux/SKILL.md +304 -0
  62. package/skills/build-modern-redux-apps/modern-redux/references/store-lifetime.md +53 -0
  63. package/skills/build-modern-redux-apps/redux-dataflow/SKILL.md +264 -0
  64. package/skills/evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps/SKILL.md +269 -0
  65. package/skills/evolve-and-diagnose-redux-apps/migrate-to-modern-redux/SKILL.md +226 -0
  66. package/skills/manage-server-data/adopt-rtk-query/SKILL.md +385 -0
  67. package/skills/manage-server-data/adopt-rtk-query/references/endpoint-lifecycle.md +36 -0
  68. package/skills/model-redux-state/build-slices-and-selectors/SKILL.md +364 -0
  69. package/skills/model-redux-state/build-slices-and-selectors/references/slice-patterns.md +59 -0
  70. package/skills/model-redux-state/design-state-ownership/SKILL.md +322 -0
  71. package/skills/model-redux-state/design-state-ownership/references/state-ownership.md +28 -0
  72. package/skills/orchestrate-side-effects/handle-side-effects/SKILL.md +271 -0
  73. package/skills/orchestrate-side-effects/handle-side-effects/references/listener-workflows.md +34 -0
  74. package/src/actionCreatorInvariantMiddleware.ts +34 -0
  75. package/src/autoBatchEnhancer.ts +146 -0
  76. package/src/combineSlices.ts +487 -0
  77. package/src/configureStore.ts +248 -0
  78. package/src/createAction.ts +324 -0
  79. package/src/createAsyncThunk.ts +791 -0
  80. package/src/createDraftSafeSelector.ts +30 -0
  81. package/src/createReducer.ts +217 -0
  82. package/src/createSlice.ts +1079 -0
  83. package/src/devtoolsExtension.ts +241 -0
  84. package/src/dynamicMiddleware/index.ts +93 -0
  85. package/src/dynamicMiddleware/react/index.ts +101 -0
  86. package/src/dynamicMiddleware/types.ts +82 -0
  87. package/src/entities/create_adapter.ts +47 -0
  88. package/src/entities/entity_state.ts +38 -0
  89. package/src/entities/index.ts +8 -0
  90. package/src/entities/models.ts +198 -0
  91. package/src/entities/sorted_state_adapter.ts +266 -0
  92. package/src/entities/state_adapter.ts +58 -0
  93. package/src/entities/state_selectors.ts +73 -0
  94. package/src/entities/unsorted_state_adapter.ts +204 -0
  95. package/src/entities/utils.ts +68 -0
  96. package/src/formatProdErrorMessage.ts +13 -0
  97. package/src/getDefaultEnhancers.ts +31 -0
  98. package/src/getDefaultMiddleware.ts +113 -0
  99. package/src/immerImports.ts +7 -0
  100. package/src/immutableStateInvariantMiddleware.ts +274 -0
  101. package/src/index.ts +213 -0
  102. package/src/listenerMiddleware/exceptions.ts +20 -0
  103. package/src/listenerMiddleware/index.ts +562 -0
  104. package/src/listenerMiddleware/task.ts +100 -0
  105. package/src/listenerMiddleware/types.ts +886 -0
  106. package/src/listenerMiddleware/utils.ts +30 -0
  107. package/src/mapBuilders.ts +298 -0
  108. package/src/matchers.ts +365 -0
  109. package/src/nanoid.ts +20 -0
  110. package/src/query/HandledError.ts +6 -0
  111. package/src/query/apiTypes.ts +120 -0
  112. package/src/query/baseQueryTypes.ts +101 -0
  113. package/src/query/core/apiState.ts +377 -0
  114. package/src/query/core/buildInitiate.ts +596 -0
  115. package/src/query/core/buildMiddleware/batchActions.ts +207 -0
  116. package/src/query/core/buildMiddleware/cacheCollection.ts +204 -0
  117. package/src/query/core/buildMiddleware/cacheLifecycle.ts +379 -0
  118. package/src/query/core/buildMiddleware/devMiddleware.ts +34 -0
  119. package/src/query/core/buildMiddleware/index.ts +162 -0
  120. package/src/query/core/buildMiddleware/invalidationByTags.ts +139 -0
  121. package/src/query/core/buildMiddleware/polling.ts +187 -0
  122. package/src/query/core/buildMiddleware/queryLifecycle.ts +508 -0
  123. package/src/query/core/buildMiddleware/types.ts +173 -0
  124. package/src/query/core/buildMiddleware/windowEventHandling.ts +64 -0
  125. package/src/query/core/buildSelectors.ts +413 -0
  126. package/src/query/core/buildSlice.ts +735 -0
  127. package/src/query/core/buildThunks.ts +1124 -0
  128. package/src/query/core/index.ts +58 -0
  129. package/src/query/core/module.ts +727 -0
  130. package/src/query/core/rtkImports.ts +24 -0
  131. package/src/query/core/setupListeners.ts +118 -0
  132. package/src/query/createApi.ts +506 -0
  133. package/src/query/defaultSerializeQueryArgs.ts +52 -0
  134. package/src/query/endpointDefinitions.ts +1717 -0
  135. package/src/query/fakeBaseQuery.ts +21 -0
  136. package/src/query/fetchBaseQuery.ts +385 -0
  137. package/src/query/index.ts +105 -0
  138. package/src/query/react/ApiProvider.tsx +69 -0
  139. package/src/query/react/buildHooks.ts +2297 -0
  140. package/src/query/react/constants.ts +2 -0
  141. package/src/query/react/index.ts +45 -0
  142. package/src/query/react/module.ts +275 -0
  143. package/src/query/react/namedHooks.ts +59 -0
  144. package/src/query/react/reactImports.ts +10 -0
  145. package/src/query/react/reactReduxImports.ts +1 -0
  146. package/src/query/react/rtkqImports.ts +8 -0
  147. package/src/query/react/useSerializedStableValue.ts +17 -0
  148. package/src/query/react/useShallowStableValue.ts +13 -0
  149. package/src/query/retry.ts +233 -0
  150. package/src/query/standardSchema.ts +35 -0
  151. package/src/query/tsHelpers.ts +48 -0
  152. package/src/query/utils/capitalize.ts +3 -0
  153. package/src/query/utils/copyWithStructuralSharing.ts +27 -0
  154. package/src/query/utils/countObjectKeys.ts +14 -0
  155. package/src/query/utils/filterMap.ts +27 -0
  156. package/src/query/utils/getCurrent.ts +6 -0
  157. package/src/query/utils/getOrInsert.ts +41 -0
  158. package/src/query/utils/immerImports.ts +9 -0
  159. package/src/query/utils/index.ts +11 -0
  160. package/src/query/utils/isAbsoluteUrl.ts +9 -0
  161. package/src/query/utils/isDocumentVisible.ts +12 -0
  162. package/src/query/utils/isNotNullish.ts +7 -0
  163. package/src/query/utils/isOnline.ts +12 -0
  164. package/src/query/utils/isValidUrl.ts +9 -0
  165. package/src/query/utils/joinUrls.ts +26 -0
  166. package/src/query/utils/signals.ts +33 -0
  167. package/src/react/index.ts +7 -0
  168. package/src/reduxImports.ts +8 -0
  169. package/src/reselectImports.ts +1 -0
  170. package/src/serializableStateInvariantMiddleware.ts +285 -0
  171. package/src/tsHelpers.ts +214 -0
  172. package/src/uncheckedindexed.ts +16 -0
  173. 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
+ }