@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,198 @@
|
|
|
1
|
+
import type { Draft } from 'immer'
|
|
2
|
+
import type { PayloadAction } from '../createAction'
|
|
3
|
+
import type { CastAny, Id } from '../tsHelpers'
|
|
4
|
+
import type { UncheckedIndexedAccess } from '../uncheckedindexed.js'
|
|
5
|
+
import type { GetSelectorsOptions } from './state_selectors'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
export type EntityId = number | string
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
export type Comparer<T> = (a: T, b: T) => number
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export type IdSelector<T, Id extends EntityId> = (model: T) => Id
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @public
|
|
24
|
+
*/
|
|
25
|
+
export type Update<T, Id extends EntityId> = { id: Id; changes: Partial<T> }
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
export interface EntityState<T, Id extends EntityId> {
|
|
31
|
+
ids: Id[]
|
|
32
|
+
entities: Record<Id, T>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @public
|
|
37
|
+
*/
|
|
38
|
+
export interface EntityAdapterOptions<T, Id extends EntityId> {
|
|
39
|
+
selectId?: IdSelector<T, Id>
|
|
40
|
+
sortComparer?: false | Comparer<T>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type PreventAny<S, T, Id extends EntityId> = CastAny<
|
|
44
|
+
S,
|
|
45
|
+
EntityState<T, Id>
|
|
46
|
+
>
|
|
47
|
+
|
|
48
|
+
export type DraftableEntityState<T, Id extends EntityId> =
|
|
49
|
+
| EntityState<T, Id>
|
|
50
|
+
| Draft<EntityState<T, Id>>
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @public
|
|
54
|
+
*/
|
|
55
|
+
export interface EntityStateAdapter<T, Id extends EntityId> {
|
|
56
|
+
addOne<S extends DraftableEntityState<T, Id>>(
|
|
57
|
+
state: PreventAny<S, T, Id>,
|
|
58
|
+
entity: T,
|
|
59
|
+
): S
|
|
60
|
+
addOne<S extends DraftableEntityState<T, Id>>(
|
|
61
|
+
state: PreventAny<S, T, Id>,
|
|
62
|
+
action: PayloadAction<T>,
|
|
63
|
+
): S
|
|
64
|
+
|
|
65
|
+
addMany<S extends DraftableEntityState<T, Id>>(
|
|
66
|
+
state: PreventAny<S, T, Id>,
|
|
67
|
+
entities: readonly T[] | Record<Id, T>,
|
|
68
|
+
): S
|
|
69
|
+
addMany<S extends DraftableEntityState<T, Id>>(
|
|
70
|
+
state: PreventAny<S, T, Id>,
|
|
71
|
+
entities: PayloadAction<readonly T[] | Record<Id, T>>,
|
|
72
|
+
): S
|
|
73
|
+
|
|
74
|
+
setOne<S extends DraftableEntityState<T, Id>>(
|
|
75
|
+
state: PreventAny<S, T, Id>,
|
|
76
|
+
entity: T,
|
|
77
|
+
): S
|
|
78
|
+
setOne<S extends DraftableEntityState<T, Id>>(
|
|
79
|
+
state: PreventAny<S, T, Id>,
|
|
80
|
+
action: PayloadAction<T>,
|
|
81
|
+
): S
|
|
82
|
+
setMany<S extends DraftableEntityState<T, Id>>(
|
|
83
|
+
state: PreventAny<S, T, Id>,
|
|
84
|
+
entities: readonly T[] | Record<Id, T>,
|
|
85
|
+
): S
|
|
86
|
+
setMany<S extends DraftableEntityState<T, Id>>(
|
|
87
|
+
state: PreventAny<S, T, Id>,
|
|
88
|
+
entities: PayloadAction<readonly T[] | Record<Id, T>>,
|
|
89
|
+
): S
|
|
90
|
+
setAll<S extends DraftableEntityState<T, Id>>(
|
|
91
|
+
state: PreventAny<S, T, Id>,
|
|
92
|
+
entities: readonly T[] | Record<Id, T>,
|
|
93
|
+
): S
|
|
94
|
+
setAll<S extends DraftableEntityState<T, Id>>(
|
|
95
|
+
state: PreventAny<S, T, Id>,
|
|
96
|
+
entities: PayloadAction<readonly T[] | Record<Id, T>>,
|
|
97
|
+
): S
|
|
98
|
+
|
|
99
|
+
removeOne<S extends DraftableEntityState<T, Id>>(
|
|
100
|
+
state: PreventAny<S, T, Id>,
|
|
101
|
+
key: Id,
|
|
102
|
+
): S
|
|
103
|
+
removeOne<S extends DraftableEntityState<T, Id>>(
|
|
104
|
+
state: PreventAny<S, T, Id>,
|
|
105
|
+
key: PayloadAction<Id>,
|
|
106
|
+
): S
|
|
107
|
+
|
|
108
|
+
removeMany<S extends DraftableEntityState<T, Id>>(
|
|
109
|
+
state: PreventAny<S, T, Id>,
|
|
110
|
+
keys: readonly Id[],
|
|
111
|
+
): S
|
|
112
|
+
removeMany<S extends DraftableEntityState<T, Id>>(
|
|
113
|
+
state: PreventAny<S, T, Id>,
|
|
114
|
+
keys: PayloadAction<readonly Id[]>,
|
|
115
|
+
): S
|
|
116
|
+
|
|
117
|
+
removeAll<S extends DraftableEntityState<T, Id>>(
|
|
118
|
+
state: PreventAny<S, T, Id>,
|
|
119
|
+
): S
|
|
120
|
+
|
|
121
|
+
updateOne<S extends DraftableEntityState<T, Id>>(
|
|
122
|
+
state: PreventAny<S, T, Id>,
|
|
123
|
+
update: Update<T, Id>,
|
|
124
|
+
): S
|
|
125
|
+
updateOne<S extends DraftableEntityState<T, Id>>(
|
|
126
|
+
state: PreventAny<S, T, Id>,
|
|
127
|
+
update: PayloadAction<Update<T, Id>>,
|
|
128
|
+
): S
|
|
129
|
+
|
|
130
|
+
updateMany<S extends DraftableEntityState<T, Id>>(
|
|
131
|
+
state: PreventAny<S, T, Id>,
|
|
132
|
+
updates: ReadonlyArray<Update<T, Id>>,
|
|
133
|
+
): S
|
|
134
|
+
updateMany<S extends DraftableEntityState<T, Id>>(
|
|
135
|
+
state: PreventAny<S, T, Id>,
|
|
136
|
+
updates: PayloadAction<ReadonlyArray<Update<T, Id>>>,
|
|
137
|
+
): S
|
|
138
|
+
|
|
139
|
+
upsertOne<S extends DraftableEntityState<T, Id>>(
|
|
140
|
+
state: PreventAny<S, T, Id>,
|
|
141
|
+
entity: T,
|
|
142
|
+
): S
|
|
143
|
+
upsertOne<S extends DraftableEntityState<T, Id>>(
|
|
144
|
+
state: PreventAny<S, T, Id>,
|
|
145
|
+
entity: PayloadAction<T>,
|
|
146
|
+
): S
|
|
147
|
+
|
|
148
|
+
upsertMany<S extends DraftableEntityState<T, Id>>(
|
|
149
|
+
state: PreventAny<S, T, Id>,
|
|
150
|
+
entities: readonly T[] | Record<Id, T>,
|
|
151
|
+
): S
|
|
152
|
+
upsertMany<S extends DraftableEntityState<T, Id>>(
|
|
153
|
+
state: PreventAny<S, T, Id>,
|
|
154
|
+
entities: PayloadAction<readonly T[] | Record<Id, T>>,
|
|
155
|
+
): S
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @public
|
|
160
|
+
*/
|
|
161
|
+
export interface EntitySelectors<T, V, IdType extends EntityId> {
|
|
162
|
+
selectIds: (state: V) => IdType[]
|
|
163
|
+
selectEntities: (state: V) => Record<IdType, T>
|
|
164
|
+
selectAll: (state: V) => T[]
|
|
165
|
+
selectTotal: (state: V) => number
|
|
166
|
+
selectById: (state: V, id: IdType) => Id<UncheckedIndexedAccess<T>>
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* @public
|
|
171
|
+
*/
|
|
172
|
+
export interface EntityStateFactory<T, Id extends EntityId> {
|
|
173
|
+
getInitialState(
|
|
174
|
+
state?: undefined,
|
|
175
|
+
entities?: Record<Id, T> | readonly T[],
|
|
176
|
+
): EntityState<T, Id>
|
|
177
|
+
getInitialState<S extends object>(
|
|
178
|
+
state: S,
|
|
179
|
+
entities?: Record<Id, T> | readonly T[],
|
|
180
|
+
): EntityState<T, Id> & S
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @public
|
|
185
|
+
*/
|
|
186
|
+
export interface EntityAdapter<T, Id extends EntityId>
|
|
187
|
+
extends EntityStateAdapter<T, Id>,
|
|
188
|
+
EntityStateFactory<T, Id>,
|
|
189
|
+
Required<EntityAdapterOptions<T, Id>> {
|
|
190
|
+
getSelectors(
|
|
191
|
+
selectState?: undefined,
|
|
192
|
+
options?: GetSelectorsOptions,
|
|
193
|
+
): EntitySelectors<T, EntityState<T, Id>, Id>
|
|
194
|
+
getSelectors<V>(
|
|
195
|
+
selectState: (state: V) => EntityState<T, Id>,
|
|
196
|
+
options?: GetSelectorsOptions,
|
|
197
|
+
): EntitySelectors<T, V, Id>
|
|
198
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IdSelector,
|
|
3
|
+
Comparer,
|
|
4
|
+
EntityStateAdapter,
|
|
5
|
+
Update,
|
|
6
|
+
EntityId,
|
|
7
|
+
DraftableEntityState,
|
|
8
|
+
} from './models'
|
|
9
|
+
import { createStateOperator } from './state_adapter'
|
|
10
|
+
import { createUnsortedStateAdapter } from './unsorted_state_adapter'
|
|
11
|
+
import {
|
|
12
|
+
selectIdValue,
|
|
13
|
+
ensureEntitiesArray,
|
|
14
|
+
splitAddedUpdatedEntities,
|
|
15
|
+
getCurrent,
|
|
16
|
+
} from './utils'
|
|
17
|
+
|
|
18
|
+
// Borrowed from Replay
|
|
19
|
+
export function findInsertIndex<T>(
|
|
20
|
+
sortedItems: T[],
|
|
21
|
+
item: T,
|
|
22
|
+
comparisonFunction: Comparer<T>,
|
|
23
|
+
): number {
|
|
24
|
+
let lowIndex = 0
|
|
25
|
+
let highIndex = sortedItems.length
|
|
26
|
+
while (lowIndex < highIndex) {
|
|
27
|
+
let middleIndex = (lowIndex + highIndex) >>> 1
|
|
28
|
+
const currentItem = sortedItems[middleIndex]
|
|
29
|
+
const res = comparisonFunction(item, currentItem)
|
|
30
|
+
if (res >= 0) {
|
|
31
|
+
lowIndex = middleIndex + 1
|
|
32
|
+
} else {
|
|
33
|
+
highIndex = middleIndex
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return lowIndex
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function insert<T>(
|
|
41
|
+
sortedItems: T[],
|
|
42
|
+
item: T,
|
|
43
|
+
comparisonFunction: Comparer<T>,
|
|
44
|
+
): T[] {
|
|
45
|
+
const insertAtIndex = findInsertIndex(sortedItems, item, comparisonFunction)
|
|
46
|
+
|
|
47
|
+
sortedItems.splice(insertAtIndex, 0, item)
|
|
48
|
+
|
|
49
|
+
return sortedItems
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function createSortedStateAdapter<T, Id extends EntityId>(
|
|
53
|
+
selectId: IdSelector<T, Id>,
|
|
54
|
+
comparer: Comparer<T>,
|
|
55
|
+
): EntityStateAdapter<T, Id> {
|
|
56
|
+
type R = DraftableEntityState<T, Id>
|
|
57
|
+
|
|
58
|
+
const { removeOne, removeMany, removeAll } =
|
|
59
|
+
createUnsortedStateAdapter(selectId)
|
|
60
|
+
|
|
61
|
+
function addOneMutably(entity: T, state: R): void {
|
|
62
|
+
return addManyMutably([entity], state)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function addManyMutably(
|
|
66
|
+
newEntities: readonly T[] | Record<Id, T>,
|
|
67
|
+
state: R,
|
|
68
|
+
existingIds?: Id[],
|
|
69
|
+
): void {
|
|
70
|
+
newEntities = ensureEntitiesArray(newEntities)
|
|
71
|
+
|
|
72
|
+
const existingKeys = new Set<Id>(existingIds ?? getCurrent(state.ids))
|
|
73
|
+
const addedKeys = new Set<Id>();
|
|
74
|
+
const models = newEntities.filter(
|
|
75
|
+
(model) => {
|
|
76
|
+
const modelId = selectIdValue(model, selectId);
|
|
77
|
+
const notAdded = !addedKeys.has(modelId);
|
|
78
|
+
if (notAdded) addedKeys.add(modelId);
|
|
79
|
+
return !existingKeys.has(modelId) && notAdded;
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
if (models.length !== 0) {
|
|
84
|
+
mergeFunction(state, models)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function setOneMutably(entity: T, state: R): void {
|
|
89
|
+
return setManyMutably([entity], state)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function setManyMutably(
|
|
93
|
+
newEntities: readonly T[] | Record<Id, T>,
|
|
94
|
+
state: R,
|
|
95
|
+
): void {
|
|
96
|
+
let deduplicatedEntities = {} as Record<Id, T>;
|
|
97
|
+
newEntities = ensureEntitiesArray(newEntities)
|
|
98
|
+
if (newEntities.length !== 0) {
|
|
99
|
+
for (const item of newEntities) {
|
|
100
|
+
const entityId = selectId(item);
|
|
101
|
+
// For multiple items with the same ID, we should keep the last one.
|
|
102
|
+
deduplicatedEntities[entityId] = item;
|
|
103
|
+
delete (state.entities as Record<Id, T>)[entityId]
|
|
104
|
+
}
|
|
105
|
+
newEntities = ensureEntitiesArray(deduplicatedEntities);
|
|
106
|
+
mergeFunction(state, newEntities)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function setAllMutably(
|
|
111
|
+
newEntities: readonly T[] | Record<Id, T>,
|
|
112
|
+
state: R,
|
|
113
|
+
): void {
|
|
114
|
+
newEntities = ensureEntitiesArray(newEntities)
|
|
115
|
+
state.entities = {} as Record<Id, T>
|
|
116
|
+
state.ids = []
|
|
117
|
+
|
|
118
|
+
addManyMutably(newEntities, state, [])
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function updateOneMutably(update: Update<T, Id>, state: R): void {
|
|
122
|
+
return updateManyMutably([update], state)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function updateManyMutably(
|
|
126
|
+
updates: ReadonlyArray<Update<T, Id>>,
|
|
127
|
+
state: R,
|
|
128
|
+
): void {
|
|
129
|
+
let appliedUpdates = false
|
|
130
|
+
let replacedIds = false
|
|
131
|
+
|
|
132
|
+
for (let update of updates) {
|
|
133
|
+
const entity: T | undefined = (state.entities as Record<Id, T>)[update.id]
|
|
134
|
+
if (!entity) {
|
|
135
|
+
continue
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
appliedUpdates = true
|
|
139
|
+
|
|
140
|
+
Object.assign(entity, update.changes)
|
|
141
|
+
const newId = selectId(entity)
|
|
142
|
+
|
|
143
|
+
if (update.id !== newId) {
|
|
144
|
+
// We do support the case where updates can change an item's ID.
|
|
145
|
+
// This makes things trickier - go ahead and swap the IDs in state now.
|
|
146
|
+
replacedIds = true
|
|
147
|
+
delete (state.entities as Record<Id, T>)[update.id]
|
|
148
|
+
const oldIndex = (state.ids as Id[]).indexOf(update.id)
|
|
149
|
+
state.ids[oldIndex] = newId
|
|
150
|
+
;(state.entities as Record<Id, T>)[newId] = entity
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (appliedUpdates) {
|
|
155
|
+
mergeFunction(state, [], appliedUpdates, replacedIds)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function upsertOneMutably(entity: T, state: R): void {
|
|
160
|
+
return upsertManyMutably([entity], state)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function upsertManyMutably(
|
|
164
|
+
newEntities: readonly T[] | Record<Id, T>,
|
|
165
|
+
state: R,
|
|
166
|
+
): void {
|
|
167
|
+
const [added, updated, existingIdsArray] = splitAddedUpdatedEntities<T, Id>(
|
|
168
|
+
newEntities,
|
|
169
|
+
selectId,
|
|
170
|
+
state,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
if (added.length) {
|
|
174
|
+
addManyMutably(added, state, existingIdsArray)
|
|
175
|
+
}
|
|
176
|
+
if (updated.length) {
|
|
177
|
+
updateManyMutably(updated, state)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function areArraysEqual(a: readonly unknown[], b: readonly unknown[]) {
|
|
182
|
+
if (a.length !== b.length) {
|
|
183
|
+
return false
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
for (let i = 0; i < a.length; i++) {
|
|
187
|
+
if (a[i] === b[i]) {
|
|
188
|
+
continue
|
|
189
|
+
}
|
|
190
|
+
return false
|
|
191
|
+
}
|
|
192
|
+
return true
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
type MergeFunction = (
|
|
196
|
+
state: R,
|
|
197
|
+
addedItems: readonly T[],
|
|
198
|
+
appliedUpdates?: boolean,
|
|
199
|
+
replacedIds?: boolean,
|
|
200
|
+
) => void
|
|
201
|
+
|
|
202
|
+
const mergeFunction: MergeFunction = (
|
|
203
|
+
state,
|
|
204
|
+
addedItems,
|
|
205
|
+
appliedUpdates,
|
|
206
|
+
replacedIds,
|
|
207
|
+
) => {
|
|
208
|
+
const currentEntities = getCurrent(state.entities)
|
|
209
|
+
const currentIds = getCurrent(state.ids)
|
|
210
|
+
|
|
211
|
+
const stateEntities = state.entities as Record<Id, T>
|
|
212
|
+
|
|
213
|
+
let ids: Iterable<Id> = currentIds
|
|
214
|
+
if (replacedIds) {
|
|
215
|
+
ids = new Set(currentIds)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
let sortedEntities: T[] = []
|
|
219
|
+
for (const id of ids) {
|
|
220
|
+
const entity = currentEntities[id]
|
|
221
|
+
if (entity) {
|
|
222
|
+
sortedEntities.push(entity)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const wasPreviouslyEmpty = sortedEntities.length === 0
|
|
226
|
+
|
|
227
|
+
// Insert/overwrite all new/updated
|
|
228
|
+
for (const item of addedItems) {
|
|
229
|
+
stateEntities[selectId(item)] = item
|
|
230
|
+
|
|
231
|
+
if (!wasPreviouslyEmpty) {
|
|
232
|
+
// Binary search insertion generally requires fewer comparisons
|
|
233
|
+
insert(sortedEntities, item, comparer)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (wasPreviouslyEmpty) {
|
|
238
|
+
// All we have is the incoming values, sort them
|
|
239
|
+
sortedEntities = addedItems.slice().sort(comparer)
|
|
240
|
+
} else if (appliedUpdates) {
|
|
241
|
+
// We should have a _mostly_-sorted array already
|
|
242
|
+
sortedEntities.sort(comparer)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const newSortedIds = sortedEntities.map(selectId)
|
|
246
|
+
|
|
247
|
+
if (!areArraysEqual(currentIds, newSortedIds)) {
|
|
248
|
+
state.ids = newSortedIds
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
removeOne,
|
|
254
|
+
removeMany,
|
|
255
|
+
removeAll,
|
|
256
|
+
addOne: createStateOperator(addOneMutably),
|
|
257
|
+
updateOne: createStateOperator(updateOneMutably),
|
|
258
|
+
upsertOne: createStateOperator(upsertOneMutably),
|
|
259
|
+
setOne: createStateOperator(setOneMutably),
|
|
260
|
+
setMany: createStateOperator(setManyMutably),
|
|
261
|
+
setAll: createStateOperator(setAllMutably),
|
|
262
|
+
addMany: createStateOperator(addManyMutably),
|
|
263
|
+
updateMany: createStateOperator(updateManyMutably),
|
|
264
|
+
upsertMany: createStateOperator(upsertManyMutably),
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { createNextState, isDraft } from '../immerImports'
|
|
2
|
+
import type { Draft } from 'immer'
|
|
3
|
+
import type { EntityId, DraftableEntityState, PreventAny } from './models'
|
|
4
|
+
import type { PayloadAction } from '../createAction'
|
|
5
|
+
import { isFSA } from '../createAction'
|
|
6
|
+
|
|
7
|
+
export const isDraftTyped = isDraft as <T>(
|
|
8
|
+
value: T | Draft<T>,
|
|
9
|
+
) => value is Draft<T>
|
|
10
|
+
|
|
11
|
+
export function createSingleArgumentStateOperator<T, Id extends EntityId>(
|
|
12
|
+
mutator: (state: DraftableEntityState<T, Id>) => void,
|
|
13
|
+
) {
|
|
14
|
+
const operator = createStateOperator(
|
|
15
|
+
(_: undefined, state: DraftableEntityState<T, Id>) => mutator(state),
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
return function operation<S extends DraftableEntityState<T, Id>>(
|
|
19
|
+
state: PreventAny<S, T, Id>,
|
|
20
|
+
): S {
|
|
21
|
+
return operator(state as S, undefined)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function createStateOperator<T, Id extends EntityId, R>(
|
|
26
|
+
mutator: (arg: R, state: DraftableEntityState<T, Id>) => void,
|
|
27
|
+
) {
|
|
28
|
+
return function operation<S extends DraftableEntityState<T, Id>>(
|
|
29
|
+
state: S,
|
|
30
|
+
arg: R | PayloadAction<R>,
|
|
31
|
+
): S {
|
|
32
|
+
function isPayloadActionArgument(
|
|
33
|
+
arg: R | PayloadAction<R>,
|
|
34
|
+
): arg is PayloadAction<R> {
|
|
35
|
+
return isFSA(arg)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const runMutator = (draft: DraftableEntityState<T, Id>) => {
|
|
39
|
+
if (isPayloadActionArgument(arg)) {
|
|
40
|
+
mutator(arg.payload, draft)
|
|
41
|
+
} else {
|
|
42
|
+
mutator(arg, draft)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (isDraftTyped<DraftableEntityState<T, Id>>(state)) {
|
|
47
|
+
// we must already be inside a `createNextState` call, likely because
|
|
48
|
+
// this is being wrapped in `createReducer` or `createSlice`.
|
|
49
|
+
// It's safe to just pass the draft to the mutator.
|
|
50
|
+
runMutator(state)
|
|
51
|
+
|
|
52
|
+
// since it's a draft, we'll just return it
|
|
53
|
+
return state
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return createNextState(state, runMutator)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { CreateSelectorFunction, Selector } from 'reselect'
|
|
2
|
+
import { createDraftSafeSelector } from '../createDraftSafeSelector'
|
|
3
|
+
import type { EntityId, EntitySelectors, EntityState } from './models'
|
|
4
|
+
|
|
5
|
+
type AnyCreateSelectorFunction = CreateSelectorFunction<any, any, any>
|
|
6
|
+
|
|
7
|
+
export type GetSelectorsOptions = {
|
|
8
|
+
createSelector?: AnyCreateSelectorFunction
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createSelectorsFactory<T, Id extends EntityId>() {
|
|
12
|
+
function getSelectors(
|
|
13
|
+
selectState?: undefined,
|
|
14
|
+
options?: GetSelectorsOptions,
|
|
15
|
+
): EntitySelectors<T, EntityState<T, Id>, Id>
|
|
16
|
+
function getSelectors<V>(
|
|
17
|
+
selectState: (state: V) => EntityState<T, Id>,
|
|
18
|
+
options?: GetSelectorsOptions,
|
|
19
|
+
): EntitySelectors<T, V, Id>
|
|
20
|
+
function getSelectors<V>(
|
|
21
|
+
selectState?: (state: V) => EntityState<T, Id>,
|
|
22
|
+
options: GetSelectorsOptions = {},
|
|
23
|
+
): EntitySelectors<T, any, Id> {
|
|
24
|
+
const {
|
|
25
|
+
createSelector = createDraftSafeSelector as AnyCreateSelectorFunction,
|
|
26
|
+
} = options
|
|
27
|
+
|
|
28
|
+
const selectIds = (state: EntityState<T, Id>) => state.ids
|
|
29
|
+
|
|
30
|
+
const selectEntities = (state: EntityState<T, Id>) => state.entities
|
|
31
|
+
|
|
32
|
+
const selectAll = createSelector(
|
|
33
|
+
selectIds,
|
|
34
|
+
selectEntities,
|
|
35
|
+
(ids, entities): T[] => ids.map((id) => entities[id]!),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
const selectId = (_: unknown, id: Id) => id
|
|
39
|
+
|
|
40
|
+
const selectById = (entities: Record<Id, T>, id: Id) => entities[id]
|
|
41
|
+
|
|
42
|
+
const selectTotal = createSelector(selectIds, (ids) => ids.length)
|
|
43
|
+
|
|
44
|
+
if (!selectState) {
|
|
45
|
+
return {
|
|
46
|
+
selectIds,
|
|
47
|
+
selectEntities,
|
|
48
|
+
selectAll,
|
|
49
|
+
selectTotal,
|
|
50
|
+
selectById: createSelector(selectEntities, selectId, selectById),
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const selectGlobalizedEntities = createSelector(
|
|
55
|
+
selectState as Selector<V, EntityState<T, Id>>,
|
|
56
|
+
selectEntities,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
selectIds: createSelector(selectState, selectIds),
|
|
61
|
+
selectEntities: selectGlobalizedEntities,
|
|
62
|
+
selectAll: createSelector(selectState, selectAll),
|
|
63
|
+
selectTotal: createSelector(selectState, selectTotal),
|
|
64
|
+
selectById: createSelector(
|
|
65
|
+
selectGlobalizedEntities,
|
|
66
|
+
selectId,
|
|
67
|
+
selectById,
|
|
68
|
+
),
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return { getSelectors }
|
|
73
|
+
}
|