@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,204 @@
|
|
|
1
|
+
import type { Draft } from 'immer'
|
|
2
|
+
import type {
|
|
3
|
+
EntityStateAdapter,
|
|
4
|
+
IdSelector,
|
|
5
|
+
Update,
|
|
6
|
+
EntityId,
|
|
7
|
+
DraftableEntityState,
|
|
8
|
+
} from './models'
|
|
9
|
+
import {
|
|
10
|
+
createStateOperator,
|
|
11
|
+
createSingleArgumentStateOperator,
|
|
12
|
+
} from './state_adapter'
|
|
13
|
+
import {
|
|
14
|
+
selectIdValue,
|
|
15
|
+
ensureEntitiesArray,
|
|
16
|
+
splitAddedUpdatedEntities,
|
|
17
|
+
} from './utils'
|
|
18
|
+
|
|
19
|
+
export function createUnsortedStateAdapter<T, Id extends EntityId>(
|
|
20
|
+
selectId: IdSelector<T, Id>,
|
|
21
|
+
): EntityStateAdapter<T, Id> {
|
|
22
|
+
type R = DraftableEntityState<T, Id>
|
|
23
|
+
|
|
24
|
+
function addOneMutably(entity: T, state: R): void {
|
|
25
|
+
const key = selectIdValue(entity, selectId)
|
|
26
|
+
|
|
27
|
+
if (key in state.entities) {
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
state.ids.push(key as Id & Draft<Id>)
|
|
32
|
+
;(state.entities as Record<Id, T>)[key] = entity
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function addManyMutably(
|
|
36
|
+
newEntities: readonly T[] | Record<Id, T>,
|
|
37
|
+
state: R,
|
|
38
|
+
): void {
|
|
39
|
+
newEntities = ensureEntitiesArray(newEntities)
|
|
40
|
+
|
|
41
|
+
for (const entity of newEntities) {
|
|
42
|
+
addOneMutably(entity, state)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function setOneMutably(entity: T, state: R): void {
|
|
47
|
+
const key = selectIdValue(entity, selectId)
|
|
48
|
+
if (!(key in state.entities)) {
|
|
49
|
+
state.ids.push(key as Id & Draft<Id>)
|
|
50
|
+
}
|
|
51
|
+
;(state.entities as Record<Id, T>)[key] = entity
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function setManyMutably(
|
|
55
|
+
newEntities: readonly T[] | Record<Id, T>,
|
|
56
|
+
state: R,
|
|
57
|
+
): void {
|
|
58
|
+
newEntities = ensureEntitiesArray(newEntities)
|
|
59
|
+
for (const entity of newEntities) {
|
|
60
|
+
setOneMutably(entity, state)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function setAllMutably(
|
|
65
|
+
newEntities: readonly T[] | Record<Id, T>,
|
|
66
|
+
state: R,
|
|
67
|
+
): void {
|
|
68
|
+
newEntities = ensureEntitiesArray(newEntities)
|
|
69
|
+
|
|
70
|
+
state.ids = []
|
|
71
|
+
state.entities = {} as Record<Id, T>
|
|
72
|
+
|
|
73
|
+
addManyMutably(newEntities, state)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function removeOneMutably(key: Id, state: R): void {
|
|
77
|
+
return removeManyMutably([key], state)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function removeManyMutably(keys: readonly Id[], state: R): void {
|
|
81
|
+
let didMutate = false
|
|
82
|
+
|
|
83
|
+
keys.forEach((key) => {
|
|
84
|
+
if (key in state.entities) {
|
|
85
|
+
delete (state.entities as Record<Id, T>)[key]
|
|
86
|
+
didMutate = true
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
if (didMutate) {
|
|
91
|
+
state.ids = (state.ids as Id[]).filter((id) => id in state.entities) as
|
|
92
|
+
| Id[]
|
|
93
|
+
| Draft<Id[]>
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function removeAllMutably(state: R): void {
|
|
98
|
+
Object.assign(state, {
|
|
99
|
+
ids: [],
|
|
100
|
+
entities: {},
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function takeNewKey(
|
|
105
|
+
keys: { [id: string]: Id },
|
|
106
|
+
update: Update<T, Id>,
|
|
107
|
+
state: R,
|
|
108
|
+
): boolean {
|
|
109
|
+
const original: T | undefined = (state.entities as Record<Id, T>)[update.id]
|
|
110
|
+
if (original === undefined) {
|
|
111
|
+
return false
|
|
112
|
+
}
|
|
113
|
+
const updated: T = Object.assign({}, original, update.changes)
|
|
114
|
+
const newKey = selectIdValue(updated, selectId)
|
|
115
|
+
const hasNewKey = newKey !== update.id
|
|
116
|
+
|
|
117
|
+
if (hasNewKey) {
|
|
118
|
+
keys[update.id] = newKey
|
|
119
|
+
delete (state.entities as Record<Id, T>)[update.id]
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
;(state.entities as Record<Id, T>)[newKey] = updated
|
|
123
|
+
|
|
124
|
+
return hasNewKey
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function updateOneMutably(update: Update<T, Id>, state: R): void {
|
|
128
|
+
return updateManyMutably([update], state)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function updateManyMutably(
|
|
132
|
+
updates: ReadonlyArray<Update<T, Id>>,
|
|
133
|
+
state: R,
|
|
134
|
+
): void {
|
|
135
|
+
const newKeys: { [id: string]: Id } = {}
|
|
136
|
+
|
|
137
|
+
const updatesPerEntity: { [id: string]: Update<T, Id> } = {}
|
|
138
|
+
|
|
139
|
+
updates.forEach((update) => {
|
|
140
|
+
// Only apply updates to entities that currently exist
|
|
141
|
+
if (update.id in state.entities) {
|
|
142
|
+
// If there are multiple updates to one entity, merge them together
|
|
143
|
+
updatesPerEntity[update.id] = {
|
|
144
|
+
id: update.id,
|
|
145
|
+
// Spreads ignore falsy values, so this works even if there isn't
|
|
146
|
+
// an existing update already at this key
|
|
147
|
+
changes: {
|
|
148
|
+
...updatesPerEntity[update.id]?.changes,
|
|
149
|
+
...update.changes,
|
|
150
|
+
},
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
updates = Object.values(updatesPerEntity)
|
|
156
|
+
|
|
157
|
+
const didMutateEntities = updates.length > 0
|
|
158
|
+
|
|
159
|
+
if (didMutateEntities) {
|
|
160
|
+
const didMutateIds =
|
|
161
|
+
updates.filter((update) => takeNewKey(newKeys, update, state)).length >
|
|
162
|
+
0
|
|
163
|
+
|
|
164
|
+
if (didMutateIds) {
|
|
165
|
+
state.ids = Object.values(state.entities).map((e) =>
|
|
166
|
+
selectIdValue(e as T, selectId),
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function upsertOneMutably(entity: T, state: R): void {
|
|
173
|
+
return upsertManyMutably([entity], state)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function upsertManyMutably(
|
|
177
|
+
newEntities: readonly T[] | Record<Id, T>,
|
|
178
|
+
state: R,
|
|
179
|
+
): void {
|
|
180
|
+
const [added, updated] = splitAddedUpdatedEntities<T, Id>(
|
|
181
|
+
newEntities,
|
|
182
|
+
selectId,
|
|
183
|
+
state,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
addManyMutably(added, state)
|
|
187
|
+
updateManyMutably(updated, state)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
removeAll: createSingleArgumentStateOperator(removeAllMutably),
|
|
192
|
+
addOne: createStateOperator(addOneMutably),
|
|
193
|
+
addMany: createStateOperator(addManyMutably),
|
|
194
|
+
setOne: createStateOperator(setOneMutably),
|
|
195
|
+
setMany: createStateOperator(setManyMutably),
|
|
196
|
+
setAll: createStateOperator(setAllMutably),
|
|
197
|
+
updateOne: createStateOperator(updateOneMutably),
|
|
198
|
+
updateMany: createStateOperator(updateManyMutably),
|
|
199
|
+
upsertOne: createStateOperator(upsertOneMutably),
|
|
200
|
+
upsertMany: createStateOperator(upsertManyMutably),
|
|
201
|
+
removeOne: createStateOperator(removeOneMutably),
|
|
202
|
+
removeMany: createStateOperator(removeManyMutably),
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { Draft } from 'immer'
|
|
2
|
+
import { current, isDraft } from '../immerImports'
|
|
3
|
+
import type {
|
|
4
|
+
DraftableEntityState,
|
|
5
|
+
EntityId,
|
|
6
|
+
IdSelector,
|
|
7
|
+
Update,
|
|
8
|
+
} from './models'
|
|
9
|
+
|
|
10
|
+
export function selectIdValue<T, Id extends EntityId>(
|
|
11
|
+
entity: T,
|
|
12
|
+
selectId: IdSelector<T, Id>,
|
|
13
|
+
) {
|
|
14
|
+
const key = selectId(entity)
|
|
15
|
+
|
|
16
|
+
if (process.env.NODE_ENV !== 'production' && key === undefined) {
|
|
17
|
+
console.warn(
|
|
18
|
+
'The entity passed to the `selectId` implementation returned undefined.',
|
|
19
|
+
'You should probably provide your own `selectId` implementation.',
|
|
20
|
+
'The entity that was passed:',
|
|
21
|
+
entity,
|
|
22
|
+
'The `selectId` implementation:',
|
|
23
|
+
selectId.toString(),
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return key
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function ensureEntitiesArray<T, Id extends EntityId>(
|
|
31
|
+
entities: readonly T[] | Record<Id, T>,
|
|
32
|
+
): readonly T[] {
|
|
33
|
+
if (!Array.isArray(entities)) {
|
|
34
|
+
entities = Object.values(entities)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return entities
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getCurrent<T>(value: T | Draft<T>): T {
|
|
41
|
+
return (isDraft(value) ? current(value) : value) as T
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function splitAddedUpdatedEntities<T, Id extends EntityId>(
|
|
45
|
+
newEntities: readonly T[] | Record<Id, T>,
|
|
46
|
+
selectId: IdSelector<T, Id>,
|
|
47
|
+
state: DraftableEntityState<T, Id>,
|
|
48
|
+
): [T[], Update<T, Id>[], Id[]] {
|
|
49
|
+
newEntities = ensureEntitiesArray(newEntities)
|
|
50
|
+
|
|
51
|
+
const existingIdsArray = getCurrent(state.ids)
|
|
52
|
+
const existingIds = new Set<Id>(existingIdsArray)
|
|
53
|
+
|
|
54
|
+
const added: T[] = []
|
|
55
|
+
const addedIds = new Set<Id>([])
|
|
56
|
+
const updated: Update<T, Id>[] = []
|
|
57
|
+
|
|
58
|
+
for (const entity of newEntities) {
|
|
59
|
+
const id = selectIdValue(entity, selectId)
|
|
60
|
+
if (existingIds.has(id) || addedIds.has(id)) {
|
|
61
|
+
updated.push({ id, changes: entity })
|
|
62
|
+
} else {
|
|
63
|
+
addedIds.add(id)
|
|
64
|
+
added.push(entity)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return [added, updated, existingIdsArray]
|
|
68
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from React: https://github.com/facebook/react/blob/master/packages/shared/formatProdErrorMessage.js
|
|
3
|
+
*
|
|
4
|
+
* Do not require this module directly! Use normal throw error calls. These messages will be replaced with error codes
|
|
5
|
+
* during build.
|
|
6
|
+
* @param {number} code
|
|
7
|
+
*/
|
|
8
|
+
export function formatProdErrorMessage(code: number) {
|
|
9
|
+
return (
|
|
10
|
+
`Minified Redux Toolkit error #${code}; visit https://redux-toolkit.js.org/Errors?code=${code} for the full message or ` +
|
|
11
|
+
'use the non-minified dev environment for full errors. '
|
|
12
|
+
)
|
|
13
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { StoreEnhancer } from 'redux'
|
|
2
|
+
import type { AutoBatchOptions } from './autoBatchEnhancer'
|
|
3
|
+
import { autoBatchEnhancer } from './autoBatchEnhancer'
|
|
4
|
+
import { Tuple } from './utils'
|
|
5
|
+
import type { Middlewares } from './configureStore'
|
|
6
|
+
import type { ExtractDispatchExtensions } from './tsHelpers'
|
|
7
|
+
|
|
8
|
+
type GetDefaultEnhancersOptions = {
|
|
9
|
+
autoBatch?: boolean | AutoBatchOptions
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type GetDefaultEnhancers<M extends Middlewares<any>> = (
|
|
13
|
+
options?: GetDefaultEnhancersOptions,
|
|
14
|
+
) => Tuple<[StoreEnhancer<{ dispatch: ExtractDispatchExtensions<M> }>]>
|
|
15
|
+
|
|
16
|
+
export const buildGetDefaultEnhancers = <M extends Middlewares<any>>(
|
|
17
|
+
middlewareEnhancer: StoreEnhancer<{ dispatch: ExtractDispatchExtensions<M> }>,
|
|
18
|
+
): GetDefaultEnhancers<M> =>
|
|
19
|
+
function getDefaultEnhancers(options) {
|
|
20
|
+
const { autoBatch = true } = options ?? {}
|
|
21
|
+
|
|
22
|
+
let enhancerArray = new Tuple<StoreEnhancer[]>(middlewareEnhancer)
|
|
23
|
+
if (autoBatch) {
|
|
24
|
+
enhancerArray.push(
|
|
25
|
+
autoBatchEnhancer(
|
|
26
|
+
typeof autoBatch === 'object' ? autoBatch : undefined,
|
|
27
|
+
),
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
return enhancerArray as any
|
|
31
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { Middleware, UnknownAction } from 'redux'
|
|
2
|
+
import type { ThunkMiddleware } from 'redux-thunk'
|
|
3
|
+
import { thunk as thunkMiddleware, withExtraArgument } from 'redux-thunk'
|
|
4
|
+
import type { ActionCreatorInvariantMiddlewareOptions } from './actionCreatorInvariantMiddleware'
|
|
5
|
+
import { createActionCreatorInvariantMiddleware } from './actionCreatorInvariantMiddleware'
|
|
6
|
+
import type { ImmutableStateInvariantMiddlewareOptions } from './immutableStateInvariantMiddleware'
|
|
7
|
+
/* PROD_START_REMOVE_UMD */
|
|
8
|
+
import { createImmutableStateInvariantMiddleware } from './immutableStateInvariantMiddleware'
|
|
9
|
+
/* PROD_STOP_REMOVE_UMD */
|
|
10
|
+
|
|
11
|
+
import type { SerializableStateInvariantMiddlewareOptions } from './serializableStateInvariantMiddleware'
|
|
12
|
+
import { createSerializableStateInvariantMiddleware } from './serializableStateInvariantMiddleware'
|
|
13
|
+
import type { ExcludeFromTuple } from './tsHelpers'
|
|
14
|
+
import { Tuple } from './utils'
|
|
15
|
+
|
|
16
|
+
function isBoolean(x: any): x is boolean {
|
|
17
|
+
return typeof x === 'boolean'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface ThunkOptions<E = any> {
|
|
21
|
+
extraArgument: E
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface GetDefaultMiddlewareOptions {
|
|
25
|
+
thunk?: boolean | ThunkOptions
|
|
26
|
+
immutableCheck?: boolean | ImmutableStateInvariantMiddlewareOptions
|
|
27
|
+
serializableCheck?: boolean | SerializableStateInvariantMiddlewareOptions
|
|
28
|
+
actionCreatorCheck?: boolean | ActionCreatorInvariantMiddlewareOptions
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type ThunkMiddlewareFor<
|
|
32
|
+
S,
|
|
33
|
+
O extends GetDefaultMiddlewareOptions = {},
|
|
34
|
+
> = O extends {
|
|
35
|
+
thunk: false
|
|
36
|
+
}
|
|
37
|
+
? never
|
|
38
|
+
: O extends { thunk: { extraArgument: infer E } }
|
|
39
|
+
? ThunkMiddleware<S, UnknownAction, E>
|
|
40
|
+
: ThunkMiddleware<S, UnknownAction>
|
|
41
|
+
|
|
42
|
+
export type GetDefaultMiddleware<S = any> = <
|
|
43
|
+
O extends GetDefaultMiddlewareOptions = {
|
|
44
|
+
thunk: true
|
|
45
|
+
immutableCheck: true
|
|
46
|
+
serializableCheck: true
|
|
47
|
+
actionCreatorCheck: true
|
|
48
|
+
},
|
|
49
|
+
>(
|
|
50
|
+
options?: O,
|
|
51
|
+
) => Tuple<ExcludeFromTuple<[ThunkMiddlewareFor<S, O>], never>>
|
|
52
|
+
|
|
53
|
+
export const buildGetDefaultMiddleware = <S = any>(): GetDefaultMiddleware<S> =>
|
|
54
|
+
function getDefaultMiddleware(options) {
|
|
55
|
+
const {
|
|
56
|
+
thunk = true,
|
|
57
|
+
immutableCheck = true,
|
|
58
|
+
serializableCheck = true,
|
|
59
|
+
actionCreatorCheck = true,
|
|
60
|
+
} = options ?? {}
|
|
61
|
+
|
|
62
|
+
let middlewareArray = new Tuple<Middleware[]>()
|
|
63
|
+
|
|
64
|
+
if (thunk) {
|
|
65
|
+
if (isBoolean(thunk)) {
|
|
66
|
+
middlewareArray.push(thunkMiddleware)
|
|
67
|
+
} else {
|
|
68
|
+
middlewareArray.push(withExtraArgument(thunk.extraArgument))
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
73
|
+
if (immutableCheck) {
|
|
74
|
+
/* PROD_START_REMOVE_UMD */
|
|
75
|
+
let immutableOptions: ImmutableStateInvariantMiddlewareOptions = {}
|
|
76
|
+
|
|
77
|
+
if (!isBoolean(immutableCheck)) {
|
|
78
|
+
immutableOptions = immutableCheck
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
middlewareArray.unshift(
|
|
82
|
+
createImmutableStateInvariantMiddleware(immutableOptions),
|
|
83
|
+
)
|
|
84
|
+
/* PROD_STOP_REMOVE_UMD */
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (serializableCheck) {
|
|
88
|
+
let serializableOptions: SerializableStateInvariantMiddlewareOptions =
|
|
89
|
+
{}
|
|
90
|
+
|
|
91
|
+
if (!isBoolean(serializableCheck)) {
|
|
92
|
+
serializableOptions = serializableCheck
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
middlewareArray.push(
|
|
96
|
+
createSerializableStateInvariantMiddleware(serializableOptions),
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
if (actionCreatorCheck) {
|
|
100
|
+
let actionCreatorOptions: ActionCreatorInvariantMiddlewareOptions = {}
|
|
101
|
+
|
|
102
|
+
if (!isBoolean(actionCreatorCheck)) {
|
|
103
|
+
actionCreatorOptions = actionCreatorCheck
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
middlewareArray.unshift(
|
|
107
|
+
createActionCreatorInvariantMiddleware(actionCreatorOptions),
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return middlewareArray as any
|
|
113
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import type { Middleware } from 'redux'
|
|
2
|
+
import type { IgnoredPaths } from './serializableStateInvariantMiddleware'
|
|
3
|
+
import { getTimeMeasureUtils } from './utils'
|
|
4
|
+
|
|
5
|
+
type EntryProcessor = (key: string, value: any) => any
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The default `isImmutable` function.
|
|
9
|
+
*
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export function isImmutableDefault(value: unknown): boolean {
|
|
13
|
+
return typeof value !== 'object' || value == null || Object.isFrozen(value)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function trackForMutations(
|
|
17
|
+
isImmutable: IsImmutableFunc,
|
|
18
|
+
ignoredPaths: IgnoredPaths | undefined,
|
|
19
|
+
obj: any,
|
|
20
|
+
) {
|
|
21
|
+
const trackedProperties = trackProperties(isImmutable, ignoredPaths, obj)
|
|
22
|
+
return {
|
|
23
|
+
detectMutations() {
|
|
24
|
+
return detectMutations(isImmutable, ignoredPaths, trackedProperties, obj)
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface TrackedProperty {
|
|
30
|
+
value: any
|
|
31
|
+
children: Record<string, any>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function trackProperties(
|
|
35
|
+
isImmutable: IsImmutableFunc,
|
|
36
|
+
ignoredPaths: IgnoredPaths = [],
|
|
37
|
+
obj: Record<string, any>,
|
|
38
|
+
path: string = '',
|
|
39
|
+
checkedObjects: Set<Record<string, any>> = new Set(),
|
|
40
|
+
) {
|
|
41
|
+
const tracked: Partial<TrackedProperty> = { value: obj }
|
|
42
|
+
|
|
43
|
+
if (!isImmutable(obj) && !checkedObjects.has(obj)) {
|
|
44
|
+
checkedObjects.add(obj)
|
|
45
|
+
tracked.children = {}
|
|
46
|
+
|
|
47
|
+
const hasIgnoredPaths = ignoredPaths.length > 0
|
|
48
|
+
|
|
49
|
+
for (const key in obj) {
|
|
50
|
+
const nestedPath = path ? path + '.' + key : key
|
|
51
|
+
|
|
52
|
+
if (hasIgnoredPaths) {
|
|
53
|
+
const hasMatches = ignoredPaths.some((ignored) => {
|
|
54
|
+
if (ignored instanceof RegExp) {
|
|
55
|
+
return ignored.test(nestedPath)
|
|
56
|
+
}
|
|
57
|
+
return nestedPath === ignored
|
|
58
|
+
})
|
|
59
|
+
if (hasMatches) {
|
|
60
|
+
continue
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
tracked.children[key] = trackProperties(
|
|
65
|
+
isImmutable,
|
|
66
|
+
ignoredPaths,
|
|
67
|
+
obj[key],
|
|
68
|
+
nestedPath,
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return tracked as TrackedProperty
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function detectMutations(
|
|
76
|
+
isImmutable: IsImmutableFunc,
|
|
77
|
+
ignoredPaths: IgnoredPaths = [],
|
|
78
|
+
trackedProperty: TrackedProperty,
|
|
79
|
+
obj: any,
|
|
80
|
+
sameParentRef: boolean = false,
|
|
81
|
+
path: string = '',
|
|
82
|
+
): { wasMutated: boolean; path?: string } {
|
|
83
|
+
const prevObj = trackedProperty ? trackedProperty.value : undefined
|
|
84
|
+
|
|
85
|
+
const sameRef = prevObj === obj
|
|
86
|
+
|
|
87
|
+
if (sameParentRef && !sameRef && !Number.isNaN(obj)) {
|
|
88
|
+
return { wasMutated: true, path }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (isImmutable(prevObj) || isImmutable(obj)) {
|
|
92
|
+
return { wasMutated: false }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Gather all keys from prev (tracked) and after objs
|
|
96
|
+
const keysToDetect: Record<string, boolean> = {}
|
|
97
|
+
for (let key in trackedProperty.children) {
|
|
98
|
+
keysToDetect[key] = true
|
|
99
|
+
}
|
|
100
|
+
for (let key in obj) {
|
|
101
|
+
keysToDetect[key] = true
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const hasIgnoredPaths = ignoredPaths.length > 0
|
|
105
|
+
|
|
106
|
+
for (let key in keysToDetect) {
|
|
107
|
+
const nestedPath = path ? path + '.' + key : key
|
|
108
|
+
|
|
109
|
+
if (hasIgnoredPaths) {
|
|
110
|
+
const hasMatches = ignoredPaths.some((ignored) => {
|
|
111
|
+
if (ignored instanceof RegExp) {
|
|
112
|
+
return ignored.test(nestedPath)
|
|
113
|
+
}
|
|
114
|
+
return nestedPath === ignored
|
|
115
|
+
})
|
|
116
|
+
if (hasMatches) {
|
|
117
|
+
continue
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const result = detectMutations(
|
|
122
|
+
isImmutable,
|
|
123
|
+
ignoredPaths,
|
|
124
|
+
trackedProperty.children[key],
|
|
125
|
+
obj[key],
|
|
126
|
+
sameRef,
|
|
127
|
+
nestedPath,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
if (result.wasMutated) {
|
|
131
|
+
return result
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return { wasMutated: false }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
type IsImmutableFunc = (value: any) => boolean
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Options for `createImmutableStateInvariantMiddleware()`.
|
|
141
|
+
*
|
|
142
|
+
* @public
|
|
143
|
+
*/
|
|
144
|
+
export interface ImmutableStateInvariantMiddlewareOptions {
|
|
145
|
+
/**
|
|
146
|
+
Callback function to check if a value is considered to be immutable.
|
|
147
|
+
This function is applied recursively to every value contained in the state.
|
|
148
|
+
The default implementation will return true for primitive types
|
|
149
|
+
(like numbers, strings, booleans, null and undefined).
|
|
150
|
+
*/
|
|
151
|
+
isImmutable?: IsImmutableFunc
|
|
152
|
+
/**
|
|
153
|
+
An array of dot-separated path strings that match named nodes from
|
|
154
|
+
the root state to ignore when checking for immutability.
|
|
155
|
+
Defaults to undefined
|
|
156
|
+
*/
|
|
157
|
+
ignoredPaths?: IgnoredPaths
|
|
158
|
+
/** Print a warning if checks take longer than N ms. Default: 32ms */
|
|
159
|
+
warnAfter?: number
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Creates a middleware that checks whether any state was mutated in between
|
|
164
|
+
* dispatches or during a dispatch. If any mutations are detected, an error is
|
|
165
|
+
* thrown.
|
|
166
|
+
*
|
|
167
|
+
* @param options Middleware options.
|
|
168
|
+
*
|
|
169
|
+
* @public
|
|
170
|
+
*/
|
|
171
|
+
export function createImmutableStateInvariantMiddleware(
|
|
172
|
+
options: ImmutableStateInvariantMiddlewareOptions = {},
|
|
173
|
+
): Middleware {
|
|
174
|
+
if (process.env.NODE_ENV === 'production') {
|
|
175
|
+
return () => (next) => (action) => next(action)
|
|
176
|
+
} else {
|
|
177
|
+
function stringify(
|
|
178
|
+
obj: any,
|
|
179
|
+
serializer?: EntryProcessor,
|
|
180
|
+
indent?: string | number,
|
|
181
|
+
decycler?: EntryProcessor,
|
|
182
|
+
): string {
|
|
183
|
+
return JSON.stringify(obj, getSerialize(serializer, decycler), indent)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function getSerialize(
|
|
187
|
+
serializer?: EntryProcessor,
|
|
188
|
+
decycler?: EntryProcessor,
|
|
189
|
+
): EntryProcessor {
|
|
190
|
+
let stack: any[] = [],
|
|
191
|
+
keys: any[] = []
|
|
192
|
+
|
|
193
|
+
if (!decycler)
|
|
194
|
+
decycler = function (_: string, value: any) {
|
|
195
|
+
if (stack[0] === value) return '[Circular ~]'
|
|
196
|
+
return (
|
|
197
|
+
'[Circular ~.' + keys.slice(0, stack.indexOf(value)).join('.') + ']'
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return function (this: any, key: string, value: any) {
|
|
202
|
+
if (stack.length > 0) {
|
|
203
|
+
var thisPos = stack.indexOf(this)
|
|
204
|
+
~thisPos ? stack.splice(thisPos + 1) : stack.push(this)
|
|
205
|
+
~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key)
|
|
206
|
+
if (~stack.indexOf(value)) value = decycler!.call(this, key, value)
|
|
207
|
+
} else stack.push(value)
|
|
208
|
+
|
|
209
|
+
return serializer == null ? value : serializer.call(this, key, value)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
let {
|
|
214
|
+
isImmutable = isImmutableDefault,
|
|
215
|
+
ignoredPaths,
|
|
216
|
+
warnAfter = 32,
|
|
217
|
+
} = options
|
|
218
|
+
|
|
219
|
+
const track = trackForMutations.bind(null, isImmutable, ignoredPaths)
|
|
220
|
+
|
|
221
|
+
return ({ getState }) => {
|
|
222
|
+
let state = getState()
|
|
223
|
+
let tracker = track(state)
|
|
224
|
+
|
|
225
|
+
let result
|
|
226
|
+
return (next) => (action) => {
|
|
227
|
+
const measureUtils = getTimeMeasureUtils(
|
|
228
|
+
warnAfter,
|
|
229
|
+
'ImmutableStateInvariantMiddleware',
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
measureUtils.measureTime(() => {
|
|
233
|
+
state = getState()
|
|
234
|
+
|
|
235
|
+
result = tracker.detectMutations()
|
|
236
|
+
// Track before potentially not meeting the invariant
|
|
237
|
+
tracker = track(state)
|
|
238
|
+
|
|
239
|
+
if (result.wasMutated) {
|
|
240
|
+
throw new Error(
|
|
241
|
+
`A state mutation was detected between dispatches, in the path '${
|
|
242
|
+
result.path || ''
|
|
243
|
+
}'. This may cause incorrect behavior. (https://redux.js.org/style-guide/style-guide#do-not-mutate-state)`,
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
const dispatchedAction = next(action)
|
|
249
|
+
|
|
250
|
+
measureUtils.measureTime(() => {
|
|
251
|
+
state = getState()
|
|
252
|
+
|
|
253
|
+
result = tracker.detectMutations()
|
|
254
|
+
// Track before potentially not meeting the invariant
|
|
255
|
+
tracker = track(state)
|
|
256
|
+
|
|
257
|
+
if (result.wasMutated) {
|
|
258
|
+
throw new Error(
|
|
259
|
+
`A state mutation was detected inside a dispatch, in the path: ${
|
|
260
|
+
result.path || ''
|
|
261
|
+
}. Take a look at the reducer(s) handling the action ${stringify(
|
|
262
|
+
action,
|
|
263
|
+
)}. (https://redux.js.org/style-guide/style-guide#do-not-mutate-state)`,
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
measureUtils.warnIfExceeded()
|
|
269
|
+
|
|
270
|
+
return dispatchedAction
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|