@buoy-gg/storage 1.7.2

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 (187) hide show
  1. package/README.md +607 -0
  2. package/lib/commonjs/index.js +34 -0
  3. package/lib/commonjs/package.json +1 -0
  4. package/lib/commonjs/preset.js +94 -0
  5. package/lib/commonjs/storage/components/DiffViewer/DiffOptionsPanel.js +356 -0
  6. package/lib/commonjs/storage/components/DiffViewer/TreeDiffViewer.js +29 -0
  7. package/lib/commonjs/storage/components/DiffViewer/components/DiffSummary.js +121 -0
  8. package/lib/commonjs/storage/components/DiffViewer/modes/ThemedSplitView.js +419 -0
  9. package/lib/commonjs/storage/components/DiffViewer/themes/diffThemes.js +122 -0
  10. package/lib/commonjs/storage/components/GameUIStorageBrowser.js +924 -0
  11. package/lib/commonjs/storage/components/GameUIStorageStats.js +746 -0
  12. package/lib/commonjs/storage/components/MMKVInstanceInfoPanel.js +257 -0
  13. package/lib/commonjs/storage/components/MMKVInstanceSelector.js +418 -0
  14. package/lib/commonjs/storage/components/SelectionActionBar.js +224 -0
  15. package/lib/commonjs/storage/components/StorageActionButtons.js +239 -0
  16. package/lib/commonjs/storage/components/StorageActions.js +192 -0
  17. package/lib/commonjs/storage/components/StorageBrowserMode.js +31 -0
  18. package/lib/commonjs/storage/components/StorageEventDetailContent.js +1025 -0
  19. package/lib/commonjs/storage/components/StorageEventFilterView.js +141 -0
  20. package/lib/commonjs/storage/components/StorageEventListener.js +357 -0
  21. package/lib/commonjs/storage/components/StorageEventsSection.js +24 -0
  22. package/lib/commonjs/storage/components/StorageFilterCards.js +345 -0
  23. package/lib/commonjs/storage/components/StorageFilterViewV2.js +42 -0
  24. package/lib/commonjs/storage/components/StorageKeyCard.js +516 -0
  25. package/lib/commonjs/storage/components/StorageKeyRow.js +356 -0
  26. package/lib/commonjs/storage/components/StorageKeySection.js +105 -0
  27. package/lib/commonjs/storage/components/StorageKeyStats.js +344 -0
  28. package/lib/commonjs/storage/components/StorageModalWithTabs.js +871 -0
  29. package/lib/commonjs/storage/components/StorageSection.js +43 -0
  30. package/lib/commonjs/storage/hooks/useAsyncStorageKeys.js +126 -0
  31. package/lib/commonjs/storage/hooks/useMMKVInstances.js +221 -0
  32. package/lib/commonjs/storage/hooks/useMMKVKeys.js +362 -0
  33. package/lib/commonjs/storage/hooks/useTickEverySecond.js +21 -0
  34. package/lib/commonjs/storage/index.js +148 -0
  35. package/lib/commonjs/storage/types.js +5 -0
  36. package/lib/commonjs/storage/utils/AsyncStorageListener.js +510 -0
  37. package/lib/commonjs/storage/utils/MMKVInstanceRegistry.js +202 -0
  38. package/lib/commonjs/storage/utils/MMKVListener.js +380 -0
  39. package/lib/commonjs/storage/utils/clearAllStorage.js +47 -0
  40. package/lib/commonjs/storage/utils/index.js +180 -0
  41. package/lib/commonjs/storage/utils/lineDiff.js +363 -0
  42. package/lib/commonjs/storage/utils/mmkvAvailability.js +62 -0
  43. package/lib/commonjs/storage/utils/mmkvTypeDetection.js +139 -0
  44. package/lib/commonjs/storage/utils/objectDiff.js +157 -0
  45. package/lib/commonjs/storage/utils/safeAsyncStorage.js +140 -0
  46. package/lib/commonjs/storage/utils/storageActionHelpers.js +46 -0
  47. package/lib/commonjs/storage/utils/storageQueryUtils.js +35 -0
  48. package/lib/commonjs/storage/utils/valueType.js +18 -0
  49. package/lib/module/index.js +7 -0
  50. package/lib/module/preset.js +89 -0
  51. package/lib/module/storage/components/DiffViewer/DiffOptionsPanel.js +352 -0
  52. package/lib/module/storage/components/DiffViewer/TreeDiffViewer.js +25 -0
  53. package/lib/module/storage/components/DiffViewer/components/DiffSummary.js +117 -0
  54. package/lib/module/storage/components/DiffViewer/modes/ThemedSplitView.js +415 -0
  55. package/lib/module/storage/components/DiffViewer/themes/diffThemes.js +118 -0
  56. package/lib/module/storage/components/GameUIStorageBrowser.js +922 -0
  57. package/lib/module/storage/components/GameUIStorageStats.js +742 -0
  58. package/lib/module/storage/components/MMKVInstanceInfoPanel.js +253 -0
  59. package/lib/module/storage/components/MMKVInstanceSelector.js +414 -0
  60. package/lib/module/storage/components/SelectionActionBar.js +221 -0
  61. package/lib/module/storage/components/StorageActionButtons.js +236 -0
  62. package/lib/module/storage/components/StorageActions.js +189 -0
  63. package/lib/module/storage/components/StorageBrowserMode.js +27 -0
  64. package/lib/module/storage/components/StorageEventDetailContent.js +1020 -0
  65. package/lib/module/storage/components/StorageEventFilterView.js +137 -0
  66. package/lib/module/storage/components/StorageEventListener.js +354 -0
  67. package/lib/module/storage/components/StorageEventsSection.js +20 -0
  68. package/lib/module/storage/components/StorageFilterCards.js +341 -0
  69. package/lib/module/storage/components/StorageFilterViewV2.js +38 -0
  70. package/lib/module/storage/components/StorageKeyCard.js +513 -0
  71. package/lib/module/storage/components/StorageKeyRow.js +353 -0
  72. package/lib/module/storage/components/StorageKeySection.js +101 -0
  73. package/lib/module/storage/components/StorageKeyStats.js +340 -0
  74. package/lib/module/storage/components/StorageModalWithTabs.js +867 -0
  75. package/lib/module/storage/components/StorageSection.js +40 -0
  76. package/lib/module/storage/hooks/useAsyncStorageKeys.js +121 -0
  77. package/lib/module/storage/hooks/useMMKVInstances.js +216 -0
  78. package/lib/module/storage/hooks/useMMKVKeys.js +359 -0
  79. package/lib/module/storage/hooks/useTickEverySecond.js +18 -0
  80. package/lib/module/storage/index.js +25 -0
  81. package/lib/module/storage/types.js +3 -0
  82. package/lib/module/storage/utils/AsyncStorageListener.js +500 -0
  83. package/lib/module/storage/utils/MMKVInstanceRegistry.js +196 -0
  84. package/lib/module/storage/utils/MMKVListener.js +367 -0
  85. package/lib/module/storage/utils/clearAllStorage.js +42 -0
  86. package/lib/module/storage/utils/index.js +22 -0
  87. package/lib/module/storage/utils/lineDiff.js +359 -0
  88. package/lib/module/storage/utils/mmkvAvailability.js +56 -0
  89. package/lib/module/storage/utils/mmkvTypeDetection.js +133 -0
  90. package/lib/module/storage/utils/objectDiff.js +153 -0
  91. package/lib/module/storage/utils/safeAsyncStorage.js +134 -0
  92. package/lib/module/storage/utils/storageActionHelpers.js +42 -0
  93. package/lib/module/storage/utils/storageQueryUtils.js +30 -0
  94. package/lib/module/storage/utils/valueType.js +14 -0
  95. package/lib/typescript/index.d.ts +3 -0
  96. package/lib/typescript/index.d.ts.map +1 -0
  97. package/lib/typescript/preset.d.ts +90 -0
  98. package/lib/typescript/preset.d.ts.map +1 -0
  99. package/lib/typescript/storage/components/DiffViewer/DiffOptionsPanel.d.ts +18 -0
  100. package/lib/typescript/storage/components/DiffViewer/DiffOptionsPanel.d.ts.map +1 -0
  101. package/lib/typescript/storage/components/DiffViewer/TreeDiffViewer.d.ts +7 -0
  102. package/lib/typescript/storage/components/DiffViewer/TreeDiffViewer.d.ts.map +1 -0
  103. package/lib/typescript/storage/components/DiffViewer/components/DiffSummary.d.ts +12 -0
  104. package/lib/typescript/storage/components/DiffViewer/components/DiffSummary.d.ts.map +1 -0
  105. package/lib/typescript/storage/components/DiffViewer/modes/ThemedSplitView.d.ts +13 -0
  106. package/lib/typescript/storage/components/DiffViewer/modes/ThemedSplitView.d.ts.map +1 -0
  107. package/lib/typescript/storage/components/DiffViewer/themes/diffThemes.d.ts +64 -0
  108. package/lib/typescript/storage/components/DiffViewer/themes/diffThemes.d.ts.map +1 -0
  109. package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts +16 -0
  110. package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts.map +1 -0
  111. package/lib/typescript/storage/components/GameUIStorageStats.d.ts +7 -0
  112. package/lib/typescript/storage/components/GameUIStorageStats.d.ts.map +1 -0
  113. package/lib/typescript/storage/components/MMKVInstanceInfoPanel.d.ts +42 -0
  114. package/lib/typescript/storage/components/MMKVInstanceInfoPanel.d.ts.map +1 -0
  115. package/lib/typescript/storage/components/MMKVInstanceSelector.d.ts +35 -0
  116. package/lib/typescript/storage/components/MMKVInstanceSelector.d.ts.map +1 -0
  117. package/lib/typescript/storage/components/SelectionActionBar.d.ts +21 -0
  118. package/lib/typescript/storage/components/SelectionActionBar.d.ts.map +1 -0
  119. package/lib/typescript/storage/components/StorageActionButtons.d.ts +21 -0
  120. package/lib/typescript/storage/components/StorageActionButtons.d.ts.map +1 -0
  121. package/lib/typescript/storage/components/StorageActions.d.ts +10 -0
  122. package/lib/typescript/storage/components/StorageActions.d.ts.map +1 -0
  123. package/lib/typescript/storage/components/StorageBrowserMode.d.ts +18 -0
  124. package/lib/typescript/storage/components/StorageBrowserMode.d.ts.map +1 -0
  125. package/lib/typescript/storage/components/StorageEventDetailContent.d.ts +40 -0
  126. package/lib/typescript/storage/components/StorageEventDetailContent.d.ts.map +1 -0
  127. package/lib/typescript/storage/components/StorageEventFilterView.d.ts +11 -0
  128. package/lib/typescript/storage/components/StorageEventFilterView.d.ts.map +1 -0
  129. package/lib/typescript/storage/components/StorageEventListener.d.ts +6 -0
  130. package/lib/typescript/storage/components/StorageEventListener.d.ts.map +1 -0
  131. package/lib/typescript/storage/components/StorageEventsSection.d.ts +7 -0
  132. package/lib/typescript/storage/components/StorageEventsSection.d.ts.map +1 -0
  133. package/lib/typescript/storage/components/StorageFilterCards.d.ts +36 -0
  134. package/lib/typescript/storage/components/StorageFilterCards.d.ts.map +1 -0
  135. package/lib/typescript/storage/components/StorageFilterViewV2.d.ts +9 -0
  136. package/lib/typescript/storage/components/StorageFilterViewV2.d.ts.map +1 -0
  137. package/lib/typescript/storage/components/StorageKeyCard.d.ts +17 -0
  138. package/lib/typescript/storage/components/StorageKeyCard.d.ts.map +1 -0
  139. package/lib/typescript/storage/components/StorageKeyRow.d.ts +15 -0
  140. package/lib/typescript/storage/components/StorageKeyRow.d.ts.map +1 -0
  141. package/lib/typescript/storage/components/StorageKeySection.d.ts +25 -0
  142. package/lib/typescript/storage/components/StorageKeySection.d.ts.map +1 -0
  143. package/lib/typescript/storage/components/StorageKeyStats.d.ts +15 -0
  144. package/lib/typescript/storage/components/StorageKeyStats.d.ts.map +1 -0
  145. package/lib/typescript/storage/components/StorageModalWithTabs.d.ts +13 -0
  146. package/lib/typescript/storage/components/StorageModalWithTabs.d.ts.map +1 -0
  147. package/lib/typescript/storage/components/StorageSection.d.ts +10 -0
  148. package/lib/typescript/storage/components/StorageSection.d.ts.map +1 -0
  149. package/lib/typescript/storage/hooks/useAsyncStorageKeys.d.ts +10 -0
  150. package/lib/typescript/storage/hooks/useAsyncStorageKeys.d.ts.map +1 -0
  151. package/lib/typescript/storage/hooks/useMMKVInstances.d.ts +114 -0
  152. package/lib/typescript/storage/hooks/useMMKVInstances.d.ts.map +1 -0
  153. package/lib/typescript/storage/hooks/useMMKVKeys.d.ts +94 -0
  154. package/lib/typescript/storage/hooks/useMMKVKeys.d.ts.map +1 -0
  155. package/lib/typescript/storage/hooks/useTickEverySecond.d.ts +6 -0
  156. package/lib/typescript/storage/hooks/useTickEverySecond.d.ts.map +1 -0
  157. package/lib/typescript/storage/index.d.ts +15 -0
  158. package/lib/typescript/storage/index.d.ts.map +1 -0
  159. package/lib/typescript/storage/types.d.ts +41 -0
  160. package/lib/typescript/storage/types.d.ts.map +1 -0
  161. package/lib/typescript/storage/utils/AsyncStorageListener.d.ts +195 -0
  162. package/lib/typescript/storage/utils/AsyncStorageListener.d.ts.map +1 -0
  163. package/lib/typescript/storage/utils/MMKVInstanceRegistry.d.ts +224 -0
  164. package/lib/typescript/storage/utils/MMKVInstanceRegistry.d.ts.map +1 -0
  165. package/lib/typescript/storage/utils/MMKVListener.d.ts +218 -0
  166. package/lib/typescript/storage/utils/MMKVListener.d.ts.map +1 -0
  167. package/lib/typescript/storage/utils/clearAllStorage.d.ts +11 -0
  168. package/lib/typescript/storage/utils/clearAllStorage.d.ts.map +1 -0
  169. package/lib/typescript/storage/utils/index.d.ts +8 -0
  170. package/lib/typescript/storage/utils/index.d.ts.map +1 -0
  171. package/lib/typescript/storage/utils/lineDiff.d.ts +34 -0
  172. package/lib/typescript/storage/utils/lineDiff.d.ts.map +1 -0
  173. package/lib/typescript/storage/utils/mmkvAvailability.d.ts +23 -0
  174. package/lib/typescript/storage/utils/mmkvAvailability.d.ts.map +1 -0
  175. package/lib/typescript/storage/utils/mmkvTypeDetection.d.ts +71 -0
  176. package/lib/typescript/storage/utils/mmkvTypeDetection.d.ts.map +1 -0
  177. package/lib/typescript/storage/utils/objectDiff.d.ts +35 -0
  178. package/lib/typescript/storage/utils/objectDiff.d.ts.map +1 -0
  179. package/lib/typescript/storage/utils/safeAsyncStorage.d.ts +56 -0
  180. package/lib/typescript/storage/utils/safeAsyncStorage.d.ts.map +1 -0
  181. package/lib/typescript/storage/utils/storageActionHelpers.d.ts +5 -0
  182. package/lib/typescript/storage/utils/storageActionHelpers.d.ts.map +1 -0
  183. package/lib/typescript/storage/utils/storageQueryUtils.d.ts +6 -0
  184. package/lib/typescript/storage/utils/storageQueryUtils.d.ts.map +1 -0
  185. package/lib/typescript/storage/utils/valueType.d.ts +3 -0
  186. package/lib/typescript/storage/utils/valueType.d.ts.map +1 -0
  187. package/package.json +68 -0
@@ -0,0 +1,500 @@
1
+ "use strict";
2
+
3
+ import AsyncStorage from "@react-native-async-storage/async-storage";
4
+
5
+ // AsyncStorage method signatures - matching the actual AsyncStorage API
6
+
7
+ // Event types for AsyncStorage operations
8
+
9
+ /**
10
+ * Singleton class for intercepting and monitoring AsyncStorage operations
11
+ *
12
+ * This class uses method swizzling to intercept all AsyncStorage operations
13
+ * (setItem, removeItem, mergeItem, clear, multiSet, multiRemove, multiMerge)
14
+ * and emits events to registered listeners. It maintains the original functionality
15
+ * while providing observability for debugging and development tools.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * // Start listening to all AsyncStorage operations
20
+ * startListening();
21
+ *
22
+ * // Add a listener for storage events
23
+ * const unsubscribe = addListener((event) => {
24
+ * console.log(`${event.action}:`, event.data);
25
+ * });
26
+ *
27
+ * // Clean up
28
+ * unsubscribe();
29
+ * stopListening();
30
+ * ```
31
+ *
32
+ * @performance Uses method interception rather than polling for zero-overhead when inactive
33
+ * @performance Includes key filtering to prevent dev tools from triggering self-events
34
+ */
35
+ class AsyncStorageListener {
36
+ listeners = [];
37
+ isListening = false;
38
+ isInitialized = false;
39
+
40
+ // Keys to ignore for dev tools to prevent self-triggering
41
+ // Only ignore specific keys that would cause infinite loops in the storage browser
42
+ ignoredKeys = new Set(["@react_buoy_storage_event_filters",
43
+ // Storage events filter settings
44
+ "@react_buoy_storage_key_filters",
45
+ // Storage browser key filters
46
+ "@react_buoy_storage_is_monitoring",
47
+ // Storage monitoring toggle
48
+ "REACT_QUERY_OFFLINE_CACHE" // React Query cache
49
+ ]);
50
+
51
+ // Store original methods
52
+ originalSetItem = null;
53
+ originalRemoveItem = null;
54
+ originalMergeItem = null;
55
+ originalClear = null;
56
+ originalMultiSet = null;
57
+ originalMultiRemove = null;
58
+ originalMultiMerge = null;
59
+
60
+ /**
61
+ * Determines if a storage key should be ignored to prevent infinite loops
62
+ *
63
+ * Dev tools often store their own state in AsyncStorage, which would trigger
64
+ * events and cause infinite loops or unnecessary noise.
65
+ *
66
+ * @param key - The storage key to check
67
+ * @returns True if the key should be ignored, false otherwise
68
+ */
69
+ shouldIgnoreKey(key) {
70
+ // Check exact matches
71
+ if (this.ignoredKeys.has(key)) return true;
72
+
73
+ // Check prefix matches
74
+ for (const ignoredKey of this.ignoredKeys) {
75
+ if (key.startsWith(ignoredKey)) return true;
76
+ }
77
+ return false;
78
+ }
79
+
80
+ /**
81
+ * Initialize the listener by loading AsyncStorage and storing original methods
82
+ *
83
+ * This method performs safety checks to ensure we don't double-initialize
84
+ * and verifies that AsyncStorage methods haven't already been swizzled.
85
+ *
86
+ * @returns Promise<boolean> - True if initialization succeeded, false otherwise
87
+ *
88
+ * @throws Will log errors if AsyncStorage is already swizzled by another instance
89
+ */
90
+ async initialize() {
91
+ if (this.isInitialized) {
92
+ // Already initialized - skipping
93
+ return true;
94
+ }
95
+
96
+ // Check if methods are already swizzled by checking the function name
97
+ if (AsyncStorage.setItem.name === "swizzled_setItem") {
98
+ // Don't store swizzled methods as originals
99
+ return false;
100
+ }
101
+
102
+ // Store original methods (these should be the real AsyncStorage methods)
103
+ this.originalSetItem = AsyncStorage.setItem.bind(AsyncStorage);
104
+ this.originalRemoveItem = AsyncStorage.removeItem.bind(AsyncStorage);
105
+ this.originalMergeItem = AsyncStorage.mergeItem.bind(AsyncStorage);
106
+ this.originalClear = AsyncStorage.clear.bind(AsyncStorage);
107
+ this.originalMultiSet = AsyncStorage.multiSet.bind(AsyncStorage);
108
+ this.originalMultiRemove = AsyncStorage.multiRemove.bind(AsyncStorage);
109
+ this.originalMultiMerge = AsyncStorage.multiMerge ? AsyncStorage.multiMerge.bind(AsyncStorage) : null;
110
+
111
+ // Original methods stored successfully
112
+ this.isInitialized = true;
113
+ return true;
114
+ }
115
+
116
+ /**
117
+ * Restore original AsyncStorage methods to their unmodified state
118
+ *
119
+ * This method undoes the method swizzling by restoring the original
120
+ * AsyncStorage methods that were saved during initialization.
121
+ */
122
+ restoreOriginalMethods() {
123
+ if (!AsyncStorage || !this.originalSetItem) {
124
+ return;
125
+ }
126
+ AsyncStorage.setItem = this.originalSetItem;
127
+ if (this.originalRemoveItem) {
128
+ AsyncStorage.removeItem = this.originalRemoveItem;
129
+ }
130
+ if (this.originalMergeItem) {
131
+ AsyncStorage.mergeItem = this.originalMergeItem;
132
+ }
133
+ if (this.originalClear) {
134
+ AsyncStorage.clear = this.originalClear;
135
+ }
136
+ if (this.originalMultiSet) {
137
+ AsyncStorage.multiSet = this.originalMultiSet;
138
+ }
139
+ if (this.originalMultiRemove) {
140
+ AsyncStorage.multiRemove = this.originalMultiRemove;
141
+ }
142
+ if (this.originalMultiMerge) {
143
+ AsyncStorage.multiMerge = this.originalMultiMerge;
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Emit an AsyncStorage event to all registered listeners
149
+ *
150
+ * @param event - The AsyncStorage event to emit
151
+ *
152
+ * @performance Skips processing when no listeners are registered
153
+ */
154
+ emit(event) {
155
+ // Skip emitting if there are no listeners
156
+ if (this.listeners.length === 0) {
157
+ return;
158
+ }
159
+ this.listeners.forEach(listener => {
160
+ try {
161
+ listener(event);
162
+ } catch (error) {
163
+ // Error in event listener - continuing with others
164
+ }
165
+ });
166
+ }
167
+
168
+ /**
169
+ * Start intercepting AsyncStorage operations by swizzling methods
170
+ *
171
+ * This method replaces all AsyncStorage methods with wrapped versions
172
+ * that emit events while preserving the original functionality.
173
+ *
174
+ * @throws Will log errors if initialization fails or methods are already swizzled
175
+ *
176
+ * @performance Uses method swizzling for minimal runtime overhead
177
+ * @performance Includes safety checks to prevent double-initialization
178
+ */
179
+ async startListening() {
180
+ if (this.isListening) {
181
+ return;
182
+ }
183
+ const initialized = await this.initialize();
184
+ if (!initialized) {
185
+ return;
186
+ }
187
+
188
+ // Check if methods are already swizzled (this can happen if initialize was called twice somehow)
189
+ if (AsyncStorage && AsyncStorage.setItem.name === "swizzled_setItem") {
190
+ this.restoreOriginalMethods();
191
+ }
192
+
193
+ // Starting to listen for AsyncStorage operations
194
+
195
+ // Swizzle setItem
196
+ const swizzled_setItem = async (key, value) => {
197
+ // Only emit event if key is not ignored
198
+ if (!this.shouldIgnoreKey(key)) {
199
+ this.emit({
200
+ action: "setItem",
201
+ timestamp: new Date(),
202
+ data: {
203
+ key,
204
+ value
205
+ }
206
+ });
207
+ }
208
+ return this.originalSetItem ? this.originalSetItem(key, value) : Promise.resolve();
209
+ };
210
+ Object.defineProperty(swizzled_setItem, "name", {
211
+ value: "swizzled_setItem"
212
+ });
213
+ if (AsyncStorage) {
214
+ AsyncStorage.setItem = swizzled_setItem;
215
+ }
216
+
217
+ // Swizzle removeItem
218
+ if (AsyncStorage) {
219
+ AsyncStorage.removeItem = async key => {
220
+ // Intercepted removeItem
221
+
222
+ // Only emit event if key is not ignored
223
+ if (!this.shouldIgnoreKey(key)) {
224
+ this.emit({
225
+ action: "removeItem",
226
+ timestamp: new Date(),
227
+ data: {
228
+ key
229
+ }
230
+ });
231
+ } else {
232
+ // Ignoring removeItem for ignored key
233
+ }
234
+ return this.originalRemoveItem ? this.originalRemoveItem(key) : Promise.resolve();
235
+ };
236
+ }
237
+
238
+ // Swizzle mergeItem
239
+ if (AsyncStorage) {
240
+ AsyncStorage.mergeItem = async (key, value) => {
241
+ // Intercepted mergeItem operation
242
+
243
+ // Only emit event if key is not ignored
244
+ if (!this.shouldIgnoreKey(key)) {
245
+ this.emit({
246
+ action: "mergeItem",
247
+ timestamp: new Date(),
248
+ data: {
249
+ key,
250
+ value
251
+ }
252
+ });
253
+ } else {
254
+ // Ignoring mergeItem for ignored key
255
+ }
256
+ return this.originalMergeItem ? this.originalMergeItem(key, value) : Promise.resolve();
257
+ };
258
+ }
259
+
260
+ // Swizzle clear
261
+ if (AsyncStorage) {
262
+ AsyncStorage.clear = async () => {
263
+ // Intercepted clear operation
264
+ this.emit({
265
+ action: "clear",
266
+ timestamp: new Date()
267
+ });
268
+ return this.originalClear ? this.originalClear() : Promise.resolve();
269
+ };
270
+ }
271
+
272
+ // Swizzle multiSet
273
+ if (AsyncStorage) {
274
+ AsyncStorage.multiSet = async keyValuePairs => {
275
+ // Intercepted multiSet operation with multiple pairs
276
+
277
+ // Filter out ignored keys
278
+ const filteredPairs = keyValuePairs.filter(([key]) => !this.shouldIgnoreKey(key));
279
+ if (filteredPairs.length > 0) {
280
+ this.emit({
281
+ action: "multiSet",
282
+ timestamp: new Date(),
283
+ data: {
284
+ pairs: filteredPairs
285
+ }
286
+ });
287
+ } else {
288
+ // All keys in multiSet are ignored
289
+ }
290
+ return this.originalMultiSet ? this.originalMultiSet(keyValuePairs) : Promise.resolve();
291
+ };
292
+ }
293
+
294
+ // Swizzle multiRemove
295
+ if (AsyncStorage) {
296
+ AsyncStorage.multiRemove = async keys => {
297
+ // Intercepted multiRemove operation with multiple keys
298
+
299
+ // Filter out ignored keys
300
+ const filteredKeys = keys.filter(key => !this.shouldIgnoreKey(key));
301
+ if (filteredKeys.length > 0) {
302
+ this.emit({
303
+ action: "multiRemove",
304
+ timestamp: new Date(),
305
+ data: {
306
+ keys: filteredKeys
307
+ }
308
+ });
309
+ } else {
310
+ // All keys in multiRemove are ignored
311
+ }
312
+ return this.originalMultiRemove ? this.originalMultiRemove(keys) : Promise.resolve();
313
+ };
314
+ }
315
+
316
+ // Swizzle multiMerge if available
317
+ if (this.originalMultiMerge && AsyncStorage) {
318
+ AsyncStorage.multiMerge = async keyValuePairs => {
319
+ // Intercepted multiMerge operation with multiple pairs
320
+
321
+ // Filter out ignored keys
322
+ const filteredPairs = keyValuePairs.filter(([key]) => !this.shouldIgnoreKey(key));
323
+ if (filteredPairs.length > 0) {
324
+ this.emit({
325
+ action: "multiMerge",
326
+ timestamp: new Date(),
327
+ data: {
328
+ pairs: filteredPairs
329
+ }
330
+ });
331
+ } else {
332
+ // All keys in multiMerge are ignored
333
+ }
334
+ return this.originalMultiMerge ? this.originalMultiMerge(keyValuePairs) : Promise.resolve();
335
+ };
336
+ }
337
+ this.isListening = true;
338
+ // Started listening successfully
339
+ }
340
+
341
+ /**
342
+ * Stop listening and restore original AsyncStorage methods
343
+ *
344
+ * This method undoes all method swizzling and restores AsyncStorage
345
+ * to its original state.
346
+ */
347
+ stopListening() {
348
+ if (!this.isListening) {
349
+ return;
350
+ }
351
+ if (!AsyncStorage) {
352
+ return;
353
+ }
354
+
355
+ // Stopping listener and restoring original methods
356
+
357
+ // Restore original methods
358
+ if (this.originalSetItem) {
359
+ AsyncStorage.setItem = this.originalSetItem;
360
+ }
361
+ if (this.originalRemoveItem) {
362
+ AsyncStorage.removeItem = this.originalRemoveItem;
363
+ }
364
+ if (this.originalMergeItem) {
365
+ AsyncStorage.mergeItem = this.originalMergeItem;
366
+ }
367
+ if (this.originalClear) {
368
+ AsyncStorage.clear = this.originalClear;
369
+ }
370
+ if (this.originalMultiSet) {
371
+ AsyncStorage.multiSet = this.originalMultiSet;
372
+ }
373
+ if (this.originalMultiRemove) {
374
+ AsyncStorage.multiRemove = this.originalMultiRemove;
375
+ }
376
+ if (this.originalMultiMerge) {
377
+ AsyncStorage.multiMerge = this.originalMultiMerge;
378
+ }
379
+ this.isListening = false;
380
+ // Stopped listening successfully
381
+ }
382
+
383
+ /**
384
+ * Add a listener for AsyncStorage events
385
+ *
386
+ * @param listener - Callback function to handle AsyncStorage events
387
+ * @returns Unsubscribe function to remove the listener
388
+ *
389
+ * @example
390
+ * ```typescript
391
+ * const unsubscribe = asyncStorageListener.addListener((event) => {
392
+ * console.log('Storage operation:', event.action, event.data);
393
+ * });
394
+ *
395
+ * // Later, remove the listener
396
+ * unsubscribe();
397
+ * ```
398
+ */
399
+ addListener(listener) {
400
+ this.listeners.push(listener);
401
+
402
+ // Return unsubscribe function
403
+ return () => {
404
+ const index = this.listeners.indexOf(listener);
405
+ if (index > -1) {
406
+ this.listeners.splice(index, 1);
407
+ }
408
+ };
409
+ }
410
+
411
+ /**
412
+ * Remove all registered event listeners
413
+ *
414
+ * Clears the internal listeners array, stopping all event notifications.
415
+ */
416
+ removeAllListeners() {
417
+ this.listeners = [];
418
+ // Removed all listeners
419
+ }
420
+
421
+ /**
422
+ * Check if the listener is currently active and intercepting operations
423
+ *
424
+ * @returns True if currently listening to AsyncStorage operations
425
+ */
426
+ get isActive() {
427
+ return this.isListening;
428
+ }
429
+
430
+ /**
431
+ * Get the number of currently registered event listeners
432
+ *
433
+ * @returns Number of active listeners
434
+ */
435
+ get listenerCount() {
436
+ return this.listeners.length;
437
+ }
438
+ }
439
+
440
+ /**
441
+ * Singleton instance of AsyncStorageListener
442
+ *
443
+ * This ensures only one listener instance exists across the entire application,
444
+ * preventing conflicts and duplicate event handling.
445
+ */
446
+ const asyncStorageListener = new AsyncStorageListener();
447
+
448
+ /**
449
+ * Start listening to AsyncStorage operations
450
+ *
451
+ * @returns Promise that resolves when listening starts successfully
452
+ */
453
+ export const startListening = () => asyncStorageListener.startListening();
454
+
455
+ /**
456
+ * Stop listening to AsyncStorage operations
457
+ */
458
+ export const stopListening = () => asyncStorageListener.stopListening();
459
+
460
+ /**
461
+ * Add an event listener for AsyncStorage operations
462
+ *
463
+ * @param listener - Callback function to handle events
464
+ * @returns Unsubscribe function to remove the listener
465
+ */
466
+ export const addListener = listener => asyncStorageListener.addListener(listener);
467
+
468
+ /**
469
+ * Remove all registered event listeners
470
+ */
471
+ export const removeAllListeners = () => asyncStorageListener.removeAllListeners();
472
+
473
+ /**
474
+ * Check if currently listening to AsyncStorage operations
475
+ *
476
+ * @returns True if actively intercepting AsyncStorage methods
477
+ */
478
+ export const isListening = () => asyncStorageListener.isActive;
479
+
480
+ /**
481
+ * Get the current number of registered event listeners
482
+ *
483
+ * @returns Number of active listeners
484
+ */
485
+ export const getListenerCount = () => asyncStorageListener.listenerCount;
486
+
487
+ /**
488
+ * Export the singleton instance for advanced usage
489
+ *
490
+ * @example
491
+ * ```typescript
492
+ * import asyncStorageListener from './AsyncStorageListener';
493
+ *
494
+ * // Access advanced methods directly
495
+ * if (asyncStorageListener.isActive) {
496
+ * console.log(`${asyncStorageListener.listenerCount} listeners active`);
497
+ * }
498
+ * ```
499
+ */
500
+ export default asyncStorageListener;
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * MMKV Instance Registry
5
+ *
6
+ * Centralized registry for tracking all MMKV instances in the application.
7
+ * Provides a singleton pattern to manage multiple MMKV instances and their metadata.
8
+ *
9
+ * @module MMKVInstanceRegistry
10
+ * @since 1.0.0
11
+ */
12
+
13
+ /**
14
+ * Type-safe representation of an MMKV instance.
15
+ * Uses conditional typing to avoid hard dependency on react-native-mmkv.
16
+ *
17
+ * @public
18
+ */
19
+
20
+ /**
21
+ * Metadata about a registered MMKV instance
22
+ *
23
+ * @public
24
+ */
25
+
26
+ /**
27
+ * Configuration options for registering an MMKV instance
28
+ *
29
+ * @public
30
+ */
31
+
32
+ class MMKVInstanceRegistry {
33
+ instances = new Map();
34
+
35
+ /**
36
+ * Register an MMKV instance for monitoring
37
+ *
38
+ * @param id - Unique identifier for this instance
39
+ * @param instance - The MMKV instance to register
40
+ * @param config - Optional configuration (e.g., encrypted flag)
41
+ */
42
+ register(id, instance, config) {
43
+ const instanceInfo = {
44
+ id,
45
+ instance,
46
+ encrypted: config?.encrypted ?? false,
47
+ readOnly: instance.isReadOnly ?? false
48
+ };
49
+ this.instances.set(id, instanceInfo);
50
+ }
51
+
52
+ /**
53
+ * Unregister an MMKV instance
54
+ */
55
+ unregister(id) {
56
+ this.instances.delete(id);
57
+ }
58
+
59
+ /**
60
+ * Get a specific instance by ID
61
+ */
62
+ get(id) {
63
+ return this.instances.get(id);
64
+ }
65
+
66
+ /**
67
+ * Get all registered instances
68
+ */
69
+ getAll() {
70
+ return Array.from(this.instances.values());
71
+ }
72
+
73
+ /**
74
+ * Check if an instance is registered
75
+ */
76
+ has(id) {
77
+ return this.instances.has(id);
78
+ }
79
+
80
+ /**
81
+ * Get count of registered instances
82
+ */
83
+ count() {
84
+ return this.instances.size;
85
+ }
86
+ }
87
+
88
+ // Singleton instance
89
+ export const mmkvInstanceRegistry = new MMKVInstanceRegistry();
90
+
91
+ /**
92
+ * Register an MMKV instance with React Buoy DevTools for monitoring and debugging.
93
+ *
94
+ * **⚠️ REQUIRED**: Manual registration is required for `react-native-mmkv` v4.
95
+ * Auto-detection is not possible due to Metro bundler and ES6 module limitations.
96
+ *
97
+ * Call this function immediately after creating each MMKV instance you want to monitor.
98
+ * The DevTools will then be able to:
99
+ * - Display all keys and values
100
+ * - Show real-time updates when data changes
101
+ * - Allow editing values directly from the DevTools
102
+ * - Track storage size and performance
103
+ *
104
+ * @param id - Unique identifier for this MMKV instance. Should match the `id` you used when creating the instance.
105
+ * @param instance - The MMKV instance returned from `createMMKV()`
106
+ * @param config - Optional configuration
107
+ * @param config.encrypted - Set to `true` if this instance uses encryption (has an `encryptionKey`)
108
+ *
109
+ * @example
110
+ * **Basic usage:**
111
+ * ```typescript
112
+ * import { createMMKV } from 'react-native-mmkv';
113
+ * import { registerMMKVInstance } from '@buoy-gg/storage';
114
+ *
115
+ * // Create and register default instance
116
+ * export const storage = createMMKV({ id: 'mmkv.default' });
117
+ * registerMMKVInstance('mmkv.default', storage);
118
+ * ```
119
+ *
120
+ * @example
121
+ * **With encryption:**
122
+ * ```typescript
123
+ * import { createMMKV } from 'react-native-mmkv';
124
+ * import { registerMMKVInstance } from '@buoy-gg/storage';
125
+ *
126
+ * // Create encrypted instance
127
+ * export const secureStorage = createMMKV({
128
+ * id: 'secure-storage',
129
+ * encryptionKey: 'my-encryption-key'
130
+ * });
131
+ *
132
+ * // Register with encrypted flag
133
+ * registerMMKVInstance('secure-storage', secureStorage, { encrypted: true });
134
+ * ```
135
+ *
136
+ * @example
137
+ * **Multiple instances:**
138
+ * ```typescript
139
+ * import { createMMKV } from 'react-native-mmkv';
140
+ * import { registerMMKVInstance } from '@buoy-gg/storage';
141
+ *
142
+ * // User preferences
143
+ * export const userPrefs = createMMKV({ id: 'user-prefs' });
144
+ * registerMMKVInstance('user-prefs', userPrefs);
145
+ *
146
+ * // Cache
147
+ * export const cache = createMMKV({ id: 'cache' });
148
+ * registerMMKVInstance('cache', cache);
149
+ *
150
+ * // Auth (encrypted)
151
+ * export const auth = createMMKV({ id: 'auth', encryptionKey: 'key' });
152
+ * registerMMKVInstance('auth', auth, { encrypted: true });
153
+ * ```
154
+ *
155
+ * @public
156
+ * @since 1.0.0
157
+ */
158
+ export function registerMMKVInstance(id, instance, config) {
159
+ // Register in registry
160
+ mmkvInstanceRegistry.register(id, instance, config);
161
+
162
+ // Add listener for real-time monitoring (CRITICAL: enables event emission)
163
+ // v4: Use ONLY native listener (addOnValueChangedListener), skip method wrapping
164
+ // Import mmkvListener dynamically to avoid circular dependency
165
+ const {
166
+ mmkvListener
167
+ } = require('./MMKVListener');
168
+ mmkvListener.addInstance(instance, id);
169
+ }
170
+
171
+ /**
172
+ * Unregister an MMKV instance from DevTools monitoring.
173
+ *
174
+ * This stops the DevTools from tracking changes to this instance.
175
+ * Typically not needed unless you're dynamically creating/destroying instances.
176
+ *
177
+ * @param id - The instance ID to unregister
178
+ *
179
+ * @example
180
+ * ```typescript
181
+ * unregisterMMKVInstance('cache');
182
+ * ```
183
+ *
184
+ * @public
185
+ * @since 1.0.0
186
+ */
187
+ export function unregisterMMKVInstance(id) {
188
+ // Remove listener first
189
+ const {
190
+ mmkvListener
191
+ } = require('./MMKVListener');
192
+ mmkvListener.removeInstance(id);
193
+
194
+ // Then remove from registry
195
+ mmkvInstanceRegistry.unregister(id);
196
+ }