@buoy-gg/storage 7.0.40 → 7.0.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +4 -0
  2. package/lib/commonjs/index.js +8 -1
  3. package/lib/commonjs/preset.js +9 -4
  4. package/lib/commonjs/storage/components/GameUIStorageBrowser.js +3 -2
  5. package/lib/commonjs/storage/components/MMKVInstanceSelector.js +3 -2
  6. package/lib/commonjs/storage/components/StorageActionButtons.js +6 -5
  7. package/lib/commonjs/storage/components/StorageEventCard.js +2 -1
  8. package/lib/commonjs/storage/components/StorageEventFilterView.js +7 -6
  9. package/lib/commonjs/storage/components/StorageKeyStats.js +5 -4
  10. package/lib/commonjs/storage/components/StorageTypeFilterBar.js +4 -3
  11. package/lib/commonjs/storage/hooks/useSecureStoreKeys.js +55 -12
  12. package/lib/commonjs/storage/sync/storageSyncAdapter.js +15 -1
  13. package/lib/commonjs/storage/utils/AsyncStorageListener.web.js +89 -0
  14. package/lib/commonjs/storage/utils/browserStorageLifecycle.js +10 -0
  15. package/lib/commonjs/storage/utils/browserStorageLifecycle.web.js +70 -0
  16. package/lib/commonjs/storage/utils/mmkvAvailability.web.js +21 -0
  17. package/lib/commonjs/storage/utils/storageLabels.js +13 -0
  18. package/lib/commonjs/storage/utils/storageLabels.web.js +13 -0
  19. package/lib/module/index.js +3 -1
  20. package/lib/module/preset.js +11 -5
  21. package/lib/module/storage/components/GameUIStorageBrowser.js +3 -2
  22. package/lib/module/storage/components/MMKVInstanceSelector.js +4 -3
  23. package/lib/module/storage/components/StorageActionButtons.js +6 -5
  24. package/lib/module/storage/components/StorageEventCard.js +2 -1
  25. package/lib/module/storage/components/StorageEventFilterView.js +7 -6
  26. package/lib/module/storage/components/StorageKeyStats.js +5 -4
  27. package/lib/module/storage/components/StorageTypeFilterBar.js +4 -3
  28. package/lib/module/storage/hooks/useSecureStoreKeys.js +55 -12
  29. package/lib/module/storage/sync/storageSyncAdapter.js +12 -0
  30. package/lib/module/storage/utils/AsyncStorageListener.web.js +76 -0
  31. package/lib/module/storage/utils/browserStorageLifecycle.js +6 -0
  32. package/lib/module/storage/utils/browserStorageLifecycle.web.js +66 -0
  33. package/lib/module/storage/utils/mmkvAvailability.web.js +15 -0
  34. package/lib/module/storage/utils/storageLabels.js +9 -0
  35. package/lib/module/storage/utils/storageLabels.web.js +9 -0
  36. package/lib/typescript/index.d.ts +1 -0
  37. package/lib/typescript/index.d.ts.map +1 -1
  38. package/lib/typescript/preset.d.ts.map +1 -1
  39. package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts.map +1 -1
  40. package/lib/typescript/storage/components/MMKVInstanceSelector.d.ts +0 -8
  41. package/lib/typescript/storage/components/MMKVInstanceSelector.d.ts.map +1 -1
  42. package/lib/typescript/storage/components/StorageActionButtons.d.ts.map +1 -1
  43. package/lib/typescript/storage/components/StorageEventCard.d.ts +0 -7
  44. package/lib/typescript/storage/components/StorageEventCard.d.ts.map +1 -1
  45. package/lib/typescript/storage/components/StorageEventFilterView.d.ts.map +1 -1
  46. package/lib/typescript/storage/components/StorageKeyRow.d.ts +2 -6
  47. package/lib/typescript/storage/components/StorageKeyRow.d.ts.map +1 -1
  48. package/lib/typescript/storage/components/StorageKeyStats.d.ts.map +1 -1
  49. package/lib/typescript/storage/components/StorageTypeFilterBar.d.ts +0 -15
  50. package/lib/typescript/storage/components/StorageTypeFilterBar.d.ts.map +1 -1
  51. package/lib/typescript/storage/hooks/useSecureStoreKeys.d.ts +2 -2
  52. package/lib/typescript/storage/hooks/useSecureStoreKeys.d.ts.map +1 -1
  53. package/lib/typescript/storage/sync/storageSyncAdapter.d.ts +7 -13
  54. package/lib/typescript/storage/sync/storageSyncAdapter.d.ts.map +1 -1
  55. package/lib/typescript/storage/utils/AsyncStorageListener.web.d.ts +24 -0
  56. package/lib/typescript/storage/utils/AsyncStorageListener.web.d.ts.map +1 -0
  57. package/lib/typescript/storage/utils/browserStorageLifecycle.d.ts +3 -0
  58. package/lib/typescript/storage/utils/browserStorageLifecycle.d.ts.map +1 -0
  59. package/lib/typescript/storage/utils/browserStorageLifecycle.web.d.ts +2 -0
  60. package/lib/typescript/storage/utils/browserStorageLifecycle.web.d.ts.map +1 -0
  61. package/lib/typescript/storage/utils/mmkvAvailability.web.d.ts +4 -0
  62. package/lib/typescript/storage/utils/mmkvAvailability.web.d.ts.map +1 -0
  63. package/lib/typescript/storage/utils/storageLabels.d.ts +8 -0
  64. package/lib/typescript/storage/utils/storageLabels.d.ts.map +1 -0
  65. package/lib/typescript/storage/utils/storageLabels.web.d.ts +8 -0
  66. package/lib/typescript/storage/utils/storageLabels.web.d.ts.map +1 -0
  67. package/lib/web/index.mjs +5 -0
  68. package/package.json +40 -14
  69. package/lib/commonjs/storage/components/DiffViewer/DiffOptionsPanel.js +0 -356
  70. package/lib/commonjs/storage/components/DiffViewer/components/DiffSummary.js +0 -122
  71. package/lib/commonjs/storage/components/StorageKeyEditorScreen.js +0 -414
  72. package/lib/commonjs/storage/components/StorageScalarKeyEditor.js +0 -61
  73. package/lib/commonjs/storage/components/StorageValueEditModal.js +0 -24
  74. package/lib/commonjs/storage/hooks/useKeyEditor.js +0 -31
  75. package/lib/commonjs/storage/hooks/useTreeDraft.js +0 -25
  76. package/lib/module/storage/components/DiffViewer/DiffOptionsPanel.js +0 -352
  77. package/lib/module/storage/components/DiffViewer/components/DiffSummary.js +0 -118
  78. package/lib/module/storage/components/StorageKeyEditorScreen.js +0 -410
  79. package/lib/module/storage/components/StorageScalarKeyEditor.js +0 -57
  80. package/lib/module/storage/components/StorageValueEditModal.js +0 -20
  81. package/lib/module/storage/hooks/useKeyEditor.js +0 -27
  82. package/lib/module/storage/hooks/useTreeDraft.js +0 -21
  83. package/lib/typescript/storage/components/DiffViewer/DiffOptionsPanel.d.ts +0 -18
  84. package/lib/typescript/storage/components/DiffViewer/DiffOptionsPanel.d.ts.map +0 -1
  85. package/lib/typescript/storage/components/DiffViewer/components/DiffSummary.d.ts +0 -12
  86. package/lib/typescript/storage/components/DiffViewer/components/DiffSummary.d.ts.map +0 -1
  87. package/lib/typescript/storage/components/StorageKeyEditorScreen.d.ts +0 -42
  88. package/lib/typescript/storage/components/StorageKeyEditorScreen.d.ts.map +0 -1
  89. package/lib/typescript/storage/components/StorageScalarKeyEditor.d.ts +0 -24
  90. package/lib/typescript/storage/components/StorageScalarKeyEditor.d.ts.map +0 -1
  91. package/lib/typescript/storage/components/StorageValueEditModal.d.ts +0 -14
  92. package/lib/typescript/storage/components/StorageValueEditModal.d.ts.map +0 -1
  93. package/lib/typescript/storage/hooks/useKeyEditor.d.ts +0 -27
  94. package/lib/typescript/storage/hooks/useKeyEditor.d.ts.map +0 -1
  95. package/lib/typescript/storage/hooks/useTreeDraft.d.ts +0 -16
  96. package/lib/typescript/storage/hooks/useTreeDraft.d.ts.map +0 -1
package/README.md CHANGED
@@ -100,3 +100,7 @@ Use a Free or Pro Buoy account. History limits and paid features vary by tool; s
100
100
  📚 [Full docs](https://buoy.gg/buoy/latest/docs/tools/storage) · [All Buoy tools](https://github.com/Buoy-gg/buoy)
101
101
 
102
102
  Proprietary software. © Buoy LLC. [Terms](https://buoy.gg/terms)
103
+
104
+ ## Web support (unreleased)
105
+
106
+ The browser uses localStorage and sessionStorage with the shared editor, event history, undo, and snapshots. Native secure storage is unavailable on web. The browser build is available in this checkout and has not been published yet. See the [web setup guide](https://buoy.gg/buoy/latest/docs/web-preview) for registration, dependencies, and browser boundaries.
@@ -207,6 +207,12 @@ Object.defineProperty(exports, "setStorageValue", {
207
207
  return _setStorageValue.setStorageValue;
208
208
  }
209
209
  });
210
+ Object.defineProperty(exports, "startBrowserStorageCapture", {
211
+ enumerable: true,
212
+ get: function () {
213
+ return _browserStorageLifecycle.startBrowserStorageCapture;
214
+ }
215
+ });
210
216
  Object.defineProperty(exports, "storageEditBlockedReason", {
211
217
  enumerable: true,
212
218
  get: function () {
@@ -343,4 +349,5 @@ var _MMKVInstanceRegistry = require("./storage/utils/MMKVInstanceRegistry");
343
349
  var _SecureStoreRegistry = require("./storage/utils/SecureStoreRegistry");
344
350
  var _storageSyncAdapter = require("./storage/sync/storageSyncAdapter");
345
351
  var _storageEventStore = require("./storage/stores/storageEventStore");
346
- var _storageBrowserRefresh = require("./storage/utils/storageBrowserRefresh");
352
+ var _storageBrowserRefresh = require("./storage/utils/storageBrowserRefresh");
353
+ var _browserStorageLifecycle = require("./storage/utils/browserStorageLifecycle");
@@ -7,6 +7,7 @@ exports.storageToolPreset = exports.createStorageTool = void 0;
7
7
  var _floatingToolsCore = require("@buoy-gg/floating-tools-core");
8
8
  var _sharedUi = require("@buoy-gg/shared-ui");
9
9
  var _StorageModalWithTabs = require("./storage/components/StorageModalWithTabs");
10
+ var _storageSyncAdapter = require("./storage/sync/storageSyncAdapter");
10
11
  var _jsxRuntime = require("react/jsx-runtime");
11
12
  /**
12
13
  * Pre-configured AsyncStorage browser tool for FloatingDevTools
@@ -74,7 +75,11 @@ const storageToolPreset = exports.storageToolPreset = (0, _sharedUi.defineToolPr
74
75
  * });
75
76
  * ```
76
77
  */
77
- const createStorageTool = exports.createStorageTool = (0, _sharedUi.createToolFactory)(storageToolPreset, options => ({
78
- requiredStorageKeys: options.requiredStorageKeys || [],
79
- enableSharedModalDimensions: options.enableSharedModalDimensions ?? false
80
- }));
78
+ const createStorageTool = exports.createStorageTool = (0, _sharedUi.createToolFactory)(storageToolPreset, options => {
79
+ // Shared with the sync adapter so the desktop panel checks the same keys.
80
+ (0, _storageSyncAdapter.setToolRequiredStorageKeys)(options.requiredStorageKeys || []);
81
+ return {
82
+ requiredStorageKeys: options.requiredStorageKeys || [],
83
+ enableSharedModalDimensions: options.enableSharedModalDimensions ?? false
84
+ };
85
+ });
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.FREE_TIER_KEY_LIMIT = void 0;
7
7
  exports.GameUIStorageBrowser = GameUIStorageBrowser;
8
+ var _storageLabels = require("../utils/storageLabels");
8
9
  var _react = require("react");
9
10
  var _reactNative = require("react-native");
10
11
  var _sharedUi = require("@buoy-gg/shared-ui");
@@ -188,7 +189,7 @@ function GameUIStorageBrowser({
188
189
  isLoading: isLoadingSecure,
189
190
  error: secureError,
190
191
  refresh: refreshSecure
191
- } = (0, _useSecureStoreKeys.useSecureStoreKeys)();
192
+ } = (0, _useSecureStoreKeys.useSecureStoreKeys)(requiredStorageKeys);
192
193
 
193
194
  // Merge storage keys from AsyncStorage, MMKV, and SecureStore
194
195
  const allStorageKeys = (0, _react.useMemo)(() => {
@@ -754,7 +755,7 @@ function GameUIStorageBrowser({
754
755
  onPress: () => setSelectedMMKVInstance(null),
755
756
  testID: "buoy-storage-instance-all",
756
757
  accessibilityRole: "button",
757
- accessibilityLabel: "All MMKV instances",
758
+ accessibilityLabel: `All ${_storageLabels.storageLabels.mmkv} instances`,
758
759
  style: [styles.instanceNavbarButton, selectedMMKVInstance === null && styles.instanceNavbarButtonActive],
759
760
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.HardDrive, {
760
761
  size: 12,
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.MMKVInstanceSelector = MMKVInstanceSelector;
7
+ var _storageLabels = require("../utils/storageLabels");
7
8
  var _react = require("react");
8
9
  var _reactNative = require("react-native");
9
10
  var _sharedUi = require("@buoy-gg/shared-ui");
@@ -65,9 +66,9 @@ function MMKVInstanceSelector({
65
66
  style: styles.container,
66
67
  children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
67
68
  style: styles.unavailableState,
68
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
69
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
69
70
  style: styles.unavailableTitle,
70
- children: "MMKV Not Available"
71
+ children: [_storageLabels.storageLabels.mmkv, " Not Available"]
71
72
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
72
73
  style: styles.unavailableDescription,
73
74
  children: (0, _mmkvAvailability.getMMKVUnavailableMessage)()
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.StorageActionButtons = StorageActionButtons;
7
+ var _storageLabels = require("../utils/storageLabels");
7
8
  var _react = require("react");
8
9
  var _reactNative = require("react-native");
9
10
  var _sharedUi = require("@buoy-gg/shared-ui");
@@ -35,7 +36,7 @@ function StorageActionButtons({
35
36
  setShowUpgradeModal(true);
36
37
  return;
37
38
  }
38
- _reactNative.Alert.alert("Clear AsyncStorage", "Delete all AsyncStorage keys? This cannot be undone.", [{
39
+ _reactNative.Alert.alert(`Clear ${_storageLabels.storageLabels.asyncFull}`, `Delete all ${_storageLabels.storageLabels.asyncFull} keys? This cannot be undone.`, [{
39
40
  text: "Cancel",
40
41
  style: "cancel"
41
42
  }, {
@@ -82,7 +83,7 @@ function StorageActionButtons({
82
83
  setShowUpgradeModal(true);
83
84
  return;
84
85
  }
85
- _reactNative.Alert.alert("Clear All MMKV", `Delete all keys from ${mmkvInstances.length} instances? This cannot be undone.`, [{
86
+ _reactNative.Alert.alert(`Clear All ${_storageLabels.storageLabels.mmkv}`, `Delete all keys from ${mmkvInstances.length} instances? This cannot be undone.`, [{
86
87
  text: "Cancel",
87
88
  style: "cancel"
88
89
  }, {
@@ -173,7 +174,7 @@ function StorageActionButtons({
173
174
  }), !isSelectMode && showClearAsync && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
174
175
  onPress: handleClearAsyncStorage,
175
176
  testID: "buoy-storage-clear-async",
176
- accessibilityLabel: "Clear AsyncStorage",
177
+ accessibilityLabel: `Clear ${_storageLabels.storageLabels.asyncFull}`,
177
178
  accessibilityRole: "button",
178
179
  style: styles.actionButton,
179
180
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Trash2, {
@@ -183,7 +184,7 @@ function StorageActionButtons({
183
184
  }), !isSelectMode && showClearAllMMKV && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
184
185
  onPress: handleClearAllMMKV,
185
186
  testID: "buoy-storage-clear-mmkv-all",
186
- accessibilityLabel: "Clear all MMKV instances",
187
+ accessibilityLabel: `Clear all ${_storageLabels.storageLabels.mmkv} instances`,
187
188
  accessibilityRole: "button",
188
189
  style: styles.actionButton,
189
190
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Trash2, {
@@ -193,7 +194,7 @@ function StorageActionButtons({
193
194
  }), !isSelectMode && showClearMMKVIndividual && mmkvInstances.length === 1 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
194
195
  onPress: () => handleClearMMKV(mmkvInstances[0].id, mmkvInstances[0].id),
195
196
  testID: "buoy-storage-clear-mmkv",
196
- accessibilityLabel: "Clear MMKV instance",
197
+ accessibilityLabel: `Clear ${_storageLabels.storageLabels.mmkv} instance`,
197
198
  accessibilityRole: "button",
198
199
  style: styles.actionButton,
199
200
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Trash2, {
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.StorageEventCard = void 0;
7
7
  exports.getValueType = getValueType;
8
+ var _storageLabels = require("../utils/storageLabels");
8
9
  var _react = require("react");
9
10
  var _sharedUi = require("@buoy-gg/shared-ui");
10
11
  var _storageActionHelpers = require("../utils/storageActionHelpers");
@@ -75,7 +76,7 @@ function getActionColor(action) {
75
76
  * Get storage type label for sublabel
76
77
  */
77
78
  function getStorageTypeSublabel(storageTypes, totalOps) {
78
- const types = Array.from(storageTypes).map(t => t === "async" ? "Async" : "MMKV").join("/");
79
+ const types = Array.from(storageTypes).map(t => t === "async" ? _storageLabels.storageLabels.async : _storageLabels.storageLabels.mmkv).join("/");
79
80
  return `${types} · ${totalOps} op${totalOps !== 1 ? "s" : ""}`;
80
81
  }
81
82
  const StorageEventCard = exports.StorageEventCard = /*#__PURE__*/(0, _react.memo)(function StorageEventCard({
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.StorageEventFilterView = StorageEventFilterView;
7
+ var _storageLabels = require("../utils/storageLabels");
7
8
  var _reactNative = require("react-native");
8
9
  var _sharedUi = require("@buoy-gg/shared-ui");
9
10
  var _jsxRuntime = require("react/jsx-runtime");
@@ -32,7 +33,7 @@ function StorageEventFilterView({
32
33
  enabled: true,
33
34
  title: "HOW EVENT FILTERS WORK",
34
35
  description: "Patterns hide matching storage keys from the event list. Storage type toggles control which storage backends are monitored.",
35
- examples: ["• react_buoy → hides keys containing react_buoy", "• @temp → hides @temp_user, @temp_data", "• redux → hides redux-persist:root", "• AsyncStorage → show/hide AsyncStorage events", "• MMKV → show/hide MMKV events"],
36
+ examples: ["• react_buoy → hides keys containing react_buoy", "• @temp → hides @temp_user, @temp_data", "• redux → hides redux-persist:root", `• ${_storageLabels.storageLabels.asyncFull}: show or hide captured events`, `• ${_storageLabels.storageLabels.mmkv}: show or hide captured events`],
36
37
  icon: _sharedUi.Filter
37
38
  },
38
39
  onPatternToggle: onTogglePattern,
@@ -55,7 +56,7 @@ function StorageEventFilterView({
55
56
  onPress: () => onToggleStorageType('async'),
56
57
  testID: "buoy-storage-backend-async",
57
58
  accessibilityRole: "button",
58
- accessibilityLabel: "Toggle AsyncStorage capture",
59
+ accessibilityLabel: `Toggle ${_storageLabels.storageLabels.asyncFull} capture`,
59
60
  accessibilityState: {
60
61
  selected: enabledStorageTypes.has('async')
61
62
  },
@@ -66,7 +67,7 @@ function StorageEventFilterView({
66
67
  style: [styles.storageTypeButtonText, enabledStorageTypes.has('async') && {
67
68
  color: _sharedUi.macOSColors.semantic.warning
68
69
  }],
69
- children: "AsyncStorage"
70
+ children: _storageLabels.storageLabels.asyncFull
70
71
  })]
71
72
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
72
73
  style: [styles.storageTypeButton, enabledStorageTypes.has('mmkv') && styles.storageTypeButtonActive, {
@@ -75,7 +76,7 @@ function StorageEventFilterView({
75
76
  onPress: () => onToggleStorageType('mmkv'),
76
77
  testID: "buoy-storage-backend-mmkv",
77
78
  accessibilityRole: "button",
78
- accessibilityLabel: "Toggle MMKV capture",
79
+ accessibilityLabel: `Toggle ${_storageLabels.storageLabels.mmkv} capture`,
79
80
  accessibilityState: {
80
81
  selected: enabledStorageTypes.has('mmkv')
81
82
  },
@@ -86,9 +87,9 @@ function StorageEventFilterView({
86
87
  style: [styles.storageTypeButtonText, enabledStorageTypes.has('mmkv') && {
87
88
  color: _sharedUi.macOSColors.semantic.info
88
89
  }],
89
- children: "MMKV"
90
+ children: _storageLabels.storageLabels.mmkv
90
91
  })]
91
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
92
+ }), _storageLabels.storageLabels.hasSecure && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
92
93
  style: [styles.storageTypeButton, enabledStorageTypes.has('secure') && styles.storageTypeButtonActive, {
93
94
  borderColor: _sharedUi.macOSColors.semantic.success + '40'
94
95
  }],
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.StorageKeyStatsSection = StorageKeyStatsSection;
7
+ var _storageLabels = require("../utils/storageLabels");
7
8
  var _reactNative = require("react-native");
8
9
  var _sharedUi = require("@buoy-gg/shared-ui");
9
10
  var _storageQueryUtils = require("../utils/storageQueryUtils");
@@ -54,13 +55,13 @@ const variableTypeData = [{
54
55
  // Storage type breakdown data
55
56
  const storageTypeData = [{
56
57
  key: "mmkv",
57
- label: "MMKV",
58
- description: "High-performance key-value storage",
58
+ label: _storageLabels.storageLabels.mmkv,
59
+ description: _storageLabels.storageLabels.hasSecure ? "Synchronous key-value storage" : "Storage for this browser tab",
59
60
  icon: _sharedUi.Database
60
61
  }, {
61
62
  key: "async",
62
- label: "AsyncStorage",
63
- description: "React Native async storage",
63
+ label: _storageLabels.storageLabels.asyncFull,
64
+ description: _storageLabels.storageLabels.hasSecure ? "React Native async storage" : "Persistent storage for this origin",
64
65
  icon: _sharedUi.Database
65
66
  }, {
66
67
  key: "secure",
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.StorageTypeFilterBar = StorageTypeFilterBar;
7
7
  exports.countByStorageType = countByStorageType;
8
+ var _storageLabels = require("../utils/storageLabels");
8
9
  var _reactNative = require("react-native");
9
10
  var _sharedUi = require("@buoy-gg/shared-ui");
10
11
  var _jsxRuntime = require("react/jsx-runtime");
@@ -35,11 +36,11 @@ const SEGMENTS = [{
35
36
  color: null
36
37
  }, {
37
38
  id: "async",
38
- label: "Async",
39
+ label: _storageLabels.storageLabels.async,
39
40
  color: _sharedUi.macOSColors.semantic.warning
40
41
  }, {
41
42
  id: "mmkv",
42
- label: "MMKV",
43
+ label: _storageLabels.storageLabels.mmkv,
43
44
  color: _sharedUi.macOSColors.semantic.info
44
45
  }, {
45
46
  id: "secure",
@@ -53,7 +54,7 @@ function StorageTypeFilterBar({
53
54
  }) {
54
55
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
55
56
  style: styles.row,
56
- children: SEGMENTS.map(segment => {
57
+ children: SEGMENTS.filter(segment => _storageLabels.storageLabels.hasSecure || segment.id !== 'secure').map(segment => {
57
58
  const isActive = active === segment.id;
58
59
  const accent = segment.color ?? _sharedUi.night.color.accent;
59
60
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.useSecureStoreKeys = useSecureStoreKeys;
7
7
  var _react = require("react");
8
+ var _requiredStorageKeyScope = require("../utils/requiredStorageKeyScope");
8
9
  var _SecureStoreRegistry = require("../utils/SecureStoreRegistry");
9
10
  /**
10
11
  * Placeholder value shown for `requireAuthentication` keys. Reading those
@@ -40,10 +41,14 @@ function signature(keys) {
40
41
  * Values are re-read on a poll and whenever the registry changes, so the
41
42
  * browser tracks writes the app makes while the tool is open.
42
43
  */
43
- function useSecureStoreKeys(options = {}) {
44
+ function useSecureStoreKeys(requiredStorageKeys = [], options = {}) {
44
45
  const {
45
46
  pollMs = DEFAULT_POLL_MS
46
47
  } = options;
48
+ // Read through a ref so a fresh array literal each render doesn't restart the poll.
49
+ const requiredRef = (0, _react.useRef)(requiredStorageKeys);
50
+ requiredRef.current = requiredStorageKeys;
51
+ const requiredSignature = JSON.stringify(requiredStorageKeys);
47
52
  const [storageKeys, setStorageKeys] = (0, _react.useState)([]);
48
53
  const [isLoading, setIsLoading] = (0, _react.useState)(true);
49
54
  const [error, setError] = (0, _react.useState)(null);
@@ -66,7 +71,12 @@ function useSecureStoreKeys(options = {}) {
66
71
  try {
67
72
  const module = _SecureStoreRegistry.secureStoreRegistry.getModule();
68
73
  const entries = _SecureStoreRegistry.secureStoreRegistry.getAll();
69
- if (!module || entries.length === 0) {
74
+ // Required keys scoped to SecureStore. Registration is what lets the tool
75
+ // read a key; `requiredStorageKeys` is what says the app needs it. A
76
+ // registered key with no value that nobody requires is simply absent.
77
+ const required = requiredRef.current.filter(req => (0, _requiredStorageKeyScope.requiredKeyAppliesTo)(req, "secure"));
78
+ const requiredFor = key => required.find(req => (typeof req === "string" ? req : req.key) === key);
79
+ if (!module || entries.length === 0 && required.length === 0) {
70
80
  commit([]);
71
81
  hasLoadedRef.current = true;
72
82
  setIsLoading(false);
@@ -78,16 +88,36 @@ function useSecureStoreKeys(options = {}) {
78
88
  if (!hasLoadedRef.current) setIsLoading(true);
79
89
  setError(null);
80
90
  const keys = [];
81
- for (const entry of entries) {
91
+
92
+ // Required keys that were never registered are still readable by name,
93
+ // with the default keychain options.
94
+ const toRead = [...entries, ...required.map(req => typeof req === "string" ? req : req.key).filter(key => !entries.some(e => e.key === key)).map(key => ({
95
+ key,
96
+ options: {},
97
+ description: undefined
98
+ }))];
99
+ for (const entry of toRead) {
100
+ const req = requiredFor(entry.key);
101
+ const reqObj = typeof req === "object" ? req : undefined;
102
+ const expectations = {
103
+ ...(reqObj?.expectedValue !== undefined && {
104
+ expectedValue: reqObj.expectedValue
105
+ }),
106
+ ...(reqObj?.expectedType !== undefined && {
107
+ expectedType: reqObj.expectedType
108
+ })
109
+ };
110
+ const description = reqObj?.description ?? entry.description;
82
111
  if (entry.options.requireAuthentication) {
83
112
  // Never auto-read: reading would fire a biometric prompt.
84
113
  keys.push({
85
114
  key: entry.key,
86
115
  value: BIOMETRIC_PLACEHOLDER,
87
116
  storageType: "secure",
88
- status: "optional_present",
89
- category: "optional",
90
- description: entry.description ?? "Protected by biometric authentication — value not read by DevTools",
117
+ status: req ? "required_present" : "optional_present",
118
+ category: req ? "required" : "optional",
119
+ description: description ?? "Protected by biometric authentication — value not read by DevTools",
120
+ ...expectations,
91
121
  ...(entry.options.keychainService && {
92
122
  instanceId: entry.options.keychainService
93
123
  })
@@ -98,8 +128,9 @@ function useSecureStoreKeys(options = {}) {
98
128
  try {
99
129
  rawValue = await module.getItemAsync(entry.key, entry.options);
100
130
  } catch {
101
- // Key unreadable (e.g. keychain state changed) — treat as missing.
131
+ // Key unreadable (e.g. keychain state changed) — treat as absent.
102
132
  }
133
+ if (rawValue === null && !req) continue;
103
134
 
104
135
  // Parse JSON values for the DataViewer, same as the AsyncStorage hook.
105
136
  let parsedValue = rawValue;
@@ -110,15 +141,26 @@ function useSecureStoreKeys(options = {}) {
110
141
  parsedValue = rawValue;
111
142
  }
112
143
  }
144
+ let status = "optional_present";
145
+ if (req) {
146
+ if (rawValue === null) status = "required_missing";else if (reqObj?.expectedValue !== undefined) status = String(parsedValue) === String(reqObj.expectedValue) ? "required_present" : "required_wrong_value";else if (reqObj?.expectedType !== undefined) {
147
+ const actualType = parsedValue === null ? "null" : Array.isArray(parsedValue) ? "array" : typeof parsedValue;
148
+ // A keychain value is always stored as a string, so a string is
149
+ // acceptable wherever the parsed form would be.
150
+ const want = reqObj.expectedType.toLowerCase();
151
+ status = actualType === want || want === "string" && typeof rawValue === "string" ? "required_present" : "required_wrong_type";
152
+ } else status = "required_present";
153
+ }
113
154
  keys.push({
114
155
  key: entry.key,
115
156
  value: rawValue === null ? undefined : parsedValue,
116
157
  storageType: "secure",
117
- status: rawValue === null ? "required_missing" : "optional_present",
118
- category: rawValue === null ? "required" : "optional",
119
- ...(entry.description && {
120
- description: entry.description
158
+ status,
159
+ category: req ? "required" : "optional",
160
+ ...(description && {
161
+ description
121
162
  }),
163
+ ...expectations,
122
164
  ...(entry.options.keychainService && {
123
165
  instanceId: entry.options.keychainService
124
166
  })
@@ -133,7 +175,8 @@ function useSecureStoreKeys(options = {}) {
133
175
  inFlightRef.current = false;
134
176
  setIsLoading(false);
135
177
  }
136
- }, [commit]);
178
+ // eslint-disable-next-line react-hooks/exhaustive-deps
179
+ }, [commit, requiredSignature]);
137
180
 
138
181
  // Initial fetch + poll. Registration usually happens at app startup, but the
139
182
  // registry is also refilled on the dashboard every time a device syncs, so
@@ -3,7 +3,9 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.storageSyncAdapter = exports.VALUE_ON_DEVICE = exports.SNAPSHOT_VALUE_INLINE_LIMIT = exports.SNAPSHOT_EVENTS_BUDGET = void 0;
6
+ exports.VALUE_ON_DEVICE = exports.SNAPSHOT_VALUE_INLINE_LIMIT = exports.SNAPSHOT_EVENTS_BUDGET = void 0;
7
+ exports.setToolRequiredStorageKeys = setToolRequiredStorageKeys;
8
+ exports.storageSyncAdapter = void 0;
7
9
  var _asyncStorage = _interopRequireDefault(require("@react-native-async-storage/async-storage"));
8
10
  var _sharedUi = require("@buoy-gg/shared-ui");
9
11
  var _asyncStorageCompat = require("../utils/asyncStorageCompat");
@@ -197,6 +199,16 @@ function assertMMKVWritable(instanceId) {
197
199
  * v2: oversized / unserializable event values are replaced with
198
200
  * VALUE_ON_DEVICE. Dashboards fetch them via `getEventDetail`.
199
201
  */
202
+ let toolRequiredStorageKeys = [];
203
+
204
+ /**
205
+ * Called by `createStorageTool` so remote dashboards validate against the
206
+ * same required keys as the on-device tool. The desktop panel used to have no
207
+ * way to learn them and showed every key as unchecked.
208
+ */
209
+ function setToolRequiredStorageKeys(keys) {
210
+ toolRequiredStorageKeys = keys;
211
+ }
200
212
  const storageSyncAdapter = exports.storageSyncAdapter = {
201
213
  // v3: a total snapshot budget, so a burst of individually-legal values
202
214
  // degrades the oldest events instead of the emit layer dropping the panel.
@@ -214,6 +226,8 @@ const storageSyncAdapter = exports.storageSyncAdapter = {
214
226
  clearEvents: () => {
215
227
  _storageEventStore.storageEventStore.clearEvents();
216
228
  },
229
+ /** The app's required storage keys, as given to `createStorageTool`. */
230
+ getRequiredKeys: () => toolRequiredStorageKeys,
217
231
  /**
218
232
  * On-demand detail: one event's real value/prevValue/pairs.
219
233
  * The per-snapshot stream stays payload-light; this is the explicit,
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.addListener = addListener;
7
+ exports.default = void 0;
8
+ exports.getListenerCount = getListenerCount;
9
+ exports.isListening = isListening;
10
+ exports.isPaused = isPaused;
11
+ exports.pauseCapture = pauseCapture;
12
+ exports.removeAllListeners = removeAllListeners;
13
+ exports.resumeCapture = resumeCapture;
14
+ exports.startListening = startListening;
15
+ exports.stopListening = stopListening;
16
+ var _utils = require("@buoy-gg/shared-ui/utils");
17
+ const listeners = new Set();
18
+ let stop;
19
+ let paused = false;
20
+ const ignoredKeys = new Set(['@react_buoy_storage_event_filters', '@react_buoy_storage_key_filters', '@react_buoy_storage_is_monitoring', 'REACT_QUERY_OFFLINE_CACHE']);
21
+ const ignored = key => [...ignoredKeys].some(prefix => key.startsWith(prefix));
22
+ async function startListening() {
23
+ if (stop || typeof window === 'undefined') return;
24
+ stop = (0, _utils.subscribeToBrowserStorage)(change => {
25
+ if (paused || change.area !== 'localStorage' || change.key !== undefined && ignored(change.key)) return;
26
+ const {
27
+ area: _,
28
+ action,
29
+ ...data
30
+ } = change;
31
+ if (data.prevPairs) data.prevPairs = data.prevPairs.filter(([key]) => !ignored(key));
32
+ const event = {
33
+ action,
34
+ data,
35
+ timestamp: new Date()
36
+ };
37
+ for (const listener of [...listeners]) {
38
+ try {
39
+ listener(event);
40
+ } catch {/* Keep observing other consumers. */}
41
+ }
42
+ });
43
+ }
44
+ function stopListening() {
45
+ stop?.();
46
+ stop = undefined;
47
+ }
48
+ function addListener(listener) {
49
+ listeners.add(listener);
50
+ return () => {
51
+ listeners.delete(listener);
52
+ };
53
+ }
54
+ function removeAllListeners() {
55
+ listeners.clear();
56
+ }
57
+ function isListening() {
58
+ return !!stop;
59
+ }
60
+ function getListenerCount() {
61
+ return listeners.size;
62
+ }
63
+ function pauseCapture() {
64
+ paused = true;
65
+ }
66
+ function resumeCapture() {
67
+ paused = false;
68
+ }
69
+ function isPaused() {
70
+ return paused;
71
+ }
72
+ const asyncStorageListener = {
73
+ startListening,
74
+ stopListening,
75
+ addListener,
76
+ removeAllListeners,
77
+ pause: pauseCapture,
78
+ resume: resumeCapture,
79
+ get isActive() {
80
+ return isListening();
81
+ },
82
+ get listenerCount() {
83
+ return listeners.size;
84
+ },
85
+ get paused() {
86
+ return paused;
87
+ }
88
+ };
89
+ var _default = exports.default = asyncStorageListener;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.startBrowserStorageCapture = startBrowserStorageCapture;
7
+ /** Native backends are registered by the host application. */
8
+ function startBrowserStorageCapture() {
9
+ return () => {};
10
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.startBrowserStorageCapture = startBrowserStorageCapture;
7
+ var _utils = require("@buoy-gg/shared-ui/utils");
8
+ var _MMKVInstanceRegistry = require("./MMKVInstanceRegistry");
9
+ let owners = 0;
10
+ const storage = () => (0, _utils.getBrowserStorage)('sessionStorage');
11
+ const keys = () => Array.from({
12
+ length: storage().length
13
+ }, (_, index) => storage().key(index)).filter(key => key !== null);
14
+ // Reuse the synchronous storage editor and snapshot provider with Web Storage strings.
15
+ const sessionBackend = {
16
+ set(key, value) {
17
+ storage().setItem(key, String(value));
18
+ },
19
+ getString(key) {
20
+ return storage().getItem(key) ?? undefined;
21
+ },
22
+ getNumber() {
23
+ return undefined;
24
+ },
25
+ getBoolean() {
26
+ return undefined;
27
+ },
28
+ getBuffer() {
29
+ return undefined;
30
+ },
31
+ contains(key) {
32
+ return storage().getItem(key) !== null;
33
+ },
34
+ delete(key) {
35
+ const found = this.contains(key);
36
+ storage().removeItem(key);
37
+ return found;
38
+ },
39
+ getAllKeys: keys,
40
+ clearAll() {
41
+ storage().clear();
42
+ },
43
+ get size() {
44
+ return keys().reduce((bytes, key) => bytes + 2 * (key.length + (storage().getItem(key)?.length ?? 0)), 0);
45
+ },
46
+ addOnValueChangedListener(listener) {
47
+ return {
48
+ remove: (0, _utils.subscribeToBrowserStorage)(change => {
49
+ if (change.area !== 'sessionStorage') return;
50
+ if (change.key !== undefined) listener(change.key);else change.prevPairs?.forEach(([key]) => listener(key));
51
+ })
52
+ };
53
+ }
54
+ };
55
+ function startBrowserStorageCapture() {
56
+ if (typeof window === 'undefined') return () => {};
57
+ try {
58
+ storage();
59
+ } catch {
60
+ return () => {};
61
+ }
62
+ if (++owners === 1) (0, _MMKVInstanceRegistry.registerMMKVInstance)('sessionStorage', sessionBackend);
63
+ let active = true;
64
+ return () => {
65
+ if (active) {
66
+ active = false;
67
+ if (--owners === 0) (0, _MMKVInstanceRegistry.unregisterMMKVInstance)('sessionStorage');
68
+ }
69
+ };
70
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getMMKVClass = getMMKVClass;
7
+ exports.getMMKVUnavailableMessage = getMMKVUnavailableMessage;
8
+ exports.isMMKVAvailable = isMMKVAvailable;
9
+ function isMMKVAvailable() {
10
+ try {
11
+ return typeof window !== 'undefined' && !!window.sessionStorage;
12
+ } catch {
13
+ return false;
14
+ }
15
+ }
16
+ function getMMKVClass() {
17
+ return null;
18
+ }
19
+ function getMMKVUnavailableMessage() {
20
+ return 'Session storage is unavailable in this browser context.';
21
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.storageLabels = void 0;
7
+ const storageLabels = exports.storageLabels = {
8
+ async: 'Async',
9
+ asyncFull: 'AsyncStorage',
10
+ mmkv: 'MMKV',
11
+ secure: 'Secure',
12
+ hasSecure: true
13
+ };