@mapsight/lib-redux 2.0.0

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 (149) hide show
  1. package/README.md +20 -0
  2. package/dist/README.md +20 -0
  3. package/dist/clone-action.d.ts +9 -0
  4. package/dist/clone-action.d.ts.map +1 -0
  5. package/dist/clone-action.js +30 -0
  6. package/dist/clone-action.js.map +1 -0
  7. package/dist/combine-sub-path-reducers.d.ts +13 -0
  8. package/dist/combine-sub-path-reducers.d.ts.map +1 -0
  9. package/dist/combine-sub-path-reducers.js +34 -0
  10. package/dist/combine-sub-path-reducers.js.map +1 -0
  11. package/dist/create-filtered-reducer-for-path.d.ts +13 -0
  12. package/dist/create-filtered-reducer-for-path.d.ts.map +1 -0
  13. package/dist/create-filtered-reducer-for-path.js +37 -0
  14. package/dist/create-filtered-reducer-for-path.js.map +1 -0
  15. package/dist/create-immutable-path-reducer.d.ts +13 -0
  16. package/dist/create-immutable-path-reducer.d.ts.map +1 -0
  17. package/dist/create-immutable-path-reducer.js +30 -0
  18. package/dist/create-immutable-path-reducer.js.map +1 -0
  19. package/dist/create-prefixed-async-action-middleware.d.ts +16 -0
  20. package/dist/create-prefixed-async-action-middleware.d.ts.map +1 -0
  21. package/dist/create-prefixed-async-action-middleware.js +25 -0
  22. package/dist/create-prefixed-async-action-middleware.js.map +1 -0
  23. package/dist/createSelectorUsingOwnProps.d.ts +23 -0
  24. package/dist/createSelectorUsingOwnProps.d.ts.map +1 -0
  25. package/dist/createSelectorUsingOwnProps.js +47 -0
  26. package/dist/createSelectorUsingOwnProps.js.map +1 -0
  27. package/dist/deep-change-state.d.ts +14 -0
  28. package/dist/deep-change-state.d.ts.map +1 -0
  29. package/dist/deep-change-state.js +29 -0
  30. package/dist/deep-change-state.js.map +1 -0
  31. package/dist/enable-async-dispatch.d.ts +13 -0
  32. package/dist/enable-async-dispatch.d.ts.map +1 -0
  33. package/dist/enable-async-dispatch.js +48 -0
  34. package/dist/enable-async-dispatch.js.map +1 -0
  35. package/dist/enable-controlled-dispatch-and-observe.d.ts +21 -0
  36. package/dist/enable-controlled-dispatch-and-observe.d.ts.map +1 -0
  37. package/dist/enable-controlled-dispatch-and-observe.js +59 -0
  38. package/dist/enable-controlled-dispatch-and-observe.js.map +1 -0
  39. package/dist/enable-initialization.d.ts +12 -0
  40. package/dist/enable-initialization.d.ts.map +1 -0
  41. package/dist/enable-initialization.js +25 -0
  42. package/dist/enable-initialization.js.map +1 -0
  43. package/dist/flatten-actions.d.ts +9 -0
  44. package/dist/flatten-actions.d.ts.map +1 -0
  45. package/dist/flatten-actions.js +18 -0
  46. package/dist/flatten-actions.js.map +1 -0
  47. package/dist/index.d.ts +2 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +3 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/local-storage.d.ts +15 -0
  52. package/dist/local-storage.d.ts.map +1 -0
  53. package/dist/local-storage.js +66 -0
  54. package/dist/local-storage.js.map +1 -0
  55. package/dist/matchPath.d.ts +15 -0
  56. package/dist/matchPath.d.ts.map +1 -0
  57. package/dist/matchPath.js +68 -0
  58. package/dist/matchPath.js.map +1 -0
  59. package/dist/matchesPath.d.ts +14 -0
  60. package/dist/matchesPath.d.ts.map +1 -0
  61. package/dist/matchesPath.js +19 -0
  62. package/dist/matchesPath.js.map +1 -0
  63. package/dist/observe-state.d.ts +8 -0
  64. package/dist/observe-state.d.ts.map +1 -0
  65. package/dist/observe-state.js +42 -0
  66. package/dist/observe-state.js.map +1 -0
  67. package/dist/package.json +52 -0
  68. package/dist/reducers/immutable/add-to.d.ts +4 -0
  69. package/dist/reducers/immutable/add-to.d.ts.map +1 -0
  70. package/dist/reducers/immutable/add-to.js +3 -0
  71. package/dist/reducers/immutable/add-to.js.map +1 -0
  72. package/dist/reducers/immutable/index.d.ts +4 -0
  73. package/dist/reducers/immutable/index.d.ts.map +1 -0
  74. package/dist/reducers/immutable/index.js +14 -0
  75. package/dist/reducers/immutable/index.js.map +1 -0
  76. package/dist/reducers/immutable/merge.d.ts +4 -0
  77. package/dist/reducers/immutable/merge.d.ts.map +1 -0
  78. package/dist/reducers/immutable/merge.js +9 -0
  79. package/dist/reducers/immutable/merge.js.map +1 -0
  80. package/dist/reducers/immutable/noop.d.ts +4 -0
  81. package/dist/reducers/immutable/noop.d.ts.map +1 -0
  82. package/dist/reducers/immutable/noop.js +3 -0
  83. package/dist/reducers/immutable/noop.js.map +1 -0
  84. package/dist/reducers/immutable/remove-from.d.ts +4 -0
  85. package/dist/reducers/immutable/remove-from.d.ts.map +1 -0
  86. package/dist/reducers/immutable/remove-from.js +3 -0
  87. package/dist/reducers/immutable/remove-from.js.map +1 -0
  88. package/dist/reducers/immutable/set.d.ts +4 -0
  89. package/dist/reducers/immutable/set.d.ts.map +1 -0
  90. package/dist/reducers/immutable/set.js +3 -0
  91. package/dist/reducers/immutable/set.js.map +1 -0
  92. package/dist/reducers/immutable-path/add-to.d.ts +3 -0
  93. package/dist/reducers/immutable-path/add-to.d.ts.map +1 -0
  94. package/dist/reducers/immutable-path/add-to.js +4 -0
  95. package/dist/reducers/immutable-path/add-to.js.map +1 -0
  96. package/dist/reducers/immutable-path/index.d.ts +11 -0
  97. package/dist/reducers/immutable-path/index.d.ts.map +1 -0
  98. package/dist/reducers/immutable-path/index.js +14 -0
  99. package/dist/reducers/immutable-path/index.js.map +1 -0
  100. package/dist/reducers/immutable-path/merge.d.ts +3 -0
  101. package/dist/reducers/immutable-path/merge.d.ts.map +1 -0
  102. package/dist/reducers/immutable-path/merge.js +4 -0
  103. package/dist/reducers/immutable-path/merge.js.map +1 -0
  104. package/dist/reducers/immutable-path/noop.d.ts +2 -0
  105. package/dist/reducers/immutable-path/noop.d.ts.map +1 -0
  106. package/dist/reducers/immutable-path/noop.js +4 -0
  107. package/dist/reducers/immutable-path/noop.js.map +1 -0
  108. package/dist/reducers/immutable-path/remove-from.d.ts +3 -0
  109. package/dist/reducers/immutable-path/remove-from.d.ts.map +1 -0
  110. package/dist/reducers/immutable-path/remove-from.js +4 -0
  111. package/dist/reducers/immutable-path/remove-from.js.map +1 -0
  112. package/dist/reducers/immutable-path/set.d.ts +3 -0
  113. package/dist/reducers/immutable-path/set.d.ts.map +1 -0
  114. package/dist/reducers/immutable-path/set.js +4 -0
  115. package/dist/reducers/immutable-path/set.js.map +1 -0
  116. package/dist/reducers/reduce-by-keys.d.ts +2 -0
  117. package/dist/reducers/reduce-by-keys.d.ts.map +1 -0
  118. package/dist/reducers/reduce-by-keys.js +14 -0
  119. package/dist/reducers/reduce-by-keys.js.map +1 -0
  120. package/package.json +51 -0
  121. package/src/js/clone-action.ts +36 -0
  122. package/src/js/combine-sub-path-reducers.ts +44 -0
  123. package/src/js/create-filtered-reducer-for-path.ts +52 -0
  124. package/src/js/create-immutable-path-reducer.ts +41 -0
  125. package/src/js/create-prefixed-async-action-middleware.ts +47 -0
  126. package/src/js/createSelectorUsingOwnProps.ts +84 -0
  127. package/src/js/deep-change-state.ts +37 -0
  128. package/src/js/enable-async-dispatch.ts +61 -0
  129. package/src/js/enable-controlled-dispatch-and-observe.ts +128 -0
  130. package/src/js/enable-initialization.ts +36 -0
  131. package/src/js/flatten-actions.ts +22 -0
  132. package/src/js/index.ts +1 -0
  133. package/src/js/local-storage.ts +96 -0
  134. package/src/js/matchPath.ts +78 -0
  135. package/src/js/matchesPath.ts +23 -0
  136. package/src/js/observe-state.ts +109 -0
  137. package/src/js/reducers/immutable/add-to.ts +6 -0
  138. package/src/js/reducers/immutable/index.ts +17 -0
  139. package/src/js/reducers/immutable/merge.ts +18 -0
  140. package/src/js/reducers/immutable/noop.ts +5 -0
  141. package/src/js/reducers/immutable/remove-from.ts +8 -0
  142. package/src/js/reducers/immutable/set.ts +5 -0
  143. package/src/js/reducers/immutable-path/add-to.ts +4 -0
  144. package/src/js/reducers/immutable-path/index.ts +17 -0
  145. package/src/js/reducers/immutable-path/merge.ts +4 -0
  146. package/src/js/reducers/immutable-path/noop.ts +3 -0
  147. package/src/js/reducers/immutable-path/remove-from.ts +4 -0
  148. package/src/js/reducers/immutable-path/set.ts +4 -0
  149. package/src/js/reducers/reduce-by-keys.ts +19 -0
@@ -0,0 +1,128 @@
1
+ import {AnyAction, Dispatch, Store} from "redux";
2
+
3
+ type CompareFunction = (a: unknown, b: unknown) => boolean;
4
+
5
+ const strictEqualCompare: CompareFunction = (a, b) => a === b;
6
+
7
+ type UncontrolledActionListener<State = unknown> = (
8
+ previousState: State,
9
+ state: State,
10
+ ) => void;
11
+ type SubscribeUncontrolled<State = unknown> = (
12
+ listener: UncontrolledActionListener<State>,
13
+ ) => () => void;
14
+
15
+ export type ObserveHandler<State = unknown, Value = unknown> = (
16
+ newValue?: Value,
17
+ previousValue?: Value,
18
+ state?: State,
19
+ ) => void;
20
+
21
+ export type ObserveUncontrolled<State = unknown, Value = unknown> = (
22
+ selector: (state: State) => Value,
23
+ onChange: ObserveHandler<State, Value>,
24
+ compare?: CompareFunction,
25
+ ) => () => void;
26
+
27
+ export type StoreExtControlledActions<TState = unknown> = {
28
+ subscribeUncontrolled: SubscribeUncontrolled<TState>;
29
+ observeUncontrolled: ObserveUncontrolled<TState>;
30
+ };
31
+
32
+ export type StoreWithControlledActions<
33
+ S extends Store = Store,
34
+ TState = unknown,
35
+ > = S & StoreExtControlledActions<TState>;
36
+
37
+ function isActionWithMeta(
38
+ action: AnyAction,
39
+ ): action is AnyAction & {meta: Record<string, unknown>} {
40
+ return "meta" in action;
41
+ }
42
+
43
+ function isControlledAction(
44
+ action: AnyAction,
45
+ controlledActionFlag: string,
46
+ ): boolean {
47
+ return (
48
+ isActionWithMeta(action) && action.meta?.[controlledActionFlag] === true
49
+ );
50
+ }
51
+
52
+ /**
53
+ * Enhances the store. Allows actions to be flagged as controlled and adds a new function
54
+ * store.observeUncontrolled(selector, onChange, compare) to the store.
55
+ *
56
+ * @param {object} store redux store
57
+ * @param {string|symbol} controlledActionFlag flag that controlled actions expose (action[controlledActionFlag] == true)
58
+ */
59
+ export default function enableControlledDispatchAndObserve<State = unknown>(
60
+ store: Store<State>,
61
+ controlledActionFlag = "isControlled",
62
+ ) {
63
+ let listeners: Array<UncontrolledActionListener<State>> = [];
64
+
65
+ function removeListener(listener: UncontrolledActionListener<State>) {
66
+ listeners = listeners.filter((l) => l !== listener);
67
+ }
68
+
69
+ function addListener(listener: UncontrolledActionListener<State>) {
70
+ listeners.push(listener);
71
+ }
72
+
73
+ const subscribeUncontrolled: SubscribeUncontrolled<State> = (listener) => {
74
+ addListener(listener);
75
+
76
+ return function removeListenerBound() {
77
+ removeListener(listener);
78
+ };
79
+ };
80
+
81
+ const observeUncontrolled: ObserveUncontrolled<State> = (
82
+ selector,
83
+ onChange,
84
+ compare = strictEqualCompare,
85
+ ) =>
86
+ subscribeUncontrolled(
87
+ function handleUncontrolledChange(previousState, state) {
88
+ const previousValue = selector(previousState);
89
+ const newValue = selector(state);
90
+
91
+ if (!compare(previousValue, newValue)) {
92
+ onChange(newValue, previousValue, state);
93
+ }
94
+ },
95
+ );
96
+
97
+ let wasControlled = false;
98
+ const baseDispatch = store.dispatch;
99
+
100
+ const enhancedDispatch: Dispatch = (action) => {
101
+ wasControlled = isControlledAction(action, controlledActionFlag);
102
+ return baseDispatch(action);
103
+ };
104
+
105
+ let previousState = store.getState();
106
+
107
+ function listenForUncontrolledActions() {
108
+ const state = store.getState();
109
+
110
+ if (!wasControlled) {
111
+ listeners.forEach(
112
+ function callUncontrolledChangeListener(listener) {
113
+ listener(previousState, state);
114
+ },
115
+ );
116
+ }
117
+
118
+ previousState = state;
119
+ }
120
+
121
+ store.subscribe(listenForUncontrolledActions);
122
+
123
+ Object.assign(store, {
124
+ dispatch: enhancedDispatch,
125
+ subscribeUncontrolled: subscribeUncontrolled,
126
+ observeUncontrolled: observeUncontrolled,
127
+ });
128
+ }
@@ -0,0 +1,36 @@
1
+ import {AnyAction, Reducer} from "redux";
2
+
3
+ /**
4
+ * Add initialization reducer to existing reducer
5
+ *
6
+ * @deprecated Use redux preloadedState or mergeAll actions instead.
7
+ *
8
+ * @param reducer base reducer to enhance
9
+ * @param [actionName] name of action, default: 'INITIALIZE'
10
+ * @returns enhanced reducer
11
+ */
12
+
13
+ export default function enableInitialization(
14
+ reducer: Reducer,
15
+ actionName = "INITIALIZE",
16
+ ) {
17
+ const aReducerWithInitialization: Reducer = <T>(
18
+ state: T,
19
+ action: AnyAction,
20
+ ) => {
21
+ if (action.type === actionName) {
22
+ return action.value as T;
23
+ }
24
+
25
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
26
+ return reducer(state, action);
27
+ };
28
+
29
+ if (reducer.name) {
30
+ Object.defineProperty(aReducerWithInitialization, "name", {
31
+ value: reducer.name + "__with-initialization",
32
+ });
33
+ }
34
+
35
+ return aReducerWithInitialization;
36
+ }
@@ -0,0 +1,22 @@
1
+ import {AnyAction} from "redux";
2
+
3
+ /**
4
+ * Flattens (deeply) batched actions into a flat array of actions
5
+ *
6
+ * @param {object} action the root action
7
+ * @returns {Array<object>} flattened array of actions
8
+ */
9
+ export default function flattenActions(action: AnyAction): Array<AnyAction> {
10
+ if (
11
+ "meta" in action &&
12
+ "payload" in action &&
13
+ Array.isArray(action.payload) &&
14
+ typeof action.meta === "object" &&
15
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
16
+ action.meta.batch === true
17
+ ) {
18
+ return action.payload.flatMap(flattenActions);
19
+ }
20
+
21
+ return [action];
22
+ }
@@ -0,0 +1 @@
1
+ //
@@ -0,0 +1,96 @@
1
+ import get from "lodash/get";
2
+ import set from "lodash/set";
3
+ import {Store} from "redux";
4
+
5
+ import {observeState} from "./observe-state";
6
+
7
+ /**
8
+ * Creates a storage object to access local storage.
9
+ *
10
+ * @param [storageKey=null] key to be used in local storage
11
+ * @returns storage adapter
12
+ */
13
+ export function createStorage(storageKey: string | null = null) {
14
+ function setStorageKey(k: string | null) {
15
+ storageKey = k;
16
+ }
17
+
18
+ function getLocalStorageState(): unknown {
19
+ if (!storageKey) {
20
+ throw Error(
21
+ "@mapsight/lib-redux: Cannot access local storage without a key. Please set a storage key using the `string: storageKey` property on `createStorage(string?: storageKey)` or using the `setStorageKey(string: storageKey)` method on an existing storage object.",
22
+ );
23
+ }
24
+
25
+ if (typeof window !== "undefined") {
26
+ const value = window.localStorage.getItem(storageKey);
27
+ return window.localStorage && value ? JSON.parse(value) : null;
28
+ }
29
+
30
+ return null;
31
+ }
32
+
33
+ function setLocalStorageState(state: unknown) {
34
+ if (!storageKey) {
35
+ throw Error(
36
+ "@mapsight/lib-redux: Cannot access local storage without a key. Please set a storage key using the `string: storageKey` property on `createStorage(string?: storageKey)` or using the `setStorageKey(string: storageKey)` method on an existing storage object.",
37
+ );
38
+ }
39
+
40
+ if (window.localStorage) {
41
+ window.localStorage.setItem(storageKey, JSON.stringify(state));
42
+ }
43
+ }
44
+
45
+ function updateLocalStorageState(
46
+ path: Array<string> | string,
47
+ value: unknown,
48
+ ) {
49
+ if (!storageKey) {
50
+ throw Error(
51
+ "@mapsight/lib-redux: Cannot access local storage without a key. Please set a storage key using the `string: storageKey` property on `createStorage(string?: storageKey)` or using the `setStorageKey(string: storageKey)` method on an existing storage object.",
52
+ );
53
+ }
54
+
55
+ setLocalStorageState(set<any>(getLocalStorageState(), path, value));
56
+ }
57
+
58
+ /**
59
+ * Observes and synchronizes the state of the given store with local storage.
60
+ *
61
+ * @see lodash/get for path definition
62
+ *
63
+ * @param store the store to observe
64
+ * @param paths array of path strings (string[]) or path arrays (string[][])
65
+ */
66
+ function synchronizePathsToLocalStorage(
67
+ store: Store,
68
+ paths: Array<string | Array<string>>,
69
+ ) {
70
+ if (!storageKey) {
71
+ throw Error(
72
+ "@mapsight/lib-redux: Cannot access local storage without a key. Please set a storage key using the `string: storageKey` property on `createStorage(string?: storageKey)` or using the `setStorageKey(string: storageKey)` method on an existing storage object.",
73
+ );
74
+ }
75
+
76
+ if (window.localStorage) {
77
+ paths.forEach(function synchronizePathToLocalStorage(path) {
78
+ observeState(
79
+ store,
80
+ (state): unknown =>
81
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
82
+ get(state, path),
83
+ (value) => updateLocalStorageState(path, value),
84
+ );
85
+ });
86
+ }
87
+ }
88
+
89
+ return {
90
+ setStorageKey: setStorageKey,
91
+ getLocalStorageState: getLocalStorageState,
92
+ setLocalStorageState: setLocalStorageState,
93
+ updateLocalStorageState: updateLocalStorageState,
94
+ synchronizePathsToLocalStorage: synchronizePathsToLocalStorage,
95
+ };
96
+ }
@@ -0,0 +1,78 @@
1
+ import {zip} from "@mapsight/lib-js/array";
2
+
3
+ type PatternSegment = {isParam: boolean; name: string};
4
+
5
+ /**
6
+ * Parses a pattern segment.
7
+ *
8
+ * @param patternSegment segment string
9
+ * @returns parsed segment
10
+ */
11
+ function parsePatternSegment(patternSegment: string): PatternSegment {
12
+ if (patternSegment.startsWith(":")) {
13
+ return {
14
+ isParam: true,
15
+ name: patternSegment.slice(1),
16
+ };
17
+ } else {
18
+ return {
19
+ isParam: false,
20
+ name: patternSegment,
21
+ };
22
+ }
23
+ }
24
+
25
+ // TODO: lru/limit cache?
26
+ const parsePathPatternCache: Record<string, Array<PatternSegment>> = {};
27
+
28
+ /**
29
+ * Parses a path pattern
30
+ *
31
+ * @param pattern path pattern
32
+ * @returns parsed pattern
33
+ */
34
+ function parsePathPattern(pattern: string): Array<PatternSegment> {
35
+ const fromCache = parsePathPatternCache[pattern];
36
+ if (fromCache !== undefined) {
37
+ return fromCache;
38
+ }
39
+
40
+ const patternSegments = pattern.split("/").map(parsePatternSegment);
41
+ parsePathPatternCache[pattern] = patternSegments;
42
+ return patternSegments;
43
+ }
44
+
45
+ /**
46
+ * This function matches the given path array to a path pattern. The pattern consists of / delimited names and/or
47
+ * named parameters defined as `:name` where name may be any string which will be used in the returned object to
48
+ * access the parameter value.
49
+ *
50
+ * If the path matches the pattern, the return value will be an object containing any present
51
+ * named parameter values that may be in the pattern, otherwise it will return false if any path segment does not
52
+ * match the specified pattern.
53
+ *
54
+ * @param pathArr path array
55
+ * @param pattern pattern to match
56
+ * @returns object containing matched named parameter values or false if the path did not patch the pattern
57
+ */
58
+ export default function matchPath(
59
+ pathArr: Array<string>,
60
+ pattern: string,
61
+ ): false | Record<string, string> {
62
+ const patternSegments = parsePathPattern(pattern);
63
+ if (patternSegments.length !== pathArr.length) {
64
+ return false;
65
+ }
66
+
67
+ const params: Record<string, string> = {};
68
+
69
+ for (const [pathValue, patternSegment] of zip(pathArr, patternSegments)) {
70
+ if (patternSegment.isParam) {
71
+ params[patternSegment.name] = pathValue;
72
+ } else if (pathValue !== patternSegment.name) {
73
+ return false;
74
+ }
75
+ }
76
+
77
+ return params;
78
+ }
@@ -0,0 +1,23 @@
1
+ import matchPath from "./matchPath";
2
+
3
+ const noMatch: Record<string, string> = {};
4
+
5
+ /**
6
+ * This function matches the given path array to a path pattern. The pattern consists of / delimited names and/or
7
+ * named parameters defined as `:name` where name may be any string which will be used in the returned object to
8
+ * access the parameter value.
9
+ *
10
+ * If the path matches the pattern it will return an object containing any present
11
+ * named parameter values that may be in the pattern otherwise an empty object is returned.
12
+ *
13
+ * @param pathArr path array
14
+ * @param pattern pattern to match
15
+ * @returns 2-ary array with boolean that is true if the path did match the pattern and an object containing matched named parameter values
16
+ */
17
+ export default function matchesPath(
18
+ pathArr: Array<string>,
19
+ pattern: string,
20
+ ): [boolean, Record<string, string>] {
21
+ const match = matchPath(pathArr, pattern);
22
+ return match ? [true, match] : [false, noMatch];
23
+ }
@@ -0,0 +1,109 @@
1
+ import {Store} from "redux";
2
+
3
+ function strictEqualCompare<T>(a: T, b: T): boolean {
4
+ return a === b;
5
+ }
6
+
7
+ //const jsonCompare = (a, b) => JSON.stringify(a) !== JSON.stringify(b);
8
+
9
+ export const AbortObserving = Symbol(); // TODO: Use Symbol()
10
+
11
+ function internalObserveState<State = unknown, Value = unknown>(
12
+ store: Store<State>,
13
+ selector: (state: State) => Value,
14
+ listener: (
15
+ newValue: Value,
16
+ oldValue: Value | null,
17
+ state: State,
18
+ ) => void | typeof AbortObserving,
19
+ compare = strictEqualCompare,
20
+ initialValue: Value | null = null,
21
+ ) {
22
+ let currentValue = initialValue;
23
+ const unsubscribe = store.subscribe(function onStateChange() {
24
+ const state = store.getState();
25
+ const newValue = selector(state);
26
+
27
+ if (!compare(currentValue, newValue)) {
28
+ const oldValue = currentValue;
29
+ currentValue = newValue;
30
+
31
+ const returnValue = listener(newValue, oldValue, state);
32
+ if (returnValue === AbortObserving) {
33
+ unsubscribe();
34
+ }
35
+ }
36
+ });
37
+
38
+ return unsubscribe;
39
+ }
40
+
41
+ export function observeState<State = unknown, Value = unknown>(
42
+ store: Store<State>,
43
+ selector: (state: State) => Value,
44
+ listener: (
45
+ newValue: Value,
46
+ oldValue: Value | null,
47
+ state: State,
48
+ ) => void | typeof AbortObserving,
49
+ compare = strictEqualCompare,
50
+ ) {
51
+ const initialValue = selector(store.getState());
52
+ return internalObserveState(
53
+ store,
54
+ selector,
55
+ listener,
56
+ compare,
57
+ initialValue,
58
+ );
59
+ }
60
+
61
+ export function observeStateOnce<State = unknown, Value = unknown>(
62
+ store: Store,
63
+ selector: (state: State) => Value,
64
+ listener: (
65
+ newValue: Value,
66
+ oldValue: Value | null,
67
+ state: State,
68
+ ) => void | typeof AbortObserving,
69
+ compare = strictEqualCompare,
70
+ ) {
71
+ const unsubscribe = observeState(
72
+ store,
73
+ selector,
74
+ function handleChange(newValue, oldValue, state: State) {
75
+ listener(newValue, oldValue, state);
76
+ unsubscribe();
77
+ },
78
+ compare,
79
+ );
80
+
81
+ return unsubscribe;
82
+ }
83
+
84
+ export function getAndObserveState<State = unknown, Value = unknown>(
85
+ store: Store<State>,
86
+ selector: (state: State) => Value,
87
+ listener: (
88
+ newValue: Value,
89
+ oldValue: Value | null,
90
+ state: State,
91
+ ) => void | typeof AbortObserving,
92
+ compare = strictEqualCompare,
93
+ ) {
94
+ const initialState = store.getState();
95
+ const initialValue = selector(initialState);
96
+
97
+ const initialReturnValue = listener(initialValue, null, initialState);
98
+ if (initialReturnValue === AbortObserving) {
99
+ return () => undefined;
100
+ }
101
+
102
+ return internalObserveState(
103
+ store,
104
+ selector,
105
+ listener,
106
+ compare,
107
+ initialValue,
108
+ );
109
+ }
@@ -0,0 +1,6 @@
1
+ import {Reducer} from "redux";
2
+
3
+ const addTo: Reducer<Array<unknown>> = (state, action) =>
4
+ (state || []).concat([action.element]);
5
+
6
+ export default addTo;
@@ -0,0 +1,17 @@
1
+ import {Reducer} from "redux";
2
+
3
+ import addTo from "./add-to";
4
+ import merge from "./merge";
5
+ import noop from "./noop";
6
+ import removeFrom from "./remove-from";
7
+ import set from "./set";
8
+
9
+ const reducers: Record<string, Reducer> = {
10
+ addTo: addTo,
11
+ merge: merge,
12
+ noop: noop,
13
+ removeFrom: removeFrom,
14
+ set: set,
15
+ };
16
+
17
+ export default reducers;
@@ -0,0 +1,18 @@
1
+ import merge from "lodash/merge";
2
+ import {AnyAction, Reducer} from "redux";
3
+
4
+ const mergeReducer: Reducer = <
5
+ T extends Array<unknown> | Record<string | number | symbol, unknown>,
6
+ >(
7
+ state: T,
8
+ action: AnyAction,
9
+ ): T => {
10
+ const result: T = (Array.isArray(state) ? [] : {}) as T;
11
+
12
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
13
+ merge(result, state, action.value);
14
+
15
+ return result;
16
+ };
17
+
18
+ export default mergeReducer;
@@ -0,0 +1,5 @@
1
+ import {Reducer} from "redux";
2
+
3
+ const noopReducer: Reducer = <T>(state: T): T => state;
4
+
5
+ export default noopReducer;
@@ -0,0 +1,8 @@
1
+ import {AnyAction, Reducer} from "redux";
2
+
3
+ const removeFromReducer: Reducer = <T extends Array<unknown>>(
4
+ state: T,
5
+ action: AnyAction,
6
+ ) => state.filter((element) => element !== action.element);
7
+
8
+ export default removeFromReducer;
@@ -0,0 +1,5 @@
1
+ import {AnyAction, Reducer} from "redux";
2
+
3
+ const setReducer: Reducer = (state, action: AnyAction): unknown => action.value;
4
+
5
+ export default setReducer;
@@ -0,0 +1,4 @@
1
+ import createImmutablePathReducer from "../../create-immutable-path-reducer";
2
+ import addTo from "../immutable/add-to";
3
+
4
+ export default createImmutablePathReducer(addTo);
@@ -0,0 +1,17 @@
1
+ import {Reducer} from "redux";
2
+
3
+ import addTo from "./add-to";
4
+ import merge from "./merge";
5
+ import noop from "./noop";
6
+ import removeFrom from "./remove-from";
7
+ import set from "./set";
8
+
9
+ const reducers = {
10
+ addTo: addTo,
11
+ merge: merge,
12
+ noop: noop,
13
+ removeFrom: removeFrom,
14
+ set: set,
15
+ } satisfies Record<string, Reducer>;
16
+
17
+ export default reducers;
@@ -0,0 +1,4 @@
1
+ import createImmutablePathReducer from "../../create-immutable-path-reducer";
2
+ import merge from "../immutable/merge";
3
+
4
+ export default createImmutablePathReducer(merge);
@@ -0,0 +1,3 @@
1
+ export default function noopReducer<T>(state: T) {
2
+ return state;
3
+ }
@@ -0,0 +1,4 @@
1
+ import createImmutablePathReducer from "../../create-immutable-path-reducer";
2
+ import removeFrom from "../immutable/remove-from";
3
+
4
+ export default createImmutablePathReducer(removeFrom);
@@ -0,0 +1,4 @@
1
+ import createImmutablePathReducer from "../../create-immutable-path-reducer";
2
+ import set from "../immutable/set";
3
+
4
+ export default createImmutablePathReducer(set);
@@ -0,0 +1,19 @@
1
+ import unique from "lodash/uniq";
2
+
3
+ export default function reduceByKeys(
4
+ keys: Array<string>,
5
+ state: Record<string, unknown>,
6
+ ) {
7
+ if (keys && state) {
8
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
9
+ const uniqueKeys: Array<string> = unique(keys);
10
+
11
+ return uniqueKeys.reduce(function reduceByKey(newState, key) {
12
+ return state[key] !== undefined
13
+ ? {...newState, [key]: state[key]}
14
+ : newState;
15
+ }, {});
16
+ }
17
+
18
+ return state;
19
+ }