@buoy-gg/storage 7.0.32 → 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/GameUIStorageBrowser.js +54 -13
- package/lib/commonjs/storage/components/StorageModalWithTabs.js +61 -155
- 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/GameUIStorageBrowser.js +55 -14
- package/lib/module/storage/components/StorageModalWithTabs.js +63 -157
- 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/GameUIStorageBrowser.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
|
+
}));
|
|
@@ -861,19 +861,60 @@ function GameUIStorageBrowser({
|
|
|
861
861
|
children: searchQuery ? "Upgrade to Pro to search your full storage" : "Upgrade to Pro to unlock full storage access"
|
|
862
862
|
})]
|
|
863
863
|
}) : null;
|
|
864
|
-
const emptyStateNode =
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
864
|
+
const emptyStateNode = (() => {
|
|
865
|
+
// Short-circuits on the first real key, so this costs nothing.
|
|
866
|
+
const hasAnyKeys = allStorageKeys.some(k => !(0, _sharedUi.isDevToolsStorageKey)(k.key));
|
|
867
|
+
|
|
868
|
+
// Three causes here are unlike any other tool's: an empty MISSING or
|
|
869
|
+
// ISSUES list is a PASS, not a failure, and an empty VALID list means
|
|
870
|
+
// everything is broken. Those skip the shared resolver and say so.
|
|
871
|
+
let content;
|
|
872
|
+
if (searchQuery || !hasAnyKeys) {
|
|
873
|
+
content = (0, _sharedUi.resolveListEmptyState)({
|
|
874
|
+
subject: "keys",
|
|
875
|
+
verb: "registered",
|
|
876
|
+
search: searchQuery,
|
|
877
|
+
total: hasAnyKeys ? 1 : 0,
|
|
878
|
+
waiting: {
|
|
879
|
+
// Previously this fell into the "all" branch and claimed every key
|
|
880
|
+
// had an issue — of the zero keys that exist.
|
|
881
|
+
otto: "empty",
|
|
882
|
+
title: "No storage keys",
|
|
883
|
+
message: "Nothing is registered for this tool to read."
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
if (searchQuery && hasAnyKeys) {
|
|
887
|
+
// No count: the pre-search total would mean re-running the whole key
|
|
888
|
+
// pipeline a second time for a number the sentence does not need.
|
|
889
|
+
content = {
|
|
890
|
+
...content,
|
|
891
|
+
message: "No keys match. Clear the search to see them."
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
} else if (activeFilter === "missing") {
|
|
895
|
+
content = {
|
|
896
|
+
otto: "thumbsup",
|
|
897
|
+
title: "No missing keys",
|
|
898
|
+
message: "All required keys are present"
|
|
899
|
+
};
|
|
900
|
+
} else if (activeFilter === "issues") {
|
|
901
|
+
content = {
|
|
902
|
+
otto: "cool",
|
|
903
|
+
title: "No issues found",
|
|
904
|
+
message: "All storage keys are correctly configured"
|
|
905
|
+
};
|
|
906
|
+
} else {
|
|
907
|
+
content = {
|
|
908
|
+
otto: "broken",
|
|
909
|
+
title: "No valid keys found",
|
|
910
|
+
message: "All keys have issues or are missing"
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolEmptyState, {
|
|
914
|
+
...content,
|
|
915
|
+
testID: "buoy-storage-keys-empty"
|
|
916
|
+
});
|
|
917
|
+
})();
|
|
877
918
|
const upgradeModalNode = /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ProUpgradeModal, {
|
|
878
919
|
visible: showUpgradeModal,
|
|
879
920
|
onClose: () => setShowUpgradeModal(false),
|
|
@@ -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
|
|
|
@@ -676,18 +574,26 @@ function StorageModalWithTabs({
|
|
|
676
574
|
const filtered = storageTypeFilter !== "all";
|
|
677
575
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
678
576
|
style: styles.flexFill,
|
|
679
|
-
children: [eventTypeFilterBar, /*#__PURE__*/(0, _jsxRuntime.
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
}
|
|
577
|
+
children: [eventTypeFilterBar, /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolEmptyState, {
|
|
578
|
+
testID: "buoy-storage-events-empty"
|
|
579
|
+
// Three literal causes, no search on this tab — so it renders
|
|
580
|
+
// through the shared shell without the shared resolver. The
|
|
581
|
+
// backend pills need their own sentence naming the pill that
|
|
582
|
+
// undoes them, which the generic "Filtered Out" would drop.
|
|
583
|
+
,
|
|
584
|
+
...(!isListening ? {
|
|
585
|
+
otto: "paused",
|
|
586
|
+
title: "Event listener is paused",
|
|
587
|
+
message: "Press play to start monitoring"
|
|
588
|
+
} : filtered ? {
|
|
589
|
+
otto: "filtering",
|
|
590
|
+
title: `No ${storageTypeFilter} events`,
|
|
591
|
+
message: "Other backends have events — pick All Types to see them"
|
|
592
|
+
} : {
|
|
593
|
+
otto: "listening",
|
|
594
|
+
title: "No storage events yet",
|
|
595
|
+
message: "Storage operations will appear here"
|
|
596
|
+
})
|
|
691
597
|
})]
|
|
692
598
|
});
|
|
693
599
|
}
|
|
@@ -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
|
}
|