@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,207 @@
|
|
|
1
|
+
import type { InternalHandlerBuilder, SubscriptionSelectors } from './types'
|
|
2
|
+
import type { SubscriptionInternalState, SubscriptionState } from '../apiState'
|
|
3
|
+
import { produceWithPatches } from '../../utils/immerImports'
|
|
4
|
+
import type { Action } from '@reduxjs/toolkit'
|
|
5
|
+
import { getOrInsertComputed, createNewMap } from '../../utils/getOrInsert'
|
|
6
|
+
|
|
7
|
+
export const buildBatchedActionsHandler: InternalHandlerBuilder<
|
|
8
|
+
[actionShouldContinue: boolean, returnValue: SubscriptionSelectors | boolean]
|
|
9
|
+
> = ({ api, queryThunk, internalState, mwApi }) => {
|
|
10
|
+
const subscriptionsPrefix = `${api.reducerPath}/subscriptions`
|
|
11
|
+
|
|
12
|
+
let previousSubscriptions: SubscriptionState =
|
|
13
|
+
null as unknown as SubscriptionState
|
|
14
|
+
|
|
15
|
+
let updateSyncTimer: ReturnType<typeof window.setTimeout> | null = null
|
|
16
|
+
|
|
17
|
+
const { updateSubscriptionOptions, unsubscribeQueryResult } =
|
|
18
|
+
api.internalActions
|
|
19
|
+
|
|
20
|
+
// Actually intentionally mutate the subscriptions state used in the middleware
|
|
21
|
+
// This is done to speed up perf when loading many components
|
|
22
|
+
const actuallyMutateSubscriptions = (
|
|
23
|
+
currentSubscriptions: SubscriptionInternalState,
|
|
24
|
+
action: Action,
|
|
25
|
+
) => {
|
|
26
|
+
if (updateSubscriptionOptions.match(action)) {
|
|
27
|
+
const { queryCacheKey, requestId, options } = action.payload
|
|
28
|
+
|
|
29
|
+
const sub = currentSubscriptions.get(queryCacheKey)
|
|
30
|
+
if (sub?.has(requestId)) {
|
|
31
|
+
sub.set(requestId, options)
|
|
32
|
+
}
|
|
33
|
+
return true
|
|
34
|
+
}
|
|
35
|
+
if (unsubscribeQueryResult.match(action)) {
|
|
36
|
+
const { queryCacheKey, requestId } = action.payload
|
|
37
|
+
const sub = currentSubscriptions.get(queryCacheKey)
|
|
38
|
+
if (sub) {
|
|
39
|
+
sub.delete(requestId)
|
|
40
|
+
}
|
|
41
|
+
return true
|
|
42
|
+
}
|
|
43
|
+
if (api.internalActions.removeQueryResult.match(action)) {
|
|
44
|
+
currentSubscriptions.delete(action.payload.queryCacheKey)
|
|
45
|
+
return true
|
|
46
|
+
}
|
|
47
|
+
if (queryThunk.pending.match(action)) {
|
|
48
|
+
const {
|
|
49
|
+
meta: { arg, requestId },
|
|
50
|
+
} = action
|
|
51
|
+
const substate = getOrInsertComputed(
|
|
52
|
+
currentSubscriptions,
|
|
53
|
+
arg.queryCacheKey,
|
|
54
|
+
createNewMap,
|
|
55
|
+
)
|
|
56
|
+
if (arg.subscribe) {
|
|
57
|
+
substate.set(
|
|
58
|
+
requestId,
|
|
59
|
+
arg.subscriptionOptions ?? substate.get(requestId) ?? {},
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
return true
|
|
63
|
+
}
|
|
64
|
+
let mutated = false
|
|
65
|
+
|
|
66
|
+
if (queryThunk.rejected.match(action)) {
|
|
67
|
+
const {
|
|
68
|
+
meta: { condition, arg, requestId },
|
|
69
|
+
} = action
|
|
70
|
+
if (condition && arg.subscribe) {
|
|
71
|
+
const substate = getOrInsertComputed(
|
|
72
|
+
currentSubscriptions,
|
|
73
|
+
arg.queryCacheKey,
|
|
74
|
+
createNewMap,
|
|
75
|
+
)
|
|
76
|
+
substate.set(
|
|
77
|
+
requestId,
|
|
78
|
+
arg.subscriptionOptions ?? substate.get(requestId) ?? {},
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
mutated = true
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return mutated
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const getSubscriptions = () => internalState.currentSubscriptions
|
|
89
|
+
const getSubscriptionCount = (queryCacheKey: string) => {
|
|
90
|
+
const subscriptions = getSubscriptions()
|
|
91
|
+
const subscriptionsForQueryArg = subscriptions.get(queryCacheKey)
|
|
92
|
+
return subscriptionsForQueryArg?.size ?? 0
|
|
93
|
+
}
|
|
94
|
+
const isRequestSubscribed = (queryCacheKey: string, requestId: string) => {
|
|
95
|
+
const subscriptions = getSubscriptions()
|
|
96
|
+
return !!subscriptions?.get(queryCacheKey)?.get(requestId)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const subscriptionSelectors: SubscriptionSelectors = {
|
|
100
|
+
getSubscriptions,
|
|
101
|
+
getSubscriptionCount,
|
|
102
|
+
isRequestSubscribed,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function serializeSubscriptions(
|
|
106
|
+
currentSubscriptions: SubscriptionInternalState,
|
|
107
|
+
): SubscriptionState {
|
|
108
|
+
// We now use nested Maps for subscriptions, instead of
|
|
109
|
+
// plain Records. Stringify this accordingly so we can
|
|
110
|
+
// convert it to the shape we need for the store.
|
|
111
|
+
return JSON.parse(
|
|
112
|
+
JSON.stringify(
|
|
113
|
+
Object.fromEntries(
|
|
114
|
+
[...currentSubscriptions].map(([k, v]) => [k, Object.fromEntries(v)]),
|
|
115
|
+
),
|
|
116
|
+
),
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
action,
|
|
122
|
+
mwApi,
|
|
123
|
+
): [
|
|
124
|
+
actionShouldContinue: boolean,
|
|
125
|
+
result: SubscriptionSelectors | boolean,
|
|
126
|
+
] => {
|
|
127
|
+
if (!previousSubscriptions) {
|
|
128
|
+
// Initialize it the first time this handler runs
|
|
129
|
+
previousSubscriptions = serializeSubscriptions(
|
|
130
|
+
internalState.currentSubscriptions,
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (api.util.resetApiState.match(action)) {
|
|
135
|
+
previousSubscriptions = {}
|
|
136
|
+
internalState.currentSubscriptions.clear()
|
|
137
|
+
updateSyncTimer = null
|
|
138
|
+
return [true, false]
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Intercept requests by hooks to see if they're subscribed
|
|
142
|
+
// We return the internal state reference so that hooks
|
|
143
|
+
// can do their own checks to see if they're still active.
|
|
144
|
+
// It's stupid and hacky, but it does cut down on some dispatch calls.
|
|
145
|
+
if (api.internalActions.internal_getRTKQSubscriptions.match(action)) {
|
|
146
|
+
return [false, subscriptionSelectors]
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Update subscription data based on this action
|
|
150
|
+
const didMutate = actuallyMutateSubscriptions(
|
|
151
|
+
internalState.currentSubscriptions,
|
|
152
|
+
action,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
let actionShouldContinue = true
|
|
156
|
+
|
|
157
|
+
// HACK Sneak the test-only polling state back out
|
|
158
|
+
if (
|
|
159
|
+
process.env.NODE_ENV === 'test' &&
|
|
160
|
+
typeof action.type === 'string' &&
|
|
161
|
+
action.type === `${api.reducerPath}/getPolling`
|
|
162
|
+
) {
|
|
163
|
+
return [false, internalState.currentPolls] as any
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (didMutate) {
|
|
167
|
+
if (!updateSyncTimer) {
|
|
168
|
+
// We only use the subscription state for the Redux DevTools at this point,
|
|
169
|
+
// as the real data is kept here in the middleware.
|
|
170
|
+
// Given that, we can throttle synchronizing this state significantly to
|
|
171
|
+
// save on overall perf.
|
|
172
|
+
// In 1.9, it was updated in a microtask, but now we do it at most every 500ms.
|
|
173
|
+
updateSyncTimer = setTimeout(() => {
|
|
174
|
+
// Deep clone the current subscription data
|
|
175
|
+
const newSubscriptions: SubscriptionState = serializeSubscriptions(
|
|
176
|
+
internalState.currentSubscriptions,
|
|
177
|
+
)
|
|
178
|
+
// Figure out a smaller diff between original and current
|
|
179
|
+
const [, patches] = produceWithPatches(
|
|
180
|
+
previousSubscriptions,
|
|
181
|
+
() => newSubscriptions,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
// Sync the store state for visibility
|
|
185
|
+
mwApi.next(api.internalActions.subscriptionsUpdated(patches))
|
|
186
|
+
// Save the cloned state for later reference
|
|
187
|
+
previousSubscriptions = newSubscriptions
|
|
188
|
+
updateSyncTimer = null
|
|
189
|
+
}, 500)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const isSubscriptionSliceAction =
|
|
193
|
+
typeof action.type == 'string' &&
|
|
194
|
+
!!action.type.startsWith(subscriptionsPrefix)
|
|
195
|
+
|
|
196
|
+
const isAdditionalSubscriptionAction =
|
|
197
|
+
queryThunk.rejected.match(action) &&
|
|
198
|
+
action.meta.condition &&
|
|
199
|
+
!!action.meta.arg.subscribe
|
|
200
|
+
|
|
201
|
+
actionShouldContinue =
|
|
202
|
+
!isSubscriptionSliceAction && !isAdditionalSubscriptionAction
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return [actionShouldContinue, false]
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { getEndpointDefinition } from '@internal/query/apiTypes'
|
|
2
|
+
import type { QueryDefinition } from '../../endpointDefinitions'
|
|
3
|
+
import type { ConfigState, QueryCacheKey, QuerySubState } from '../apiState'
|
|
4
|
+
import { isAnyOf } from '../rtkImports'
|
|
5
|
+
import type {
|
|
6
|
+
ApiMiddlewareInternalHandler,
|
|
7
|
+
InternalHandlerBuilder,
|
|
8
|
+
QueryStateMeta,
|
|
9
|
+
SubMiddlewareApi,
|
|
10
|
+
TimeoutId,
|
|
11
|
+
} from './types'
|
|
12
|
+
|
|
13
|
+
export type ReferenceCacheCollection = never
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* // codeblock-meta title="keepUnusedDataFor example"
|
|
19
|
+
* import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
|
|
20
|
+
* interface Post {
|
|
21
|
+
* id: number
|
|
22
|
+
* name: string
|
|
23
|
+
* }
|
|
24
|
+
* type PostsResponse = Post[]
|
|
25
|
+
*
|
|
26
|
+
* const api = createApi({
|
|
27
|
+
* baseQuery: fetchBaseQuery({ baseUrl: '/' }),
|
|
28
|
+
* endpoints: (build) => ({
|
|
29
|
+
* getPosts: build.query<PostsResponse, void>({
|
|
30
|
+
* query: () => 'posts',
|
|
31
|
+
* // highlight-start
|
|
32
|
+
* keepUnusedDataFor: 5
|
|
33
|
+
* // highlight-end
|
|
34
|
+
* })
|
|
35
|
+
* })
|
|
36
|
+
* })
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export type CacheCollectionQueryExtraOptions = {
|
|
40
|
+
/**
|
|
41
|
+
* Overrides the api-wide definition of `keepUnusedDataFor` for this endpoint only. _(This value is in seconds.)_
|
|
42
|
+
*
|
|
43
|
+
* This is how long RTK Query will keep your data cached for **after** the last component unsubscribes. For example, if you query an endpoint, then unmount the component, then mount another component that makes the same request within the given time frame, the most recent value will be served from the cache.
|
|
44
|
+
*/
|
|
45
|
+
keepUnusedDataFor?: number
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Per https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value , browsers store
|
|
49
|
+
// `setTimeout()` timer values in a 32-bit int. If we pass a value in that's larger than that,
|
|
50
|
+
// it wraps and ends up executing immediately.
|
|
51
|
+
// Our `keepUnusedDataFor` values are in seconds, so adjust the numbers here accordingly.
|
|
52
|
+
export const THIRTY_TWO_BIT_MAX_INT = 2_147_483_647
|
|
53
|
+
export const THIRTY_TWO_BIT_MAX_TIMER_SECONDS = 2_147_483_647 / 1_000 - 1
|
|
54
|
+
|
|
55
|
+
export const buildCacheCollectionHandler: InternalHandlerBuilder = ({
|
|
56
|
+
reducerPath,
|
|
57
|
+
api,
|
|
58
|
+
queryThunk,
|
|
59
|
+
context,
|
|
60
|
+
internalState,
|
|
61
|
+
selectors: { selectQueryEntry, selectConfig },
|
|
62
|
+
getRunningQueryThunk,
|
|
63
|
+
mwApi,
|
|
64
|
+
}) => {
|
|
65
|
+
const { removeQueryResult, unsubscribeQueryResult, cacheEntriesUpserted } =
|
|
66
|
+
api.internalActions
|
|
67
|
+
|
|
68
|
+
const canTriggerUnsubscribe = isAnyOf(
|
|
69
|
+
unsubscribeQueryResult.match,
|
|
70
|
+
queryThunk.fulfilled,
|
|
71
|
+
queryThunk.rejected,
|
|
72
|
+
cacheEntriesUpserted.match,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
function anySubscriptionsRemainingForKey(queryCacheKey: string) {
|
|
76
|
+
const subscriptions = internalState.currentSubscriptions.get(queryCacheKey)
|
|
77
|
+
if (!subscriptions) {
|
|
78
|
+
return false
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const hasSubscriptions = subscriptions.size > 0
|
|
82
|
+
return hasSubscriptions
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const currentRemovalTimeouts: QueryStateMeta<TimeoutId> = {}
|
|
86
|
+
|
|
87
|
+
function abortAllPromises<T extends { abort?: () => void }>(
|
|
88
|
+
promiseMap: Map<string, T | undefined>,
|
|
89
|
+
): void {
|
|
90
|
+
for (const promise of promiseMap.values()) {
|
|
91
|
+
promise?.abort?.()
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const handler: ApiMiddlewareInternalHandler = (action, mwApi) => {
|
|
96
|
+
const state = mwApi.getState()
|
|
97
|
+
const config = selectConfig(state)
|
|
98
|
+
|
|
99
|
+
if (canTriggerUnsubscribe(action)) {
|
|
100
|
+
let queryCacheKeys: QueryCacheKey[]
|
|
101
|
+
|
|
102
|
+
if (cacheEntriesUpserted.match(action)) {
|
|
103
|
+
queryCacheKeys = action.payload.map(
|
|
104
|
+
(entry) => entry.queryDescription.queryCacheKey,
|
|
105
|
+
)
|
|
106
|
+
} else {
|
|
107
|
+
const { queryCacheKey } = unsubscribeQueryResult.match(action)
|
|
108
|
+
? action.payload
|
|
109
|
+
: action.meta.arg
|
|
110
|
+
queryCacheKeys = [queryCacheKey]
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
handleUnsubscribeMany(queryCacheKeys, mwApi, config)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (api.util.resetApiState.match(action)) {
|
|
117
|
+
for (const [key, timeout] of Object.entries(currentRemovalTimeouts)) {
|
|
118
|
+
if (timeout) clearTimeout(timeout)
|
|
119
|
+
delete currentRemovalTimeouts[key]
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
abortAllPromises(internalState.runningQueries)
|
|
123
|
+
abortAllPromises(internalState.runningMutations)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (context.hasRehydrationInfo(action)) {
|
|
127
|
+
const { queries } = context.extractRehydrationInfo(action)!
|
|
128
|
+
// Gotcha:
|
|
129
|
+
// If rehydrating before the endpoint has been injected,the global `keepUnusedDataFor`
|
|
130
|
+
// will be used instead of the endpoint-specific one.
|
|
131
|
+
handleUnsubscribeMany(
|
|
132
|
+
Object.keys(queries) as QueryCacheKey[],
|
|
133
|
+
mwApi,
|
|
134
|
+
config,
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function handleUnsubscribeMany(
|
|
140
|
+
cacheKeys: QueryCacheKey[],
|
|
141
|
+
api: SubMiddlewareApi,
|
|
142
|
+
config: ConfigState<string>,
|
|
143
|
+
) {
|
|
144
|
+
const state = api.getState()
|
|
145
|
+
for (const queryCacheKey of cacheKeys) {
|
|
146
|
+
const entry = selectQueryEntry(state, queryCacheKey)
|
|
147
|
+
if (entry?.endpointName) {
|
|
148
|
+
handleUnsubscribe(queryCacheKey, entry.endpointName, api, config)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function handleUnsubscribe(
|
|
154
|
+
queryCacheKey: QueryCacheKey,
|
|
155
|
+
endpointName: string,
|
|
156
|
+
api: SubMiddlewareApi,
|
|
157
|
+
config: ConfigState<string>,
|
|
158
|
+
) {
|
|
159
|
+
const endpointDefinition = getEndpointDefinition(
|
|
160
|
+
context,
|
|
161
|
+
endpointName,
|
|
162
|
+
) as QueryDefinition<any, any, any, any>
|
|
163
|
+
const keepUnusedDataFor =
|
|
164
|
+
endpointDefinition?.keepUnusedDataFor ?? config.keepUnusedDataFor
|
|
165
|
+
|
|
166
|
+
if (keepUnusedDataFor === Infinity) {
|
|
167
|
+
// Hey, user said keep this forever!
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
// Prevent `setTimeout` timers from overflowing a 32-bit internal int, by
|
|
171
|
+
// clamping the max value to be at most 1000ms less than the 32-bit max.
|
|
172
|
+
// Look, a 24.8-day keepalive ought to be enough for anybody, right? :)
|
|
173
|
+
// Also avoid negative values too.
|
|
174
|
+
const finalKeepUnusedDataFor = Math.max(
|
|
175
|
+
0,
|
|
176
|
+
Math.min(keepUnusedDataFor, THIRTY_TWO_BIT_MAX_TIMER_SECONDS),
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
if (!anySubscriptionsRemainingForKey(queryCacheKey)) {
|
|
180
|
+
const currentTimeout = currentRemovalTimeouts[queryCacheKey]
|
|
181
|
+
if (currentTimeout) {
|
|
182
|
+
clearTimeout(currentTimeout)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
currentRemovalTimeouts[queryCacheKey] = setTimeout(() => {
|
|
186
|
+
if (!anySubscriptionsRemainingForKey(queryCacheKey)) {
|
|
187
|
+
// Try to abort any running query for this cache key
|
|
188
|
+
const entry = selectQueryEntry(api.getState(), queryCacheKey)
|
|
189
|
+
|
|
190
|
+
if (entry?.endpointName) {
|
|
191
|
+
const runningQuery = api.dispatch(
|
|
192
|
+
getRunningQueryThunk(entry.endpointName, entry.originalArgs),
|
|
193
|
+
)
|
|
194
|
+
runningQuery?.abort()
|
|
195
|
+
}
|
|
196
|
+
api.dispatch(removeQueryResult({ queryCacheKey }))
|
|
197
|
+
}
|
|
198
|
+
delete currentRemovalTimeouts![queryCacheKey]
|
|
199
|
+
}, finalKeepUnusedDataFor * 1000)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return handler
|
|
204
|
+
}
|