@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,146 @@
1
+ import type { StoreEnhancer } from 'redux'
2
+
3
+ export const SHOULD_AUTOBATCH = 'RTK_autoBatch'
4
+
5
+ export const prepareAutoBatched =
6
+ <T>() =>
7
+ (payload: T): { payload: T; meta: unknown } => ({
8
+ payload,
9
+ meta: { [SHOULD_AUTOBATCH]: true },
10
+ })
11
+
12
+ const createQueueWithTimer = (timeout: number) => {
13
+ return (notify: () => void) => {
14
+ setTimeout(notify, timeout)
15
+ }
16
+ }
17
+
18
+ const createRafWithFallbackTimer = (
19
+ raf: typeof requestAnimationFrame,
20
+ timeout: number,
21
+ ) => {
22
+ return (notify: () => void) => {
23
+ let called = false
24
+ const callback = () => {
25
+ if (called) return
26
+ called = true
27
+ cancelAnimationFrame(rafId)
28
+ clearTimeout(timerId)
29
+ notify()
30
+ }
31
+ const rafId = raf(callback)
32
+ const timerId = setTimeout(callback, timeout)
33
+ }
34
+ }
35
+
36
+ export type AutoBatchOptions =
37
+ | { type: 'tick' }
38
+ | { type: 'timer'; timeout: number }
39
+ | { type: 'raf' }
40
+ | { type: 'callback'; queueNotification: (notify: () => void) => void }
41
+
42
+ /**
43
+ * A Redux store enhancer that watches for "low-priority" actions, and delays
44
+ * notifying subscribers until either the queued callback executes or the
45
+ * next "standard-priority" action is dispatched.
46
+ *
47
+ * This allows dispatching multiple "low-priority" actions in a row with only
48
+ * a single subscriber notification to the UI after the sequence of actions
49
+ * is finished, thus improving UI re-render performance.
50
+ *
51
+ * Watches for actions with the `action.meta[SHOULD_AUTOBATCH]` attribute.
52
+ * This can be added to `action.meta` manually, or by using the
53
+ * `prepareAutoBatched` helper.
54
+ *
55
+ * By default, it will queue a notification for the end of the event loop tick.
56
+ * However, you can pass several other options to configure the behavior:
57
+ * - `{type: 'tick'}`: queues using `queueMicrotask`
58
+ * - `{type: 'timer', timeout: number}`: queues using `setTimeout`
59
+ * - `{type: 'raf'}`: queues using `requestAnimationFrame` (default)
60
+ * - `{type: 'callback', queueNotification: (notify: () => void) => void}`: lets you provide your own callback
61
+ *
62
+ *
63
+ */
64
+ export const autoBatchEnhancer =
65
+ (options: AutoBatchOptions = { type: 'raf' }): StoreEnhancer =>
66
+ (next) =>
67
+ (...args) => {
68
+ const store = next(...args)
69
+
70
+ let notifying = true
71
+ let shouldNotifyAtEndOfTick = false
72
+ let notificationQueued = false
73
+
74
+ const listeners = new Set<() => void>()
75
+
76
+ const queueCallback =
77
+ options.type === 'tick'
78
+ ? queueMicrotask
79
+ : options.type === 'raf'
80
+ ? // requestAnimationFrame won't exist in SSR environments. Fall back to a vague approximation just to keep from erroring.
81
+ typeof window !== 'undefined' && window.requestAnimationFrame
82
+ ? createRafWithFallbackTimer(window.requestAnimationFrame, 100)
83
+ : createQueueWithTimer(10)
84
+ : options.type === 'callback'
85
+ ? options.queueNotification
86
+ : createQueueWithTimer(options.timeout)
87
+
88
+ const notifyListeners = () => {
89
+ // We're running at the end of the event loop tick.
90
+ // Run the real listener callbacks to actually update the UI.
91
+ notificationQueued = false
92
+ if (shouldNotifyAtEndOfTick) {
93
+ shouldNotifyAtEndOfTick = false
94
+ listeners.forEach((l) => l())
95
+ }
96
+ }
97
+
98
+ return Object.assign({}, store, {
99
+ // Override the base `store.subscribe` method to keep original listeners
100
+ // from running if we're delaying notifications
101
+ subscribe(listener: () => void) {
102
+ // Each wrapped listener will only call the real listener if
103
+ // the `notifying` flag is currently active when it's called.
104
+ // This lets the base store work as normal, while the actual UI
105
+ // update becomes controlled by this enhancer.
106
+ const wrappedListener: typeof listener = () => notifying && listener()
107
+ const unsubscribe = store.subscribe(wrappedListener)
108
+ listeners.add(listener)
109
+ return () => {
110
+ unsubscribe()
111
+ listeners.delete(listener)
112
+ }
113
+ },
114
+ // Override the base `store.dispatch` method so that we can check actions
115
+ // for the `shouldAutoBatch` flag and determine if batching is active
116
+ dispatch(action: any) {
117
+ try {
118
+ // If the action does _not_ have the `shouldAutoBatch` flag,
119
+ // we resume/continue normal notify-after-each-dispatch behavior
120
+ notifying = !action?.meta?.[SHOULD_AUTOBATCH]
121
+ // If a `notifyListeners` microtask was queued, you can't cancel it.
122
+ // Instead, we set a flag so that it's a no-op when it does run
123
+ shouldNotifyAtEndOfTick = !notifying
124
+ if (shouldNotifyAtEndOfTick) {
125
+ // We've seen at least 1 action with `SHOULD_AUTOBATCH`. Try to queue
126
+ // a microtask to notify listeners at the end of the event loop tick.
127
+ // Make sure we only enqueue this _once_ per tick.
128
+ if (!notificationQueued) {
129
+ notificationQueued = true
130
+ queueCallback(notifyListeners)
131
+ }
132
+ }
133
+ // Go ahead and process the action as usual, including reducers.
134
+ // If normal notification behavior is enabled, the store will notify
135
+ // all of its own listeners, and the wrapper callbacks above will
136
+ // see `notifying` is true and pass on to the real listener callbacks.
137
+ // If we're "batching" behavior, then the wrapped callbacks will
138
+ // bail out, causing the base store notification behavior to be no-ops.
139
+ return store.dispatch(action)
140
+ } finally {
141
+ // Assume we're back to normal behavior after each action
142
+ notifying = true
143
+ }
144
+ },
145
+ })
146
+ }
@@ -0,0 +1,487 @@
1
+ import type {
2
+ PreloadedStateShapeFromReducersMapObject,
3
+ Reducer,
4
+ StateFromReducersMapObject,
5
+ UnknownAction,
6
+ } from 'redux'
7
+ import { combineReducers } from 'redux'
8
+ import { nanoid } from './nanoid'
9
+ import type {
10
+ Id,
11
+ NonUndefined,
12
+ Tail,
13
+ UnionToIntersection,
14
+ WithOptionalProp,
15
+ } from './tsHelpers'
16
+ import { getOrInsertComputed } from './utils'
17
+
18
+ type SliceLike<ReducerPath extends string, State, PreloadedState = State> = {
19
+ reducerPath: ReducerPath
20
+ reducer: Reducer<State, any, PreloadedState>
21
+ }
22
+
23
+ type AnySliceLike = SliceLike<string, any>
24
+
25
+ type SliceLikeReducerPath<A extends AnySliceLike> =
26
+ A extends SliceLike<infer ReducerPath, any> ? ReducerPath : never
27
+
28
+ type SliceLikeState<A extends AnySliceLike> =
29
+ A extends SliceLike<any, infer State, any> ? State : never
30
+
31
+ type SliceLikePreloadedState<A extends AnySliceLike> =
32
+ A extends SliceLike<any, any, infer PreloadedState> ? PreloadedState : never
33
+
34
+ export type WithSlice<A extends AnySliceLike> = {
35
+ [Path in SliceLikeReducerPath<A>]: SliceLikeState<A>
36
+ }
37
+
38
+ export type WithSlicePreloadedState<A extends AnySliceLike> = {
39
+ [Path in SliceLikeReducerPath<A>]: SliceLikePreloadedState<A>
40
+ }
41
+
42
+ type ReducerMap = Record<string, Reducer>
43
+
44
+ type ExistingSliceLike<DeclaredState, PreloadedState> = {
45
+ [ReducerPath in keyof DeclaredState]: SliceLike<
46
+ ReducerPath & string,
47
+ NonUndefined<DeclaredState[ReducerPath]>,
48
+ NonUndefined<PreloadedState[ReducerPath & keyof PreloadedState]>
49
+ >
50
+ }[keyof DeclaredState]
51
+
52
+ export type InjectConfig = {
53
+ /**
54
+ * Allow replacing reducer with a different reference. Normally, an error will be thrown if a different reducer instance to the one already injected is used.
55
+ */
56
+ overrideExisting?: boolean
57
+ }
58
+
59
+ /**
60
+ * A reducer that allows for slices/reducers to be injected after initialisation.
61
+ */
62
+ export interface CombinedSliceReducer<
63
+ InitialState,
64
+ DeclaredState extends InitialState = InitialState,
65
+ PreloadedState extends Partial<
66
+ Record<keyof PreloadedState, any>
67
+ > = Partial<DeclaredState>,
68
+ > extends Reducer<DeclaredState, UnknownAction, PreloadedState> {
69
+ /**
70
+ * Provide a type for slices that will be injected lazily.
71
+ *
72
+ * One way to do this would be with interface merging:
73
+ * ```ts
74
+ *
75
+ * export interface LazyLoadedSlices {}
76
+ *
77
+ * export const rootReducer = combineSlices(stringSlice).withLazyLoadedSlices<LazyLoadedSlices>();
78
+ *
79
+ * // elsewhere
80
+ *
81
+ * declare module './reducer' {
82
+ * export interface LazyLoadedSlices extends WithSlice<typeof booleanSlice> {}
83
+ * }
84
+ *
85
+ * const withBoolean = rootReducer.inject(booleanSlice);
86
+ *
87
+ * // elsewhere again
88
+ *
89
+ * declare module './reducer' {
90
+ * export interface LazyLoadedSlices {
91
+ * customName: CustomState
92
+ * }
93
+ * }
94
+ *
95
+ * const withCustom = rootReducer.inject({ reducerPath: "customName", reducer: customSlice.reducer })
96
+ * ```
97
+ */
98
+ withLazyLoadedSlices<Lazy = {}, LazyPreloaded = Lazy>(): CombinedSliceReducer<
99
+ InitialState,
100
+ Id<DeclaredState & Partial<Lazy>>,
101
+ Id<PreloadedState & Partial<LazyPreloaded>>
102
+ >
103
+
104
+ /**
105
+ * Inject a slice.
106
+ *
107
+ * Accepts an individual slice, RTKQ API instance, or a "slice-like" { reducerPath, reducer } object.
108
+ *
109
+ * ```ts
110
+ * rootReducer.inject(booleanSlice)
111
+ * rootReducer.inject(baseApi)
112
+ * rootReducer.inject({ reducerPath: 'boolean' as const, reducer: newReducer }, { overrideExisting: true })
113
+ * ```
114
+ *
115
+ */
116
+ inject<Sl extends Id<ExistingSliceLike<DeclaredState, PreloadedState>>>(
117
+ slice: Sl,
118
+ config?: InjectConfig,
119
+ ): CombinedSliceReducer<
120
+ InitialState,
121
+ Id<DeclaredState & WithSlice<Sl>>,
122
+ Id<PreloadedState & Partial<WithSlicePreloadedState<Sl>>>
123
+ >
124
+
125
+ /**
126
+ * Inject a slice.
127
+ *
128
+ * Accepts an individual slice, RTKQ API instance, or a "slice-like" { reducerPath, reducer } object.
129
+ *
130
+ * ```ts
131
+ * rootReducer.inject(booleanSlice)
132
+ * rootReducer.inject(baseApi)
133
+ * rootReducer.inject({ reducerPath: 'boolean' as const, reducer: newReducer }, { overrideExisting: true })
134
+ * ```
135
+ *
136
+ */
137
+ inject<ReducerPath extends string, State, PreloadedState = State>(
138
+ slice: SliceLike<
139
+ ReducerPath,
140
+ State & (ReducerPath extends keyof DeclaredState ? never : State),
141
+ PreloadedState &
142
+ (ReducerPath extends keyof PreloadedState ? never : PreloadedState)
143
+ >,
144
+ config?: InjectConfig,
145
+ ): CombinedSliceReducer<
146
+ InitialState,
147
+ Id<DeclaredState & WithSlice<SliceLike<ReducerPath, State>>>,
148
+ Id<
149
+ PreloadedState &
150
+ WithSlicePreloadedState<SliceLike<ReducerPath, State, PreloadedState>>
151
+ >
152
+ >
153
+
154
+ /**
155
+ * Create a selector that guarantees that the slices injected will have a defined value when selector is run.
156
+ *
157
+ * ```ts
158
+ * const selectBooleanWithoutInjection = (state: RootState) => state.boolean;
159
+ * // ^? boolean | undefined
160
+ *
161
+ * const selectBoolean = rootReducer.inject(booleanSlice).selector((state) => {
162
+ * // if action hasn't been dispatched since slice was injected, this would usually be undefined
163
+ * // however selector() uses a Proxy around the first parameter to ensure that it evaluates to the initial state instead, if undefined
164
+ * return state.boolean;
165
+ * // ^? boolean
166
+ * })
167
+ * ```
168
+ *
169
+ * If the reducer is nested inside the root state, a selectState callback can be passed to retrieve the reducer's state.
170
+ *
171
+ * ```ts
172
+ *
173
+ * export interface LazyLoadedSlices {};
174
+ *
175
+ * export const innerReducer = combineSlices(stringSlice).withLazyLoadedSlices<LazyLoadedSlices>();
176
+ *
177
+ * export const rootReducer = combineSlices({ inner: innerReducer });
178
+ *
179
+ * export type RootState = ReturnType<typeof rootReducer>;
180
+ *
181
+ * // elsewhere
182
+ *
183
+ * declare module "./reducer.ts" {
184
+ * export interface LazyLoadedSlices extends WithSlice<typeof booleanSlice> {}
185
+ * }
186
+ *
187
+ * const withBool = innerReducer.inject(booleanSlice);
188
+ *
189
+ * const selectBoolean = withBool.selector(
190
+ * (state) => state.boolean,
191
+ * (rootState: RootState) => state.inner
192
+ * );
193
+ * // now expects to be passed RootState instead of innerReducer state
194
+ *
195
+ * ```
196
+ *
197
+ * Value passed to selectorFn will be a Proxy - use selector.original(proxy) to get original state value (useful for debugging)
198
+ *
199
+ * ```ts
200
+ * const injectedReducer = rootReducer.inject(booleanSlice);
201
+ * const selectBoolean = injectedReducer.selector((state) => {
202
+ * console.log(injectedReducer.selector.original(state).boolean) // possibly undefined
203
+ * return state.boolean
204
+ * })
205
+ * ```
206
+ */
207
+ selector: {
208
+ /**
209
+ * Create a selector that guarantees that the slices injected will have a defined value when selector is run.
210
+ *
211
+ * ```ts
212
+ * const selectBooleanWithoutInjection = (state: RootState) => state.boolean;
213
+ * // ^? boolean | undefined
214
+ *
215
+ * const selectBoolean = rootReducer.inject(booleanSlice).selector((state) => {
216
+ * // if action hasn't been dispatched since slice was injected, this would usually be undefined
217
+ * // however selector() uses a Proxy around the first parameter to ensure that it evaluates to the initial state instead, if undefined
218
+ * return state.boolean;
219
+ * // ^? boolean
220
+ * })
221
+ * ```
222
+ *
223
+ * Value passed to selectorFn will be a Proxy - use selector.original(proxy) to get original state value (useful for debugging)
224
+ *
225
+ * ```ts
226
+ * const injectedReducer = rootReducer.inject(booleanSlice);
227
+ * const selectBoolean = injectedReducer.selector((state) => {
228
+ * console.log(injectedReducer.selector.original(state).boolean) // undefined
229
+ * return state.boolean
230
+ * })
231
+ * ```
232
+ */
233
+ <Selector extends (state: DeclaredState, ...args: any[]) => unknown>(
234
+ selectorFn: Selector,
235
+ ): (
236
+ state: WithOptionalProp<
237
+ Parameters<Selector>[0],
238
+ Exclude<keyof DeclaredState, keyof InitialState>
239
+ >,
240
+ ...args: Tail<Parameters<Selector>>
241
+ ) => ReturnType<Selector>
242
+
243
+ /**
244
+ * Create a selector that guarantees that the slices injected will have a defined value when selector is run.
245
+ *
246
+ * ```ts
247
+ * const selectBooleanWithoutInjection = (state: RootState) => state.boolean;
248
+ * // ^? boolean | undefined
249
+ *
250
+ * const selectBoolean = rootReducer.inject(booleanSlice).selector((state) => {
251
+ * // if action hasn't been dispatched since slice was injected, this would usually be undefined
252
+ * // however selector() uses a Proxy around the first parameter to ensure that it evaluates to the initial state instead, if undefined
253
+ * return state.boolean;
254
+ * // ^? boolean
255
+ * })
256
+ * ```
257
+ *
258
+ * If the reducer is nested inside the root state, a selectState callback can be passed to retrieve the reducer's state.
259
+ *
260
+ * ```ts
261
+ *
262
+ * interface LazyLoadedSlices {};
263
+ *
264
+ * const innerReducer = combineSlices(stringSlice).withLazyLoadedSlices<LazyLoadedSlices>();
265
+ *
266
+ * const rootReducer = combineSlices({ inner: innerReducer });
267
+ *
268
+ * type RootState = ReturnType<typeof rootReducer>;
269
+ *
270
+ * // elsewhere
271
+ *
272
+ * declare module "./reducer.ts" {
273
+ * interface LazyLoadedSlices extends WithSlice<typeof booleanSlice> {}
274
+ * }
275
+ *
276
+ * const withBool = innerReducer.inject(booleanSlice);
277
+ *
278
+ * const selectBoolean = withBool.selector(
279
+ * (state) => state.boolean,
280
+ * (rootState: RootState) => state.inner
281
+ * );
282
+ * // now expects to be passed RootState instead of innerReducer state
283
+ *
284
+ * ```
285
+ *
286
+ * Value passed to selectorFn will be a Proxy - use selector.original(proxy) to get original state value (useful for debugging)
287
+ *
288
+ * ```ts
289
+ * const injectedReducer = rootReducer.inject(booleanSlice);
290
+ * const selectBoolean = injectedReducer.selector((state) => {
291
+ * console.log(injectedReducer.selector.original(state).boolean) // possibly undefined
292
+ * return state.boolean
293
+ * })
294
+ * ```
295
+ */
296
+ <
297
+ Selector extends (state: DeclaredState, ...args: any[]) => unknown,
298
+ RootState,
299
+ >(
300
+ selectorFn: Selector,
301
+ selectState: (
302
+ rootState: RootState,
303
+ ...args: Tail<Parameters<Selector>>
304
+ ) => WithOptionalProp<
305
+ Parameters<Selector>[0],
306
+ Exclude<keyof DeclaredState, keyof InitialState>
307
+ >,
308
+ ): (
309
+ state: RootState,
310
+ ...args: Tail<Parameters<Selector>>
311
+ ) => ReturnType<Selector>
312
+ /**
313
+ * Returns the unproxied state. Useful for debugging.
314
+ * @param state state Proxy, that ensures injected reducers have value
315
+ * @returns original, unproxied state
316
+ * @throws if value passed is not a state Proxy
317
+ */
318
+ original: (state: DeclaredState) => InitialState & Partial<DeclaredState>
319
+ }
320
+ }
321
+
322
+ type InitialState<Slices extends Array<AnySliceLike | ReducerMap>> =
323
+ UnionToIntersection<
324
+ Slices[number] extends infer Slice
325
+ ? Slice extends AnySliceLike
326
+ ? WithSlice<Slice>
327
+ : StateFromReducersMapObject<Slice>
328
+ : never
329
+ >
330
+
331
+ type InitialPreloadedState<Slices extends Array<AnySliceLike | ReducerMap>> =
332
+ UnionToIntersection<
333
+ Slices[number] extends infer Slice
334
+ ? Slice extends AnySliceLike
335
+ ? WithSlicePreloadedState<Slice>
336
+ : PreloadedStateShapeFromReducersMapObject<Slice>
337
+ : never
338
+ >
339
+
340
+ const isSliceLike = (
341
+ maybeSliceLike: AnySliceLike | ReducerMap,
342
+ ): maybeSliceLike is AnySliceLike =>
343
+ 'reducerPath' in maybeSliceLike &&
344
+ typeof maybeSliceLike.reducerPath === 'string'
345
+
346
+ const getReducers = (slices: Array<AnySliceLike | ReducerMap>) =>
347
+ slices.flatMap<[string, Reducer]>((sliceOrMap) =>
348
+ isSliceLike(sliceOrMap)
349
+ ? [[sliceOrMap.reducerPath, sliceOrMap.reducer]]
350
+ : Object.entries(sliceOrMap),
351
+ )
352
+
353
+ const ORIGINAL_STATE = Symbol.for('rtk-state-proxy-original')
354
+
355
+ const isStateProxy = (value: any) => !!value && !!value[ORIGINAL_STATE]
356
+
357
+ const stateProxyMap = new WeakMap<object, object>()
358
+
359
+ const createStateProxy = <State extends object>(
360
+ state: State,
361
+ reducerMap: Partial<Record<PropertyKey, Reducer>>,
362
+ initialStateCache: Record<PropertyKey, unknown>,
363
+ ) =>
364
+ getOrInsertComputed(
365
+ stateProxyMap,
366
+ state,
367
+ () =>
368
+ new Proxy(state, {
369
+ get: (target, prop, receiver) => {
370
+ if (prop === ORIGINAL_STATE) return target
371
+ const result = Reflect.get(target, prop, receiver)
372
+ if (typeof result === 'undefined') {
373
+ const cached = initialStateCache[prop]
374
+ if (typeof cached !== 'undefined') return cached
375
+ const reducer = reducerMap[prop]
376
+ if (reducer) {
377
+ // ensure action type is random, to prevent reducer treating it differently
378
+ const reducerResult = reducer(undefined, { type: nanoid() })
379
+ if (typeof reducerResult === 'undefined') {
380
+ throw new Error(
381
+ `The slice reducer for key "${prop.toString()}" returned undefined when called for selector(). ` +
382
+ `If the state passed to the reducer is undefined, you must ` +
383
+ `explicitly return the initial state. The initial state may ` +
384
+ `not be undefined. If you don't want to set a value for this reducer, ` +
385
+ `you can use null instead of undefined.`,
386
+ )
387
+ }
388
+ initialStateCache[prop] = reducerResult
389
+ return reducerResult
390
+ }
391
+ }
392
+ return result
393
+ },
394
+ }),
395
+ ) as State
396
+
397
+ const original = (state: any) => {
398
+ if (!isStateProxy(state)) {
399
+ throw new Error('original must be used on state Proxy')
400
+ }
401
+ return state[ORIGINAL_STATE]
402
+ }
403
+
404
+ const emptyObject = {}
405
+ const noopReducer: Reducer<Record<string, any>> = (state = emptyObject) => state
406
+
407
+ export function combineSlices<Slices extends Array<AnySliceLike | ReducerMap>>(
408
+ ...slices: Slices
409
+ ): CombinedSliceReducer<
410
+ Id<InitialState<Slices>>,
411
+ Id<InitialState<Slices>>,
412
+ Partial<Id<InitialPreloadedState<Slices>>>
413
+ > {
414
+ const reducerMap = Object.fromEntries(getReducers(slices))
415
+
416
+ const getReducer = () =>
417
+ Object.keys(reducerMap).length ? combineReducers(reducerMap) : noopReducer
418
+
419
+ let reducer = getReducer()
420
+
421
+ function combinedReducer(
422
+ state: Record<string, unknown>,
423
+ action: UnknownAction,
424
+ ) {
425
+ return reducer(state, action)
426
+ }
427
+
428
+ combinedReducer.withLazyLoadedSlices = () => combinedReducer
429
+
430
+ const initialStateCache: Record<PropertyKey, unknown> = {}
431
+
432
+ const inject = (
433
+ slice: AnySliceLike,
434
+ config: InjectConfig = {},
435
+ ): typeof combinedReducer => {
436
+ const { reducerPath, reducer: reducerToInject } = slice
437
+
438
+ const currentReducer = reducerMap[reducerPath]
439
+ if (
440
+ !config.overrideExisting &&
441
+ currentReducer &&
442
+ currentReducer !== reducerToInject
443
+ ) {
444
+ if (
445
+ typeof process !== 'undefined' &&
446
+ process.env.NODE_ENV === 'development'
447
+ ) {
448
+ console.error(
449
+ `called \`inject\` to override already-existing reducer ${reducerPath} without specifying \`overrideExisting: true\``,
450
+ )
451
+ }
452
+
453
+ return combinedReducer
454
+ }
455
+
456
+ if (config.overrideExisting && currentReducer !== reducerToInject) {
457
+ delete initialStateCache[reducerPath]
458
+ }
459
+
460
+ reducerMap[reducerPath] = reducerToInject
461
+
462
+ reducer = getReducer()
463
+
464
+ return combinedReducer
465
+ }
466
+
467
+ const selector = Object.assign(
468
+ function makeSelector<State extends object, RootState, Args extends any[]>(
469
+ selectorFn: (state: State, ...args: Args) => any,
470
+ selectState?: (rootState: RootState, ...args: Args) => State,
471
+ ) {
472
+ return function selector(state: State, ...args: Args) {
473
+ return selectorFn(
474
+ createStateProxy(
475
+ selectState ? selectState(state as any, ...args) : state,
476
+ reducerMap,
477
+ initialStateCache,
478
+ ),
479
+ ...args,
480
+ )
481
+ }
482
+ },
483
+ { original },
484
+ )
485
+
486
+ return Object.assign(combinedReducer, { inject, selector }) as any
487
+ }