@signaltree/core 4.0.15 → 4.1.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 (109) hide show
  1. package/README.md +106 -38
  2. package/dist/constants.js +6 -0
  3. package/dist/deep-clone.js +80 -0
  4. package/dist/deep-equal.js +41 -0
  5. package/dist/enhancers/batching/lib/batching.js +141 -135
  6. package/dist/enhancers/computed/lib/computed.js +18 -16
  7. package/dist/enhancers/devtools/lib/devtools.js +303 -260
  8. package/dist/enhancers/entities/lib/entities.js +109 -104
  9. package/dist/enhancers/index.js +65 -77
  10. package/dist/enhancers/memoization/lib/memoization.js +339 -351
  11. package/dist/enhancers/middleware/lib/async-helpers.js +71 -79
  12. package/dist/enhancers/middleware/lib/middleware.js +126 -169
  13. package/dist/enhancers/presets/lib/presets.js +82 -71
  14. package/dist/enhancers/serialization/constants.js +14 -13
  15. package/dist/enhancers/serialization/lib/serialization.js +615 -623
  16. package/dist/enhancers/time-travel/lib/time-travel.js +178 -177
  17. package/dist/index.d.ts +1 -17
  18. package/dist/index.js +19 -16
  19. package/dist/is-built-in-object.js +23 -0
  20. package/dist/lib/constants.js +51 -55
  21. package/dist/lib/memory/memory-manager.js +152 -154
  22. package/dist/lib/performance/diff-engine.js +141 -141
  23. package/dist/lib/performance/path-index.js +139 -137
  24. package/dist/lib/performance/update-engine.js +171 -176
  25. package/dist/lib/security/security-validator.js +110 -128
  26. package/dist/lib/signal-tree.js +577 -611
  27. package/dist/lib/types.js +3 -9
  28. package/dist/lib/utils.js +236 -268
  29. package/dist/lru-cache.js +64 -0
  30. package/dist/parse-path.js +13 -0
  31. package/package.json +30 -16
  32. package/src/index.d.ts +17 -0
  33. package/{dist → src}/lib/utils.d.ts +1 -0
  34. package/dist/enhancers/batching/index.js +0 -1
  35. package/dist/enhancers/batching/jest.config.js +0 -21
  36. package/dist/enhancers/batching/test-setup.js +0 -5
  37. package/dist/enhancers/computed/index.js +0 -1
  38. package/dist/enhancers/computed/jest.config.js +0 -21
  39. package/dist/enhancers/devtools/index.js +0 -1
  40. package/dist/enhancers/devtools/jest.config.js +0 -21
  41. package/dist/enhancers/devtools/test-setup.js +0 -5
  42. package/dist/enhancers/entities/index.js +0 -1
  43. package/dist/enhancers/entities/jest.config.js +0 -21
  44. package/dist/enhancers/entities/test-setup.js +0 -5
  45. package/dist/enhancers/memoization/index.js +0 -1
  46. package/dist/enhancers/memoization/jest.config.js +0 -21
  47. package/dist/enhancers/memoization/test-setup.js +0 -5
  48. package/dist/enhancers/middleware/index.js +0 -2
  49. package/dist/enhancers/middleware/jest.config.js +0 -21
  50. package/dist/enhancers/middleware/test-setup.js +0 -5
  51. package/dist/enhancers/presets/index.js +0 -1
  52. package/dist/enhancers/presets/jest.config.js +0 -21
  53. package/dist/enhancers/presets/test-setup.js +0 -5
  54. package/dist/enhancers/serialization/index.js +0 -2
  55. package/dist/enhancers/serialization/jest.config.js +0 -21
  56. package/dist/enhancers/serialization/test-setup.js +0 -5
  57. package/dist/enhancers/time-travel/index.js +0 -1
  58. package/dist/enhancers/time-travel/jest.config.js +0 -21
  59. package/dist/enhancers/time-travel/lib/utils.js +0 -1
  60. package/dist/enhancers/time-travel/test-setup.js +0 -5
  61. package/dist/enhancers/types.js +0 -0
  62. /package/{dist → src}/enhancers/batching/index.d.ts +0 -0
  63. /package/{dist → src}/enhancers/batching/jest.config.d.ts +0 -0
  64. /package/{dist → src}/enhancers/batching/lib/batching.d.ts +0 -0
  65. /package/{dist → src}/enhancers/batching/test-setup.d.ts +0 -0
  66. /package/{dist → src}/enhancers/computed/index.d.ts +0 -0
  67. /package/{dist → src}/enhancers/computed/jest.config.d.ts +0 -0
  68. /package/{dist → src}/enhancers/computed/lib/computed.d.ts +0 -0
  69. /package/{dist → src}/enhancers/devtools/index.d.ts +0 -0
  70. /package/{dist → src}/enhancers/devtools/jest.config.d.ts +0 -0
  71. /package/{dist → src}/enhancers/devtools/lib/devtools.d.ts +0 -0
  72. /package/{dist → src}/enhancers/devtools/test-setup.d.ts +0 -0
  73. /package/{dist → src}/enhancers/entities/index.d.ts +0 -0
  74. /package/{dist → src}/enhancers/entities/jest.config.d.ts +0 -0
  75. /package/{dist → src}/enhancers/entities/lib/entities.d.ts +0 -0
  76. /package/{dist → src}/enhancers/entities/test-setup.d.ts +0 -0
  77. /package/{dist → src}/enhancers/index.d.ts +0 -0
  78. /package/{dist → src}/enhancers/memoization/index.d.ts +0 -0
  79. /package/{dist → src}/enhancers/memoization/jest.config.d.ts +0 -0
  80. /package/{dist → src}/enhancers/memoization/lib/memoization.d.ts +0 -0
  81. /package/{dist → src}/enhancers/memoization/test-setup.d.ts +0 -0
  82. /package/{dist → src}/enhancers/middleware/index.d.ts +0 -0
  83. /package/{dist → src}/enhancers/middleware/jest.config.d.ts +0 -0
  84. /package/{dist → src}/enhancers/middleware/lib/async-helpers.d.ts +0 -0
  85. /package/{dist → src}/enhancers/middleware/lib/middleware.d.ts +0 -0
  86. /package/{dist → src}/enhancers/middleware/test-setup.d.ts +0 -0
  87. /package/{dist → src}/enhancers/presets/index.d.ts +0 -0
  88. /package/{dist → src}/enhancers/presets/jest.config.d.ts +0 -0
  89. /package/{dist → src}/enhancers/presets/lib/presets.d.ts +0 -0
  90. /package/{dist → src}/enhancers/presets/test-setup.d.ts +0 -0
  91. /package/{dist → src}/enhancers/serialization/constants.d.ts +0 -0
  92. /package/{dist → src}/enhancers/serialization/index.d.ts +0 -0
  93. /package/{dist → src}/enhancers/serialization/jest.config.d.ts +0 -0
  94. /package/{dist → src}/enhancers/serialization/lib/serialization.d.ts +0 -0
  95. /package/{dist → src}/enhancers/serialization/test-setup.d.ts +0 -0
  96. /package/{dist → src}/enhancers/time-travel/index.d.ts +0 -0
  97. /package/{dist → src}/enhancers/time-travel/jest.config.d.ts +0 -0
  98. /package/{dist → src}/enhancers/time-travel/lib/time-travel.d.ts +0 -0
  99. /package/{dist → src}/enhancers/time-travel/lib/utils.d.ts +0 -0
  100. /package/{dist → src}/enhancers/time-travel/test-setup.d.ts +0 -0
  101. /package/{dist → src}/enhancers/types.d.ts +0 -0
  102. /package/{dist → src}/lib/constants.d.ts +0 -0
  103. /package/{dist → src}/lib/memory/memory-manager.d.ts +0 -0
  104. /package/{dist → src}/lib/performance/diff-engine.d.ts +0 -0
  105. /package/{dist → src}/lib/performance/path-index.d.ts +0 -0
  106. /package/{dist → src}/lib/performance/update-engine.d.ts +0 -0
  107. /package/{dist → src}/lib/security/security-validator.d.ts +0 -0
  108. /package/{dist → src}/lib/signal-tree.d.ts +0 -0
  109. /package/{dist → src}/lib/types.d.ts +0 -0
@@ -1,391 +1,379 @@
1
- import { deepEqual, LRUCache } from '@signaltree/shared';
2
- import { isNodeAccessor } from '../../../lib/utils';
1
+ import { isNodeAccessor } from '../../../lib/utils.js';
2
+ import { LRUCache } from '../../../lru-cache.js';
3
+ import { deepEqual } from '../../../deep-equal.js';
4
+
3
5
  const MAX_CACHE_SIZE = 1000;
4
6
  const DEFAULT_TTL = 5 * 60 * 1000;
5
7
  const memoizationCache = new Map();
6
8
  function createMemoCacheStore(maxSize, enableLRU) {
7
- if (enableLRU) {
8
- const cache = new LRUCache(maxSize);
9
- const shadow = new Map();
10
- const pruneShadow = () => {
11
- while (shadow.size > cache.size()) {
12
- const oldestKey = shadow.keys().next().value;
13
- if (oldestKey === undefined) {
14
- break;
15
- }
16
- shadow.delete(oldestKey);
17
- }
18
- };
19
- return {
20
- get: (key) => {
21
- const value = cache.get(key);
22
- if (value !== undefined) {
23
- shadow.set(key, value);
24
- }
25
- else if (shadow.has(key)) {
26
- shadow.delete(key);
27
- }
28
- return value;
29
- },
30
- set: (key, value) => {
31
- cache.set(key, value);
32
- shadow.set(key, value);
33
- pruneShadow();
34
- },
35
- delete: (key) => {
36
- cache.delete(key);
37
- shadow.delete(key);
38
- },
39
- clear: () => {
40
- cache.clear();
41
- shadow.clear();
42
- },
43
- size: () => shadow.size,
44
- forEach: (callback) => {
45
- shadow.forEach((value, key) => callback(value, key));
46
- },
47
- keys: () => shadow.keys(),
48
- };
49
- }
50
- const store = new Map();
9
+ if (enableLRU) {
10
+ const cache = new LRUCache(maxSize);
11
+ const shadow = new Map();
12
+ const pruneShadow = () => {
13
+ while (shadow.size > cache.size()) {
14
+ const oldestKey = shadow.keys().next().value;
15
+ if (oldestKey === undefined) {
16
+ break;
17
+ }
18
+ shadow.delete(oldestKey);
19
+ }
20
+ };
51
21
  return {
52
- get: (key) => store.get(key),
53
- set: (key, value) => {
54
- store.set(key, value);
55
- },
56
- delete: (key) => store.delete(key),
57
- clear: () => store.clear(),
58
- size: () => store.size,
59
- forEach: (callback) => {
60
- store.forEach((value, key) => callback(value, key));
61
- },
62
- keys: () => store.keys(),
22
+ get: key => {
23
+ const value = cache.get(key);
24
+ if (value !== undefined) {
25
+ shadow.set(key, value);
26
+ } else if (shadow.has(key)) {
27
+ shadow.delete(key);
28
+ }
29
+ return value;
30
+ },
31
+ set: (key, value) => {
32
+ cache.set(key, value);
33
+ shadow.set(key, value);
34
+ pruneShadow();
35
+ },
36
+ delete: key => {
37
+ cache.delete(key);
38
+ shadow.delete(key);
39
+ },
40
+ clear: () => {
41
+ cache.clear();
42
+ shadow.clear();
43
+ },
44
+ size: () => shadow.size,
45
+ forEach: callback => {
46
+ shadow.forEach((value, key) => callback(value, key));
47
+ },
48
+ keys: () => shadow.keys()
63
49
  };
50
+ }
51
+ const store = new Map();
52
+ return {
53
+ get: key => store.get(key),
54
+ set: (key, value) => {
55
+ store.set(key, value);
56
+ },
57
+ delete: key => store.delete(key),
58
+ clear: () => store.clear(),
59
+ size: () => store.size,
60
+ forEach: callback => {
61
+ store.forEach((value, key) => callback(value, key));
62
+ },
63
+ keys: () => store.keys()
64
+ };
64
65
  }
65
66
  function getCleanupInterval(tree) {
66
- return tree._memoCleanupInterval;
67
+ return tree._memoCleanupInterval;
67
68
  }
68
69
  function setCleanupInterval(tree, interval) {
69
- if (interval === undefined) {
70
- delete tree._memoCleanupInterval;
71
- return;
72
- }
73
- tree._memoCleanupInterval = interval;
70
+ if (interval === undefined) {
71
+ delete tree._memoCleanupInterval;
72
+ return;
73
+ }
74
+ tree._memoCleanupInterval = interval;
74
75
  }
75
76
  function clearCleanupInterval(tree) {
76
- const interval = getCleanupInterval(tree);
77
- if (!interval) {
78
- return;
79
- }
80
- try {
81
- clearInterval(interval);
82
- }
83
- catch {
84
- }
85
- setCleanupInterval(tree);
77
+ const interval = getCleanupInterval(tree);
78
+ if (!interval) {
79
+ return;
80
+ }
81
+ try {
82
+ clearInterval(interval);
83
+ } catch {}
84
+ setCleanupInterval(tree);
86
85
  }
87
86
  function resetMemoizationCaches() {
88
- memoizationCache.forEach((cache, tree) => {
89
- cache.clear();
90
- clearCleanupInterval(tree);
91
- });
92
- memoizationCache.clear();
87
+ memoizationCache.forEach((cache, tree) => {
88
+ cache.clear();
89
+ clearCleanupInterval(tree);
90
+ });
91
+ memoizationCache.clear();
93
92
  }
94
- export function cleanupMemoizationCache() {
95
- resetMemoizationCaches();
93
+ function cleanupMemoizationCache() {
94
+ resetMemoizationCaches();
96
95
  }
97
96
  function shallowEqual(a, b) {
98
- if (a === b)
99
- return true;
100
- if (a == null || b == null)
101
- return false;
102
- if (typeof a !== typeof b)
103
- return false;
104
- if (typeof a === 'object' && typeof b === 'object') {
105
- const objA = a;
106
- const objB = b;
107
- let countA = 0;
108
- for (const key in objA) {
109
- if (!Object.prototype.hasOwnProperty.call(objA, key))
110
- continue;
111
- countA++;
112
- if (!(key in objB) || objA[key] !== objB[key])
113
- return false;
114
- }
115
- let countB = 0;
116
- for (const key in objB) {
117
- if (Object.prototype.hasOwnProperty.call(objB, key))
118
- countB++;
119
- }
120
- return countA === countB;
97
+ if (a === b) return true;
98
+ if (a == null || b == null) return false;
99
+ if (typeof a !== typeof b) return false;
100
+ if (typeof a === 'object' && typeof b === 'object') {
101
+ const objA = a;
102
+ const objB = b;
103
+ let countA = 0;
104
+ for (const key in objA) {
105
+ if (!Object.prototype.hasOwnProperty.call(objA, key)) continue;
106
+ countA++;
107
+ if (!(key in objB) || objA[key] !== objB[key]) return false;
121
108
  }
122
- return false;
109
+ let countB = 0;
110
+ for (const key in objB) {
111
+ if (Object.prototype.hasOwnProperty.call(objB, key)) countB++;
112
+ }
113
+ return countA === countB;
114
+ }
115
+ return false;
123
116
  }
124
117
  function generateCacheKey(fn, args) {
125
- try {
126
- return `${fn.name || 'anonymous'}_${JSON.stringify(args)}`;
127
- }
128
- catch {
129
- return `${fn.name || 'anonymous'}_${args.length}`;
130
- }
118
+ try {
119
+ return `${fn.name || 'anonymous'}_${JSON.stringify(args)}`;
120
+ } catch {
121
+ return `${fn.name || 'anonymous'}_${args.length}`;
122
+ }
131
123
  }
132
124
  function getEqualityFn(strategy) {
133
- switch (strategy) {
134
- case 'shallow':
135
- return shallowEqual;
136
- case 'reference':
137
- return (a, b) => a === b;
138
- case 'deep':
139
- default:
140
- return deepEqual;
141
- }
142
- }
143
- export function memoize(fn, keyFn, config = {}) {
144
- const maxSize = config.maxCacheSize ?? MAX_CACHE_SIZE;
145
- const ttl = config.ttl ?? DEFAULT_TTL;
146
- const equality = getEqualityFn(config.equality ?? 'shallow');
147
- const enableLRU = config.enableLRU ?? false;
148
- const cache = createMemoCacheStore(maxSize, enableLRU);
149
- const cleanExpiredEntries = () => {
150
- if (!ttl)
151
- return;
152
- const now = Date.now();
153
- cache.forEach((entry, key) => {
154
- if (entry.timestamp && now - entry.timestamp > ttl) {
155
- cache.delete(key);
156
- }
157
- });
158
- };
159
- return (...args) => {
160
- if (ttl && Math.random() < 0.01) {
161
- cleanExpiredEntries();
162
- }
163
- const key = keyFn
164
- ? keyFn(...args)
165
- : generateCacheKey(fn, args);
166
- const cached = cache.get(key);
167
- if (cached && (keyFn || equality(cached.deps, args))) {
168
- if (enableLRU) {
169
- cached.hitCount += 1;
170
- }
171
- return cached.value;
172
- }
173
- const result = fn(...args);
174
- cache.set(key, {
175
- value: result,
176
- deps: args,
177
- timestamp: Date.now(),
178
- hitCount: 1,
179
- });
180
- return result;
181
- };
125
+ switch (strategy) {
126
+ case 'shallow':
127
+ return shallowEqual;
128
+ case 'reference':
129
+ return (a, b) => a === b;
130
+ case 'deep':
131
+ default:
132
+ return deepEqual;
133
+ }
182
134
  }
183
- export function memoizeShallow(fn, keyFn) {
184
- return memoize(fn, keyFn, {
185
- equality: 'shallow',
186
- enableLRU: false,
187
- ttl: undefined,
188
- maxCacheSize: 100,
135
+ function memoize(fn, keyFn, config = {}) {
136
+ const maxSize = config.maxCacheSize ?? MAX_CACHE_SIZE;
137
+ const ttl = config.ttl ?? DEFAULT_TTL;
138
+ const equality = getEqualityFn(config.equality ?? 'shallow');
139
+ const enableLRU = config.enableLRU ?? false;
140
+ const cache = createMemoCacheStore(maxSize, enableLRU);
141
+ const cleanExpiredEntries = () => {
142
+ if (!ttl) return;
143
+ const now = Date.now();
144
+ cache.forEach((entry, key) => {
145
+ if (entry.timestamp && now - entry.timestamp > ttl) {
146
+ cache.delete(key);
147
+ }
189
148
  });
190
- }
191
- export function memoizeReference(fn, keyFn) {
192
- return memoize(fn, keyFn, {
193
- equality: 'reference',
194
- enableLRU: false,
195
- ttl: undefined,
196
- maxCacheSize: 50,
149
+ };
150
+ return (...args) => {
151
+ if (ttl && Math.random() < 0.01) {
152
+ cleanExpiredEntries();
153
+ }
154
+ const key = keyFn ? keyFn(...args) : generateCacheKey(fn, args);
155
+ const cached = cache.get(key);
156
+ if (cached && (keyFn || equality(cached.deps, args))) {
157
+ if (enableLRU) {
158
+ cached.hitCount += 1;
159
+ }
160
+ return cached.value;
161
+ }
162
+ const result = fn(...args);
163
+ cache.set(key, {
164
+ value: result,
165
+ deps: args,
166
+ timestamp: Date.now(),
167
+ hitCount: 1
197
168
  });
169
+ return result;
170
+ };
198
171
  }
199
- export const MEMOIZATION_PRESETS = {
200
- selector: {
201
- equality: 'reference',
202
- maxCacheSize: 10,
203
- enableLRU: false,
204
- ttl: undefined,
205
- },
206
- computed: {
207
- equality: 'shallow',
208
- maxCacheSize: 100,
209
- enableLRU: false,
210
- ttl: undefined,
211
- },
212
- deepState: {
213
- equality: 'deep',
214
- maxCacheSize: 1000,
215
- enableLRU: true,
216
- ttl: 5 * 60 * 1000,
217
- },
218
- highFrequency: {
219
- equality: 'reference',
220
- maxCacheSize: 5,
221
- enableLRU: false,
222
- ttl: undefined,
223
- },
172
+ function memoizeShallow(fn, keyFn) {
173
+ return memoize(fn, keyFn, {
174
+ equality: 'shallow',
175
+ enableLRU: false,
176
+ ttl: undefined,
177
+ maxCacheSize: 100
178
+ });
179
+ }
180
+ function memoizeReference(fn, keyFn) {
181
+ return memoize(fn, keyFn, {
182
+ equality: 'reference',
183
+ enableLRU: false,
184
+ ttl: undefined,
185
+ maxCacheSize: 50
186
+ });
187
+ }
188
+ const MEMOIZATION_PRESETS = {
189
+ selector: {
190
+ equality: 'reference',
191
+ maxCacheSize: 10,
192
+ enableLRU: false,
193
+ ttl: undefined
194
+ },
195
+ computed: {
196
+ equality: 'shallow',
197
+ maxCacheSize: 100,
198
+ enableLRU: false,
199
+ ttl: undefined
200
+ },
201
+ deepState: {
202
+ equality: 'deep',
203
+ maxCacheSize: 1000,
204
+ enableLRU: true,
205
+ ttl: 5 * 60 * 1000
206
+ },
207
+ highFrequency: {
208
+ equality: 'reference',
209
+ maxCacheSize: 5,
210
+ enableLRU: false,
211
+ ttl: undefined
212
+ }
224
213
  };
225
- export function withSelectorMemoization() {
226
- return withMemoization(MEMOIZATION_PRESETS.selector);
214
+ function withSelectorMemoization() {
215
+ return withMemoization(MEMOIZATION_PRESETS.selector);
227
216
  }
228
- export function withComputedMemoization() {
229
- return withMemoization(MEMOIZATION_PRESETS.computed);
217
+ function withComputedMemoization() {
218
+ return withMemoization(MEMOIZATION_PRESETS.computed);
230
219
  }
231
- export function withDeepStateMemoization() {
232
- return withMemoization(MEMOIZATION_PRESETS.deepState);
220
+ function withDeepStateMemoization() {
221
+ return withMemoization(MEMOIZATION_PRESETS.deepState);
233
222
  }
234
- export function withHighFrequencyMemoization() {
235
- return withMemoization(MEMOIZATION_PRESETS.highFrequency);
223
+ function withHighFrequencyMemoization() {
224
+ return withMemoization(MEMOIZATION_PRESETS.highFrequency);
236
225
  }
237
- export function withMemoization(config = {}) {
238
- const { enabled = true, maxCacheSize = 1000, ttl, equality = 'deep', enableLRU = true, } = config;
239
- return (tree) => {
240
- const originalTreeCall = tree.bind(tree);
241
- const applyUpdateResult = (result) => {
242
- Object.entries(result).forEach(([propKey, value]) => {
243
- const property = tree.state[propKey];
244
- if (property && 'set' in property) {
245
- property.set(value);
246
- }
247
- else if (isNodeAccessor(property)) {
248
- property(value);
249
- }
250
- });
251
- };
252
- if (!enabled) {
253
- const memoTree = tree;
254
- memoTree.memoizedUpdate = (updater) => {
255
- const currentState = originalTreeCall();
256
- const result = updater(currentState);
257
- applyUpdateResult(result);
258
- };
259
- memoTree.clearMemoCache = () => {
260
- };
261
- memoTree.getCacheStats = () => ({
262
- size: 0,
263
- hitRate: 0,
264
- totalHits: 0,
265
- totalMisses: 0,
266
- keys: [],
267
- });
268
- return memoTree;
226
+ function withMemoization(config = {}) {
227
+ const {
228
+ enabled = true,
229
+ maxCacheSize = 1000,
230
+ ttl,
231
+ equality = 'deep',
232
+ enableLRU = true
233
+ } = config;
234
+ return tree => {
235
+ const originalTreeCall = tree.bind(tree);
236
+ const applyUpdateResult = result => {
237
+ Object.entries(result).forEach(([propKey, value]) => {
238
+ const property = tree.state[propKey];
239
+ if (property && 'set' in property) {
240
+ property.set(value);
241
+ } else if (isNodeAccessor(property)) {
242
+ property(value);
269
243
  }
270
- const cache = createMemoCacheStore(maxCacheSize, enableLRU);
271
- memoizationCache.set(tree, cache);
272
- const equalityFn = getEqualityFn(equality);
273
- tree.memoizedUpdate = (updater, cacheKey) => {
274
- const currentState = originalTreeCall();
275
- const key = cacheKey ||
276
- generateCacheKey(updater, [
277
- currentState,
278
- ]);
279
- const cached = cache.get(key);
280
- if (cached && equalityFn(cached.deps, [currentState])) {
281
- const cachedUpdate = cached.value;
282
- applyUpdateResult(cachedUpdate);
283
- return;
284
- }
285
- const result = updater(currentState);
286
- cache.set(key, {
287
- value: result,
288
- deps: [currentState],
289
- timestamp: Date.now(),
290
- hitCount: 1,
291
- });
292
- applyUpdateResult(result);
293
- };
294
- tree.clearMemoCache = (key) => {
295
- if (key) {
296
- cache.delete(key);
297
- }
298
- else {
299
- cache.clear();
300
- }
301
- };
302
- tree.getCacheStats = () => {
303
- let totalHits = 0;
304
- let totalMisses = 0;
305
- cache.forEach((entry) => {
306
- totalHits += entry.hitCount || 0;
307
- totalMisses += Math.floor((entry.hitCount || 0) / 2);
308
- });
309
- const hitRate = totalHits + totalMisses > 0 ? totalHits / (totalHits + totalMisses) : 0;
310
- return {
311
- size: cache.size(),
312
- hitRate,
313
- totalHits,
314
- totalMisses,
315
- keys: Array.from(cache.keys()),
316
- };
317
- };
318
- const maybeInterval = getCleanupInterval(tree);
319
- if (maybeInterval) {
320
- const origClear = tree.clearMemoCache.bind(tree);
321
- tree.clearMemoCache = (key) => {
322
- origClear(key);
323
- clearCleanupInterval(tree);
324
- };
325
- }
326
- if (ttl) {
327
- const cleanup = () => {
328
- const now = Date.now();
329
- cache.forEach((entry, key) => {
330
- if (entry.timestamp && now - entry.timestamp > ttl) {
331
- cache.delete(key);
332
- }
333
- });
334
- };
335
- const intervalId = setInterval(cleanup, ttl);
336
- setCleanupInterval(tree, intervalId);
337
- }
338
- return tree;
244
+ });
339
245
  };
246
+ if (!enabled) {
247
+ const memoTree = tree;
248
+ memoTree.memoizedUpdate = updater => {
249
+ const currentState = originalTreeCall();
250
+ const result = updater(currentState);
251
+ applyUpdateResult(result);
252
+ };
253
+ memoTree.clearMemoCache = () => {};
254
+ memoTree.getCacheStats = () => ({
255
+ size: 0,
256
+ hitRate: 0,
257
+ totalHits: 0,
258
+ totalMisses: 0,
259
+ keys: []
260
+ });
261
+ return memoTree;
262
+ }
263
+ const cache = createMemoCacheStore(maxCacheSize, enableLRU);
264
+ memoizationCache.set(tree, cache);
265
+ const equalityFn = getEqualityFn(equality);
266
+ tree.memoizedUpdate = (updater, cacheKey) => {
267
+ const currentState = originalTreeCall();
268
+ const key = cacheKey || generateCacheKey(updater, [currentState]);
269
+ const cached = cache.get(key);
270
+ if (cached && equalityFn(cached.deps, [currentState])) {
271
+ const cachedUpdate = cached.value;
272
+ applyUpdateResult(cachedUpdate);
273
+ return;
274
+ }
275
+ const result = updater(currentState);
276
+ cache.set(key, {
277
+ value: result,
278
+ deps: [currentState],
279
+ timestamp: Date.now(),
280
+ hitCount: 1
281
+ });
282
+ applyUpdateResult(result);
283
+ };
284
+ tree.clearMemoCache = key => {
285
+ if (key) {
286
+ cache.delete(key);
287
+ } else {
288
+ cache.clear();
289
+ }
290
+ };
291
+ tree.getCacheStats = () => {
292
+ let totalHits = 0;
293
+ let totalMisses = 0;
294
+ cache.forEach(entry => {
295
+ totalHits += entry.hitCount || 0;
296
+ totalMisses += Math.floor((entry.hitCount || 0) / 2);
297
+ });
298
+ const hitRate = totalHits + totalMisses > 0 ? totalHits / (totalHits + totalMisses) : 0;
299
+ return {
300
+ size: cache.size(),
301
+ hitRate,
302
+ totalHits,
303
+ totalMisses,
304
+ keys: Array.from(cache.keys())
305
+ };
306
+ };
307
+ const maybeInterval = getCleanupInterval(tree);
308
+ if (maybeInterval) {
309
+ const origClear = tree.clearMemoCache.bind(tree);
310
+ tree.clearMemoCache = key => {
311
+ origClear(key);
312
+ clearCleanupInterval(tree);
313
+ };
314
+ }
315
+ if (ttl) {
316
+ const cleanup = () => {
317
+ const now = Date.now();
318
+ cache.forEach((entry, key) => {
319
+ if (entry.timestamp && now - entry.timestamp > ttl) {
320
+ cache.delete(key);
321
+ }
322
+ });
323
+ };
324
+ const intervalId = setInterval(cleanup, ttl);
325
+ setCleanupInterval(tree, intervalId);
326
+ }
327
+ return tree;
328
+ };
340
329
  }
341
- export function enableMemoization() {
342
- return withMemoization({ enabled: true });
343
- }
344
- export function withHighPerformanceMemoization() {
345
- return withMemoization({
346
- enabled: true,
347
- maxCacheSize: 10000,
348
- ttl: 300000,
349
- equality: 'shallow',
350
- enableLRU: true,
351
- });
330
+ function withHighPerformanceMemoization() {
331
+ return withMemoization({
332
+ enabled: true,
333
+ maxCacheSize: 10000,
334
+ ttl: 300000,
335
+ equality: 'shallow',
336
+ enableLRU: true
337
+ });
352
338
  }
353
- export function withLightweightMemoization() {
354
- return withMemoization({
355
- enabled: true,
356
- maxCacheSize: 100,
357
- ttl: undefined,
358
- equality: 'reference',
359
- enableLRU: false,
360
- });
339
+ function withLightweightMemoization() {
340
+ return withMemoization({
341
+ enabled: true,
342
+ maxCacheSize: 100,
343
+ ttl: undefined,
344
+ equality: 'reference',
345
+ enableLRU: false
346
+ });
361
347
  }
362
- export function withShallowMemoization() {
363
- return withMemoization({
364
- enabled: true,
365
- maxCacheSize: 1000,
366
- ttl: 60000,
367
- equality: 'shallow',
368
- enableLRU: true,
369
- });
348
+ function withShallowMemoization() {
349
+ return withMemoization({
350
+ enabled: true,
351
+ maxCacheSize: 1000,
352
+ ttl: 60000,
353
+ equality: 'shallow',
354
+ enableLRU: true
355
+ });
370
356
  }
371
- export function clearAllCaches() {
372
- resetMemoizationCaches();
357
+ function clearAllCaches() {
358
+ resetMemoizationCaches();
373
359
  }
374
- export function getGlobalCacheStats() {
375
- let totalSize = 0;
376
- let totalHits = 0;
377
- let treeCount = 0;
378
- memoizationCache.forEach((cache) => {
379
- treeCount++;
380
- totalSize += cache.size();
381
- cache.forEach((entry) => {
382
- totalHits += entry.hitCount || 0;
383
- });
360
+ function getGlobalCacheStats() {
361
+ let totalSize = 0;
362
+ let totalHits = 0;
363
+ let treeCount = 0;
364
+ memoizationCache.forEach(cache => {
365
+ treeCount++;
366
+ totalSize += cache.size();
367
+ cache.forEach(entry => {
368
+ totalHits += entry.hitCount || 0;
384
369
  });
385
- return {
386
- treeCount,
387
- totalSize,
388
- totalHits,
389
- averageCacheSize: treeCount > 0 ? totalSize / treeCount : 0,
390
- };
370
+ });
371
+ return {
372
+ treeCount,
373
+ totalSize,
374
+ totalHits,
375
+ averageCacheSize: treeCount > 0 ? totalSize / treeCount : 0
376
+ };
391
377
  }
378
+
379
+ export { MEMOIZATION_PRESETS, cleanupMemoizationCache, clearAllCaches, getGlobalCacheStats, memoize, memoizeReference, memoizeShallow, withComputedMemoization, withDeepStateMemoization, withHighFrequencyMemoization, withHighPerformanceMemoization, withLightweightMemoization, withMemoization, withSelectorMemoization, withShallowMemoization };