react-redux-cache 0.20.0 → 0.22.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.
@@ -45,7 +45,7 @@ export const defaultGetCacheKey = (params) => {
45
45
  }
46
46
 
47
47
  export const applyEntityChanges = (entities, changes, options) => {
48
- var _a, _b, _c, _d
48
+ var _a, _b, _c
49
49
  if (changes.merge && changes.entities) {
50
50
  logWarn('applyEntityChanges', 'merge and entities should not be both set')
51
51
  }
@@ -53,6 +53,7 @@ export const applyEntityChanges = (entities, changes, options) => {
53
53
  if (!merge && !replace && !remove) {
54
54
  return undefined
55
55
  }
56
+ const mutable = options.mutableCollections
56
57
  const deepEqual = options.deepComparisonEnabled ? optionalUtils.deepEqual : undefined
57
58
  let result
58
59
  const objectWithAllTypenames = Object.assign(
@@ -95,37 +96,48 @@ export const applyEntityChanges = (entities, changes, options) => {
95
96
  logWarn('applyEntityChanges', 'merge, replace and remove changes have intersections for: ' + typename)
96
97
  }
97
98
  }
98
- const oldEntities = (_d = entities[typename]) !== null && _d !== void 0 ? _d : EMPTY_OBJECT
99
+ const oldEntities = entities[typename]
99
100
  let newEntities
100
101
  entitiesToRemove === null || entitiesToRemove === void 0
101
102
  ? void 0
102
103
  : entitiesToRemove.forEach((id) => {
103
- if (oldEntities[id]) {
104
+ if (oldEntities === null || oldEntities === void 0 ? void 0 : oldEntities[id]) {
104
105
  newEntities !== null && newEntities !== void 0
105
106
  ? newEntities
106
- : (newEntities = Object.assign({}, oldEntities))
107
+ : (newEntities = mutable ? oldEntities : Object.assign({}, oldEntities))
107
108
  delete newEntities[id]
108
109
  }
109
110
  })
110
111
  if (entitiesToReplace) {
111
112
  for (const id in entitiesToReplace) {
112
113
  const newEntity = entitiesToReplace[id]
113
- if (!(deepEqual === null || deepEqual === void 0 ? void 0 : deepEqual(oldEntities[id], newEntity))) {
114
+ if (
115
+ oldEntities === undefined ||
116
+ !(deepEqual === null || deepEqual === void 0 ? void 0 : deepEqual(oldEntities[id], newEntity))
117
+ ) {
114
118
  newEntities !== null && newEntities !== void 0
115
119
  ? newEntities
116
- : (newEntities = Object.assign({}, oldEntities))
120
+ : (newEntities = mutable
121
+ ? oldEntities !== null && oldEntities !== void 0
122
+ ? oldEntities
123
+ : {}
124
+ : Object.assign({}, oldEntities))
117
125
  newEntities[id] = newEntity
118
126
  }
119
127
  }
120
128
  }
121
129
  if (entitiesToMerge) {
122
130
  for (const id in entitiesToMerge) {
123
- const oldEntity = oldEntities[id]
131
+ const oldEntity = oldEntities === null || oldEntities === void 0 ? void 0 : oldEntities[id]
124
132
  const newEntity = Object.assign(Object.assign({}, oldEntity), entitiesToMerge[id])
125
133
  if (!(deepEqual === null || deepEqual === void 0 ? void 0 : deepEqual(oldEntity, newEntity))) {
126
134
  newEntities !== null && newEntities !== void 0
127
135
  ? newEntities
128
- : (newEntities = Object.assign({}, oldEntities))
136
+ : (newEntities = mutable
137
+ ? oldEntities !== null && oldEntities !== void 0
138
+ ? oldEntities
139
+ : {}
140
+ : Object.assign({}, oldEntities))
129
141
  newEntities[id] = newEntity
130
142
  }
131
143
  }
@@ -133,7 +145,15 @@ export const applyEntityChanges = (entities, changes, options) => {
133
145
  if (!newEntities) {
134
146
  continue
135
147
  }
136
- result !== null && result !== void 0 ? result : (result = Object.assign({}, entities))
148
+ if (mutable) {
149
+ incrementChangeKey(newEntities)
150
+ if (result === undefined) {
151
+ incrementChangeKey(entities)
152
+ result = entities
153
+ }
154
+ } else {
155
+ result !== null && result !== void 0 ? result : (result = Object.assign({}, entities))
156
+ }
137
157
  result[typename] = newEntities
138
158
  }
139
159
  options.logsEnabled &&
@@ -146,8 +166,8 @@ export const applyEntityChanges = (entities, changes, options) => {
146
166
  return result
147
167
  }
148
168
 
149
- export const isEmptyObject = (o) => {
150
- for (const _ in o) {
169
+ export const isEmptyObject = (obj) => {
170
+ for (const _ in obj) {
151
171
  return false
152
172
  }
153
173
  return true
@@ -177,3 +197,11 @@ export const FetchPolicy = {
177
197
  },
178
198
  Always: () => true,
179
199
  }
200
+
201
+ export const incrementChangeKey = (mutable) => {
202
+ if (mutable._changeKey === undefined) {
203
+ mutable._changeKey = 0
204
+ } else {
205
+ mutable._changeKey += 1
206
+ }
207
+ }
@@ -2,7 +2,6 @@ import type {
2
2
  Cache,
3
3
  CacheOptions,
4
4
  CacheState,
5
- Dict,
6
5
  EntitiesMap,
7
6
  EntityChanges,
8
7
  Globals,
@@ -32,7 +31,7 @@ import {applyEntityChanges} from './utilsAndConstants'
32
31
  * `const cache = withTypenames<MyTypenames>().createCache({...})`
33
32
  */
34
33
  export declare const withTypenames: <T extends Typenames = Typenames>() => {
35
- /** Creates reducer, actions and hooks for managing queries and mutations through redux cache. */
34
+ /** Creates reducer, actions and hooks for managing queries and mutations. */
36
35
  createCache: <N extends string, QP, QR, MP, MR>(
37
36
  partialCache: OptionalPartial<
38
37
  Omit<Cache<N, T, QP, QR, MP, MR>, 'globals'>,
@@ -43,7 +42,7 @@ export declare const withTypenames: <T extends Typenames = Typenames>() => {
43
42
  ) => {
44
43
  /** Keeps all options, passed while creating the cache. */
45
44
  cache: Cache<N, T, QP, QR, MP, MR>
46
- /** Reducer of the cache, should be added to redux store. */
45
+ /** Reducer of the cache, should be added to redux/zustand store. */
47
46
  reducer: (
48
47
  state: CacheState<T, QP, QR, MP, MR> | undefined,
49
48
  action:
@@ -127,7 +126,7 @@ export declare const withTypenames: <T extends Typenames = Typenames>() => {
127
126
  }
128
127
  type: `@rrc/${N}/mergeEntityChanges`
129
128
  }
130
- /** Invalidates query states. */
129
+ /** Sets expiresAt to Date.now(). */
131
130
  invalidateQuery: {
132
131
  <K extends keyof QP & keyof QR>(
133
132
  queries: {
@@ -170,7 +169,7 @@ export declare const withTypenames: <T extends Typenames = Typenames>() => {
170
169
  }
171
170
  type: `@rrc/${N}/clearMutationState`
172
171
  }
173
- /** Replaces cache state with initial, optionally merging with provided state. Doesn't cancel running fetches and shoult be used with caution. */
172
+ /** Replaces cache state with initial, optionally merging with provided state. Doesn't cancel running fetches and should be used with caution. */
174
173
  clearCache: {
175
174
  (stateToKeep?: Partial<CacheState<T, QP, QR, MP, MR>> | undefined): {
176
175
  type: `@rrc/${N}/clearCache`
@@ -255,9 +254,12 @@ export declare const withTypenames: <T extends Typenames = Typenames>() => {
255
254
  typename: TN
256
255
  ) => T[TN] | undefined
257
256
  /** Selects all entities. */
258
- selectEntities: (state: unknown) => EntitiesMap<T>
257
+ selectEntities: (state: unknown) => EntitiesMap<T> & import('./types').Mutable
259
258
  /** Selects all entities of provided typename. */
260
- selectEntitiesByTypename: <TN extends keyof T>(state: unknown, typename: TN) => EntitiesMap<T>[TN]
259
+ selectEntitiesByTypename: <TN extends keyof T>(
260
+ state: unknown,
261
+ typename: TN
262
+ ) => (EntitiesMap<T> & import('./types').Mutable)[TN]
261
263
  }
262
264
  hooks: {
263
265
  /** Returns memoized object with query and mutate functions. Memoization dependency is the store. */
@@ -331,6 +333,13 @@ export declare const withTypenames: <T extends Typenames = Typenames>() => {
331
333
  ]
332
334
  /** useSelector + selectEntityById. */
333
335
  useSelectEntityById: <TN extends keyof T>(id: Key | null | undefined, typename: TN) => T[TN] | undefined
336
+ /**
337
+ * useSelector + selectEntitiesByTypename. Also subscribes to collection's change key if `mutableCollections` enabled.
338
+ * @warning Subscribing to collections should be avoided.
339
+ * */
340
+ useEntitiesByTypename: <TN extends keyof T>(
341
+ typename: TN
342
+ ) => (EntitiesMap<T> & import('./types').Mutable)[TN]
334
343
  }
335
344
  utils: {
336
345
  /** Creates client by providing the store. Can be used when the store is a singleton - to not use a useClient hook for getting the client, but import it directly. */
@@ -352,8 +361,12 @@ export declare const withTypenames: <T extends Typenames = Typenames>() => {
352
361
  }
353
362
  /** Generates the initial state by calling a reducer. Not needed for redux — it already generates it the same way when creating the store. */
354
363
  getInitialState: () => CacheState<T, QP, QR, MP, MR>
355
- /** Apply changes to the entities map.
356
- * @returns `undefined` if nothing to change, otherwise new `EntitiesMap<T>` with applied changes. */
364
+ /**
365
+ * Apply changes to the entities map.
366
+ * Returns `undefined` if nothing to change, otherwise new `EntitiesMap<T>` with applied changes.
367
+ * Uses deep comparison if `deepComparisonEnabled` option is `true`.
368
+ * Performs additional checks for intersections if `additionalValidation` option is `true`, and prints warnings if finds any issues.
369
+ */
357
370
  applyEntityChanges: (
358
371
  entities: Parameters<typeof applyEntityChanges<T>>[0],
359
372
  changes: Parameters<typeof applyEntityChanges<T>>[1]
@@ -362,9 +375,7 @@ export declare const withTypenames: <T extends Typenames = Typenames>() => {
362
375
  }
363
376
  }
364
377
 
365
- /**
366
- * Creates reducer, actions and hooks for managing queries and mutations through redux cache.
367
- */
378
+ /** Creates reducer, actions and hooks for managing queries and mutations. */
368
379
  export declare const createCache: <N extends string, QP, QR, MP, MR>(
369
380
  partialCache: Partial<{
370
381
  queries: Partial<{
@@ -393,7 +404,7 @@ export declare const createCache: <N extends string, QP, QR, MP, MR>(
393
404
  ) => {
394
405
  /** Keeps all options, passed while creating the cache. */
395
406
  cache: Cache<N, Typenames, QP, QR, MP, MR>
396
- /** Reducer of the cache, should be added to redux store. */
407
+ /** Reducer of the cache, should be added to redux/zustand store. */
397
408
  reducer: (
398
409
  state: CacheState<Typenames, QP, QR, MP, MR> | undefined,
399
410
  action:
@@ -479,7 +490,7 @@ export declare const createCache: <N extends string, QP, QR, MP, MR>(
479
490
  }
480
491
  type: `@rrc/${N}/mergeEntityChanges`
481
492
  }
482
- /** Invalidates query states. */
493
+ /** Sets expiresAt to Date.now(). */
483
494
  invalidateQuery: {
484
495
  <K extends keyof QP & keyof QR>(
485
496
  queries: {
@@ -522,7 +533,7 @@ export declare const createCache: <N extends string, QP, QR, MP, MR>(
522
533
  }
523
534
  type: `@rrc/${N}/clearMutationState`
524
535
  }
525
- /** Replaces cache state with initial, optionally merging with provided state. Doesn't cancel running fetches and shoult be used with caution. */
536
+ /** Replaces cache state with initial, optionally merging with provided state. Doesn't cancel running fetches and should be used with caution. */
526
537
  clearCache: {
527
538
  (stateToKeep?: Partial<CacheState<Typenames, QP, QR, MP, MR>> | undefined): {
528
539
  type: `@rrc/${N}/clearCache`
@@ -614,9 +625,12 @@ export declare const createCache: <N extends string, QP, QR, MP, MR>(
614
625
  typename: TN
615
626
  ) => object | undefined
616
627
  /** Selects all entities. */
617
- selectEntities: (state: unknown) => EntitiesMap<Typenames>
628
+ selectEntities: (state: unknown) => EntitiesMap<Typenames> & import('./types').Mutable
618
629
  /** Selects all entities of provided typename. */
619
- selectEntitiesByTypename: <TN extends string>(state: unknown, typename: TN) => Dict<object> | undefined
630
+ selectEntitiesByTypename: <TN extends string>(
631
+ state: unknown,
632
+ typename: TN
633
+ ) => (EntitiesMap<Typenames> & import('./types').Mutable)[TN]
620
634
  }
621
635
  hooks: {
622
636
  /** Returns memoized object with query and mutate functions. Memoization dependency is the store. */
@@ -690,6 +704,13 @@ export declare const createCache: <N extends string, QP, QR, MP, MR>(
690
704
  ]
691
705
  /** useSelector + selectEntityById. */
692
706
  useSelectEntityById: <TN extends string>(id: Key | null | undefined, typename: TN) => object | undefined
707
+ /**
708
+ * useSelector + selectEntitiesByTypename. Also subscribes to collection's change key if `mutableCollections` enabled.
709
+ * @warning Subscribing to collections should be avoided.
710
+ * */
711
+ useEntitiesByTypename: <TN extends string>(
712
+ typename: TN
713
+ ) => (EntitiesMap<Typenames> & import('./types').Mutable)[TN]
693
714
  }
694
715
  utils: {
695
716
  /** Creates client by providing the store. Can be used when the store is a singleton - to not use a useClient hook for getting the client, but import it directly. */
@@ -711,10 +732,14 @@ export declare const createCache: <N extends string, QP, QR, MP, MR>(
711
732
  }
712
733
  /** Generates the initial state by calling a reducer. Not needed for redux — it already generates it the same way when creating the store. */
713
734
  getInitialState: () => CacheState<Typenames, QP, QR, MP, MR>
714
- /** Apply changes to the entities map.
715
- * @returns `undefined` if nothing to change, otherwise new `EntitiesMap<T>` with applied changes. */
735
+ /**
736
+ * Apply changes to the entities map.
737
+ * Returns `undefined` if nothing to change, otherwise new `EntitiesMap<T>` with applied changes.
738
+ * Uses deep comparison if `deepComparisonEnabled` option is `true`.
739
+ * Performs additional checks for intersections if `additionalValidation` option is `true`, and prints warnings if finds any issues.
740
+ */
716
741
  applyEntityChanges: (
717
- entities: EntitiesMap<Typenames>,
742
+ entities: EntitiesMap<Typenames> & import('./types').Mutable,
718
743
  changes: EntityChanges<Typenames>
719
744
  ) => EntitiesMap<Typenames> | undefined
720
745
  }
@@ -12,6 +12,11 @@ export type Selectors<
12
12
  export declare const createSelectors: <N extends string, T extends Typenames, QP, QR, MP, MR>(
13
13
  cache: Cache<N, T, QP, QR, MP, MR>
14
14
  ) => {
15
+ selectEntityById: <TN extends keyof T>(
16
+ state: unknown,
17
+ id: Key | null | undefined,
18
+ typename: TN
19
+ ) => T[TN] | undefined
15
20
  selectQueryState: <QK extends keyof (QP & QR)>(
16
21
  state: unknown,
17
22
  query: QK,
@@ -67,14 +72,9 @@ export declare const createSelectors: <N extends string, T extends Typenames, QP
67
72
  state: unknown,
68
73
  mutation: MK
69
74
  ) => (MK extends keyof MP & keyof MR ? MP[MK] : never) | undefined
70
- selectEntityById: <TN extends keyof T>(
71
- state: unknown,
72
- id: Key | null | undefined,
73
- typename: TN
74
- ) => T[TN] | undefined
75
- selectEntities: (state: unknown) => import('./types').EntitiesMap<T>
75
+ selectEntities: (state: unknown) => import('./types').EntitiesMap<T> & import('./types').Mutable
76
76
  selectEntitiesByTypename: <TN extends keyof T>(
77
77
  state: unknown,
78
78
  typename: TN
79
- ) => import('./types').EntitiesMap<T>[TN]
79
+ ) => (import('./types').EntitiesMap<T> & import('./types').Mutable)[TN]
80
80
  }
@@ -3,7 +3,15 @@ import type {Selectors} from './createSelectors'
3
3
 
4
4
  export type Key = string | number | symbol
5
5
 
6
- export type Dict<T> = Record<Key, T>
6
+ export type Mutable = {
7
+ /**
8
+ * Used only when mutable cache enabled. Always incremented when collection changed by reducer to allow subscribe on changes.
9
+ * Should not be used for comparing different collections as supposed to be compared only with previously saved changeKey of the same collection.
10
+ */
11
+ _changeKey?: number
12
+ }
13
+
14
+ export type Dict<T> = Record<Key, T> & Mutable
7
15
 
8
16
  export type OptionalPartial<T, K extends keyof T> = Partial<{
9
17
  [A in K]: Partial<T[A]>
@@ -95,7 +103,14 @@ export type Globals<N extends string, T extends Typenames, QP, QR, MP, MR> = {
95
103
  }
96
104
 
97
105
  export type CacheOptions = {
98
- /** Enables additional validation with logging to console.warn. Recommened to enable in dev/testing mode. @Default true in dev mode. */
106
+ /**
107
+ * BETA: Optimization that makes state collections mutable.
108
+ * Subscription to mutable collecitons will work only when subscribed to both collection and its change key - collections
109
+ * still can be replaced with the new ones instead of mutating e.g. when clearing state.
110
+ * @Default false
111
+ * */
112
+ mutableCollections: boolean
113
+ /** Enables additional validation with logging to console.warn. Recommended to enable in dev/testing mode. @Default true in dev mode. */
99
114
  additionalValidation: boolean
100
115
  /** Enables debug logs. @Default false */
101
116
  logsEnabled: boolean
@@ -121,13 +136,13 @@ export type EntityIds<T extends Typenames> = {
121
136
  }
122
137
 
123
138
  export type CacheState<T extends Typenames, QP, QR, MP, MR> = {
124
- entities: EntitiesMap<T>
139
+ entities: EntitiesMap<T> & Mutable
125
140
  queries: {
126
141
  [QK in keyof (QP | QR)]: Dict<QueryState<T, QP[QK], QR[QK]> | undefined>
127
- }
142
+ } & Mutable
128
143
  mutations: {
129
144
  [MK in keyof (MP | MR)]: MutationState<T, MP[MK], MR[MK]>
130
- }
145
+ } & Mutable
131
146
  }
132
147
 
133
148
  export type QueryInfo<
@@ -165,7 +180,7 @@ export type QueryInfo<
165
180
  * Default implementation uses `String()` or `JSON.stringify` depending on type.
166
181
  * It is recommended to override it when default implementation is not optimal or when keys in params object can be sorted in random order etc.
167
182
  */
168
- getCacheKey?: (params?: P) => Key
183
+ getCacheKey?: (params: P) => Key
169
184
  /** Called after fetch completed either successfully or not. */
170
185
  onCompleted?: (
171
186
  response: NormalizedQueryResponse<T, R> | undefined,
@@ -272,17 +287,21 @@ export type QueryOptions<
272
287
 
273
288
  export type QueryResponse<R = unknown> = {
274
289
  result: R
275
- /** If defined, overrides this value for query state, ignoring `secondsToLive`. */
290
+ /** If defined, overrides this value in the query state, ignoring `secondsToLive` option. */
276
291
  expiresAt?: number
277
292
  }
278
293
 
279
294
  export type NormalizedQueryResponse<T extends Typenames = Typenames, R = unknown> = EntityChanges<T> &
280
295
  QueryResponse<R>
281
296
 
282
- /** Current result is always returned, even if cancelled or finished with error. */
297
+ /** Result is always returned, even if cancelled or finished with error. */
283
298
  export type QueryResult<R = unknown> = {
284
299
  error?: unknown
285
- /** Fetch cancelled reason. */
300
+ /**
301
+ * Fetch cancelled reason.
302
+ * @value loading - already loading. Result of current fetch is returned.
303
+ * @value not-expired - not expired yet. Current state result is returned.
304
+ */
286
305
  cancelled?: 'loading' | 'not-expired'
287
306
  result?: R
288
307
  }
@@ -3,6 +3,7 @@ import type {
3
3
  EntitiesMap,
4
4
  EntityChanges,
5
5
  Key,
6
+ Mutable,
6
7
  QueryState,
7
8
  QueryStateComparer,
8
9
  Typenames,
@@ -31,13 +32,13 @@ export declare const noop: () => void
31
32
  export declare const defaultGetCacheKey: <P = unknown>(params: P) => Key
32
33
 
33
34
  export declare const applyEntityChanges: <T extends Typenames>(
34
- entities: EntitiesMap<T>,
35
+ entities: EntitiesMap<T> & Mutable,
35
36
  changes: EntityChanges<T>,
36
37
  options: CacheOptions
37
38
  ) => EntitiesMap<T> | undefined
38
39
 
39
40
  /** Returns true if object has no keys. */
40
- export declare const isEmptyObject: (o: object) => boolean
41
+ export declare const isEmptyObject: (obj: object) => boolean
41
42
 
42
43
  /** Returns query state comparer that compares only provided fields. Used in implementation of `selectorComparer` option. */
43
44
  export declare const createStateComparer: <T extends Typenames = Typenames, Q = unknown, P = unknown>(
@@ -45,7 +46,10 @@ export declare const createStateComparer: <T extends Typenames = Typenames, Q =
45
46
  ) => QueryStateComparer<T, Q, P>
46
47
 
47
48
  export declare const FetchPolicy: {
48
- /** Only if cache does not exist (result is undefined) or expired. @Default */
49
+ /**
50
+ * Only if cache does not exist (result is undefined) or expired. Default.
51
+ * @param expired `true` when `expiresAt` is defined and lower than `Date.now()`
52
+ */
49
53
  NoCacheOrExpired: <T extends Typenames = Typenames, P = unknown, R = unknown>(
50
54
  expired: boolean,
51
55
  _params: P,
@@ -54,3 +58,5 @@ export declare const FetchPolicy: {
54
58
  /** Every fetch trigger. */
55
59
  Always: () => boolean
56
60
  }
61
+
62
+ export declare const incrementChangeKey: (mutable: Mutable) => void
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "react-redux-cache",
3
3
  "author": "Alexander Danilov",
4
4
  "license": "MIT",
5
- "version": "0.20.0",
5
+ "version": "0.22.0",
6
6
  "description": "Powerful data fetching and caching library for Redux and Zustand that supports normalization.",
7
7
  "main": "./dist/cjs/index.js",
8
8
  "module": "./dist/esm/index.js",
@@ -17,8 +17,8 @@
17
17
  "scripts": {
18
18
  "example": "(cd example && yarn --production && yarn dev)",
19
19
  "clean": "rm -rf dist",
20
- "lint": "yarn eslint",
21
- "lint-fix": "yarn eslint --fix",
20
+ "lint": "yarn eslint ./src",
21
+ "lint-fix": "yarn eslint --fix ./src",
22
22
  "lint-fix-dist": "yarn eslint --quiet --fix dist/ > /dev/null 2>&1 || true",
23
23
  "build-cjs": "tsc -p tsconfig.cjs.json && rm -rf dist/cjs/testing && rm -rf dist/cjs/__tests__",
24
24
  "build-esm": "tsc -p tsconfig.esm.json > /dev/null ; rm -rf dist/esm/testing && rm -rf dist/esm/__tests__",
@@ -31,7 +31,8 @@
31
31
  "publish-rc": "npm publish --tag rc",
32
32
  "remove-rc": "npm version <same-version-without-rc>",
33
33
  "prepublishOnly": "yarn build && yarn test",
34
- "generate-docs": "node --experimental-strip-types scripts/generate-docs.ts"
34
+ "generate-docs": "node --experimental-strip-types scripts/generate-docs.ts",
35
+ "benchmark": "NODE_ENV=production node -r ts-node/register --expose-gc benchmark.ts"
35
36
  },
36
37
  "peerDependencies": {
37
38
  "fast-deep-equal": "*",
@@ -72,6 +73,7 @@
72
73
  "redux": "4.2.1",
73
74
  "redux-logger": "3.0.6",
74
75
  "ts-jest": "29.1.0",
76
+ "ts-node": "10.9.2",
75
77
  "typescript": "5.6.3"
76
78
  },
77
79
  "files": [