1import type { Middleware, AnyAction } from 'redux'
2import type { ThunkMiddleware } from 'redux-thunk'
3import thunkMiddleware from 'redux-thunk'
4import type { ImmutableStateInvariantMiddlewareOptions } from './immutableStateInvariantMiddleware'
5/* PROD_START_REMOVE_UMD */
6import { createImmutableStateInvariantMiddleware } from './immutableStateInvariantMiddleware'
7/* PROD_STOP_REMOVE_UMD */
8
9import type { SerializableStateInvariantMiddlewareOptions } from './serializableStateInvariantMiddleware'
10import { createSerializableStateInvariantMiddleware } from './serializableStateInvariantMiddleware'
11import { MiddlewareArray } from './utils'
12
13function isBoolean(x: any): x is boolean {
14  return typeof x === 'boolean'
15}
16
17interface ThunkOptions<E = any> {
18  extraArgument: E
19}
20
21interface GetDefaultMiddlewareOptions {
22  thunk?: boolean | ThunkOptions
23  immutableCheck?: boolean | ImmutableStateInvariantMiddlewareOptions
24  serializableCheck?: boolean | SerializableStateInvariantMiddlewareOptions
25}
26
27export type ThunkMiddlewareFor<
28  S,
29  O extends GetDefaultMiddlewareOptions = {}
30> = O extends {
31  thunk: false
32}
33  ? never
34  : O extends { thunk: { extraArgument: infer E } }
35  ? ThunkMiddleware<S, AnyAction, E>
36  :
37      | ThunkMiddleware<S, AnyAction, null> //The ThunkMiddleware with a `null` ExtraArgument is here to provide backwards-compatibility.
38      | ThunkMiddleware<S, AnyAction>
39
40export type CurriedGetDefaultMiddleware<S = any> = <
41  O extends Partial<GetDefaultMiddlewareOptions> = {
42    thunk: true
43    immutableCheck: true
44    serializableCheck: true
45  }
46>(
47  options?: O
48) => MiddlewareArray<Middleware<{}, S> | ThunkMiddlewareFor<S, O>>
49
50export function curryGetDefaultMiddleware<
51  S = any
52>(): CurriedGetDefaultMiddleware<S> {
53  return function curriedGetDefaultMiddleware(options) {
54    return getDefaultMiddleware(options)
55  }
56}
57
58/**
59 * Returns any array containing the default middleware installed by
60 * `configureStore()`. Useful if you want to configure your store with a custom
61 * `middleware` array but still keep the default set.
62 *
63 * @return The default middleware used by `configureStore()`.
64 *
65 * @public
66 *
67 * @deprecated Prefer to use the callback notation for the `middleware` option in `configureStore`
68 * to access a pre-typed `getDefaultMiddleware` instead.
69 */
70export function getDefaultMiddleware<
71  S = any,
72  O extends Partial<GetDefaultMiddlewareOptions> = {
73    thunk: true
74    immutableCheck: true
75    serializableCheck: true
76  }
77>(
78  options: O = {} as O
79): MiddlewareArray<Middleware<{}, S> | ThunkMiddlewareFor<S, O>> {
80  const {
81    thunk = true,
82    immutableCheck = true,
83    serializableCheck = true,
84  } = options
85
86  let middlewareArray: Middleware<{}, S>[] = new MiddlewareArray()
87
88  if (thunk) {
89    if (isBoolean(thunk)) {
90      middlewareArray.push(thunkMiddleware)
91    } else {
92      middlewareArray.push(
93        thunkMiddleware.withExtraArgument(thunk.extraArgument)
94      )
95    }
96  }
97
98  if (process.env.NODE_ENV !== 'production') {
99    if (immutableCheck) {
100      /* PROD_START_REMOVE_UMD */
101      let immutableOptions: ImmutableStateInvariantMiddlewareOptions = {}
102
103      if (!isBoolean(immutableCheck)) {
104        immutableOptions = immutableCheck
105      }
106
107      middlewareArray.unshift(
108        createImmutableStateInvariantMiddleware(immutableOptions)
109      )
110      /* PROD_STOP_REMOVE_UMD */
111    }
112
113    if (serializableCheck) {
114      let serializableOptions: SerializableStateInvariantMiddlewareOptions = {}
115
116      if (!isBoolean(serializableCheck)) {
117        serializableOptions = serializableCheck
118      }
119
120      middlewareArray.push(
121        createSerializableStateInvariantMiddleware(serializableOptions)
122      )
123    }
124  }
125
126  return middlewareArray as any
127}
128