@buoy-gg/storage 7.0.33 → 7.0.34
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/lib/commonjs/preset.js +8 -22
- package/lib/commonjs/storage/components/StorageModalWithTabs.js +41 -143
- package/lib/commonjs/storage/hooks/useStorageEvents.js +15 -52
- package/lib/commonjs/storage/utils/snapshotProvider.js +37 -100
- package/lib/module/preset.js +7 -20
- package/lib/module/storage/components/StorageModalWithTabs.js +42 -144
- package/lib/module/storage/hooks/useStorageEvents.js +16 -53
- package/lib/module/storage/utils/snapshotProvider.js +38 -101
- package/lib/typescript/preset.d.ts +2 -8
- package/lib/typescript/preset.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageModalWithTabs.d.ts.map +1 -1
- package/lib/typescript/storage/hooks/useStorageEvents.d.ts.map +1 -1
- package/package.json +5 -5
package/lib/commonjs/preset.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.createStorageTool =
|
|
7
|
-
exports.storageToolPreset = void 0;
|
|
6
|
+
exports.storageToolPreset = exports.createStorageTool = void 0;
|
|
8
7
|
var _floatingToolsCore = require("@buoy-gg/floating-tools-core");
|
|
8
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
9
9
|
var _StorageModalWithTabs = require("./storage/components/StorageModalWithTabs");
|
|
10
10
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
11
11
|
/**
|
|
@@ -33,7 +33,7 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
33
33
|
* - Live storage event monitoring
|
|
34
34
|
* - Required key validation
|
|
35
35
|
*/
|
|
36
|
-
const storageToolPreset = exports.storageToolPreset = {
|
|
36
|
+
const storageToolPreset = exports.storageToolPreset = (0, _sharedUi.defineToolPreset)({
|
|
37
37
|
id: "storage",
|
|
38
38
|
name: "STORAGE",
|
|
39
39
|
description: "AsyncStorage browser",
|
|
@@ -48,7 +48,7 @@ const storageToolPreset = exports.storageToolPreset = {
|
|
|
48
48
|
requiredStorageKeys: [],
|
|
49
49
|
enableSharedModalDimensions: false
|
|
50
50
|
}
|
|
51
|
-
};
|
|
51
|
+
});
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
54
|
* Create a custom AsyncStorage browser tool configuration.
|
|
@@ -74,21 +74,7 @@ const storageToolPreset = exports.storageToolPreset = {
|
|
|
74
74
|
* });
|
|
75
75
|
* ```
|
|
76
76
|
*/
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
description: options?.description || "AsyncStorage browser",
|
|
82
|
-
slot: "both",
|
|
83
|
-
icon: ({
|
|
84
|
-
size
|
|
85
|
-
}) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_floatingToolsCore.StorageIcon, {
|
|
86
|
-
size: size
|
|
87
|
-
}),
|
|
88
|
-
component: _StorageModalWithTabs.StorageModalWithTabs,
|
|
89
|
-
props: {
|
|
90
|
-
requiredStorageKeys: options?.requiredStorageKeys || [],
|
|
91
|
-
enableSharedModalDimensions: options?.enableSharedModalDimensions !== undefined ? options.enableSharedModalDimensions : false
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
}
|
|
77
|
+
const createStorageTool = exports.createStorageTool = (0, _sharedUi.createToolFactory)(storageToolPreset, options => ({
|
|
78
|
+
requiredStorageKeys: options.requiredStorageKeys || [],
|
|
79
|
+
enableSharedModalDimensions: options.enableSharedModalDimensions ?? false
|
|
80
|
+
}));
|
|
@@ -29,6 +29,41 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
29
29
|
* because it is part of the package's public exports.
|
|
30
30
|
*/
|
|
31
31
|
const FREE_TIER_EVENT_LIMIT = exports.FREE_TIER_EVENT_LIMIT = _sharedUi.TIER_LIMITS["storage.events"].free;
|
|
32
|
+
/**
|
|
33
|
+
* Persistence recipes. These keys were always written straight to
|
|
34
|
+
* AsyncStorage (not the FileSystem-first persistentStorage), so they keep
|
|
35
|
+
* that backend — a user's saved tab, filters and pins survive the upgrade.
|
|
36
|
+
*/
|
|
37
|
+
const ASYNC_STORAGE_BACKEND = {
|
|
38
|
+
getItem: key => _asyncStorage.default.getItem(key),
|
|
39
|
+
setItem: (key, value) => _asyncStorage.default.setItem(key, value)
|
|
40
|
+
};
|
|
41
|
+
const TAB_PERSISTENCE = {
|
|
42
|
+
storage: ASYNC_STORAGE_BACKEND,
|
|
43
|
+
// Stored as the bare tab name, not JSON.
|
|
44
|
+
parse: raw => raw === "browser" || raw === "events" ? raw : undefined,
|
|
45
|
+
serialize: tab => tab
|
|
46
|
+
};
|
|
47
|
+
const BOOLEAN_PERSISTENCE = {
|
|
48
|
+
storage: ASYNC_STORAGE_BACKEND,
|
|
49
|
+
// Stored as "true" / "false".
|
|
50
|
+
parse: raw => raw === "true",
|
|
51
|
+
serialize: value => value.toString()
|
|
52
|
+
};
|
|
53
|
+
const STRING_SET_PERSISTENCE = {
|
|
54
|
+
storage: ASYNC_STORAGE_BACKEND,
|
|
55
|
+
parse: raw => new Set(JSON.parse(raw)),
|
|
56
|
+
serialize: set => JSON.stringify(Array.from(set))
|
|
57
|
+
};
|
|
58
|
+
const IGNORED_PATTERNS_PERSISTENCE = {
|
|
59
|
+
...STRING_SET_PERSISTENCE,
|
|
60
|
+
parse: raw => {
|
|
61
|
+
const filters = JSON.parse(raw);
|
|
62
|
+
// Always ensure @react_buoy is included to hide dev tool keys
|
|
63
|
+
if (!filters.includes("@react_buoy")) filters.push("@react_buoy");
|
|
64
|
+
return new Set(filters);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
32
67
|
function StorageModalWithTabs({
|
|
33
68
|
visible,
|
|
34
69
|
onClose,
|
|
@@ -43,7 +78,7 @@ function StorageModalWithTabs({
|
|
|
43
78
|
}) {
|
|
44
79
|
const isPro = (0, _license.useIsPro)();
|
|
45
80
|
const eventLimit = (0, _sharedUi.useLimit)("storage.events");
|
|
46
|
-
const [activeTab, setActiveTab] = (0,
|
|
81
|
+
const [activeTab, setActiveTab] = (0, _sharedUi.usePersistedState)(_sharedUi.devToolsStorageKeys.storage.activeTab(), "browser", TAB_PERSISTENCE);
|
|
47
82
|
(0, _react.useEffect)(() => {
|
|
48
83
|
onTabChange?.(activeTab);
|
|
49
84
|
}, [activeTab, onTabChange]);
|
|
@@ -59,7 +94,7 @@ function StorageModalWithTabs({
|
|
|
59
94
|
const [isSearchActive, setIsSearchActive] = (0, _react.useState)(false);
|
|
60
95
|
|
|
61
96
|
// Local state to control subscription (true = subscribed, false = unsubscribed)
|
|
62
|
-
const [isListeningEnabled, setIsListeningEnabled] = (0,
|
|
97
|
+
const [isListeningEnabled, setIsListeningEnabled] = (0, _sharedUi.usePersistedState)(_sharedUi.devToolsStorageKeys.storage.isMonitoring(), true, BOOLEAN_PERSISTENCE);
|
|
63
98
|
|
|
64
99
|
// Event Listener state - using centralized store via hook
|
|
65
100
|
// Pass enabled option to control subscription - this allows events tool to
|
|
@@ -76,8 +111,9 @@ function StorageModalWithTabs({
|
|
|
76
111
|
const [selectedHistoryEventIndex, setSelectedHistoryEventIndex] = (0, _react.useState)(null);
|
|
77
112
|
const [selectedEventIndex, setSelectedEventIndex] = (0, _react.useState)(0);
|
|
78
113
|
const [showFilters, setShowFilters] = (0, _react.useState)(false);
|
|
79
|
-
const [ignoredPatterns, setIgnoredPatterns] = (0,
|
|
80
|
-
|
|
114
|
+
const [ignoredPatterns, setIgnoredPatterns] = (0, _sharedUi.usePersistedState)(_sharedUi.devToolsStorageKeys.storage.eventFilters(),
|
|
115
|
+
// Auto-hide dev tool keys by default
|
|
116
|
+
new Set(["@react_buoy", "@RNAsyncStorage", "redux-persist", "persist:"]), IGNORED_PATTERNS_PERSISTENCE);
|
|
81
117
|
/**
|
|
82
118
|
* Which backend both tabs are looking at.
|
|
83
119
|
*
|
|
@@ -93,12 +129,8 @@ function StorageModalWithTabs({
|
|
|
93
129
|
const [storageTypeFilter, setStorageTypeFilter] = (0, _react.useState)("all");
|
|
94
130
|
const [enabledStorageTypes, setEnabledStorageTypes] = (0, _react.useState)(new Set(['async', 'mmkv', 'secure']) // All enabled by default
|
|
95
131
|
);
|
|
96
|
-
const [pinnedKeys, setPinnedKeys] = (0,
|
|
132
|
+
const [pinnedKeys, setPinnedKeys] = (0, _sharedUi.usePersistedState)(_sharedUi.devToolsStorageKeys.storage.pinnedKeys(), new Set(), STRING_SET_PERSISTENCE);
|
|
97
133
|
const lastEventRef = (0, _react.useRef)(null);
|
|
98
|
-
const hasLoadedFilters = (0, _react.useRef)(false);
|
|
99
|
-
const hasLoadedPins = (0, _react.useRef)(false);
|
|
100
|
-
const hasLoadedTabState = (0, _react.useRef)(false);
|
|
101
|
-
const hasLoadedMonitoringState = (0, _react.useRef)(false);
|
|
102
134
|
|
|
103
135
|
// isListening = enabled and capturing (has subscribers)
|
|
104
136
|
const isListening = isListeningEnabled && isCapturing;
|
|
@@ -113,140 +145,6 @@ function StorageModalWithTabs({
|
|
|
113
145
|
|
|
114
146
|
// Timer removed - using useTickEveryMinute hook instead
|
|
115
147
|
|
|
116
|
-
// Load persisted tab state on mount
|
|
117
|
-
(0, _react.useEffect)(() => {
|
|
118
|
-
if (!visible || hasLoadedTabState.current) return;
|
|
119
|
-
const loadTabState = async () => {
|
|
120
|
-
try {
|
|
121
|
-
const storedTab = await _asyncStorage.default.getItem(_sharedUi.devToolsStorageKeys.storage.activeTab());
|
|
122
|
-
if (storedTab && (storedTab === "browser" || storedTab === "events")) {
|
|
123
|
-
setActiveTab(storedTab);
|
|
124
|
-
}
|
|
125
|
-
hasLoadedTabState.current = true;
|
|
126
|
-
} catch (error) {
|
|
127
|
-
// Failed to load tab state
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
loadTabState();
|
|
131
|
-
}, [visible]);
|
|
132
|
-
|
|
133
|
-
// Load persisted monitoring state on mount
|
|
134
|
-
(0, _react.useEffect)(() => {
|
|
135
|
-
if (!visible || hasLoadedMonitoringState.current) return;
|
|
136
|
-
const loadMonitoringState = async () => {
|
|
137
|
-
try {
|
|
138
|
-
const storedMonitoring = await _asyncStorage.default.getItem(_sharedUi.devToolsStorageKeys.storage.isMonitoring());
|
|
139
|
-
if (storedMonitoring !== null) {
|
|
140
|
-
const shouldMonitor = storedMonitoring === "true";
|
|
141
|
-
setIsListeningEnabled(shouldMonitor);
|
|
142
|
-
}
|
|
143
|
-
hasLoadedMonitoringState.current = true;
|
|
144
|
-
} catch (error) {
|
|
145
|
-
// Failed to load monitoring state
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
loadMonitoringState();
|
|
149
|
-
}, [visible]);
|
|
150
|
-
|
|
151
|
-
// Note: Conversations will appear when storage events are triggered
|
|
152
|
-
// Click on any conversation to see the unified view with toggle cards
|
|
153
|
-
|
|
154
|
-
// Save tab state when it changes
|
|
155
|
-
(0, _react.useEffect)(() => {
|
|
156
|
-
if (!hasLoadedTabState.current) return; // Don't save on initial load
|
|
157
|
-
|
|
158
|
-
const saveTabState = async () => {
|
|
159
|
-
try {
|
|
160
|
-
await _asyncStorage.default.setItem(_sharedUi.devToolsStorageKeys.storage.activeTab(), activeTab);
|
|
161
|
-
} catch (error) {
|
|
162
|
-
// Failed to save tab state
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
saveTabState();
|
|
166
|
-
}, [activeTab]);
|
|
167
|
-
|
|
168
|
-
// Save monitoring state when it changes
|
|
169
|
-
(0, _react.useEffect)(() => {
|
|
170
|
-
if (!hasLoadedMonitoringState.current) return; // Don't save on initial load
|
|
171
|
-
|
|
172
|
-
const saveMonitoringState = async () => {
|
|
173
|
-
try {
|
|
174
|
-
await _asyncStorage.default.setItem(_sharedUi.devToolsStorageKeys.storage.isMonitoring(), isListening.toString());
|
|
175
|
-
} catch (error) {
|
|
176
|
-
// Failed to save monitoring state
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
saveMonitoringState();
|
|
180
|
-
}, [isListening]);
|
|
181
|
-
|
|
182
|
-
// Load persisted filters on mount
|
|
183
|
-
(0, _react.useEffect)(() => {
|
|
184
|
-
if (!visible || hasLoadedFilters.current) return;
|
|
185
|
-
const loadFilters = async () => {
|
|
186
|
-
try {
|
|
187
|
-
const storedFilters = await _asyncStorage.default.getItem(_sharedUi.devToolsStorageKeys.storage.eventFilters());
|
|
188
|
-
if (storedFilters) {
|
|
189
|
-
const filters = JSON.parse(storedFilters);
|
|
190
|
-
// Always ensure @react_buoy is included to hide dev tool keys
|
|
191
|
-
if (!filters.includes("@react_buoy")) {
|
|
192
|
-
filters.push("@react_buoy");
|
|
193
|
-
}
|
|
194
|
-
setIgnoredPatterns(new Set(filters));
|
|
195
|
-
}
|
|
196
|
-
hasLoadedFilters.current = true;
|
|
197
|
-
} catch (error) {
|
|
198
|
-
// Failed to load filters
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
loadFilters();
|
|
202
|
-
}, [visible]);
|
|
203
|
-
|
|
204
|
-
// Save filters when they change
|
|
205
|
-
(0, _react.useEffect)(() => {
|
|
206
|
-
if (!hasLoadedFilters.current) return; // Don't save on initial load
|
|
207
|
-
|
|
208
|
-
const saveFilters = async () => {
|
|
209
|
-
try {
|
|
210
|
-
const filters = Array.from(ignoredPatterns);
|
|
211
|
-
await _asyncStorage.default.setItem(_sharedUi.devToolsStorageKeys.storage.eventFilters(), JSON.stringify(filters));
|
|
212
|
-
} catch (error) {
|
|
213
|
-
// Failed to save filters
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
saveFilters();
|
|
217
|
-
}, [ignoredPatterns]);
|
|
218
|
-
|
|
219
|
-
// Load persisted pinned keys on mount
|
|
220
|
-
(0, _react.useEffect)(() => {
|
|
221
|
-
if (!visible || hasLoadedPins.current) return;
|
|
222
|
-
const loadPins = async () => {
|
|
223
|
-
try {
|
|
224
|
-
const stored = await _asyncStorage.default.getItem(_sharedUi.devToolsStorageKeys.storage.pinnedKeys());
|
|
225
|
-
if (stored) {
|
|
226
|
-
setPinnedKeys(new Set(JSON.parse(stored)));
|
|
227
|
-
}
|
|
228
|
-
hasLoadedPins.current = true;
|
|
229
|
-
} catch (error) {
|
|
230
|
-
// Failed to load pinned keys
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
loadPins();
|
|
234
|
-
}, [visible]);
|
|
235
|
-
|
|
236
|
-
// Save pinned keys when they change
|
|
237
|
-
(0, _react.useEffect)(() => {
|
|
238
|
-
if (!hasLoadedPins.current) return; // Don't save on initial load
|
|
239
|
-
|
|
240
|
-
const savePins = async () => {
|
|
241
|
-
try {
|
|
242
|
-
await _asyncStorage.default.setItem(_sharedUi.devToolsStorageKeys.storage.pinnedKeys(), JSON.stringify(Array.from(pinnedKeys)));
|
|
243
|
-
} catch (error) {
|
|
244
|
-
// Failed to save pinned keys
|
|
245
|
-
}
|
|
246
|
-
};
|
|
247
|
-
savePins();
|
|
248
|
-
}, [pinnedKeys]);
|
|
249
|
-
|
|
250
148
|
// Event listener setup - now handled by useStorageEvents hook
|
|
251
149
|
// The hook subscribes to the centralized storageEventStore
|
|
252
150
|
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.useStorageEvents = useStorageEvents;
|
|
7
7
|
var _react = require("react");
|
|
8
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
8
9
|
var _storageEventStore = require("../stores/storageEventStore");
|
|
9
10
|
/**
|
|
10
11
|
* useStorageEvents Hook
|
|
@@ -39,60 +40,22 @@ function useStorageEvents(options = {}) {
|
|
|
39
40
|
maxEvents = 500,
|
|
40
41
|
enabled = true
|
|
41
42
|
} = options;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
// Frame-coalesced (same pattern as network's useNetworkEventList):
|
|
56
|
-
// bursts of storage writes land as ONE commit per frame instead of
|
|
57
|
-
// one per emission — the dominant live-churn cost measured on the
|
|
58
|
-
// network tool (tool-bench 2026-07-06).
|
|
59
|
-
let rafId = null;
|
|
60
|
-
let pending = null;
|
|
61
|
-
const unsubscribe = _storageEventStore.storageEventStore.subscribeToEvents(allEvents => {
|
|
62
|
-
pending = allEvents;
|
|
63
|
-
if (rafId != null) return;
|
|
64
|
-
rafId = requestAnimationFrame(() => {
|
|
65
|
-
rafId = null;
|
|
66
|
-
if (!pending) return;
|
|
67
|
-
// Filter by storage type if specified
|
|
68
|
-
let filtered = pending;
|
|
69
|
-
pending = null;
|
|
70
|
-
if (storageType !== "all") {
|
|
71
|
-
filtered = filtered.filter(e => e.storageType === storageType);
|
|
72
|
-
}
|
|
73
|
-
// Limit events
|
|
74
|
-
if (filtered.length > maxEvents) {
|
|
75
|
-
filtered = filtered.slice(0, maxEvents);
|
|
76
|
-
}
|
|
77
|
-
setEvents(filtered);
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
// Mark as capturing after successful subscription
|
|
82
|
-
setIsCapturing(true);
|
|
83
|
-
return () => {
|
|
84
|
-
unsubscribe();
|
|
85
|
-
if (rafId != null) cancelAnimationFrame(rafId);
|
|
86
|
-
// Note: Don't set isCapturing=false here because we might be
|
|
87
|
-
// re-subscribing due to dependency change, not actually stopping
|
|
88
|
-
};
|
|
89
|
-
}, [storageType, maxEvents, enabled]);
|
|
90
|
-
const clearEvents = (0, _react.useCallback)(() => {
|
|
91
|
-
_storageEventStore.storageEventStore.clearEvents();
|
|
92
|
-
}, []);
|
|
43
|
+
// Stable per storageType so the shared hook doesn't resubscribe every render.
|
|
44
|
+
const select = (0, _react.useMemo)(() => storageType === "all" ? undefined : events => events.filter(e => e.storageType === storageType), [storageType]);
|
|
45
|
+
// Subscribing auto-starts the storage listener (Subscribable pattern), and
|
|
46
|
+
// the shared hook frame-coalesces write bursts into one commit.
|
|
47
|
+
const {
|
|
48
|
+
events,
|
|
49
|
+
clearEvents,
|
|
50
|
+
isActive
|
|
51
|
+
} = (0, _sharedUi.useEventStoreEvents)(_storageEventStore.storageEventStore, {
|
|
52
|
+
maxEvents,
|
|
53
|
+
enabled,
|
|
54
|
+
select
|
|
55
|
+
});
|
|
93
56
|
return {
|
|
94
57
|
events,
|
|
95
58
|
clearEvents,
|
|
96
|
-
isCapturing
|
|
59
|
+
isCapturing: isActive
|
|
97
60
|
};
|
|
98
61
|
}
|
|
@@ -108,24 +108,12 @@ async function capture() {
|
|
|
108
108
|
}
|
|
109
109
|
async function restore(data, options) {
|
|
110
110
|
const snapshot = data;
|
|
111
|
-
const
|
|
112
|
-
const includedSet = options?.includeKeys ? new Set(options.includeKeys) : null;
|
|
111
|
+
const scope = (0, _sharedUi.createRestoreScope)(options);
|
|
113
112
|
/** True when this item may be touched (scope + exclusions agree). */
|
|
114
|
-
const touch =
|
|
115
|
-
|
|
116
|
-
const result = {
|
|
117
|
-
ok: true,
|
|
118
|
-
applied: 0,
|
|
119
|
-
skipped: [],
|
|
120
|
-
warnings: []
|
|
121
|
-
};
|
|
113
|
+
const touch = scope.touch;
|
|
114
|
+
const result = (0, _sharedUi.emptyRestoreResult)();
|
|
122
115
|
if (!snapshot) {
|
|
123
|
-
return
|
|
124
|
-
ok: false,
|
|
125
|
-
applied: 0,
|
|
126
|
-
skipped: [],
|
|
127
|
-
warnings: ["empty storage snapshot"]
|
|
128
|
-
};
|
|
116
|
+
return (0, _sharedUi.failedRestore)("empty storage snapshot");
|
|
129
117
|
}
|
|
130
118
|
|
|
131
119
|
// ── AsyncStorage: delete app keys missing from the snapshot, write the rest ──
|
|
@@ -134,7 +122,7 @@ async function restore(data, options) {
|
|
|
134
122
|
const snapKeys = new Set(snapPairs.map(([k]) => k));
|
|
135
123
|
const liveKeys = (await _asyncStorage.default.getAllKeys()).filter(k => !(0, _sharedUi.isDevToolsStorageKey)(k));
|
|
136
124
|
const toRemove = liveKeys.filter(k => !snapKeys.has(k) && touch(`async:${k}`));
|
|
137
|
-
|
|
125
|
+
scope.countExcluded((snapshot.async ?? []).length - snapPairs.length);
|
|
138
126
|
if (toRemove.length > 0) await (0, _asyncStorageCompat.removeMany)(toRemove);
|
|
139
127
|
if (snapPairs.length > 0) await (0, _asyncStorageCompat.writeMany)(snapPairs);
|
|
140
128
|
result.applied += snapPairs.length + toRemove.length;
|
|
@@ -167,10 +155,7 @@ async function restore(data, options) {
|
|
|
167
155
|
const snapKeys = new Set(entries.map(e => e.key));
|
|
168
156
|
const liveKeys = instance.getAllKeys().filter(key => !(0, _sharedUi.isDevToolsStorageKey)(key));
|
|
169
157
|
for (const key of liveKeys) {
|
|
170
|
-
if (!
|
|
171
|
-
excludedHits += 1;
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
158
|
+
if (!scope.allows(`mmkv:${id}/${key}`)) continue;
|
|
174
159
|
if (!snapKeys.has(key)) {
|
|
175
160
|
deleteMMKVKey(instance, key);
|
|
176
161
|
result.applied += 1;
|
|
@@ -201,10 +186,7 @@ async function restore(data, options) {
|
|
|
201
186
|
key,
|
|
202
187
|
options
|
|
203
188
|
} of _SecureStoreRegistry.secureStoreRegistry.getAll()) {
|
|
204
|
-
if (!
|
|
205
|
-
excludedHits += 1;
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
189
|
+
if (!scope.allows(`secure:${key}`)) continue;
|
|
208
190
|
if (options.requireAuthentication) {
|
|
209
191
|
if (key in secureSnap) {
|
|
210
192
|
result.skipped.push({
|
|
@@ -237,20 +219,14 @@ async function restore(data, options) {
|
|
|
237
219
|
});
|
|
238
220
|
}
|
|
239
221
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
222
|
+
const excludedWarning = scope.excludedWarning("item(s)");
|
|
223
|
+
if (excludedWarning) result.warnings.push(excludedWarning);
|
|
243
224
|
return result;
|
|
244
225
|
}
|
|
245
226
|
|
|
246
227
|
/** Wipe to fresh-install state (Buoy keys preserved). Used by baseline restore. */
|
|
247
228
|
async function clear() {
|
|
248
|
-
const result =
|
|
249
|
-
ok: true,
|
|
250
|
-
applied: 0,
|
|
251
|
-
skipped: [],
|
|
252
|
-
warnings: []
|
|
253
|
-
};
|
|
229
|
+
const result = (0, _sharedUi.emptyRestoreResult)();
|
|
254
230
|
try {
|
|
255
231
|
await (0, _clearAllStorage.clearAllAppStorage)();
|
|
256
232
|
result.applied += 1;
|
|
@@ -301,40 +277,24 @@ async function clear() {
|
|
|
301
277
|
return result;
|
|
302
278
|
}
|
|
303
279
|
const ITEM_RENDER_CHAR_LIMIT = 256 * 1024;
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Storage values are strings (or MMKV primitives): identity equality and a
|
|
283
|
+
* character-length size gate are the right tests, not a deep walk.
|
|
284
|
+
*/
|
|
304
285
|
function classifyValue(key, label, oldValue, newValue, inOld, inNew) {
|
|
305
|
-
|
|
286
|
+
return (0, _sharedUi.classifyPreviewItem)({
|
|
306
287
|
key,
|
|
307
288
|
label,
|
|
308
|
-
verdict: "removed",
|
|
309
|
-
oldValue
|
|
310
|
-
};
|
|
311
|
-
if (!inOld) return {
|
|
312
|
-
key,
|
|
313
|
-
label,
|
|
314
|
-
verdict: "added",
|
|
315
|
-
newValue
|
|
316
|
-
};
|
|
317
|
-
if (Object.is(oldValue, newValue)) return {
|
|
318
|
-
key,
|
|
319
|
-
label,
|
|
320
|
-
verdict: "unchanged"
|
|
321
|
-
};
|
|
322
|
-
const tooLarge = typeof oldValue === "string" && oldValue.length > ITEM_RENDER_CHAR_LIMIT || typeof newValue === "string" && newValue.length > ITEM_RENDER_CHAR_LIMIT;
|
|
323
|
-
if (tooLarge) {
|
|
324
|
-
return {
|
|
325
|
-
key,
|
|
326
|
-
label,
|
|
327
|
-
verdict: "too-large",
|
|
328
|
-
reason: "value too large to render a diff — restore still applies it"
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
return {
|
|
332
|
-
key,
|
|
333
|
-
label,
|
|
334
|
-
verdict: "changed",
|
|
335
289
|
oldValue,
|
|
336
|
-
newValue
|
|
337
|
-
|
|
290
|
+
newValue,
|
|
291
|
+
inOld,
|
|
292
|
+
inNew
|
|
293
|
+
}, {
|
|
294
|
+
equal: Object.is,
|
|
295
|
+
tooLarge: value => typeof value === "string" && value.length > ITEM_RENDER_CHAR_LIMIT,
|
|
296
|
+
tooLargeReason: "value too large to render a diff — restore still applies it"
|
|
297
|
+
});
|
|
338
298
|
}
|
|
339
299
|
|
|
340
300
|
/** Per-key restore preview across all three backends. */
|
|
@@ -356,46 +316,31 @@ async function previewItems(target, base) {
|
|
|
356
316
|
// ── AsyncStorage ──
|
|
357
317
|
const oldAsync = new Map(baseData.async ?? []);
|
|
358
318
|
const newAsync = new Map(targetData.async ?? []);
|
|
359
|
-
for (const key of
|
|
319
|
+
for (const key of (0, _sharedUi.unionKeys)(oldAsync, newAsync)) {
|
|
360
320
|
if ((0, _sharedUi.isDevToolsStorageKey)(key)) continue;
|
|
361
321
|
items.push(classifyValue(`async:${key}`, key, oldAsync.get(key), newAsync.get(key), oldAsync.has(key), newAsync.has(key)));
|
|
362
322
|
}
|
|
363
323
|
|
|
364
324
|
// ── MMKV ──
|
|
365
|
-
const
|
|
366
|
-
for (const id of instanceIds) {
|
|
325
|
+
for (const id of (0, _sharedUi.unionKeys)(baseData.mmkv, targetData.mmkv)) {
|
|
367
326
|
const registered = liveMode ? _MMKVInstanceRegistry.mmkvInstanceRegistry.get(id) : null;
|
|
368
327
|
const instanceUnavailable = liveMode && !registered;
|
|
369
328
|
const readOnly = liveMode && registered?.instance.isReadOnly === true;
|
|
370
329
|
const oldEntries = new Map((baseData.mmkv?.[id] ?? []).map(e => [e.key, e]));
|
|
371
330
|
const newEntries = new Map((targetData.mmkv?.[id] ?? []).map(e => [e.key, e]));
|
|
372
|
-
for (const key of
|
|
331
|
+
for (const key of (0, _sharedUi.unionKeys)(oldEntries, newEntries)) {
|
|
373
332
|
if ((0, _sharedUi.isDevToolsStorageKey)(key)) continue;
|
|
374
333
|
const itemKey = `mmkv:${id}/${key}`;
|
|
375
334
|
const label = `${id} · ${key}`;
|
|
376
335
|
const oldE = oldEntries.get(key);
|
|
377
336
|
const newE = newEntries.get(key);
|
|
378
337
|
let item = classifyValue(itemKey, label, oldE?.value, newE?.value, oldEntries.has(key), newEntries.has(key));
|
|
379
|
-
if (
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
};
|
|
386
|
-
} else if (readOnly) {
|
|
387
|
-
item = {
|
|
388
|
-
...item,
|
|
389
|
-
verdict: "wont-apply",
|
|
390
|
-
reason: "MMKV instance registered read-only"
|
|
391
|
-
};
|
|
392
|
-
} else if (newE?.valueType === "buffer" || oldE?.valueType === "buffer") {
|
|
393
|
-
item = {
|
|
394
|
-
...item,
|
|
395
|
-
verdict: "wont-apply",
|
|
396
|
-
reason: "ArrayBuffer values cannot be restored"
|
|
397
|
-
};
|
|
398
|
-
}
|
|
338
|
+
if (instanceUnavailable) {
|
|
339
|
+
item = (0, _sharedUi.wontApply)(item, "MMKV instance not registered");
|
|
340
|
+
} else if (readOnly) {
|
|
341
|
+
item = (0, _sharedUi.wontApply)(item, "MMKV instance registered read-only");
|
|
342
|
+
} else if (newE?.valueType === "buffer" || oldE?.valueType === "buffer") {
|
|
343
|
+
item = (0, _sharedUi.wontApply)(item, "ArrayBuffer values cannot be restored");
|
|
399
344
|
}
|
|
400
345
|
items.push(item);
|
|
401
346
|
}
|
|
@@ -405,22 +350,14 @@ async function previewItems(target, base) {
|
|
|
405
350
|
const oldSecure = baseData.secure ?? {};
|
|
406
351
|
const newSecure = targetData.secure ?? {};
|
|
407
352
|
const registeredSecure = liveMode ? new Map(_SecureStoreRegistry.secureStoreRegistry.getAll().map(e => [e.key, e])) : null;
|
|
408
|
-
for (const key of
|
|
353
|
+
for (const key of (0, _sharedUi.unionKeys)(oldSecure, newSecure)) {
|
|
409
354
|
let item = classifyValue(`secure:${key}`, key, oldSecure[key], newSecure[key], key in oldSecure, key in newSecure);
|
|
410
|
-
if (liveMode
|
|
355
|
+
if (liveMode) {
|
|
411
356
|
const entry = registeredSecure?.get(key);
|
|
412
357
|
if (!entry) {
|
|
413
|
-
item =
|
|
414
|
-
...item,
|
|
415
|
-
verdict: "wont-apply",
|
|
416
|
-
reason: "key no longer registered with registerSecureStoreKeys"
|
|
417
|
-
};
|
|
358
|
+
item = (0, _sharedUi.wontApply)(item, "key no longer registered with registerSecureStoreKeys");
|
|
418
359
|
} else if (entry.options.requireAuthentication) {
|
|
419
|
-
item =
|
|
420
|
-
...item,
|
|
421
|
-
verdict: "wont-apply",
|
|
422
|
-
reason: "biometric-protected keys are never written"
|
|
423
|
-
};
|
|
360
|
+
item = (0, _sharedUi.wontApply)(item, "biometric-protected keys are never written");
|
|
424
361
|
}
|
|
425
362
|
}
|
|
426
363
|
items.push(item);
|
package/lib/module/preset.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { StorageIcon } from "@buoy-gg/floating-tools-core";
|
|
21
|
+
import { createToolFactory, defineToolPreset } from "@buoy-gg/shared-ui";
|
|
21
22
|
import { StorageModalWithTabs } from "./storage/components/StorageModalWithTabs";
|
|
22
23
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
23
24
|
/**
|
|
@@ -28,7 +29,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
28
29
|
* - Live storage event monitoring
|
|
29
30
|
* - Required key validation
|
|
30
31
|
*/
|
|
31
|
-
export const storageToolPreset = {
|
|
32
|
+
export const storageToolPreset = defineToolPreset({
|
|
32
33
|
id: "storage",
|
|
33
34
|
name: "STORAGE",
|
|
34
35
|
description: "AsyncStorage browser",
|
|
@@ -43,7 +44,7 @@ export const storageToolPreset = {
|
|
|
43
44
|
requiredStorageKeys: [],
|
|
44
45
|
enableSharedModalDimensions: false
|
|
45
46
|
}
|
|
46
|
-
};
|
|
47
|
+
});
|
|
47
48
|
|
|
48
49
|
/**
|
|
49
50
|
* Create a custom AsyncStorage browser tool configuration.
|
|
@@ -69,21 +70,7 @@ export const storageToolPreset = {
|
|
|
69
70
|
* });
|
|
70
71
|
* ```
|
|
71
72
|
*/
|
|
72
|
-
export
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
description: options?.description || "AsyncStorage browser",
|
|
77
|
-
slot: "both",
|
|
78
|
-
icon: ({
|
|
79
|
-
size
|
|
80
|
-
}) => /*#__PURE__*/_jsx(StorageIcon, {
|
|
81
|
-
size: size
|
|
82
|
-
}),
|
|
83
|
-
component: StorageModalWithTabs,
|
|
84
|
-
props: {
|
|
85
|
-
requiredStorageKeys: options?.requiredStorageKeys || [],
|
|
86
|
-
enableSharedModalDimensions: options?.enableSharedModalDimensions !== undefined ? options.enableSharedModalDimensions : false
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
}
|
|
73
|
+
export const createStorageTool = createToolFactory(storageToolPreset, options => ({
|
|
74
|
+
requiredStorageKeys: options.requiredStorageKeys || [],
|
|
75
|
+
enableSharedModalDimensions: options.enableSharedModalDimensions ?? false
|
|
76
|
+
}));
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { useState, useCallback, useEffect, useRef, useMemo } from "react";
|
|
4
4
|
import { View, TouchableOpacity, StyleSheet, FlatList, Alert } from "react-native";
|
|
5
5
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
6
|
-
import { JsModal, ModalHeader, TabSelector, parseValue, devToolsStorageKeys, macOSColors, night, ToolEmptyState, Filter, RefreshCw, Search, SearchBar, PowerToggleButton, ToolbarCopyButton, ToolbarClearButton, truncatePayload, TickProvider, useLimit, TIER_LIMITS, windowDragProps } from "@buoy-gg/shared-ui";
|
|
6
|
+
import { JsModal, ModalHeader, TabSelector, parseValue, devToolsStorageKeys, macOSColors, night, ToolEmptyState, Filter, RefreshCw, Search, SearchBar, PowerToggleButton, ToolbarCopyButton, ToolbarClearButton, truncatePayload, TickProvider, useLimit, TIER_LIMITS, windowDragProps, usePersistedState } from "@buoy-gg/shared-ui";
|
|
7
7
|
import { StorageBrowserMode } from "./StorageBrowserMode";
|
|
8
8
|
import { StorageTypeFilterBar, countByStorageType } from "./StorageTypeFilterBar";
|
|
9
9
|
import { clearAllAppStorage, clearAllStorageIncludingDevTools } from "../utils/clearAllStorage";
|
|
@@ -24,6 +24,41 @@ import { useIsPro } from "@buoy-gg/license";
|
|
|
24
24
|
*/
|
|
25
25
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
26
26
|
export const FREE_TIER_EVENT_LIMIT = TIER_LIMITS["storage.events"].free;
|
|
27
|
+
/**
|
|
28
|
+
* Persistence recipes. These keys were always written straight to
|
|
29
|
+
* AsyncStorage (not the FileSystem-first persistentStorage), so they keep
|
|
30
|
+
* that backend — a user's saved tab, filters and pins survive the upgrade.
|
|
31
|
+
*/
|
|
32
|
+
const ASYNC_STORAGE_BACKEND = {
|
|
33
|
+
getItem: key => AsyncStorage.getItem(key),
|
|
34
|
+
setItem: (key, value) => AsyncStorage.setItem(key, value)
|
|
35
|
+
};
|
|
36
|
+
const TAB_PERSISTENCE = {
|
|
37
|
+
storage: ASYNC_STORAGE_BACKEND,
|
|
38
|
+
// Stored as the bare tab name, not JSON.
|
|
39
|
+
parse: raw => raw === "browser" || raw === "events" ? raw : undefined,
|
|
40
|
+
serialize: tab => tab
|
|
41
|
+
};
|
|
42
|
+
const BOOLEAN_PERSISTENCE = {
|
|
43
|
+
storage: ASYNC_STORAGE_BACKEND,
|
|
44
|
+
// Stored as "true" / "false".
|
|
45
|
+
parse: raw => raw === "true",
|
|
46
|
+
serialize: value => value.toString()
|
|
47
|
+
};
|
|
48
|
+
const STRING_SET_PERSISTENCE = {
|
|
49
|
+
storage: ASYNC_STORAGE_BACKEND,
|
|
50
|
+
parse: raw => new Set(JSON.parse(raw)),
|
|
51
|
+
serialize: set => JSON.stringify(Array.from(set))
|
|
52
|
+
};
|
|
53
|
+
const IGNORED_PATTERNS_PERSISTENCE = {
|
|
54
|
+
...STRING_SET_PERSISTENCE,
|
|
55
|
+
parse: raw => {
|
|
56
|
+
const filters = JSON.parse(raw);
|
|
57
|
+
// Always ensure @react_buoy is included to hide dev tool keys
|
|
58
|
+
if (!filters.includes("@react_buoy")) filters.push("@react_buoy");
|
|
59
|
+
return new Set(filters);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
27
62
|
export function StorageModalWithTabs({
|
|
28
63
|
visible,
|
|
29
64
|
onClose,
|
|
@@ -38,7 +73,7 @@ export function StorageModalWithTabs({
|
|
|
38
73
|
}) {
|
|
39
74
|
const isPro = useIsPro();
|
|
40
75
|
const eventLimit = useLimit("storage.events");
|
|
41
|
-
const [activeTab, setActiveTab] =
|
|
76
|
+
const [activeTab, setActiveTab] = usePersistedState(devToolsStorageKeys.storage.activeTab(), "browser", TAB_PERSISTENCE);
|
|
42
77
|
useEffect(() => {
|
|
43
78
|
onTabChange?.(activeTab);
|
|
44
79
|
}, [activeTab, onTabChange]);
|
|
@@ -54,7 +89,7 @@ export function StorageModalWithTabs({
|
|
|
54
89
|
const [isSearchActive, setIsSearchActive] = useState(false);
|
|
55
90
|
|
|
56
91
|
// Local state to control subscription (true = subscribed, false = unsubscribed)
|
|
57
|
-
const [isListeningEnabled, setIsListeningEnabled] =
|
|
92
|
+
const [isListeningEnabled, setIsListeningEnabled] = usePersistedState(devToolsStorageKeys.storage.isMonitoring(), true, BOOLEAN_PERSISTENCE);
|
|
58
93
|
|
|
59
94
|
// Event Listener state - using centralized store via hook
|
|
60
95
|
// Pass enabled option to control subscription - this allows events tool to
|
|
@@ -71,8 +106,9 @@ export function StorageModalWithTabs({
|
|
|
71
106
|
const [selectedHistoryEventIndex, setSelectedHistoryEventIndex] = useState(null);
|
|
72
107
|
const [selectedEventIndex, setSelectedEventIndex] = useState(0);
|
|
73
108
|
const [showFilters, setShowFilters] = useState(false);
|
|
74
|
-
const [ignoredPatterns, setIgnoredPatterns] =
|
|
75
|
-
|
|
109
|
+
const [ignoredPatterns, setIgnoredPatterns] = usePersistedState(devToolsStorageKeys.storage.eventFilters(),
|
|
110
|
+
// Auto-hide dev tool keys by default
|
|
111
|
+
new Set(["@react_buoy", "@RNAsyncStorage", "redux-persist", "persist:"]), IGNORED_PATTERNS_PERSISTENCE);
|
|
76
112
|
/**
|
|
77
113
|
* Which backend both tabs are looking at.
|
|
78
114
|
*
|
|
@@ -88,12 +124,8 @@ export function StorageModalWithTabs({
|
|
|
88
124
|
const [storageTypeFilter, setStorageTypeFilter] = useState("all");
|
|
89
125
|
const [enabledStorageTypes, setEnabledStorageTypes] = useState(new Set(['async', 'mmkv', 'secure']) // All enabled by default
|
|
90
126
|
);
|
|
91
|
-
const [pinnedKeys, setPinnedKeys] =
|
|
127
|
+
const [pinnedKeys, setPinnedKeys] = usePersistedState(devToolsStorageKeys.storage.pinnedKeys(), new Set(), STRING_SET_PERSISTENCE);
|
|
92
128
|
const lastEventRef = useRef(null);
|
|
93
|
-
const hasLoadedFilters = useRef(false);
|
|
94
|
-
const hasLoadedPins = useRef(false);
|
|
95
|
-
const hasLoadedTabState = useRef(false);
|
|
96
|
-
const hasLoadedMonitoringState = useRef(false);
|
|
97
129
|
|
|
98
130
|
// isListening = enabled and capturing (has subscribers)
|
|
99
131
|
const isListening = isListeningEnabled && isCapturing;
|
|
@@ -108,140 +140,6 @@ export function StorageModalWithTabs({
|
|
|
108
140
|
|
|
109
141
|
// Timer removed - using useTickEveryMinute hook instead
|
|
110
142
|
|
|
111
|
-
// Load persisted tab state on mount
|
|
112
|
-
useEffect(() => {
|
|
113
|
-
if (!visible || hasLoadedTabState.current) return;
|
|
114
|
-
const loadTabState = async () => {
|
|
115
|
-
try {
|
|
116
|
-
const storedTab = await AsyncStorage.getItem(devToolsStorageKeys.storage.activeTab());
|
|
117
|
-
if (storedTab && (storedTab === "browser" || storedTab === "events")) {
|
|
118
|
-
setActiveTab(storedTab);
|
|
119
|
-
}
|
|
120
|
-
hasLoadedTabState.current = true;
|
|
121
|
-
} catch (error) {
|
|
122
|
-
// Failed to load tab state
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
loadTabState();
|
|
126
|
-
}, [visible]);
|
|
127
|
-
|
|
128
|
-
// Load persisted monitoring state on mount
|
|
129
|
-
useEffect(() => {
|
|
130
|
-
if (!visible || hasLoadedMonitoringState.current) return;
|
|
131
|
-
const loadMonitoringState = async () => {
|
|
132
|
-
try {
|
|
133
|
-
const storedMonitoring = await AsyncStorage.getItem(devToolsStorageKeys.storage.isMonitoring());
|
|
134
|
-
if (storedMonitoring !== null) {
|
|
135
|
-
const shouldMonitor = storedMonitoring === "true";
|
|
136
|
-
setIsListeningEnabled(shouldMonitor);
|
|
137
|
-
}
|
|
138
|
-
hasLoadedMonitoringState.current = true;
|
|
139
|
-
} catch (error) {
|
|
140
|
-
// Failed to load monitoring state
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
loadMonitoringState();
|
|
144
|
-
}, [visible]);
|
|
145
|
-
|
|
146
|
-
// Note: Conversations will appear when storage events are triggered
|
|
147
|
-
// Click on any conversation to see the unified view with toggle cards
|
|
148
|
-
|
|
149
|
-
// Save tab state when it changes
|
|
150
|
-
useEffect(() => {
|
|
151
|
-
if (!hasLoadedTabState.current) return; // Don't save on initial load
|
|
152
|
-
|
|
153
|
-
const saveTabState = async () => {
|
|
154
|
-
try {
|
|
155
|
-
await AsyncStorage.setItem(devToolsStorageKeys.storage.activeTab(), activeTab);
|
|
156
|
-
} catch (error) {
|
|
157
|
-
// Failed to save tab state
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
saveTabState();
|
|
161
|
-
}, [activeTab]);
|
|
162
|
-
|
|
163
|
-
// Save monitoring state when it changes
|
|
164
|
-
useEffect(() => {
|
|
165
|
-
if (!hasLoadedMonitoringState.current) return; // Don't save on initial load
|
|
166
|
-
|
|
167
|
-
const saveMonitoringState = async () => {
|
|
168
|
-
try {
|
|
169
|
-
await AsyncStorage.setItem(devToolsStorageKeys.storage.isMonitoring(), isListening.toString());
|
|
170
|
-
} catch (error) {
|
|
171
|
-
// Failed to save monitoring state
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
saveMonitoringState();
|
|
175
|
-
}, [isListening]);
|
|
176
|
-
|
|
177
|
-
// Load persisted filters on mount
|
|
178
|
-
useEffect(() => {
|
|
179
|
-
if (!visible || hasLoadedFilters.current) return;
|
|
180
|
-
const loadFilters = async () => {
|
|
181
|
-
try {
|
|
182
|
-
const storedFilters = await AsyncStorage.getItem(devToolsStorageKeys.storage.eventFilters());
|
|
183
|
-
if (storedFilters) {
|
|
184
|
-
const filters = JSON.parse(storedFilters);
|
|
185
|
-
// Always ensure @react_buoy is included to hide dev tool keys
|
|
186
|
-
if (!filters.includes("@react_buoy")) {
|
|
187
|
-
filters.push("@react_buoy");
|
|
188
|
-
}
|
|
189
|
-
setIgnoredPatterns(new Set(filters));
|
|
190
|
-
}
|
|
191
|
-
hasLoadedFilters.current = true;
|
|
192
|
-
} catch (error) {
|
|
193
|
-
// Failed to load filters
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
loadFilters();
|
|
197
|
-
}, [visible]);
|
|
198
|
-
|
|
199
|
-
// Save filters when they change
|
|
200
|
-
useEffect(() => {
|
|
201
|
-
if (!hasLoadedFilters.current) return; // Don't save on initial load
|
|
202
|
-
|
|
203
|
-
const saveFilters = async () => {
|
|
204
|
-
try {
|
|
205
|
-
const filters = Array.from(ignoredPatterns);
|
|
206
|
-
await AsyncStorage.setItem(devToolsStorageKeys.storage.eventFilters(), JSON.stringify(filters));
|
|
207
|
-
} catch (error) {
|
|
208
|
-
// Failed to save filters
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
saveFilters();
|
|
212
|
-
}, [ignoredPatterns]);
|
|
213
|
-
|
|
214
|
-
// Load persisted pinned keys on mount
|
|
215
|
-
useEffect(() => {
|
|
216
|
-
if (!visible || hasLoadedPins.current) return;
|
|
217
|
-
const loadPins = async () => {
|
|
218
|
-
try {
|
|
219
|
-
const stored = await AsyncStorage.getItem(devToolsStorageKeys.storage.pinnedKeys());
|
|
220
|
-
if (stored) {
|
|
221
|
-
setPinnedKeys(new Set(JSON.parse(stored)));
|
|
222
|
-
}
|
|
223
|
-
hasLoadedPins.current = true;
|
|
224
|
-
} catch (error) {
|
|
225
|
-
// Failed to load pinned keys
|
|
226
|
-
}
|
|
227
|
-
};
|
|
228
|
-
loadPins();
|
|
229
|
-
}, [visible]);
|
|
230
|
-
|
|
231
|
-
// Save pinned keys when they change
|
|
232
|
-
useEffect(() => {
|
|
233
|
-
if (!hasLoadedPins.current) return; // Don't save on initial load
|
|
234
|
-
|
|
235
|
-
const savePins = async () => {
|
|
236
|
-
try {
|
|
237
|
-
await AsyncStorage.setItem(devToolsStorageKeys.storage.pinnedKeys(), JSON.stringify(Array.from(pinnedKeys)));
|
|
238
|
-
} catch (error) {
|
|
239
|
-
// Failed to save pinned keys
|
|
240
|
-
}
|
|
241
|
-
};
|
|
242
|
-
savePins();
|
|
243
|
-
}, [pinnedKeys]);
|
|
244
|
-
|
|
245
143
|
// Event listener setup - now handled by useStorageEvents hook
|
|
246
144
|
// The hook subscribes to the centralized storageEventStore
|
|
247
145
|
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
* ```
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
import {
|
|
28
|
+
import { useMemo } from "react";
|
|
29
|
+
import { useEventStoreEvents } from "@buoy-gg/shared-ui";
|
|
29
30
|
import { storageEventStore } from "../stores/storageEventStore";
|
|
30
31
|
|
|
31
32
|
// Re-export StorageEvent for convenience
|
|
@@ -36,60 +37,22 @@ export function useStorageEvents(options = {}) {
|
|
|
36
37
|
maxEvents = 500,
|
|
37
38
|
enabled = true
|
|
38
39
|
} = options;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// Frame-coalesced (same pattern as network's useNetworkEventList):
|
|
53
|
-
// bursts of storage writes land as ONE commit per frame instead of
|
|
54
|
-
// one per emission — the dominant live-churn cost measured on the
|
|
55
|
-
// network tool (tool-bench 2026-07-06).
|
|
56
|
-
let rafId = null;
|
|
57
|
-
let pending = null;
|
|
58
|
-
const unsubscribe = storageEventStore.subscribeToEvents(allEvents => {
|
|
59
|
-
pending = allEvents;
|
|
60
|
-
if (rafId != null) return;
|
|
61
|
-
rafId = requestAnimationFrame(() => {
|
|
62
|
-
rafId = null;
|
|
63
|
-
if (!pending) return;
|
|
64
|
-
// Filter by storage type if specified
|
|
65
|
-
let filtered = pending;
|
|
66
|
-
pending = null;
|
|
67
|
-
if (storageType !== "all") {
|
|
68
|
-
filtered = filtered.filter(e => e.storageType === storageType);
|
|
69
|
-
}
|
|
70
|
-
// Limit events
|
|
71
|
-
if (filtered.length > maxEvents) {
|
|
72
|
-
filtered = filtered.slice(0, maxEvents);
|
|
73
|
-
}
|
|
74
|
-
setEvents(filtered);
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// Mark as capturing after successful subscription
|
|
79
|
-
setIsCapturing(true);
|
|
80
|
-
return () => {
|
|
81
|
-
unsubscribe();
|
|
82
|
-
if (rafId != null) cancelAnimationFrame(rafId);
|
|
83
|
-
// Note: Don't set isCapturing=false here because we might be
|
|
84
|
-
// re-subscribing due to dependency change, not actually stopping
|
|
85
|
-
};
|
|
86
|
-
}, [storageType, maxEvents, enabled]);
|
|
87
|
-
const clearEvents = useCallback(() => {
|
|
88
|
-
storageEventStore.clearEvents();
|
|
89
|
-
}, []);
|
|
40
|
+
// Stable per storageType so the shared hook doesn't resubscribe every render.
|
|
41
|
+
const select = useMemo(() => storageType === "all" ? undefined : events => events.filter(e => e.storageType === storageType), [storageType]);
|
|
42
|
+
// Subscribing auto-starts the storage listener (Subscribable pattern), and
|
|
43
|
+
// the shared hook frame-coalesces write bursts into one commit.
|
|
44
|
+
const {
|
|
45
|
+
events,
|
|
46
|
+
clearEvents,
|
|
47
|
+
isActive
|
|
48
|
+
} = useEventStoreEvents(storageEventStore, {
|
|
49
|
+
maxEvents,
|
|
50
|
+
enabled,
|
|
51
|
+
select
|
|
52
|
+
});
|
|
90
53
|
return {
|
|
91
54
|
events,
|
|
92
55
|
clearEvents,
|
|
93
|
-
isCapturing
|
|
56
|
+
isCapturing: isActive
|
|
94
57
|
};
|
|
95
58
|
}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* - SecureStore keys outside the registered descriptor list are invisible.
|
|
19
19
|
*/
|
|
20
20
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
21
|
-
import { isDevToolsStorageKey, registerStateSnapshotProvider } from "@buoy-gg/shared-ui";
|
|
21
|
+
import { classifyPreviewItem, createRestoreScope, emptyRestoreResult, failedRestore, isDevToolsStorageKey, registerStateSnapshotProvider, unionKeys, wontApply } from "@buoy-gg/shared-ui";
|
|
22
22
|
import { readMany, writeMany, removeMany } from "./asyncStorageCompat";
|
|
23
23
|
import { clearAllAppStorage } from "./clearAllStorage";
|
|
24
24
|
import { mmkvInstanceRegistry } from "./MMKVInstanceRegistry";
|
|
@@ -106,24 +106,12 @@ async function capture() {
|
|
|
106
106
|
}
|
|
107
107
|
async function restore(data, options) {
|
|
108
108
|
const snapshot = data;
|
|
109
|
-
const
|
|
110
|
-
const includedSet = options?.includeKeys ? new Set(options.includeKeys) : null;
|
|
109
|
+
const scope = createRestoreScope(options);
|
|
111
110
|
/** True when this item may be touched (scope + exclusions agree). */
|
|
112
|
-
const touch =
|
|
113
|
-
|
|
114
|
-
const result = {
|
|
115
|
-
ok: true,
|
|
116
|
-
applied: 0,
|
|
117
|
-
skipped: [],
|
|
118
|
-
warnings: []
|
|
119
|
-
};
|
|
111
|
+
const touch = scope.touch;
|
|
112
|
+
const result = emptyRestoreResult();
|
|
120
113
|
if (!snapshot) {
|
|
121
|
-
return
|
|
122
|
-
ok: false,
|
|
123
|
-
applied: 0,
|
|
124
|
-
skipped: [],
|
|
125
|
-
warnings: ["empty storage snapshot"]
|
|
126
|
-
};
|
|
114
|
+
return failedRestore("empty storage snapshot");
|
|
127
115
|
}
|
|
128
116
|
|
|
129
117
|
// ── AsyncStorage: delete app keys missing from the snapshot, write the rest ──
|
|
@@ -132,7 +120,7 @@ async function restore(data, options) {
|
|
|
132
120
|
const snapKeys = new Set(snapPairs.map(([k]) => k));
|
|
133
121
|
const liveKeys = (await AsyncStorage.getAllKeys()).filter(k => !isDevToolsStorageKey(k));
|
|
134
122
|
const toRemove = liveKeys.filter(k => !snapKeys.has(k) && touch(`async:${k}`));
|
|
135
|
-
|
|
123
|
+
scope.countExcluded((snapshot.async ?? []).length - snapPairs.length);
|
|
136
124
|
if (toRemove.length > 0) await removeMany(toRemove);
|
|
137
125
|
if (snapPairs.length > 0) await writeMany(snapPairs);
|
|
138
126
|
result.applied += snapPairs.length + toRemove.length;
|
|
@@ -165,10 +153,7 @@ async function restore(data, options) {
|
|
|
165
153
|
const snapKeys = new Set(entries.map(e => e.key));
|
|
166
154
|
const liveKeys = instance.getAllKeys().filter(key => !isDevToolsStorageKey(key));
|
|
167
155
|
for (const key of liveKeys) {
|
|
168
|
-
if (!
|
|
169
|
-
excludedHits += 1;
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
156
|
+
if (!scope.allows(`mmkv:${id}/${key}`)) continue;
|
|
172
157
|
if (!snapKeys.has(key)) {
|
|
173
158
|
deleteMMKVKey(instance, key);
|
|
174
159
|
result.applied += 1;
|
|
@@ -199,10 +184,7 @@ async function restore(data, options) {
|
|
|
199
184
|
key,
|
|
200
185
|
options
|
|
201
186
|
} of secureStoreRegistry.getAll()) {
|
|
202
|
-
if (!
|
|
203
|
-
excludedHits += 1;
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
187
|
+
if (!scope.allows(`secure:${key}`)) continue;
|
|
206
188
|
if (options.requireAuthentication) {
|
|
207
189
|
if (key in secureSnap) {
|
|
208
190
|
result.skipped.push({
|
|
@@ -235,20 +217,14 @@ async function restore(data, options) {
|
|
|
235
217
|
});
|
|
236
218
|
}
|
|
237
219
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
220
|
+
const excludedWarning = scope.excludedWarning("item(s)");
|
|
221
|
+
if (excludedWarning) result.warnings.push(excludedWarning);
|
|
241
222
|
return result;
|
|
242
223
|
}
|
|
243
224
|
|
|
244
225
|
/** Wipe to fresh-install state (Buoy keys preserved). Used by baseline restore. */
|
|
245
226
|
async function clear() {
|
|
246
|
-
const result =
|
|
247
|
-
ok: true,
|
|
248
|
-
applied: 0,
|
|
249
|
-
skipped: [],
|
|
250
|
-
warnings: []
|
|
251
|
-
};
|
|
227
|
+
const result = emptyRestoreResult();
|
|
252
228
|
try {
|
|
253
229
|
await clearAllAppStorage();
|
|
254
230
|
result.applied += 1;
|
|
@@ -299,40 +275,24 @@ async function clear() {
|
|
|
299
275
|
return result;
|
|
300
276
|
}
|
|
301
277
|
const ITEM_RENDER_CHAR_LIMIT = 256 * 1024;
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Storage values are strings (or MMKV primitives): identity equality and a
|
|
281
|
+
* character-length size gate are the right tests, not a deep walk.
|
|
282
|
+
*/
|
|
302
283
|
function classifyValue(key, label, oldValue, newValue, inOld, inNew) {
|
|
303
|
-
|
|
284
|
+
return classifyPreviewItem({
|
|
304
285
|
key,
|
|
305
286
|
label,
|
|
306
|
-
verdict: "removed",
|
|
307
|
-
oldValue
|
|
308
|
-
};
|
|
309
|
-
if (!inOld) return {
|
|
310
|
-
key,
|
|
311
|
-
label,
|
|
312
|
-
verdict: "added",
|
|
313
|
-
newValue
|
|
314
|
-
};
|
|
315
|
-
if (Object.is(oldValue, newValue)) return {
|
|
316
|
-
key,
|
|
317
|
-
label,
|
|
318
|
-
verdict: "unchanged"
|
|
319
|
-
};
|
|
320
|
-
const tooLarge = typeof oldValue === "string" && oldValue.length > ITEM_RENDER_CHAR_LIMIT || typeof newValue === "string" && newValue.length > ITEM_RENDER_CHAR_LIMIT;
|
|
321
|
-
if (tooLarge) {
|
|
322
|
-
return {
|
|
323
|
-
key,
|
|
324
|
-
label,
|
|
325
|
-
verdict: "too-large",
|
|
326
|
-
reason: "value too large to render a diff — restore still applies it"
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
return {
|
|
330
|
-
key,
|
|
331
|
-
label,
|
|
332
|
-
verdict: "changed",
|
|
333
287
|
oldValue,
|
|
334
|
-
newValue
|
|
335
|
-
|
|
288
|
+
newValue,
|
|
289
|
+
inOld,
|
|
290
|
+
inNew
|
|
291
|
+
}, {
|
|
292
|
+
equal: Object.is,
|
|
293
|
+
tooLarge: value => typeof value === "string" && value.length > ITEM_RENDER_CHAR_LIMIT,
|
|
294
|
+
tooLargeReason: "value too large to render a diff — restore still applies it"
|
|
295
|
+
});
|
|
336
296
|
}
|
|
337
297
|
|
|
338
298
|
/** Per-key restore preview across all three backends. */
|
|
@@ -354,46 +314,31 @@ async function previewItems(target, base) {
|
|
|
354
314
|
// ── AsyncStorage ──
|
|
355
315
|
const oldAsync = new Map(baseData.async ?? []);
|
|
356
316
|
const newAsync = new Map(targetData.async ?? []);
|
|
357
|
-
for (const key of
|
|
317
|
+
for (const key of unionKeys(oldAsync, newAsync)) {
|
|
358
318
|
if (isDevToolsStorageKey(key)) continue;
|
|
359
319
|
items.push(classifyValue(`async:${key}`, key, oldAsync.get(key), newAsync.get(key), oldAsync.has(key), newAsync.has(key)));
|
|
360
320
|
}
|
|
361
321
|
|
|
362
322
|
// ── MMKV ──
|
|
363
|
-
const
|
|
364
|
-
for (const id of instanceIds) {
|
|
323
|
+
for (const id of unionKeys(baseData.mmkv, targetData.mmkv)) {
|
|
365
324
|
const registered = liveMode ? mmkvInstanceRegistry.get(id) : null;
|
|
366
325
|
const instanceUnavailable = liveMode && !registered;
|
|
367
326
|
const readOnly = liveMode && registered?.instance.isReadOnly === true;
|
|
368
327
|
const oldEntries = new Map((baseData.mmkv?.[id] ?? []).map(e => [e.key, e]));
|
|
369
328
|
const newEntries = new Map((targetData.mmkv?.[id] ?? []).map(e => [e.key, e]));
|
|
370
|
-
for (const key of
|
|
329
|
+
for (const key of unionKeys(oldEntries, newEntries)) {
|
|
371
330
|
if (isDevToolsStorageKey(key)) continue;
|
|
372
331
|
const itemKey = `mmkv:${id}/${key}`;
|
|
373
332
|
const label = `${id} · ${key}`;
|
|
374
333
|
const oldE = oldEntries.get(key);
|
|
375
334
|
const newE = newEntries.get(key);
|
|
376
335
|
let item = classifyValue(itemKey, label, oldE?.value, newE?.value, oldEntries.has(key), newEntries.has(key));
|
|
377
|
-
if (
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
};
|
|
384
|
-
} else if (readOnly) {
|
|
385
|
-
item = {
|
|
386
|
-
...item,
|
|
387
|
-
verdict: "wont-apply",
|
|
388
|
-
reason: "MMKV instance registered read-only"
|
|
389
|
-
};
|
|
390
|
-
} else if (newE?.valueType === "buffer" || oldE?.valueType === "buffer") {
|
|
391
|
-
item = {
|
|
392
|
-
...item,
|
|
393
|
-
verdict: "wont-apply",
|
|
394
|
-
reason: "ArrayBuffer values cannot be restored"
|
|
395
|
-
};
|
|
396
|
-
}
|
|
336
|
+
if (instanceUnavailable) {
|
|
337
|
+
item = wontApply(item, "MMKV instance not registered");
|
|
338
|
+
} else if (readOnly) {
|
|
339
|
+
item = wontApply(item, "MMKV instance registered read-only");
|
|
340
|
+
} else if (newE?.valueType === "buffer" || oldE?.valueType === "buffer") {
|
|
341
|
+
item = wontApply(item, "ArrayBuffer values cannot be restored");
|
|
397
342
|
}
|
|
398
343
|
items.push(item);
|
|
399
344
|
}
|
|
@@ -403,22 +348,14 @@ async function previewItems(target, base) {
|
|
|
403
348
|
const oldSecure = baseData.secure ?? {};
|
|
404
349
|
const newSecure = targetData.secure ?? {};
|
|
405
350
|
const registeredSecure = liveMode ? new Map(secureStoreRegistry.getAll().map(e => [e.key, e])) : null;
|
|
406
|
-
for (const key of
|
|
351
|
+
for (const key of unionKeys(oldSecure, newSecure)) {
|
|
407
352
|
let item = classifyValue(`secure:${key}`, key, oldSecure[key], newSecure[key], key in oldSecure, key in newSecure);
|
|
408
|
-
if (liveMode
|
|
353
|
+
if (liveMode) {
|
|
409
354
|
const entry = registeredSecure?.get(key);
|
|
410
355
|
if (!entry) {
|
|
411
|
-
item =
|
|
412
|
-
...item,
|
|
413
|
-
verdict: "wont-apply",
|
|
414
|
-
reason: "key no longer registered with registerSecureStoreKeys"
|
|
415
|
-
};
|
|
356
|
+
item = wontApply(item, "key no longer registered with registerSecureStoreKeys");
|
|
416
357
|
} else if (entry.options.requireAuthentication) {
|
|
417
|
-
item =
|
|
418
|
-
...item,
|
|
419
|
-
verdict: "wont-apply",
|
|
420
|
-
reason: "biometric-protected keys are never written"
|
|
421
|
-
};
|
|
358
|
+
item = wontApply(item, "biometric-protected keys are never written");
|
|
422
359
|
}
|
|
423
360
|
}
|
|
424
361
|
items.push(item);
|
|
@@ -62,18 +62,12 @@ export declare const storageToolPreset: {
|
|
|
62
62
|
* });
|
|
63
63
|
* ```
|
|
64
64
|
*/
|
|
65
|
-
export declare
|
|
66
|
-
/** Tool name (default: "STORAGE") */
|
|
67
|
-
name?: string;
|
|
68
|
-
/** Tool description */
|
|
69
|
-
description?: string;
|
|
70
|
-
/** Custom tool ID (default: "storage") */
|
|
71
|
-
id?: string;
|
|
65
|
+
export declare const createStorageTool: (options?: ({
|
|
72
66
|
/** Array of required storage keys to validate */
|
|
73
67
|
requiredStorageKeys?: RequiredStorageKey[];
|
|
74
68
|
/** Enable shared modal dimensions */
|
|
75
69
|
enableSharedModalDimensions?: boolean;
|
|
76
|
-
})
|
|
70
|
+
} & import("@buoy-gg/shared-ui").ToolPresetOverrides) | undefined) => {
|
|
77
71
|
id: string;
|
|
78
72
|
name: string;
|
|
79
73
|
description: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preset.d.ts","sourceRoot":"","sources":["../../src/preset.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;
|
|
1
|
+
{"version":3,"file":"preset.d.ts","sourceRoot":"","sources":["../../src/preset.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAE1D;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB;;;;;qBAKX;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;;;6BAGJ,kBAAkB,EAAE;;;CAGjD,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,iBAAiB;IAG1B,iDAAiD;0BAC3B,kBAAkB,EAAE;IAC1C,qCAAqC;kCACP,OAAO;;;;;;qBAtCtB;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;;;6BAGJ,kBAAkB,EAAE;;;CAwClD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StorageModalWithTabs.d.ts","sourceRoot":"","sources":["../../../../src/storage/components/StorageModalWithTabs.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"StorageModalWithTabs.d.ts","sourceRoot":"","sources":["../../../../src/storage/components/StorageModalWithTabs.tsx"],"names":[],"mappings":"AAmCA,OAAO,EAAE,kBAAkB,EAAkB,MAAM,UAAU,CAAC;AAY9D,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAchF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,QAAqC,CAAC;AAExE,UAAU,yBAAyB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,CAAC;IACvC,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC3C;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,SAAS,GAAG,QAAQ,KAAK,IAAI,CAAC;IAClD;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CAC/C;AA0DD,wBAAgB,oBAAoB,CAAC,EACnC,OAAO,EACP,OAAO,EACP,MAAM,EACN,UAAU,EACV,2BAAmC,EACnC,mBAAwB,EACxB,MAAc,EACd,WAAW,EACX,SAAS,EACT,aAAa,GACd,EAAE,yBAAyB,sCA24B3B;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,GAAE,IAAI,CAAC,yBAAyB,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAM,+BAG9E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useStorageEvents.d.ts","sourceRoot":"","sources":["../../../../src/storage/hooks/useStorageEvents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;
|
|
1
|
+
{"version":3,"file":"useStorageEvents.d.ts","sourceRoot":"","sources":["../../../../src/storage/hooks/useStorageEvents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,EAEL,KAAK,YAAY,EAClB,MAAM,6BAA6B,CAAC;AAGrC,YAAY,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEhE,MAAM,WAAW,uBAAuB;IACtC,6BAA6B;IAC7B,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;IACvC,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,sBAAsB;IACrC,wCAAwC;IACxC,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,uBAAuB;IACvB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,0DAA0D;IAC1D,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,GAAE,uBAA4B,GACpC,sBAAsB,CAkBxB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buoy-gg/storage",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.34",
|
|
4
4
|
"description": "Browse & edit AsyncStorage, MMKV & SecureStore inside your React Native app, with change history. Part of Buoy devtools.",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
"**/snapshotProvider.ts"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@buoy-gg/floating-tools-core": "^7.0.
|
|
33
|
-
"@buoy-gg/shared-ui": "^7.0.
|
|
32
|
+
"@buoy-gg/floating-tools-core": "^7.0.34",
|
|
33
|
+
"@buoy-gg/shared-ui": "^7.0.34"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"@buoy-gg/license": "7.0.
|
|
36
|
+
"@buoy-gg/license": "7.0.34",
|
|
37
37
|
"@react-native-async-storage/async-storage": "^2.0.0 || ^3.1.0",
|
|
38
38
|
"react": "*",
|
|
39
39
|
"react-native": "*"
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"@types/react": "^19.1.0",
|
|
44
44
|
"@types/react-native": "^0.73.0",
|
|
45
45
|
"typescript": "~5.8.3",
|
|
46
|
-
"@buoy-gg/license": "^7.0.
|
|
46
|
+
"@buoy-gg/license": "^7.0.34"
|
|
47
47
|
},
|
|
48
48
|
"react-native-builder-bob": {
|
|
49
49
|
"source": "src",
|