@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,30 @@
1
+ export const assertFunction: (
2
+ func: unknown,
3
+ expected: string,
4
+ ) => asserts func is (...args: unknown[]) => unknown = (
5
+ func: unknown,
6
+ expected: string,
7
+ ) => {
8
+ if (typeof func !== 'function') {
9
+ throw new TypeError(`${expected} is not a function`)
10
+ }
11
+ }
12
+
13
+ export const noop = () => {}
14
+
15
+ export const catchRejection = <T>(
16
+ promise: Promise<T>,
17
+ onError = noop,
18
+ ): Promise<T> => {
19
+ promise.catch(onError)
20
+
21
+ return promise
22
+ }
23
+
24
+ export const addAbortSignalListener = (
25
+ abortSignal: AbortSignal,
26
+ callback: (evt: Event) => void,
27
+ ) => {
28
+ abortSignal.addEventListener('abort', callback, { once: true })
29
+ return () => abortSignal.removeEventListener('abort', callback)
30
+ }
@@ -0,0 +1,298 @@
1
+ import type { Action } from 'redux'
2
+ import type {
3
+ CaseReducer,
4
+ CaseReducers,
5
+ ActionMatcherDescriptionCollection,
6
+ } from './createReducer'
7
+ import type { TypeGuard } from './tsHelpers'
8
+ import type { AsyncThunk, AsyncThunkConfig } from './createAsyncThunk'
9
+
10
+ export type AsyncThunkReducers<
11
+ State,
12
+ ThunkArg extends any,
13
+ Returned = unknown,
14
+ ThunkApiConfig extends AsyncThunkConfig = {},
15
+ > = {
16
+ pending?: CaseReducer<
17
+ State,
18
+ ReturnType<AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['pending']>
19
+ >
20
+ rejected?: CaseReducer<
21
+ State,
22
+ ReturnType<AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['rejected']>
23
+ >
24
+ fulfilled?: CaseReducer<
25
+ State,
26
+ ReturnType<AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['fulfilled']>
27
+ >
28
+ settled?: CaseReducer<
29
+ State,
30
+ ReturnType<
31
+ AsyncThunk<Returned, ThunkArg, ThunkApiConfig>['rejected' | 'fulfilled']
32
+ >
33
+ >
34
+ }
35
+
36
+ export type TypedActionCreator<Type extends string> = {
37
+ (...args: any[]): Action<Type>
38
+ type: Type
39
+ }
40
+
41
+ /**
42
+ * A builder for an action <-> reducer map.
43
+ *
44
+ * @public
45
+ */
46
+ export interface ActionReducerMapBuilder<State> {
47
+ /**
48
+ * Adds a case reducer to handle a single exact action type.
49
+ * @remarks
50
+ * All calls to `builder.addCase` must come before any calls to `builder.addMatcher` or `builder.addDefaultCase`.
51
+ * @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
52
+ * @param reducer - The actual case reducer function.
53
+ */
54
+ addCase<ActionCreator extends TypedActionCreator<string>>(
55
+ actionCreator: ActionCreator,
56
+ reducer: CaseReducer<State, ReturnType<ActionCreator>>,
57
+ ): ActionReducerMapBuilder<State>
58
+ /**
59
+ * Adds a case reducer to handle a single exact action type.
60
+ * @remarks
61
+ * All calls to `builder.addCase` must come before any calls to `builder.addAsyncThunk`, `builder.addMatcher` or `builder.addDefaultCase`.
62
+ * @param actionCreator - Either a plain action type string, or an action creator generated by [`createAction`](./createAction) that can be used to determine the action type.
63
+ * @param reducer - The actual case reducer function.
64
+ */
65
+ addCase<Type extends string, A extends Action<Type>>(
66
+ type: Type,
67
+ reducer: CaseReducer<State, A>,
68
+ ): ActionReducerMapBuilder<State>
69
+
70
+ /**
71
+ * Adds case reducers to handle actions based on a `AsyncThunk` action creator.
72
+ * @remarks
73
+ * All calls to `builder.addAsyncThunk` must come before after any calls to `builder.addCase` and before any calls to `builder.addMatcher` or `builder.addDefaultCase`.
74
+ * @param asyncThunk - The async thunk action creator itself.
75
+ * @param reducers - A mapping from each of the `AsyncThunk` action types to the case reducer that should handle those actions.
76
+ * @example
77
+ * ```ts no-transpile
78
+ * import { createAsyncThunk, createReducer } from '@reduxjs/toolkit';
79
+ *
80
+ * const fetchUserById = createAsyncThunk('users/fetchUser', async (id) => {
81
+ * const response = await fetch(`https://reqres.in/api/users/${id}`);
82
+ * return (await response.json()).data;
83
+ * });
84
+ *
85
+ * const reducer = createReducer(initialState, (builder) => {
86
+ * builder.addAsyncThunk(fetchUserById, {
87
+ * pending: (state, action) => {
88
+ * state.fetchUserById.loading = 'pending';
89
+ * },
90
+ * fulfilled: (state, action) => {
91
+ * state.fetchUserById.data = action.payload;
92
+ * },
93
+ * rejected: (state, action) => {
94
+ * state.fetchUserById.error = action.error;
95
+ * },
96
+ * settled: (state, action) => {
97
+ * state.fetchUserById.loading = action.meta.requestStatus;
98
+ * },
99
+ * });
100
+ * });
101
+ * ```
102
+ */
103
+ addAsyncThunk<
104
+ Returned,
105
+ ThunkArg,
106
+ ThunkApiConfig extends AsyncThunkConfig = {},
107
+ >(
108
+ asyncThunk: AsyncThunk<Returned, ThunkArg, ThunkApiConfig>,
109
+ reducers: AsyncThunkReducers<State, ThunkArg, Returned, ThunkApiConfig>,
110
+ ): Omit<ActionReducerMapBuilder<State>, 'addCase'>
111
+
112
+ /**
113
+ * Allows you to match your incoming actions against your own filter function instead of only the `action.type` property.
114
+ * @remarks
115
+ * If multiple matcher reducers match, all of them will be executed in the order
116
+ * they were defined in - even if a case reducer already matched.
117
+ * All calls to `builder.addMatcher` must come after any calls to `builder.addCase` and `builder.addAsyncThunk` and before any calls to `builder.addDefaultCase`.
118
+ * @param matcher - A matcher function. In TypeScript, this should be a [type predicate](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)
119
+ * function
120
+ * @param reducer - The actual case reducer function.
121
+ *
122
+ * @example
123
+ * ```ts
124
+ * import type { AsyncThunk, UnknownAction } from '@reduxjs/toolkit';
125
+ * import { createAction, createReducer } from '@reduxjs/toolkit';
126
+ *
127
+ * type GenericAsyncThunk = AsyncThunk<unknown, unknown, any>;
128
+ *
129
+ * type PendingAction = ReturnType<GenericAsyncThunk['pending']>;
130
+ * type RejectedAction = ReturnType<GenericAsyncThunk['rejected']>;
131
+ * type FulfilledAction = ReturnType<GenericAsyncThunk['fulfilled']>;
132
+ *
133
+ * const initialState: Record<string, string> = {};
134
+ * const resetAction = createAction('reset-tracked-loading-state');
135
+ *
136
+ * function isPendingAction(action: UnknownAction): action is PendingAction {
137
+ * return typeof action.type === 'string' && action.type.endsWith('/pending');
138
+ * }
139
+ *
140
+ * const reducer = createReducer(initialState, (builder) => {
141
+ * builder
142
+ * .addCase(resetAction, () => initialState)
143
+ * // matcher can be defined outside as a type predicate function
144
+ * .addMatcher(isPendingAction, (state, action) => {
145
+ * state[action.meta.requestId] = 'pending';
146
+ * })
147
+ * .addMatcher(
148
+ * // matcher can be defined inline as a type predicate function
149
+ * (action): action is RejectedAction => action.type.endsWith('/rejected'),
150
+ * (state, action) => {
151
+ * state[action.meta.requestId] = 'rejected';
152
+ * },
153
+ * )
154
+ * // matcher can just return boolean and the matcher can receive a generic argument
155
+ * .addMatcher<FulfilledAction>(
156
+ * (action) => action.type.endsWith('/fulfilled'),
157
+ * (state, action) => {
158
+ * state[action.meta.requestId] = 'fulfilled';
159
+ * },
160
+ * );
161
+ * });
162
+ * ```
163
+ */
164
+ addMatcher<A>(
165
+ matcher: TypeGuard<A> | ((action: any) => boolean),
166
+ reducer: CaseReducer<State, A extends Action ? A : A & Action>,
167
+ ): Omit<ActionReducerMapBuilder<State>, 'addCase' | 'addAsyncThunk'>
168
+
169
+ /**
170
+ * Adds a "default case" reducer that is executed if no case reducer and no matcher
171
+ * reducer was executed for this action.
172
+ * @param reducer - The fallback "default case" reducer function.
173
+ *
174
+ * @example
175
+ * ```ts
176
+ * import { createReducer } from '@reduxjs/toolkit';
177
+ *
178
+ * const initialState = { otherActions: 0 };
179
+ *
180
+ * const reducer = createReducer(initialState, (builder) => {
181
+ * builder
182
+ * // .addCase(...)
183
+ * // .addMatcher(...)
184
+ * .addDefaultCase((state, action) => {
185
+ * state.otherActions++;
186
+ * });
187
+ * });
188
+ * ```
189
+ */
190
+ addDefaultCase(reducer: CaseReducer<State, Action>): {}
191
+ }
192
+
193
+ export function executeReducerBuilderCallback<S>(
194
+ builderCallback: (builder: ActionReducerMapBuilder<S>) => void,
195
+ ): [
196
+ CaseReducers<S, any>,
197
+ ActionMatcherDescriptionCollection<S>,
198
+ CaseReducer<S, Action> | undefined,
199
+ ] {
200
+ const actionsMap: CaseReducers<S, any> = {}
201
+ const actionMatchers: ActionMatcherDescriptionCollection<S> = []
202
+ let defaultCaseReducer: CaseReducer<S, Action> | undefined
203
+ const builder = {
204
+ addCase(
205
+ typeOrActionCreator: string | TypedActionCreator<any>,
206
+ reducer: CaseReducer<S>,
207
+ ) {
208
+ if (process.env.NODE_ENV !== 'production') {
209
+ /*
210
+ to keep the definition by the user in line with actual behavior,
211
+ we enforce `addCase` to always be called before calling `addMatcher`
212
+ as matching cases take precedence over matchers
213
+ */
214
+ if (actionMatchers.length > 0) {
215
+ throw new Error(
216
+ '`builder.addCase` should only be called before calling `builder.addMatcher`',
217
+ )
218
+ }
219
+ if (defaultCaseReducer) {
220
+ throw new Error(
221
+ '`builder.addCase` should only be called before calling `builder.addDefaultCase`',
222
+ )
223
+ }
224
+ }
225
+ const type =
226
+ typeof typeOrActionCreator === 'string'
227
+ ? typeOrActionCreator
228
+ : typeOrActionCreator.type
229
+ if (!type) {
230
+ throw new Error(
231
+ '`builder.addCase` cannot be called with an empty action type',
232
+ )
233
+ }
234
+ if (type in actionsMap) {
235
+ throw new Error(
236
+ '`builder.addCase` cannot be called with two reducers for the same action type ' +
237
+ `'${type}'`,
238
+ )
239
+ }
240
+ actionsMap[type] = reducer
241
+ return builder
242
+ },
243
+ addAsyncThunk<
244
+ Returned,
245
+ ThunkArg,
246
+ ThunkApiConfig extends AsyncThunkConfig = {},
247
+ >(
248
+ asyncThunk: AsyncThunk<Returned, ThunkArg, ThunkApiConfig>,
249
+ reducers: AsyncThunkReducers<S, ThunkArg, Returned, ThunkApiConfig>,
250
+ ) {
251
+ if (process.env.NODE_ENV !== 'production') {
252
+ // since this uses both action cases and matchers, we can't enforce the order in runtime other than checking for default case
253
+ if (defaultCaseReducer) {
254
+ throw new Error(
255
+ '`builder.addAsyncThunk` should only be called before calling `builder.addDefaultCase`',
256
+ )
257
+ }
258
+ }
259
+ if (reducers.pending)
260
+ actionsMap[asyncThunk.pending.type] = reducers.pending
261
+ if (reducers.rejected)
262
+ actionsMap[asyncThunk.rejected.type] = reducers.rejected
263
+ if (reducers.fulfilled)
264
+ actionsMap[asyncThunk.fulfilled.type] = reducers.fulfilled
265
+ if (reducers.settled)
266
+ actionMatchers.push({
267
+ matcher: asyncThunk.settled,
268
+ reducer: reducers.settled,
269
+ })
270
+ return builder
271
+ },
272
+ addMatcher<A>(
273
+ matcher: TypeGuard<A>,
274
+ reducer: CaseReducer<S, A extends Action ? A : A & Action>,
275
+ ) {
276
+ if (process.env.NODE_ENV !== 'production') {
277
+ if (defaultCaseReducer) {
278
+ throw new Error(
279
+ '`builder.addMatcher` should only be called before calling `builder.addDefaultCase`',
280
+ )
281
+ }
282
+ }
283
+ actionMatchers.push({ matcher, reducer })
284
+ return builder
285
+ },
286
+ addDefaultCase(reducer: CaseReducer<S, Action>) {
287
+ if (process.env.NODE_ENV !== 'production') {
288
+ if (defaultCaseReducer) {
289
+ throw new Error('`builder.addDefaultCase` can only be called once')
290
+ }
291
+ }
292
+ defaultCaseReducer = reducer
293
+ return builder
294
+ },
295
+ }
296
+ builderCallback(builder)
297
+ return [actionsMap, actionMatchers, defaultCaseReducer]
298
+ }
@@ -0,0 +1,365 @@
1
+ import type {
2
+ ActionFromMatcher,
3
+ Matcher,
4
+ UnionToIntersection,
5
+ } from './tsHelpers'
6
+ import { hasMatchFunction } from './tsHelpers'
7
+ import type {
8
+ AsyncThunk,
9
+ AsyncThunkFulfilledActionCreator,
10
+ AsyncThunkPendingActionCreator,
11
+ AsyncThunkRejectedActionCreator,
12
+ } from './createAsyncThunk'
13
+
14
+ /** @public */
15
+ export type ActionMatchingAnyOf<Matchers extends Matcher<any>[]> =
16
+ ActionFromMatcher<Matchers[number]>
17
+
18
+ /** @public */
19
+ export type ActionMatchingAllOf<Matchers extends Matcher<any>[]> =
20
+ UnionToIntersection<ActionMatchingAnyOf<Matchers>>
21
+
22
+ const matches = (matcher: Matcher<any>, action: any) => {
23
+ if (hasMatchFunction(matcher)) {
24
+ return matcher.match(action)
25
+ } else {
26
+ return matcher(action)
27
+ }
28
+ }
29
+
30
+ /**
31
+ * A higher-order function that returns a function that may be used to check
32
+ * whether an action matches any one of the supplied type guards or action
33
+ * creators.
34
+ *
35
+ * @param matchers The type guards or action creators to match against.
36
+ *
37
+ * @public
38
+ */
39
+ export function isAnyOf<Matchers extends Matcher<any>[]>(
40
+ ...matchers: Matchers
41
+ ) {
42
+ return (action: any): action is ActionMatchingAnyOf<Matchers> => {
43
+ return matchers.some((matcher) => matches(matcher, action))
44
+ }
45
+ }
46
+
47
+ /**
48
+ * A higher-order function that returns a function that may be used to check
49
+ * whether an action matches all of the supplied type guards or action
50
+ * creators.
51
+ *
52
+ * @param matchers The type guards or action creators to match against.
53
+ *
54
+ * @public
55
+ */
56
+ export function isAllOf<Matchers extends Matcher<any>[]>(
57
+ ...matchers: Matchers
58
+ ) {
59
+ return (action: any): action is ActionMatchingAllOf<Matchers> => {
60
+ return matchers.every((matcher) => matches(matcher, action))
61
+ }
62
+ }
63
+
64
+ /**
65
+ * @param action A redux action
66
+ * @param validStatus An array of valid meta.requestStatus values
67
+ *
68
+ * @internal
69
+ */
70
+ export function hasExpectedRequestMetadata(
71
+ action: any,
72
+ validStatus: readonly string[],
73
+ ) {
74
+ if (!action || !action.meta) return false
75
+
76
+ const hasValidRequestId = typeof action.meta.requestId === 'string'
77
+ const hasValidRequestStatus =
78
+ validStatus.indexOf(action.meta.requestStatus) > -1
79
+
80
+ return hasValidRequestId && hasValidRequestStatus
81
+ }
82
+
83
+ function isAsyncThunkArray(a: [any] | AnyAsyncThunk[]): a is AnyAsyncThunk[] {
84
+ return (
85
+ typeof a[0] === 'function' &&
86
+ 'pending' in a[0] &&
87
+ 'fulfilled' in a[0] &&
88
+ 'rejected' in a[0]
89
+ )
90
+ }
91
+
92
+ export type UnknownAsyncThunkPendingAction = ReturnType<
93
+ AsyncThunkPendingActionCreator<unknown>
94
+ >
95
+
96
+ export type PendingActionFromAsyncThunk<T extends AnyAsyncThunk> =
97
+ ActionFromMatcher<T['pending']>
98
+
99
+ /**
100
+ * A higher-order function that returns a function that may be used to check
101
+ * whether an action was created by an async thunk action creator, and that
102
+ * the action is pending.
103
+ *
104
+ * @public
105
+ */
106
+ export function isPending(): (
107
+ action: any,
108
+ ) => action is UnknownAsyncThunkPendingAction
109
+ /**
110
+ * A higher-order function that returns a function that may be used to check
111
+ * whether an action belongs to one of the provided async thunk action creators,
112
+ * and that the action is pending.
113
+ *
114
+ * @param asyncThunks (optional) The async thunk action creators to match against.
115
+ *
116
+ * @public
117
+ */
118
+ export function isPending<
119
+ AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
120
+ >(
121
+ ...asyncThunks: AsyncThunks
122
+ ): (action: any) => action is PendingActionFromAsyncThunk<AsyncThunks[number]>
123
+ /**
124
+ * Tests if `action` is a pending thunk action
125
+ * @public
126
+ */
127
+ export function isPending(action: any): action is UnknownAsyncThunkPendingAction
128
+ export function isPending<
129
+ AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
130
+ >(...asyncThunks: AsyncThunks | [any]) {
131
+ if (asyncThunks.length === 0) {
132
+ return (action: any) => hasExpectedRequestMetadata(action, ['pending'])
133
+ }
134
+
135
+ if (!isAsyncThunkArray(asyncThunks)) {
136
+ return isPending()(asyncThunks[0])
137
+ }
138
+
139
+ return isAnyOf(...asyncThunks.map((asyncThunk) => asyncThunk.pending))
140
+ }
141
+
142
+ export type UnknownAsyncThunkRejectedAction = ReturnType<
143
+ AsyncThunkRejectedActionCreator<unknown, unknown>
144
+ >
145
+
146
+ export type RejectedActionFromAsyncThunk<T extends AnyAsyncThunk> =
147
+ ActionFromMatcher<T['rejected']>
148
+
149
+ /**
150
+ * A higher-order function that returns a function that may be used to check
151
+ * whether an action was created by an async thunk action creator, and that
152
+ * the action is rejected.
153
+ *
154
+ * @public
155
+ */
156
+ export function isRejected(): (
157
+ action: any,
158
+ ) => action is UnknownAsyncThunkRejectedAction
159
+ /**
160
+ * A higher-order function that returns a function that may be used to check
161
+ * whether an action belongs to one of the provided async thunk action creators,
162
+ * and that the action is rejected.
163
+ *
164
+ * @param asyncThunks (optional) The async thunk action creators to match against.
165
+ *
166
+ * @public
167
+ */
168
+ export function isRejected<
169
+ AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
170
+ >(
171
+ ...asyncThunks: AsyncThunks
172
+ ): (action: any) => action is RejectedActionFromAsyncThunk<AsyncThunks[number]>
173
+ /**
174
+ * Tests if `action` is a rejected thunk action
175
+ * @public
176
+ */
177
+ export function isRejected(
178
+ action: any,
179
+ ): action is UnknownAsyncThunkRejectedAction
180
+ export function isRejected<
181
+ AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
182
+ >(...asyncThunks: AsyncThunks | [any]) {
183
+ if (asyncThunks.length === 0) {
184
+ return (action: any) => hasExpectedRequestMetadata(action, ['rejected'])
185
+ }
186
+
187
+ if (!isAsyncThunkArray(asyncThunks)) {
188
+ return isRejected()(asyncThunks[0])
189
+ }
190
+
191
+ return isAnyOf(...asyncThunks.map((asyncThunk) => asyncThunk.rejected))
192
+ }
193
+
194
+ export type UnknownAsyncThunkRejectedWithValueAction = ReturnType<
195
+ AsyncThunkRejectedActionCreator<unknown, unknown>
196
+ >
197
+
198
+ export type RejectedWithValueActionFromAsyncThunk<T extends AnyAsyncThunk> =
199
+ ActionFromMatcher<T['rejected']> &
200
+ (T extends AsyncThunk<any, any, { rejectValue: infer RejectedValue }>
201
+ ? { payload: RejectedValue }
202
+ : unknown)
203
+
204
+ /**
205
+ * A higher-order function that returns a function that may be used to check
206
+ * whether an action was created by an async thunk action creator, and that
207
+ * the action is rejected with value.
208
+ *
209
+ * @public
210
+ */
211
+ export function isRejectedWithValue(): (
212
+ action: any,
213
+ ) => action is UnknownAsyncThunkRejectedAction
214
+ /**
215
+ * A higher-order function that returns a function that may be used to check
216
+ * whether an action belongs to one of the provided async thunk action creators,
217
+ * and that the action is rejected with value.
218
+ *
219
+ * @param asyncThunks (optional) The async thunk action creators to match against.
220
+ *
221
+ * @public
222
+ */
223
+ export function isRejectedWithValue<
224
+ AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
225
+ >(
226
+ ...asyncThunks: AsyncThunks
227
+ ): (
228
+ action: any,
229
+ ) => action is RejectedWithValueActionFromAsyncThunk<AsyncThunks[number]>
230
+ /**
231
+ * Tests if `action` is a rejected thunk action with value
232
+ * @public
233
+ */
234
+ export function isRejectedWithValue(
235
+ action: any,
236
+ ): action is UnknownAsyncThunkRejectedAction
237
+ export function isRejectedWithValue<
238
+ AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
239
+ >(...asyncThunks: AsyncThunks | [any]) {
240
+ const hasFlag = (action: any): action is any => {
241
+ return action && action.meta && action.meta.rejectedWithValue
242
+ }
243
+
244
+ if (asyncThunks.length === 0) {
245
+ return isAllOf(isRejected(...asyncThunks), hasFlag)
246
+ }
247
+
248
+ if (!isAsyncThunkArray(asyncThunks)) {
249
+ return isRejectedWithValue()(asyncThunks[0])
250
+ }
251
+
252
+ return isAllOf(isRejected(...asyncThunks), hasFlag)
253
+ }
254
+
255
+ export type UnknownAsyncThunkFulfilledAction = ReturnType<
256
+ AsyncThunkFulfilledActionCreator<unknown, unknown>
257
+ >
258
+
259
+ export type FulfilledActionFromAsyncThunk<T extends AnyAsyncThunk> =
260
+ ActionFromMatcher<T['fulfilled']>
261
+
262
+ /**
263
+ * A higher-order function that returns a function that may be used to check
264
+ * whether an action was created by an async thunk action creator, and that
265
+ * the action is fulfilled.
266
+ *
267
+ * @public
268
+ */
269
+ export function isFulfilled(): (
270
+ action: any,
271
+ ) => action is UnknownAsyncThunkFulfilledAction
272
+ /**
273
+ * A higher-order function that returns a function that may be used to check
274
+ * whether an action belongs to one of the provided async thunk action creators,
275
+ * and that the action is fulfilled.
276
+ *
277
+ * @param asyncThunks (optional) The async thunk action creators to match against.
278
+ *
279
+ * @public
280
+ */
281
+ export function isFulfilled<
282
+ AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
283
+ >(
284
+ ...asyncThunks: AsyncThunks
285
+ ): (action: any) => action is FulfilledActionFromAsyncThunk<AsyncThunks[number]>
286
+ /**
287
+ * Tests if `action` is a fulfilled thunk action
288
+ * @public
289
+ */
290
+ export function isFulfilled(
291
+ action: any,
292
+ ): action is UnknownAsyncThunkFulfilledAction
293
+ export function isFulfilled<
294
+ AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
295
+ >(...asyncThunks: AsyncThunks | [any]) {
296
+ if (asyncThunks.length === 0) {
297
+ return (action: any) => hasExpectedRequestMetadata(action, ['fulfilled'])
298
+ }
299
+
300
+ if (!isAsyncThunkArray(asyncThunks)) {
301
+ return isFulfilled()(asyncThunks[0])
302
+ }
303
+
304
+ return isAnyOf(...asyncThunks.map((asyncThunk) => asyncThunk.fulfilled))
305
+ }
306
+
307
+ export type UnknownAsyncThunkAction =
308
+ | UnknownAsyncThunkPendingAction
309
+ | UnknownAsyncThunkRejectedAction
310
+ | UnknownAsyncThunkFulfilledAction
311
+
312
+ export type AnyAsyncThunk = {
313
+ pending: { match: (action: any) => action is any }
314
+ fulfilled: { match: (action: any) => action is any }
315
+ rejected: { match: (action: any) => action is any }
316
+ }
317
+
318
+ export type ActionsFromAsyncThunk<T extends AnyAsyncThunk> =
319
+ | ActionFromMatcher<T['pending']>
320
+ | ActionFromMatcher<T['fulfilled']>
321
+ | ActionFromMatcher<T['rejected']>
322
+
323
+ /**
324
+ * A higher-order function that returns a function that may be used to check
325
+ * whether an action was created by an async thunk action creator.
326
+ *
327
+ * @public
328
+ */
329
+ export function isAsyncThunkAction(): (
330
+ action: any,
331
+ ) => action is UnknownAsyncThunkAction
332
+ /**
333
+ * A higher-order function that returns a function that may be used to check
334
+ * whether an action belongs to one of the provided async thunk action creators.
335
+ *
336
+ * @param asyncThunks (optional) The async thunk action creators to match against.
337
+ *
338
+ * @public
339
+ */
340
+ export function isAsyncThunkAction<
341
+ AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
342
+ >(
343
+ ...asyncThunks: AsyncThunks
344
+ ): (action: any) => action is ActionsFromAsyncThunk<AsyncThunks[number]>
345
+ /**
346
+ * Tests if `action` is a thunk action
347
+ * @public
348
+ */
349
+ export function isAsyncThunkAction(
350
+ action: any,
351
+ ): action is UnknownAsyncThunkAction
352
+ export function isAsyncThunkAction<
353
+ AsyncThunks extends [AnyAsyncThunk, ...AnyAsyncThunk[]],
354
+ >(...asyncThunks: AsyncThunks | [any]) {
355
+ if (asyncThunks.length === 0) {
356
+ return (action: any) =>
357
+ hasExpectedRequestMetadata(action, ['pending', 'fulfilled', 'rejected'])
358
+ }
359
+
360
+ if (!isAsyncThunkArray(asyncThunks)) {
361
+ return isAsyncThunkAction()(asyncThunks[0])
362
+ }
363
+
364
+ return isAnyOf(...asyncThunks.flatMap(asyncThunk => [asyncThunk.pending, asyncThunk.rejected, asyncThunk.fulfilled]))
365
+ }