@umituz/react-native-design-system 2.8.7 → 2.8.8

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 (128) hide show
  1. package/package.json +5 -6
  2. package/src/device/infrastructure/repositories/LegacyDeviceIdRepository.ts +1 -1
  3. package/src/device/infrastructure/services/DeviceFeatureService.ts +1 -1
  4. package/src/exception/infrastructure/services/ExceptionLogger.ts +1 -1
  5. package/src/exception/infrastructure/storage/ExceptionStore.ts +1 -1
  6. package/src/exports/filesystem.ts +1 -0
  7. package/src/exports/storage.ts +1 -0
  8. package/src/filesystem/domain/constants/FileConstants.ts +20 -0
  9. package/src/filesystem/domain/entities/File.ts +20 -0
  10. package/src/filesystem/domain/types/FileTypes.ts +43 -0
  11. package/src/filesystem/domain/utils/FileUtils.ts +86 -0
  12. package/src/filesystem/index.ts +23 -0
  13. package/src/filesystem/infrastructure/services/FileSystemService.ts +45 -0
  14. package/src/filesystem/infrastructure/services/cache.service.ts +48 -0
  15. package/src/filesystem/infrastructure/services/directory.service.ts +66 -0
  16. package/src/filesystem/infrastructure/services/download.constants.ts +6 -0
  17. package/src/filesystem/infrastructure/services/download.service.ts +74 -0
  18. package/src/filesystem/infrastructure/services/download.types.ts +7 -0
  19. package/src/filesystem/infrastructure/services/encoding.service.ts +25 -0
  20. package/src/filesystem/infrastructure/services/file-info.service.ts +52 -0
  21. package/src/filesystem/infrastructure/services/file-manager.service.ts +81 -0
  22. package/src/filesystem/infrastructure/services/file-path.service.ts +22 -0
  23. package/src/filesystem/infrastructure/services/file-reader.service.ts +52 -0
  24. package/src/filesystem/infrastructure/services/file-writer.service.ts +32 -0
  25. package/src/filesystem/infrastructure/utils/blob.utils.ts +20 -0
  26. package/src/image/infrastructure/services/ImageStorageService.ts +1 -1
  27. package/src/index.ts +9 -0
  28. package/src/molecules/alerts/AlertStore.ts +1 -1
  29. package/src/molecules/calendar/infrastructure/storage/EventActions.ts +1 -1
  30. package/src/molecules/calendar/infrastructure/stores/storageAdapter.ts +1 -1
  31. package/src/offline/infrastructure/storage/OfflineStore.ts +1 -1
  32. package/src/onboarding/infrastructure/storage/OnboardingStore.ts +2 -2
  33. package/src/onboarding/infrastructure/storage/__tests__/OnboardingStore.test.ts +1 -1
  34. package/src/onboarding/infrastructure/storage/actions/answerActions.ts +1 -1
  35. package/src/onboarding/infrastructure/storage/actions/storageHelpers.ts +1 -1
  36. package/src/storage/README.md +185 -0
  37. package/src/storage/__tests__/integration.test.ts +391 -0
  38. package/src/storage/__tests__/mocks/asyncStorage.mock.ts +52 -0
  39. package/src/storage/__tests__/performance.test.tsx +352 -0
  40. package/src/storage/__tests__/setup.ts +63 -0
  41. package/src/storage/application/README.md +158 -0
  42. package/src/storage/application/ports/IStorageRepository.ts +61 -0
  43. package/src/storage/application/ports/README.md +127 -0
  44. package/src/storage/cache/README.md +154 -0
  45. package/src/storage/cache/__tests__/PerformanceAndMemory.test.ts +387 -0
  46. package/src/storage/cache/__tests__/setup.ts +19 -0
  47. package/src/storage/cache/domain/Cache.ts +146 -0
  48. package/src/storage/cache/domain/CacheManager.md +83 -0
  49. package/src/storage/cache/domain/CacheManager.ts +48 -0
  50. package/src/storage/cache/domain/CacheStatsTracker.md +169 -0
  51. package/src/storage/cache/domain/CacheStatsTracker.ts +49 -0
  52. package/src/storage/cache/domain/CachedValue.md +97 -0
  53. package/src/storage/cache/domain/ErrorHandler.md +99 -0
  54. package/src/storage/cache/domain/ErrorHandler.ts +42 -0
  55. package/src/storage/cache/domain/PatternMatcher.md +122 -0
  56. package/src/storage/cache/domain/PatternMatcher.ts +30 -0
  57. package/src/storage/cache/domain/README.md +118 -0
  58. package/src/storage/cache/domain/__tests__/Cache.test.ts +293 -0
  59. package/src/storage/cache/domain/__tests__/CacheManager.test.ts +276 -0
  60. package/src/storage/cache/domain/__tests__/ErrorHandler.test.ts +303 -0
  61. package/src/storage/cache/domain/__tests__/PatternMatcher.test.ts +261 -0
  62. package/src/storage/cache/domain/strategies/EvictionStrategy.ts +9 -0
  63. package/src/storage/cache/domain/strategies/FIFOStrategy.ts +12 -0
  64. package/src/storage/cache/domain/strategies/LFUStrategy.ts +22 -0
  65. package/src/storage/cache/domain/strategies/LRUStrategy.ts +22 -0
  66. package/src/storage/cache/domain/strategies/README.md +117 -0
  67. package/src/storage/cache/domain/strategies/TTLStrategy.ts +23 -0
  68. package/src/storage/cache/domain/strategies/__tests__/EvictionStrategies.test.ts +293 -0
  69. package/src/storage/cache/domain/types/Cache.ts +28 -0
  70. package/src/storage/cache/domain/types/README.md +107 -0
  71. package/src/storage/cache/index.ts +28 -0
  72. package/src/storage/cache/infrastructure/README.md +126 -0
  73. package/src/storage/cache/infrastructure/TTLCache.ts +103 -0
  74. package/src/storage/cache/infrastructure/__tests__/TTLCache.test.ts +303 -0
  75. package/src/storage/cache/presentation/README.md +123 -0
  76. package/src/storage/cache/presentation/__tests__/ReactHooks.test.ts +514 -0
  77. package/src/storage/cache/presentation/useCache.ts +76 -0
  78. package/src/storage/cache/presentation/useCachedValue.ts +88 -0
  79. package/src/storage/cache/types.d.ts +3 -0
  80. package/src/storage/domain/README.md +128 -0
  81. package/src/storage/domain/constants/CacheDefaults.ts +64 -0
  82. package/src/storage/domain/constants/README.md +105 -0
  83. package/src/storage/domain/entities/CachedValue.ts +86 -0
  84. package/src/storage/domain/entities/README.md +109 -0
  85. package/src/storage/domain/entities/StorageResult.ts +75 -0
  86. package/src/storage/domain/entities/__tests__/CachedValue.test.ts +149 -0
  87. package/src/storage/domain/entities/__tests__/StorageResult.test.ts +122 -0
  88. package/src/storage/domain/errors/README.md +126 -0
  89. package/src/storage/domain/errors/StorageError.ts +81 -0
  90. package/src/storage/domain/errors/__tests__/StorageError.test.ts +127 -0
  91. package/src/storage/domain/factories/README.md +138 -0
  92. package/src/storage/domain/factories/StoreFactory.ts +59 -0
  93. package/src/storage/domain/types/README.md +522 -0
  94. package/src/storage/domain/types/Store.ts +44 -0
  95. package/src/storage/domain/utils/CacheKeyGenerator.ts +66 -0
  96. package/src/storage/domain/utils/README.md +127 -0
  97. package/src/storage/domain/utils/__tests__/devUtils.test.ts +97 -0
  98. package/src/storage/domain/utils/devUtils.ts +37 -0
  99. package/src/storage/domain/value-objects/README.md +120 -0
  100. package/src/storage/domain/value-objects/StorageKey.ts +60 -0
  101. package/src/storage/index.ts +175 -0
  102. package/src/storage/infrastructure/README.md +165 -0
  103. package/src/storage/infrastructure/adapters/README.md +175 -0
  104. package/src/storage/infrastructure/adapters/StorageService.md +103 -0
  105. package/src/storage/infrastructure/adapters/StorageService.ts +49 -0
  106. package/src/storage/infrastructure/repositories/AsyncStorageRepository.ts +98 -0
  107. package/src/storage/infrastructure/repositories/BaseStorageOperations.ts +100 -0
  108. package/src/storage/infrastructure/repositories/BatchStorageOperations.ts +42 -0
  109. package/src/storage/infrastructure/repositories/README.md +121 -0
  110. package/src/storage/infrastructure/repositories/StringStorageOperations.ts +44 -0
  111. package/src/storage/infrastructure/repositories/__tests__/AsyncStorageRepository.test.ts +170 -0
  112. package/src/storage/infrastructure/repositories/__tests__/BaseStorageOperations.test.ts +201 -0
  113. package/src/storage/presentation/README.md +181 -0
  114. package/src/storage/presentation/hooks/CacheStorageOperations.ts +94 -0
  115. package/src/storage/presentation/hooks/README.md +128 -0
  116. package/src/storage/presentation/hooks/__tests__/usePersistentCache.test.ts +405 -0
  117. package/src/storage/presentation/hooks/__tests__/useStorage.test.ts +247 -0
  118. package/src/storage/presentation/hooks/__tests__/useStorageState.test.ts +293 -0
  119. package/src/storage/presentation/hooks/useCacheState.ts +53 -0
  120. package/src/storage/presentation/hooks/usePersistentCache.ts +154 -0
  121. package/src/storage/presentation/hooks/useStorage.ts +102 -0
  122. package/src/storage/presentation/hooks/useStorageState.ts +71 -0
  123. package/src/storage/presentation/hooks/useStore.ts +15 -0
  124. package/src/storage/types/README.md +103 -0
  125. package/src/theme/infrastructure/globalThemeStore.ts +1 -1
  126. package/src/theme/infrastructure/storage/ThemeStorage.ts +1 -1
  127. package/src/theme/infrastructure/stores/themeStore.ts +1 -1
  128. package/src/utilities/sharing/infrastructure/services/SharingService.ts +1 -1
@@ -0,0 +1,303 @@
1
+ /**
2
+ * TTLCache Tests
3
+ */
4
+
5
+ import { TTLCache } from '../TTLCache';
6
+ import type { CacheConfig } from '../domain/types/Cache';
7
+
8
+ describe('TTLCache', () => {
9
+ let cache: TTLCache<string>;
10
+
11
+ beforeEach(() => {
12
+ jest.useFakeTimers();
13
+ cache = new TTLCache<string>({
14
+ maxSize: 5,
15
+ defaultTTL: 1000,
16
+ cleanupIntervalMs: 500
17
+ });
18
+ });
19
+
20
+ afterEach(() => {
21
+ jest.useRealTimers();
22
+ cache.destroy();
23
+ });
24
+
25
+ describe('Basic Functionality', () => {
26
+ test('should inherit from Cache', () => {
27
+ expect(cache).toHaveProperty('set');
28
+ expect(cache).toHaveProperty('get');
29
+ expect(cache).toHaveProperty('has');
30
+ expect(cache).toHaveProperty('delete');
31
+ expect(cache).toHaveProperty('clear');
32
+ });
33
+
34
+ test('should set and get values', () => {
35
+ cache.set('key1', 'value1');
36
+ expect(cache.get('key1')).toBe('value1');
37
+ });
38
+
39
+ test('should use default TTL', () => {
40
+ cache.set('key1', 'value1');
41
+ expect(cache.get('key1')).toBe('value1');
42
+
43
+ jest.advanceTimersByTime(1001);
44
+ expect(cache.get('key1')).toBeUndefined();
45
+ });
46
+
47
+ test('should use custom TTL', () => {
48
+ cache.set('key1', 'value1', 2000);
49
+ expect(cache.get('key1')).toBe('value1');
50
+
51
+ jest.advanceTimersByTime(1001);
52
+ expect(cache.get('key1')).toBe('value1'); // Should still exist
53
+
54
+ jest.advanceTimersByTime(1000);
55
+ expect(cache.get('key1')).toBeUndefined(); // Should be expired
56
+ });
57
+ });
58
+
59
+ describe('Automatic Cleanup', () => {
60
+ test('should start cleanup interval on creation', () => {
61
+ const setIntervalSpy = jest.spyOn(global, 'setInterval');
62
+ new TTLCache<string>({ cleanupIntervalMs: 1000 });
63
+
64
+ expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 1000);
65
+ setIntervalSpy.mockRestore();
66
+ });
67
+
68
+ test('should cleanup expired entries automatically', () => {
69
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
70
+
71
+ cache.set('key1', 'value1', 500);
72
+ cache.set('key2', 'value2', 1500);
73
+
74
+ // Fast forward to trigger first cleanup
75
+ jest.advanceTimersByTime(500);
76
+
77
+ // key1 should be cleaned up, key2 should remain
78
+ expect(cache.get('key1')).toBeUndefined();
79
+ expect(cache.get('key2')).toBe('value2');
80
+
81
+ expect(consoleSpy).toHaveBeenCalledWith('TTLCache: Cleaned up 1 expired entries');
82
+
83
+ consoleSpy.mockRestore();
84
+ });
85
+
86
+ test('should not log when no entries are cleaned up', () => {
87
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
88
+
89
+ cache.set('key1', 'value1', 2000);
90
+
91
+ // Fast forward, but no entries should be expired yet
92
+ jest.advanceTimersByTime(500);
93
+
94
+ expect(consoleSpy).not.toHaveBeenCalled();
95
+
96
+ consoleSpy.mockRestore();
97
+ });
98
+
99
+ test('should handle multiple cleanup cycles', () => {
100
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
101
+
102
+ cache.set('key1', 'value1', 300);
103
+ cache.set('key2', 'value2', 800);
104
+ cache.set('key3', 'value3', 1300);
105
+
106
+ // First cleanup - key1 expires
107
+ jest.advanceTimersByTime(500);
108
+ expect(cache.get('key1')).toBeUndefined();
109
+ expect(cache.get('key2')).toBe('value2');
110
+ expect(cache.get('key3')).toBe('value3');
111
+
112
+ // Second cleanup - key2 expires
113
+ jest.advanceTimersByTime(500);
114
+ expect(cache.get('key2')).toBeUndefined();
115
+ expect(cache.get('key3')).toBe('value3');
116
+
117
+ // Third cleanup - key3 expires
118
+ jest.advanceTimersByTime(500);
119
+ expect(cache.get('key3')).toBeUndefined();
120
+
121
+ expect(consoleSpy).toHaveBeenCalledTimes(3);
122
+
123
+ consoleSpy.mockRestore();
124
+ });
125
+ });
126
+
127
+ describe('Destroy Method', () => {
128
+ test('should clear cleanup interval on destroy', () => {
129
+ const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
130
+
131
+ cache.destroy();
132
+
133
+ expect(clearIntervalSpy).toHaveBeenCalled();
134
+ clearIntervalSpy.mockRestore();
135
+ });
136
+
137
+ test('should clear all data on destroy', () => {
138
+ cache.set('key1', 'value1');
139
+ cache.set('key2', 'value2');
140
+
141
+ expect(cache.getStats().size).toBe(2);
142
+
143
+ cache.destroy();
144
+
145
+ expect(cache.getStats().size).toBe(0);
146
+ });
147
+
148
+ test('should handle multiple destroy calls', () => {
149
+ const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
150
+
151
+ cache.destroy();
152
+ cache.destroy();
153
+ cache.destroy();
154
+
155
+ // Should only call clearInterval once
156
+ expect(clearIntervalSpy).toHaveBeenCalledTimes(1);
157
+
158
+ clearIntervalSpy.mockRestore();
159
+ });
160
+
161
+ test('should not cleanup after destroy', () => {
162
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
163
+
164
+ cache.set('key1', 'value1', 300);
165
+
166
+ cache.destroy();
167
+
168
+ // Advance time - should not trigger cleanup
169
+ jest.advanceTimersByTime(1000);
170
+
171
+ expect(consoleSpy).not.toHaveBeenCalled();
172
+
173
+ consoleSpy.mockRestore();
174
+ });
175
+ });
176
+
177
+ describe('Destroyed State Handling', () => {
178
+ beforeEach(() => {
179
+ cache.destroy();
180
+ });
181
+
182
+ test('should warn on set after destroy', () => {
183
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
184
+
185
+ cache.set('key1', 'value1');
186
+
187
+ expect(consoleSpy).toHaveBeenCalledWith('TTLCache: Attempted to set value on destroyed cache');
188
+ expect(cache.get('key1')).toBeUndefined();
189
+
190
+ consoleSpy.mockRestore();
191
+ });
192
+
193
+ test('should warn on get after destroy', () => {
194
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
195
+
196
+ const result = cache.get('key1');
197
+
198
+ expect(consoleSpy).toHaveBeenCalledWith('TTLCache: Attempted to get value from destroyed cache');
199
+ expect(result).toBeUndefined();
200
+
201
+ consoleSpy.mockRestore();
202
+ });
203
+
204
+ test('should return false for has after destroy', () => {
205
+ expect(cache.has('key1')).toBe(false);
206
+ });
207
+
208
+ test('should return false for delete after destroy', () => {
209
+ expect(cache.delete('key1')).toBe(false);
210
+ });
211
+
212
+ test('should not throw on clear after destroy', () => {
213
+ expect(() => cache.clear()).not.toThrow();
214
+ });
215
+ });
216
+
217
+ describe('Configuration', () => {
218
+ test('should use custom cleanup interval', () => {
219
+ const setIntervalSpy = jest.spyOn(global, 'setInterval');
220
+
221
+ new TTLCache<string>({ cleanupIntervalMs: 2000 });
222
+
223
+ expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 2000);
224
+ setIntervalSpy.mockRestore();
225
+ });
226
+
227
+ test('should use default cleanup interval when not specified', () => {
228
+ const setIntervalSpy = jest.spyOn(global, 'setInterval');
229
+
230
+ new TTLCache<string>();
231
+
232
+ expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 60000);
233
+ setIntervalSpy.mockRestore();
234
+ });
235
+
236
+ test('should pass configuration to parent Cache', () => {
237
+ const config: CacheConfig = {
238
+ maxSize: 10,
239
+ defaultTTL: 5000,
240
+ };
241
+
242
+ const customCache = new TTLCache<string>(config);
243
+
244
+ customCache.set('key1', 'value1');
245
+ expect(customCache.get('key1')).toBe('value1');
246
+
247
+ // Should use custom default TTL
248
+ jest.advanceTimersByTime(5001);
249
+ expect(customCache.get('key1')).toBeUndefined();
250
+
251
+ customCache.destroy();
252
+ });
253
+ });
254
+
255
+ describe('Memory Management', () => {
256
+ test('should not memory leak with many entries', () => {
257
+ const entryCount = 1000;
258
+
259
+ // Create many entries with short TTL
260
+ for (let i = 0; i < entryCount; i++) {
261
+ cache.set(`key${i}`, `value${i}`, 100);
262
+ }
263
+
264
+ expect(cache.getStats().size).toBe(entryCount);
265
+
266
+ // Let all entries expire and cleanup
267
+ jest.advanceTimersByTime(500);
268
+
269
+ expect(cache.getStats().size).toBe(0);
270
+ });
271
+
272
+ test('should handle rapid create/destroy cycles', () => {
273
+ for (let i = 0; i < 10; i++) {
274
+ const tempCache = new TTLCache<string>({ cleanupIntervalMs: 100 });
275
+ tempCache.set('key', 'value');
276
+ tempCache.destroy();
277
+ }
278
+
279
+ // Should not throw or cause issues
280
+ expect(true).toBe(true);
281
+ });
282
+ });
283
+
284
+ describe('Edge Cases', () => {
285
+ test('should handle zero cleanup interval', () => {
286
+ expect(() => {
287
+ new TTLCache<string>({ cleanupIntervalMs: 0 });
288
+ }).not.toThrow();
289
+ });
290
+
291
+ test('should handle negative cleanup interval', () => {
292
+ expect(() => {
293
+ new TTLCache<string>({ cleanupIntervalMs: -100 });
294
+ }).not.toThrow();
295
+ });
296
+
297
+ test('should handle very large cleanup interval', () => {
298
+ expect(() => {
299
+ new TTLCache<string>({ cleanupIntervalMs: Number.MAX_SAFE_INTEGER });
300
+ }).not.toThrow();
301
+ });
302
+ });
303
+ });
@@ -0,0 +1,123 @@
1
+ # Cache Presentation
2
+
3
+ React hooks and UI integration for cache system.
4
+
5
+ ## Overview
6
+
7
+ Presentation layer provides React hooks and components for integrating cache functionality with React applications. Located at `src/cache/presentation/`.
8
+
9
+ ## Strategies
10
+
11
+ ### Hook Design
12
+ - Create hooks for common caching patterns
13
+ - Provide simple, composable APIs
14
+ - Support React Suspense where applicable
15
+ - Handle loading and error states
16
+
17
+ ### React Integration
18
+ - Follow React rules of hooks
19
+ - Support dependency arrays correctly
20
+ - Clean up on component unmount
21
+ - Avoid unnecessary re-renders
22
+
23
+ ### State Management
24
+ - Expose cache state reactively
25
+ - Provide update mechanisms
26
+ - Support invalidation and refresh
27
+ - Handle stale data appropriately
28
+
29
+ ### Performance
30
+ - Use memoization to prevent redundant operations
31
+ - Implement efficient re-render strategies
32
+ - Debounce rapid cache operations
33
+ - Minimize hook overhead
34
+
35
+ ## Restrictions
36
+
37
+ ### Hook Usage
38
+ - DO NOT call hooks outside React components
39
+ - DO NOT call hooks conditionally
40
+ - DO NOT call hooks in loops
41
+ - DO NOT create new cache instances on every render
42
+
43
+ ### State Management
44
+ - DO NOT cause infinite re-render loops
45
+ - DO NOT mutate state directly
46
+ - DO NOT ignore loading states
47
+ - DO NOT swallow errors silently
48
+
49
+ ### Performance
50
+ - DO NOT recreate functions on every render
51
+ - DO NOT subscribe to entire cache when slice needed
52
+ - DO NOT perform expensive operations in render
53
+ - DO NOT cache large computed values in hook state
54
+
55
+ ### Cleanup
56
+ - DO NOT leak subscriptions
57
+ - DO NOT forget cleanup on unmount
58
+ - DO NOT create memory leaks
59
+ - DO NOT leave timers running
60
+
61
+ ## Rules
62
+
63
+ ### Hook Implementation
64
+ - MUST follow React rules of hooks
65
+ - MUST use `use` prefix for hook names
66
+ - MUST provide TypeScript types
67
+ - MUST handle errors gracefully
68
+
69
+ ### useCache Hook
70
+ - MUST accept cache name parameter
71
+ - MUST return cache interface methods
72
+ - MUST provide consistent API across renders
73
+ - MUST handle non-existent cache gracefully
74
+
75
+ ### useCachedValue Hook
76
+ - MUST accept key and fetcher function
77
+ - MUST accept optional TTL parameter
78
+ - MUST return value, setter, and invalidate function
79
+ - MUST handle loading state
80
+ - MUST handle error state
81
+
82
+ ### State Updates
83
+ - MUST trigger re-renders on cache changes
84
+ - MUST use React state for reactive values
85
+ - MUST memoize callbacks to prevent re-renders
86
+ - MUST update state atomically
87
+
88
+ ### Cleanup Behavior
89
+ - MUST clean up subscriptions on unmount
90
+ - MUST cancel pending operations
91
+ - MUST clear timers
92
+ - MUST not leak memory
93
+
94
+ ### Error Handling
95
+ - MUST expose error state to caller
96
+ - MUST allow error recovery
97
+ - MUST log errors in development
98
+ - MUST provide error boundaries for usage
99
+
100
+ ### TypeScript Types
101
+ - MUST provide generic type parameters
102
+ - MUST infer types from cache
103
+ - MUST enforce type safety
104
+ - MUST document complex types
105
+
106
+ ### Performance Rules
107
+ - MUST use useCallback for stable function references
108
+ - MUST use useMemo for expensive computations
109
+ - MUST provide stable references across renders
110
+ - MUST minimize re-render frequency
111
+
112
+ ### Testing Requirements
113
+ - MUST test with @testing-library/react-hooks
114
+ - MUST test cleanup behavior
115
+ - MUST test error scenarios
116
+ - MUST test type safety
117
+ - MUST test re-render behavior
118
+
119
+ ### Export Rules
120
+ - MUST export hooks from index file
121
+ - MUST provide consistent naming
122
+ - MUST document hook contracts
123
+ - MUST maintain backward compatibility