@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.
- package/README.md +607 -0
- package/lib/commonjs/index.js +34 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/preset.js +94 -0
- package/lib/commonjs/storage/components/DiffViewer/DiffOptionsPanel.js +356 -0
- package/lib/commonjs/storage/components/DiffViewer/TreeDiffViewer.js +29 -0
- package/lib/commonjs/storage/components/DiffViewer/components/DiffSummary.js +121 -0
- package/lib/commonjs/storage/components/DiffViewer/modes/ThemedSplitView.js +419 -0
- package/lib/commonjs/storage/components/DiffViewer/themes/diffThemes.js +122 -0
- package/lib/commonjs/storage/components/GameUIStorageBrowser.js +924 -0
- package/lib/commonjs/storage/components/GameUIStorageStats.js +746 -0
- package/lib/commonjs/storage/components/MMKVInstanceInfoPanel.js +257 -0
- package/lib/commonjs/storage/components/MMKVInstanceSelector.js +418 -0
- package/lib/commonjs/storage/components/SelectionActionBar.js +224 -0
- package/lib/commonjs/storage/components/StorageActionButtons.js +239 -0
- package/lib/commonjs/storage/components/StorageActions.js +192 -0
- package/lib/commonjs/storage/components/StorageBrowserMode.js +31 -0
- package/lib/commonjs/storage/components/StorageEventDetailContent.js +1025 -0
- package/lib/commonjs/storage/components/StorageEventFilterView.js +141 -0
- package/lib/commonjs/storage/components/StorageEventListener.js +357 -0
- package/lib/commonjs/storage/components/StorageEventsSection.js +24 -0
- package/lib/commonjs/storage/components/StorageFilterCards.js +345 -0
- package/lib/commonjs/storage/components/StorageFilterViewV2.js +42 -0
- package/lib/commonjs/storage/components/StorageKeyCard.js +516 -0
- package/lib/commonjs/storage/components/StorageKeyRow.js +356 -0
- package/lib/commonjs/storage/components/StorageKeySection.js +105 -0
- package/lib/commonjs/storage/components/StorageKeyStats.js +344 -0
- package/lib/commonjs/storage/components/StorageModalWithTabs.js +871 -0
- package/lib/commonjs/storage/components/StorageSection.js +43 -0
- package/lib/commonjs/storage/hooks/useAsyncStorageKeys.js +126 -0
- package/lib/commonjs/storage/hooks/useMMKVInstances.js +221 -0
- package/lib/commonjs/storage/hooks/useMMKVKeys.js +362 -0
- package/lib/commonjs/storage/hooks/useTickEverySecond.js +21 -0
- package/lib/commonjs/storage/index.js +148 -0
- package/lib/commonjs/storage/types.js +5 -0
- package/lib/commonjs/storage/utils/AsyncStorageListener.js +510 -0
- package/lib/commonjs/storage/utils/MMKVInstanceRegistry.js +202 -0
- package/lib/commonjs/storage/utils/MMKVListener.js +380 -0
- package/lib/commonjs/storage/utils/clearAllStorage.js +47 -0
- package/lib/commonjs/storage/utils/index.js +180 -0
- package/lib/commonjs/storage/utils/lineDiff.js +363 -0
- package/lib/commonjs/storage/utils/mmkvAvailability.js +62 -0
- package/lib/commonjs/storage/utils/mmkvTypeDetection.js +139 -0
- package/lib/commonjs/storage/utils/objectDiff.js +157 -0
- package/lib/commonjs/storage/utils/safeAsyncStorage.js +140 -0
- package/lib/commonjs/storage/utils/storageActionHelpers.js +46 -0
- package/lib/commonjs/storage/utils/storageQueryUtils.js +35 -0
- package/lib/commonjs/storage/utils/valueType.js +18 -0
- package/lib/module/index.js +7 -0
- package/lib/module/preset.js +89 -0
- package/lib/module/storage/components/DiffViewer/DiffOptionsPanel.js +352 -0
- package/lib/module/storage/components/DiffViewer/TreeDiffViewer.js +25 -0
- package/lib/module/storage/components/DiffViewer/components/DiffSummary.js +117 -0
- package/lib/module/storage/components/DiffViewer/modes/ThemedSplitView.js +415 -0
- package/lib/module/storage/components/DiffViewer/themes/diffThemes.js +118 -0
- package/lib/module/storage/components/GameUIStorageBrowser.js +922 -0
- package/lib/module/storage/components/GameUIStorageStats.js +742 -0
- package/lib/module/storage/components/MMKVInstanceInfoPanel.js +253 -0
- package/lib/module/storage/components/MMKVInstanceSelector.js +414 -0
- package/lib/module/storage/components/SelectionActionBar.js +221 -0
- package/lib/module/storage/components/StorageActionButtons.js +236 -0
- package/lib/module/storage/components/StorageActions.js +189 -0
- package/lib/module/storage/components/StorageBrowserMode.js +27 -0
- package/lib/module/storage/components/StorageEventDetailContent.js +1020 -0
- package/lib/module/storage/components/StorageEventFilterView.js +137 -0
- package/lib/module/storage/components/StorageEventListener.js +354 -0
- package/lib/module/storage/components/StorageEventsSection.js +20 -0
- package/lib/module/storage/components/StorageFilterCards.js +341 -0
- package/lib/module/storage/components/StorageFilterViewV2.js +38 -0
- package/lib/module/storage/components/StorageKeyCard.js +513 -0
- package/lib/module/storage/components/StorageKeyRow.js +353 -0
- package/lib/module/storage/components/StorageKeySection.js +101 -0
- package/lib/module/storage/components/StorageKeyStats.js +340 -0
- package/lib/module/storage/components/StorageModalWithTabs.js +867 -0
- package/lib/module/storage/components/StorageSection.js +40 -0
- package/lib/module/storage/hooks/useAsyncStorageKeys.js +121 -0
- package/lib/module/storage/hooks/useMMKVInstances.js +216 -0
- package/lib/module/storage/hooks/useMMKVKeys.js +359 -0
- package/lib/module/storage/hooks/useTickEverySecond.js +18 -0
- package/lib/module/storage/index.js +25 -0
- package/lib/module/storage/types.js +3 -0
- package/lib/module/storage/utils/AsyncStorageListener.js +500 -0
- package/lib/module/storage/utils/MMKVInstanceRegistry.js +196 -0
- package/lib/module/storage/utils/MMKVListener.js +367 -0
- package/lib/module/storage/utils/clearAllStorage.js +42 -0
- package/lib/module/storage/utils/index.js +22 -0
- package/lib/module/storage/utils/lineDiff.js +359 -0
- package/lib/module/storage/utils/mmkvAvailability.js +56 -0
- package/lib/module/storage/utils/mmkvTypeDetection.js +133 -0
- package/lib/module/storage/utils/objectDiff.js +153 -0
- package/lib/module/storage/utils/safeAsyncStorage.js +134 -0
- package/lib/module/storage/utils/storageActionHelpers.js +42 -0
- package/lib/module/storage/utils/storageQueryUtils.js +30 -0
- package/lib/module/storage/utils/valueType.js +14 -0
- package/lib/typescript/index.d.ts +3 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/preset.d.ts +90 -0
- package/lib/typescript/preset.d.ts.map +1 -0
- package/lib/typescript/storage/components/DiffViewer/DiffOptionsPanel.d.ts +18 -0
- package/lib/typescript/storage/components/DiffViewer/DiffOptionsPanel.d.ts.map +1 -0
- package/lib/typescript/storage/components/DiffViewer/TreeDiffViewer.d.ts +7 -0
- package/lib/typescript/storage/components/DiffViewer/TreeDiffViewer.d.ts.map +1 -0
- package/lib/typescript/storage/components/DiffViewer/components/DiffSummary.d.ts +12 -0
- package/lib/typescript/storage/components/DiffViewer/components/DiffSummary.d.ts.map +1 -0
- package/lib/typescript/storage/components/DiffViewer/modes/ThemedSplitView.d.ts +13 -0
- package/lib/typescript/storage/components/DiffViewer/modes/ThemedSplitView.d.ts.map +1 -0
- package/lib/typescript/storage/components/DiffViewer/themes/diffThemes.d.ts +64 -0
- package/lib/typescript/storage/components/DiffViewer/themes/diffThemes.d.ts.map +1 -0
- package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts +16 -0
- package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts.map +1 -0
- package/lib/typescript/storage/components/GameUIStorageStats.d.ts +7 -0
- package/lib/typescript/storage/components/GameUIStorageStats.d.ts.map +1 -0
- package/lib/typescript/storage/components/MMKVInstanceInfoPanel.d.ts +42 -0
- package/lib/typescript/storage/components/MMKVInstanceInfoPanel.d.ts.map +1 -0
- package/lib/typescript/storage/components/MMKVInstanceSelector.d.ts +35 -0
- package/lib/typescript/storage/components/MMKVInstanceSelector.d.ts.map +1 -0
- package/lib/typescript/storage/components/SelectionActionBar.d.ts +21 -0
- package/lib/typescript/storage/components/SelectionActionBar.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageActionButtons.d.ts +21 -0
- package/lib/typescript/storage/components/StorageActionButtons.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageActions.d.ts +10 -0
- package/lib/typescript/storage/components/StorageActions.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageBrowserMode.d.ts +18 -0
- package/lib/typescript/storage/components/StorageBrowserMode.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageEventDetailContent.d.ts +40 -0
- package/lib/typescript/storage/components/StorageEventDetailContent.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageEventFilterView.d.ts +11 -0
- package/lib/typescript/storage/components/StorageEventFilterView.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageEventListener.d.ts +6 -0
- package/lib/typescript/storage/components/StorageEventListener.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageEventsSection.d.ts +7 -0
- package/lib/typescript/storage/components/StorageEventsSection.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageFilterCards.d.ts +36 -0
- package/lib/typescript/storage/components/StorageFilterCards.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageFilterViewV2.d.ts +9 -0
- package/lib/typescript/storage/components/StorageFilterViewV2.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageKeyCard.d.ts +17 -0
- package/lib/typescript/storage/components/StorageKeyCard.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageKeyRow.d.ts +15 -0
- package/lib/typescript/storage/components/StorageKeyRow.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageKeySection.d.ts +25 -0
- package/lib/typescript/storage/components/StorageKeySection.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageKeyStats.d.ts +15 -0
- package/lib/typescript/storage/components/StorageKeyStats.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageModalWithTabs.d.ts +13 -0
- package/lib/typescript/storage/components/StorageModalWithTabs.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageSection.d.ts +10 -0
- package/lib/typescript/storage/components/StorageSection.d.ts.map +1 -0
- package/lib/typescript/storage/hooks/useAsyncStorageKeys.d.ts +10 -0
- package/lib/typescript/storage/hooks/useAsyncStorageKeys.d.ts.map +1 -0
- package/lib/typescript/storage/hooks/useMMKVInstances.d.ts +114 -0
- package/lib/typescript/storage/hooks/useMMKVInstances.d.ts.map +1 -0
- package/lib/typescript/storage/hooks/useMMKVKeys.d.ts +94 -0
- package/lib/typescript/storage/hooks/useMMKVKeys.d.ts.map +1 -0
- package/lib/typescript/storage/hooks/useTickEverySecond.d.ts +6 -0
- package/lib/typescript/storage/hooks/useTickEverySecond.d.ts.map +1 -0
- package/lib/typescript/storage/index.d.ts +15 -0
- package/lib/typescript/storage/index.d.ts.map +1 -0
- package/lib/typescript/storage/types.d.ts +41 -0
- package/lib/typescript/storage/types.d.ts.map +1 -0
- package/lib/typescript/storage/utils/AsyncStorageListener.d.ts +195 -0
- package/lib/typescript/storage/utils/AsyncStorageListener.d.ts.map +1 -0
- package/lib/typescript/storage/utils/MMKVInstanceRegistry.d.ts +224 -0
- package/lib/typescript/storage/utils/MMKVInstanceRegistry.d.ts.map +1 -0
- package/lib/typescript/storage/utils/MMKVListener.d.ts +218 -0
- package/lib/typescript/storage/utils/MMKVListener.d.ts.map +1 -0
- package/lib/typescript/storage/utils/clearAllStorage.d.ts +11 -0
- package/lib/typescript/storage/utils/clearAllStorage.d.ts.map +1 -0
- package/lib/typescript/storage/utils/index.d.ts +8 -0
- package/lib/typescript/storage/utils/index.d.ts.map +1 -0
- package/lib/typescript/storage/utils/lineDiff.d.ts +34 -0
- package/lib/typescript/storage/utils/lineDiff.d.ts.map +1 -0
- package/lib/typescript/storage/utils/mmkvAvailability.d.ts +23 -0
- package/lib/typescript/storage/utils/mmkvAvailability.d.ts.map +1 -0
- package/lib/typescript/storage/utils/mmkvTypeDetection.d.ts +71 -0
- package/lib/typescript/storage/utils/mmkvTypeDetection.d.ts.map +1 -0
- package/lib/typescript/storage/utils/objectDiff.d.ts +35 -0
- package/lib/typescript/storage/utils/objectDiff.d.ts.map +1 -0
- package/lib/typescript/storage/utils/safeAsyncStorage.d.ts +56 -0
- package/lib/typescript/storage/utils/safeAsyncStorage.d.ts.map +1 -0
- package/lib/typescript/storage/utils/storageActionHelpers.d.ts +5 -0
- package/lib/typescript/storage/utils/storageActionHelpers.d.ts.map +1 -0
- package/lib/typescript/storage/utils/storageQueryUtils.d.ts +6 -0
- package/lib/typescript/storage/utils/storageQueryUtils.d.ts.map +1 -0
- package/lib/typescript/storage/utils/valueType.d.ts +3 -0
- package/lib/typescript/storage/utils/valueType.d.ts.map +1 -0
- 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
|
+
}
|