@buoy-gg/storage 7.0.41 → 7.0.42

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 (180) hide show
  1. package/README.md +11 -90
  2. package/lib/commonjs/index.js +1 -353
  3. package/lib/commonjs/preset.js +1 -85
  4. package/lib/commonjs/storage/components/DiffViewer/TreeDiffViewer.js +1 -36
  5. package/lib/commonjs/storage/components/DiffViewer/modes/ThemedSplitView.js +1 -12
  6. package/lib/commonjs/storage/components/DiffViewer/themes/diffThemes.js +1 -24
  7. package/lib/commonjs/storage/components/GameUIStorageBrowser.js +3 -1259
  8. package/lib/commonjs/storage/components/MMKVInstanceInfoPanel.js +1 -257
  9. package/lib/commonjs/storage/components/MMKVInstanceSelector.js +1 -419
  10. package/lib/commonjs/storage/components/SelectionActionBar.js +1 -207
  11. package/lib/commonjs/storage/components/StorageActionButtons.js +1 -242
  12. package/lib/commonjs/storage/components/StorageBrowserMode.js +1 -61
  13. package/lib/commonjs/storage/components/StorageEventActionButton.js +1 -121
  14. package/lib/commonjs/storage/components/StorageEventCard.js +1 -118
  15. package/lib/commonjs/storage/components/StorageEventDetailContent.js +1 -494
  16. package/lib/commonjs/storage/components/StorageEventFilterView.js +1 -160
  17. package/lib/commonjs/storage/components/StorageEventsSection.js +1 -24
  18. package/lib/commonjs/storage/components/StorageFilterCards.js +1 -231
  19. package/lib/commonjs/storage/components/StorageKeyCard.js +1 -520
  20. package/lib/commonjs/storage/components/StorageKeyDetailScreen.js +1 -579
  21. package/lib/commonjs/storage/components/StorageKeyRow.js +1 -559
  22. package/lib/commonjs/storage/components/StorageKeySection.js +1 -160
  23. package/lib/commonjs/storage/components/StorageKeyStats.js +1 -345
  24. package/lib/commonjs/storage/components/StorageModalWithTabs.js +1 -954
  25. package/lib/commonjs/storage/components/StorageSaveNotice.js +1 -171
  26. package/lib/commonjs/storage/components/StorageSection.js +1 -43
  27. package/lib/commonjs/storage/components/StorageTypeFilterBar.js +1 -144
  28. package/lib/commonjs/storage/hooks/useAsyncStorageKeys.js +1 -141
  29. package/lib/commonjs/storage/hooks/useMMKVInstances.js +1 -210
  30. package/lib/commonjs/storage/hooks/useMMKVKeys.js +1 -376
  31. package/lib/commonjs/storage/hooks/useSecureStoreKeys.js +1 -202
  32. package/lib/commonjs/storage/hooks/useStorageEvents.js +1 -61
  33. package/lib/commonjs/storage/stores/storageEventStore.js +1 -312
  34. package/lib/commonjs/storage/sync/storageSyncAdapter.js +1 -492
  35. package/lib/commonjs/storage/types.js +1 -5
  36. package/lib/commonjs/storage/utils/AsyncStorageListener.js +1 -691
  37. package/lib/commonjs/storage/utils/AsyncStorageListener.web.js +1 -89
  38. package/lib/commonjs/storage/utils/MMKVInstanceRegistry.js +1 -203
  39. package/lib/commonjs/storage/utils/MMKVListener.js +1 -416
  40. package/lib/commonjs/storage/utils/SecureStoreRegistry.js +1 -200
  41. package/lib/commonjs/storage/utils/asyncStorageCompat.js +1 -89
  42. package/lib/commonjs/storage/utils/browserStorageLifecycle.js +1 -10
  43. package/lib/commonjs/storage/utils/browserStorageLifecycle.web.js +1 -70
  44. package/lib/commonjs/storage/utils/clearAllStorage.js +1 -48
  45. package/lib/commonjs/storage/utils/deleteStorageKeys.js +1 -82
  46. package/lib/commonjs/storage/utils/lineDiff.js +1 -30
  47. package/lib/commonjs/storage/utils/mmkvAvailability.js +1 -62
  48. package/lib/commonjs/storage/utils/mmkvAvailability.web.js +1 -21
  49. package/lib/commonjs/storage/utils/mmkvTypeDetection.js +1 -154
  50. package/lib/commonjs/storage/utils/requiredStorageKeyScope.js +1 -17
  51. package/lib/commonjs/storage/utils/setStorageValue.js +1 -218
  52. package/lib/commonjs/storage/utils/snapshotProvider.js +1 -382
  53. package/lib/commonjs/storage/utils/storageActionHelpers.js +1 -46
  54. package/lib/commonjs/storage/utils/storageBrowserRefresh.js +1 -49
  55. package/lib/commonjs/storage/utils/storageLabels.js +1 -13
  56. package/lib/commonjs/storage/utils/storageLabels.web.js +1 -13
  57. package/lib/commonjs/storage/utils/storageOps.js +1 -53
  58. package/lib/commonjs/storage/utils/storageQueryUtils.js +1 -35
  59. package/lib/commonjs/storage/utils/storageTimeTravelUtils.js +1 -288
  60. package/lib/commonjs/storage/utils/valueType.js +1 -83
  61. package/lib/module/index.js +1 -114
  62. package/lib/module/preset.js +1 -82
  63. package/lib/module/storage/components/DiffViewer/TreeDiffViewer.js +1 -32
  64. package/lib/module/storage/components/DiffViewer/modes/ThemedSplitView.js +1 -9
  65. package/lib/module/storage/components/DiffViewer/themes/diffThemes.js +1 -10
  66. package/lib/module/storage/components/GameUIStorageBrowser.js +3 -1257
  67. package/lib/module/storage/components/MMKVInstanceInfoPanel.js +1 -253
  68. package/lib/module/storage/components/MMKVInstanceSelector.js +1 -415
  69. package/lib/module/storage/components/SelectionActionBar.js +1 -204
  70. package/lib/module/storage/components/StorageActionButtons.js +1 -238
  71. package/lib/module/storage/components/StorageBrowserMode.js +1 -57
  72. package/lib/module/storage/components/StorageEventActionButton.js +1 -118
  73. package/lib/module/storage/components/StorageEventCard.js +1 -113
  74. package/lib/module/storage/components/StorageEventDetailContent.js +1 -490
  75. package/lib/module/storage/components/StorageEventFilterView.js +1 -156
  76. package/lib/module/storage/components/StorageEventsSection.js +1 -20
  77. package/lib/module/storage/components/StorageFilterCards.js +1 -227
  78. package/lib/module/storage/components/StorageKeyCard.js +1 -517
  79. package/lib/module/storage/components/StorageKeyDetailScreen.js +1 -577
  80. package/lib/module/storage/components/StorageKeyRow.js +1 -554
  81. package/lib/module/storage/components/StorageKeySection.js +1 -155
  82. package/lib/module/storage/components/StorageKeyStats.js +1 -342
  83. package/lib/module/storage/components/StorageModalWithTabs.js +1 -949
  84. package/lib/module/storage/components/StorageSaveNotice.js +1 -168
  85. package/lib/module/storage/components/StorageSection.js +1 -40
  86. package/lib/module/storage/components/StorageTypeFilterBar.js +1 -139
  87. package/lib/module/storage/hooks/useAsyncStorageKeys.js +1 -136
  88. package/lib/module/storage/hooks/useMMKVInstances.js +1 -204
  89. package/lib/module/storage/hooks/useMMKVKeys.js +1 -371
  90. package/lib/module/storage/hooks/useSecureStoreKeys.js +1 -199
  91. package/lib/module/storage/hooks/useStorageEvents.js +1 -58
  92. package/lib/module/storage/stores/storageEventStore.js +1 -301
  93. package/lib/module/storage/sync/storageSyncAdapter.js +1 -486
  94. package/lib/module/storage/types.js +1 -3
  95. package/lib/module/storage/utils/AsyncStorageListener.js +1 -679
  96. package/lib/module/storage/utils/AsyncStorageListener.web.js +1 -76
  97. package/lib/module/storage/utils/MMKVInstanceRegistry.js +1 -197
  98. package/lib/module/storage/utils/MMKVListener.js +1 -403
  99. package/lib/module/storage/utils/SecureStoreRegistry.js +1 -194
  100. package/lib/module/storage/utils/asyncStorageCompat.js +1 -81
  101. package/lib/module/storage/utils/browserStorageLifecycle.js +1 -6
  102. package/lib/module/storage/utils/browserStorageLifecycle.web.js +1 -66
  103. package/lib/module/storage/utils/clearAllStorage.js +1 -43
  104. package/lib/module/storage/utils/deleteStorageKeys.js +1 -78
  105. package/lib/module/storage/utils/lineDiff.js +1 -12
  106. package/lib/module/storage/utils/mmkvAvailability.js +1 -56
  107. package/lib/module/storage/utils/mmkvAvailability.web.js +1 -15
  108. package/lib/module/storage/utils/mmkvTypeDetection.js +1 -148
  109. package/lib/module/storage/utils/requiredStorageKeyScope.js +1 -13
  110. package/lib/module/storage/utils/setStorageValue.js +1 -209
  111. package/lib/module/storage/utils/snapshotProvider.js +1 -380
  112. package/lib/module/storage/utils/storageActionHelpers.js +1 -42
  113. package/lib/module/storage/utils/storageBrowserRefresh.js +1 -44
  114. package/lib/module/storage/utils/storageLabels.js +1 -9
  115. package/lib/module/storage/utils/storageLabels.web.js +1 -9
  116. package/lib/module/storage/utils/storageOps.js +1 -46
  117. package/lib/module/storage/utils/storageQueryUtils.js +1 -30
  118. package/lib/module/storage/utils/storageTimeTravelUtils.js +1 -281
  119. package/lib/module/storage/utils/valueType.js +1 -76
  120. package/lib/web/index.mjs +2 -3
  121. package/package.json +5 -5
  122. package/lib/typescript/index.d.ts.map +0 -1
  123. package/lib/typescript/preset.d.ts.map +0 -1
  124. package/lib/typescript/storage/components/DiffViewer/TreeDiffViewer.d.ts.map +0 -1
  125. package/lib/typescript/storage/components/DiffViewer/modes/ThemedSplitView.d.ts.map +0 -1
  126. package/lib/typescript/storage/components/DiffViewer/themes/diffThemes.d.ts.map +0 -1
  127. package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts.map +0 -1
  128. package/lib/typescript/storage/components/MMKVInstanceInfoPanel.d.ts.map +0 -1
  129. package/lib/typescript/storage/components/MMKVInstanceSelector.d.ts.map +0 -1
  130. package/lib/typescript/storage/components/SelectionActionBar.d.ts.map +0 -1
  131. package/lib/typescript/storage/components/StorageActionButtons.d.ts.map +0 -1
  132. package/lib/typescript/storage/components/StorageBrowserMode.d.ts.map +0 -1
  133. package/lib/typescript/storage/components/StorageEventActionButton.d.ts.map +0 -1
  134. package/lib/typescript/storage/components/StorageEventCard.d.ts.map +0 -1
  135. package/lib/typescript/storage/components/StorageEventDetailContent.d.ts.map +0 -1
  136. package/lib/typescript/storage/components/StorageEventFilterView.d.ts.map +0 -1
  137. package/lib/typescript/storage/components/StorageEventsSection.d.ts.map +0 -1
  138. package/lib/typescript/storage/components/StorageFilterCards.d.ts.map +0 -1
  139. package/lib/typescript/storage/components/StorageKeyCard.d.ts.map +0 -1
  140. package/lib/typescript/storage/components/StorageKeyDetailScreen.d.ts.map +0 -1
  141. package/lib/typescript/storage/components/StorageKeyRow.d.ts.map +0 -1
  142. package/lib/typescript/storage/components/StorageKeySection.d.ts.map +0 -1
  143. package/lib/typescript/storage/components/StorageKeyStats.d.ts.map +0 -1
  144. package/lib/typescript/storage/components/StorageModalWithTabs.d.ts.map +0 -1
  145. package/lib/typescript/storage/components/StorageSaveNotice.d.ts.map +0 -1
  146. package/lib/typescript/storage/components/StorageSection.d.ts.map +0 -1
  147. package/lib/typescript/storage/components/StorageTypeFilterBar.d.ts.map +0 -1
  148. package/lib/typescript/storage/hooks/useAsyncStorageKeys.d.ts.map +0 -1
  149. package/lib/typescript/storage/hooks/useMMKVInstances.d.ts.map +0 -1
  150. package/lib/typescript/storage/hooks/useMMKVKeys.d.ts.map +0 -1
  151. package/lib/typescript/storage/hooks/useSecureStoreKeys.d.ts.map +0 -1
  152. package/lib/typescript/storage/hooks/useStorageEvents.d.ts.map +0 -1
  153. package/lib/typescript/storage/stores/storageEventStore.d.ts.map +0 -1
  154. package/lib/typescript/storage/sync/storageSyncAdapter.d.ts.map +0 -1
  155. package/lib/typescript/storage/types.d.ts.map +0 -1
  156. package/lib/typescript/storage/utils/AsyncStorageListener.d.ts.map +0 -1
  157. package/lib/typescript/storage/utils/AsyncStorageListener.web.d.ts.map +0 -1
  158. package/lib/typescript/storage/utils/MMKVInstanceRegistry.d.ts.map +0 -1
  159. package/lib/typescript/storage/utils/MMKVListener.d.ts.map +0 -1
  160. package/lib/typescript/storage/utils/SecureStoreRegistry.d.ts.map +0 -1
  161. package/lib/typescript/storage/utils/asyncStorageCompat.d.ts.map +0 -1
  162. package/lib/typescript/storage/utils/browserStorageLifecycle.d.ts.map +0 -1
  163. package/lib/typescript/storage/utils/browserStorageLifecycle.web.d.ts.map +0 -1
  164. package/lib/typescript/storage/utils/clearAllStorage.d.ts.map +0 -1
  165. package/lib/typescript/storage/utils/deleteStorageKeys.d.ts.map +0 -1
  166. package/lib/typescript/storage/utils/lineDiff.d.ts.map +0 -1
  167. package/lib/typescript/storage/utils/mmkvAvailability.d.ts.map +0 -1
  168. package/lib/typescript/storage/utils/mmkvAvailability.web.d.ts.map +0 -1
  169. package/lib/typescript/storage/utils/mmkvTypeDetection.d.ts.map +0 -1
  170. package/lib/typescript/storage/utils/requiredStorageKeyScope.d.ts.map +0 -1
  171. package/lib/typescript/storage/utils/setStorageValue.d.ts.map +0 -1
  172. package/lib/typescript/storage/utils/snapshotProvider.d.ts.map +0 -1
  173. package/lib/typescript/storage/utils/storageActionHelpers.d.ts.map +0 -1
  174. package/lib/typescript/storage/utils/storageBrowserRefresh.d.ts.map +0 -1
  175. package/lib/typescript/storage/utils/storageLabels.d.ts.map +0 -1
  176. package/lib/typescript/storage/utils/storageLabels.web.d.ts.map +0 -1
  177. package/lib/typescript/storage/utils/storageOps.d.ts.map +0 -1
  178. package/lib/typescript/storage/utils/storageQueryUtils.d.ts.map +0 -1
  179. package/lib/typescript/storage/utils/storageTimeTravelUtils.d.ts.map +0 -1
  180. package/lib/typescript/storage/utils/valueType.d.ts.map +0 -1
@@ -1,199 +1 @@
1
- "use strict";
2
-
3
- import { useState, useEffect, useCallback, useRef } from "react";
4
- import { requiredKeyAppliesTo } from "../utils/requiredStorageKeyScope";
5
- import { secureStoreRegistry } from "../utils/SecureStoreRegistry";
6
-
7
- /**
8
- * Placeholder value shown for `requireAuthentication` keys. Reading those
9
- * triggers a biometric (Face ID / fingerprint) prompt, so the tool never
10
- * auto-reads them — it just shows they exist.
11
- */
12
- const BIOMETRIC_PLACEHOLDER = "•••••• (biometric protected)";
13
-
14
- /**
15
- * How often to re-read registered SecureStore values.
16
- *
17
- * SecureStore is the one backend with no change feed at all: AsyncStorage and
18
- * MMKV writes are intercepted (and MMKV has a native value-changed listener),
19
- * but the keychain gives no notification, so a value written by the app while
20
- * the tool is open would otherwise stay stale until the browser remounts.
21
- * Polling is the only option — kept cheap by re-reading a handful of registered
22
- * keys and only committing state when a value actually changed.
23
- */
24
- const DEFAULT_POLL_MS = 3000;
25
- /** Identity signature used to skip no-op state updates. */
26
- function signature(keys) {
27
- return JSON.stringify(keys);
28
- }
29
-
30
- /**
31
- * Read all registered SecureStore keys into StorageKeyInfo[] for the browser.
32
- *
33
- * SecureStore has no enumeration API, so this only surfaces keys declared via
34
- * `registerSecureStoreKeys()` (see SecureStoreRegistry). Values are read with
35
- * the options each key was registered with (keychainService must match), and
36
- * biometric-protected keys are listed without being read.
37
- *
38
- * Values are re-read on a poll and whenever the registry changes, so the
39
- * browser tracks writes the app makes while the tool is open.
40
- */
41
- export function useSecureStoreKeys(requiredStorageKeys = [], options = {}) {
42
- const {
43
- pollMs = DEFAULT_POLL_MS
44
- } = options;
45
- // Read through a ref so a fresh array literal each render doesn't restart the poll.
46
- const requiredRef = useRef(requiredStorageKeys);
47
- requiredRef.current = requiredStorageKeys;
48
- const requiredSignature = JSON.stringify(requiredStorageKeys);
49
- const [storageKeys, setStorageKeys] = useState([]);
50
- const [isLoading, setIsLoading] = useState(true);
51
- const [error, setError] = useState(null);
52
-
53
- // Last committed content, so a poll that finds nothing new costs zero
54
- // renders (this hook feeds a non-virtualized list of every storage key).
55
- const signatureRef = useRef(signature([]));
56
- const hasLoadedRef = useRef(false);
57
- const inFlightRef = useRef(false);
58
- const commit = useCallback(keys => {
59
- const next = signature(keys);
60
- if (next === signatureRef.current) return;
61
- signatureRef.current = next;
62
- setStorageKeys(keys);
63
- }, []);
64
- const fetchSecureStoreData = useCallback(async () => {
65
- // Reads are async and the poll is unconditional — never stack them.
66
- if (inFlightRef.current) return;
67
- inFlightRef.current = true;
68
- try {
69
- const module = secureStoreRegistry.getModule();
70
- const entries = secureStoreRegistry.getAll();
71
- // Required keys scoped to SecureStore. Registration is what lets the tool
72
- // read a key; `requiredStorageKeys` is what says the app needs it. A
73
- // registered key with no value that nobody requires is simply absent.
74
- const required = requiredRef.current.filter(req => requiredKeyAppliesTo(req, "secure"));
75
- const requiredFor = key => required.find(req => (typeof req === "string" ? req : req.key) === key);
76
- if (!module || entries.length === 0 && required.length === 0) {
77
- commit([]);
78
- hasLoadedRef.current = true;
79
- setIsLoading(false);
80
- return;
81
- }
82
-
83
- // Only the first read is a "loading" state; polls refresh in place so the
84
- // browser never flashes its spinner over data it already has.
85
- if (!hasLoadedRef.current) setIsLoading(true);
86
- setError(null);
87
- const keys = [];
88
-
89
- // Required keys that were never registered are still readable by name,
90
- // with the default keychain options.
91
- const toRead = [...entries, ...required.map(req => typeof req === "string" ? req : req.key).filter(key => !entries.some(e => e.key === key)).map(key => ({
92
- key,
93
- options: {},
94
- description: undefined
95
- }))];
96
- for (const entry of toRead) {
97
- const req = requiredFor(entry.key);
98
- const reqObj = typeof req === "object" ? req : undefined;
99
- const expectations = {
100
- ...(reqObj?.expectedValue !== undefined && {
101
- expectedValue: reqObj.expectedValue
102
- }),
103
- ...(reqObj?.expectedType !== undefined && {
104
- expectedType: reqObj.expectedType
105
- })
106
- };
107
- const description = reqObj?.description ?? entry.description;
108
- if (entry.options.requireAuthentication) {
109
- // Never auto-read: reading would fire a biometric prompt.
110
- keys.push({
111
- key: entry.key,
112
- value: BIOMETRIC_PLACEHOLDER,
113
- storageType: "secure",
114
- status: req ? "required_present" : "optional_present",
115
- category: req ? "required" : "optional",
116
- description: description ?? "Protected by biometric authentication — value not read by DevTools",
117
- ...expectations,
118
- ...(entry.options.keychainService && {
119
- instanceId: entry.options.keychainService
120
- })
121
- });
122
- continue;
123
- }
124
- let rawValue = null;
125
- try {
126
- rawValue = await module.getItemAsync(entry.key, entry.options);
127
- } catch {
128
- // Key unreadable (e.g. keychain state changed) — treat as absent.
129
- }
130
- if (rawValue === null && !req) continue;
131
-
132
- // Parse JSON values for the DataViewer, same as the AsyncStorage hook.
133
- let parsedValue = rawValue;
134
- if (rawValue) {
135
- try {
136
- parsedValue = JSON.parse(rawValue);
137
- } catch {
138
- parsedValue = rawValue;
139
- }
140
- }
141
- let status = "optional_present";
142
- if (req) {
143
- 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) {
144
- const actualType = parsedValue === null ? "null" : Array.isArray(parsedValue) ? "array" : typeof parsedValue;
145
- // A keychain value is always stored as a string, so a string is
146
- // acceptable wherever the parsed form would be.
147
- const want = reqObj.expectedType.toLowerCase();
148
- status = actualType === want || want === "string" && typeof rawValue === "string" ? "required_present" : "required_wrong_type";
149
- } else status = "required_present";
150
- }
151
- keys.push({
152
- key: entry.key,
153
- value: rawValue === null ? undefined : parsedValue,
154
- storageType: "secure",
155
- status,
156
- category: req ? "required" : "optional",
157
- ...(description && {
158
- description
159
- }),
160
- ...expectations,
161
- ...(entry.options.keychainService && {
162
- instanceId: entry.options.keychainService
163
- })
164
- });
165
- }
166
- commit(keys);
167
- } catch (err) {
168
- setError(err instanceof Error ? err : new Error("Failed to read SecureStore"));
169
- commit([]);
170
- } finally {
171
- hasLoadedRef.current = true;
172
- inFlightRef.current = false;
173
- setIsLoading(false);
174
- }
175
- // eslint-disable-next-line react-hooks/exhaustive-deps
176
- }, [commit, requiredSignature]);
177
-
178
- // Initial fetch + poll. Registration usually happens at app startup, but the
179
- // registry is also refilled on the dashboard every time a device syncs, so
180
- // subscribe as well rather than assuming the key list is final at mount.
181
- useEffect(() => {
182
- fetchSecureStoreData();
183
- const unsubscribe = secureStoreRegistry.subscribe(() => {
184
- fetchSecureStoreData();
185
- });
186
- if (pollMs <= 0) return unsubscribe;
187
- const interval = setInterval(fetchSecureStoreData, pollMs);
188
- return () => {
189
- unsubscribe();
190
- clearInterval(interval);
191
- };
192
- }, [fetchSecureStoreData, pollMs]);
193
- return {
194
- storageKeys,
195
- isLoading,
196
- error,
197
- refresh: fetchSecureStoreData
198
- };
199
- }
1
+ "use strict";import{useState as k,useEffect as M,useCallback as L,useRef as p}from"react";import{requiredKeyAppliesTo as J}from"../utils/requiredStorageKeyScope";import{secureStoreRegistry as _}from"../utils/SecureStoreRegistry";const N="\u2022\u2022\u2022\u2022\u2022\u2022 (biometric protected)";const P=3e3;function R(c){return JSON.stringify(c)}export function useSecureStoreKeys(c=[],A={}){const{pollMs:d=P}=A;const x=p(c);x.current=c;const E=JSON.stringify(c);const[I,w]=k([]);const[O,f]=k(true);const[V,v]=k(null);const T=p(R([]));const y=p(false);const g=p(false);const l=L(r=>{const o=R(r);if(o===T.current)return;T.current=o;w(r)},[]);const u=L(async()=>{if(g.current)return;g.current=true;try{const r=_.getModule();const o=_.getAll();const S=x.current.filter(e=>J(e,"secure"));const C=e=>S.find(t=>(typeof t==="string"?t:t.key)===e);if(!r||o.length===0&&S.length===0){l([]);y.current=true;f(false);return}if(!y.current)f(true);v(null);const h=[];const D=[...o,...S.map(e=>typeof e==="string"?e:e.key).filter(e=>!o.some(t=>t.key===e)).map(e=>({key:e,options:{},description:void 0}))];for(const e of D){const t=C(e.key);const n=typeof t==="object"?t:void 0;const b={...n?.expectedValue!==void 0&&{expectedValue:n.expectedValue},...n?.expectedType!==void 0&&{expectedType:n.expectedType}};const m=n?.description??e.description;if(e.options.requireAuthentication){h.push({key:e.key,value:N,storageType:"secure",status:t?"required_present":"optional_present",category:t?"required":"optional",description:m??"Protected by biometric authentication \u2014 value not read by DevTools",...b,...e.options.keychainService&&{instanceId:e.options.keychainService}});continue}let i=null;try{i=await r.getItemAsync(e.key,e.options)}catch{}if(i===null&&!t)continue;let s=i;if(i){try{s=JSON.parse(i)}catch{s=i}}let a="optional_present";if(t){if(i===null)a="required_missing";else if(n?.expectedValue!==void 0)a=String(s)===String(n.expectedValue)?"required_present":"required_wrong_value";else if(n?.expectedType!==void 0){const F=s===null?"null":Array.isArray(s)?"array":typeof s;const q=n.expectedType.toLowerCase();a=F===q||q==="string"&&typeof i==="string"?"required_present":"required_wrong_type"}else a="required_present"}h.push({key:e.key,value:i===null?void 0:s,storageType:"secure",status:a,category:t?"required":"optional",...m&&{description:m},...b,...e.options.keychainService&&{instanceId:e.options.keychainService}})}l(h)}catch(r){v(r instanceof Error?r:new Error("Failed to read SecureStore"));l([])}finally{y.current=true;g.current=false;f(false)}},[l,E]);M(()=>{u();const r=_.subscribe(()=>{u()});if(d<=0)return r;const o=setInterval(u,d);return()=>{r();clearInterval(o)}},[u,d]);return{storageKeys:I,isLoading:O,error:V,refresh:u}}
@@ -1,58 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * useStorageEvents Hook
5
- *
6
- * React hook for subscribing to storage events from the centralized store.
7
- * Uses self-managing Subscribable pattern - the storage listener automatically
8
- * starts when this hook mounts and stops when all subscribers unmount.
9
- *
10
- * @example
11
- * ```typescript
12
- * function MyComponent() {
13
- * const { events, clearEvents, isCapturing } = useStorageEvents();
14
- *
15
- * return (
16
- * <View>
17
- * {events.map((event) => (
18
- * <Text key={event.timestamp.toString()}>
19
- * {event.action}: {event.data?.key}
20
- * </Text>
21
- * ))}
22
- * </View>
23
- * );
24
- * }
25
- * ```
26
- */
27
-
28
- import { useMemo } from "react";
29
- import { useEventStoreEvents } from "@buoy-gg/shared-ui";
30
- import { storageEventStore } from "../stores/storageEventStore";
31
-
32
- // Re-export StorageEvent for convenience
33
-
34
- export function useStorageEvents(options = {}) {
35
- const {
36
- storageType = "all",
37
- maxEvents = 500,
38
- enabled = true
39
- } = options;
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
- });
53
- return {
54
- events,
55
- clearEvents,
56
- isCapturing: isActive
57
- };
58
- }
1
+ "use strict";import{useMemo as l}from"react";import{useEventStoreEvents as m}from"@buoy-gg/shared-ui";import{storageEventStore as v}from"../stores/storageEventStore";export function useStorageEvents(t={}){const{storageType:e="all",maxEvents:r=500,enabled:s=true}=t;const o=l(()=>e==="all"?void 0:u=>u.filter(c=>c.storageType===e),[e]);const{events:n,clearEvents:i,isActive:a}=m(v,{maxEvents:r,enabled:s,select:o});return{events:n,clearEvents:i,isCapturing:a}}
@@ -1,301 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * Storage Event Store
5
- *
6
- * Centralized store that aggregates storage events from AsyncStorage and MMKV
7
- * into a single event stream. Uses BaseEventStore pattern - the storage listener
8
- * automatically starts when the first subscriber joins and stops when the last
9
- * subscriber leaves.
10
- *
11
- * @example
12
- * ```typescript
13
- * import { storageEventStore } from '@buoy-gg/storage';
14
- *
15
- * // Subscribe to storage events - automatically starts capturing!
16
- * const unsubscribe = storageEventStore.subscribeToEvents((events) => {
17
- * console.log('Storage events:', events);
18
- * });
19
- *
20
- * // Later, clean up - automatically stops if no other subscribers
21
- * unsubscribe();
22
- * ```
23
- */
24
-
25
- import { BaseEventStore } from "@buoy-gg/shared-ui";
26
- import { startListening as startAsyncStorageListening, addListener as addAsyncStorageListener, isListening as isAsyncStorageListening } from "../utils/AsyncStorageListener";
27
- import { addMMKVListener } from "../utils/MMKVListener";
28
- import { mmkvInstanceRegistry } from "../utils/MMKVInstanceRegistry";
29
- import { detectMMKVType } from "../utils/mmkvTypeDetection";
30
-
31
- /**
32
- * Unified storage event type combining AsyncStorage and MMKV events.
33
- * `id` is assigned by the store when the event enters — needed because raw
34
- * events have no unique identifier (rapid same-key writes can share a
35
- * millisecond timestamp), and React keys must be unique per render.
36
- */
37
-
38
- /** True when a field is the wire-form VALUE_ON_DEVICE marker. */
39
- export function isValueOnDevice(value) {
40
- return !!value && typeof value === "object" && value.__buoyValueOnDevice === true;
41
- }
42
-
43
- /**
44
- * `pairs` / `prevPairs` are AsyncStorage-only — they carry the batch
45
- * operations (multiSet / multiRemove / multiMerge / clear), and MMKV has no
46
- * batch API, so its `data` has no such fields. Reading them off the
47
- * `StorageEvent` union needs narrowing, and this is the one place that does
48
- * it; writing them unconditionally would also graft meaningless keys onto
49
- * every MMKV event on the wire.
50
- */
51
- export function batchPairsOf(event) {
52
- if (event.storageType !== "async" || !event.data) return {};
53
- return {
54
- pairs: event.data.pairs,
55
- prevPairs: event.data.prevPairs
56
- };
57
- }
58
- function eventHasOnDeviceValues(event) {
59
- const data = event.data;
60
- if (!data) return false;
61
- const {
62
- pairs,
63
- prevPairs
64
- } = batchPairsOf(event);
65
- return isValueOnDevice(data.value) || isValueOnDevice(data.prevValue) || isValueOnDevice(pairs) || isValueOnDevice(prevPairs);
66
- }
67
- let storageEventIdCounter = 0;
68
- function nextStorageEventId() {
69
- return `se-${Date.now()}-${++storageEventIdCounter}`;
70
- }
71
-
72
- /**
73
- * Listener callback type for storage events array
74
- */
75
-
76
- /**
77
- * Listener callback for individual new events
78
- */
79
-
80
- class StorageEventStore extends BaseEventStore {
81
- // Unsubscribe functions for raw listeners
82
- asyncStorageUnsubscribe = null;
83
- mmkvUnsubscribe = null;
84
- hasScannedInitialState = false;
85
- constructor() {
86
- super({
87
- storeName: "storage",
88
- maxEvents: 500
89
- });
90
- }
91
-
92
- /**
93
- * Scan all registered MMKV instances and AsyncStorage for existing keys,
94
- * creating synthetic events so that getEvents() includes the current state
95
- * (not just changes made after capture started). Only runs once per store
96
- * lifetime to avoid duplicates.
97
- */
98
- async scanExistingState() {
99
- if (this.hasScannedInitialState) return;
100
- this.hasScannedInitialState = true;
101
- const now = new Date();
102
-
103
- // ── MMKV: synchronous scan ──
104
- const instances = mmkvInstanceRegistry.getAll();
105
- for (const {
106
- id: instanceId,
107
- instance
108
- } of instances) {
109
- try {
110
- const keys = instance.getAllKeys();
111
- for (const key of keys) {
112
- const {
113
- value,
114
- type: valueType
115
- } = detectMMKVType(instance, key);
116
- const actionMap = {
117
- string: "set.string",
118
- number: "set.number",
119
- boolean: "set.boolean",
120
- buffer: "set.buffer"
121
- };
122
- const action = actionMap[valueType] ?? "set.string";
123
- this.addEvent({
124
- action,
125
- timestamp: now,
126
- instanceId,
127
- data: {
128
- key,
129
- value,
130
- valueType,
131
- initialScan: true
132
- },
133
- storageType: "mmkv",
134
- id: nextStorageEventId()
135
- });
136
- }
137
- } catch {
138
- // Instance may not be accessible — skip silently
139
- }
140
- }
141
-
142
- // ── AsyncStorage: async scan ──
143
- try {
144
- const {
145
- asyncStorage,
146
- readMany
147
- } = require("../utils/asyncStorageCompat");
148
- if (asyncStorage) {
149
- const allKeys = await asyncStorage.getAllKeys();
150
- if (allKeys.length > 0) {
151
- const pairs = await readMany(allKeys);
152
- for (const [key, rawValue] of pairs) {
153
- this.addEvent({
154
- action: "setItem",
155
- timestamp: now,
156
- data: {
157
- key,
158
- value: rawValue ?? undefined,
159
- initialScan: true
160
- },
161
- storageType: "async",
162
- id: nextStorageEventId()
163
- });
164
- }
165
- }
166
- }
167
- } catch {
168
- // AsyncStorage not available — skip silently
169
- }
170
- }
171
-
172
- /**
173
- * Start capturing storage events from both AsyncStorage and MMKV
174
- */
175
- async startCapturing() {
176
- // Start AsyncStorage listening if not already active
177
- if (!isAsyncStorageListening()) {
178
- await startAsyncStorageListening();
179
- }
180
-
181
- // Subscribe to AsyncStorage events
182
- if (!this.asyncStorageUnsubscribe) {
183
- this.asyncStorageUnsubscribe = addAsyncStorageListener(event => {
184
- const storageEvent = {
185
- ...event,
186
- storageType: "async",
187
- id: nextStorageEventId()
188
- };
189
- this.addEvent(storageEvent);
190
- });
191
- }
192
-
193
- // Subscribe to MMKV events
194
- if (!this.mmkvUnsubscribe) {
195
- this.mmkvUnsubscribe = addMMKVListener(event => {
196
- const storageEvent = {
197
- ...event,
198
- storageType: "mmkv",
199
- id: nextStorageEventId()
200
- };
201
- this.addEvent(storageEvent);
202
- });
203
- }
204
-
205
- // Scan existing keys so getEvents() includes pre-existing state
206
- await this.scanExistingState();
207
- }
208
-
209
- /**
210
- * Stop capturing storage events
211
- */
212
- stopCapturing() {
213
- // Unsubscribe from AsyncStorage
214
- if (this.asyncStorageUnsubscribe) {
215
- this.asyncStorageUnsubscribe();
216
- this.asyncStorageUnsubscribe = null;
217
- }
218
-
219
- // Unsubscribe from MMKV
220
- if (this.mmkvUnsubscribe) {
221
- this.mmkvUnsubscribe();
222
- this.mmkvUnsubscribe = null;
223
- }
224
- }
225
-
226
- /**
227
- * Check if currently capturing events (has subscribers)
228
- */
229
- isCapturing() {
230
- return this.asyncStorageUnsubscribe !== null || this.mmkvUnsubscribe !== null;
231
- }
232
-
233
- /**
234
- * Get events filtered by storage type
235
- */
236
- getEventsByType(storageType) {
237
- return this.events.filter(event => event.storageType === storageType);
238
- }
239
-
240
- /**
241
- * Replace the event list, keeping values a remote mirror already hydrated.
242
- * Wire-form snapshots always carry VALUE_ON_DEVICE markers for oversized
243
- * payloads; a later snapshot must not blank the detail pane (or wipe
244
- * `prevValue` that time-travel undo needs).
245
- */
246
- replaceEvents(events) {
247
- const hydrated = new Map();
248
- for (const existing of this.events) {
249
- if (!eventHasOnDeviceValues(existing)) {
250
- hydrated.set(existing.id, existing);
251
- }
252
- }
253
- const merged = events.map(incoming => {
254
- const kept = hydrated.get(incoming.id);
255
- if (!kept || !eventHasOnDeviceValues(incoming)) return incoming;
256
- // Restore only the fields this event's storage type actually has, so an
257
- // MMKV event never grows `pairs`/`prevPairs` it has no meaning for.
258
- return {
259
- ...incoming,
260
- data: {
261
- ...incoming.data,
262
- value: kept.data?.value,
263
- prevValue: kept.data?.prevValue,
264
- ...batchPairsOf(kept)
265
- }
266
- };
267
- });
268
- super.replaceEvents(merged);
269
- }
270
-
271
- /**
272
- * Hydrate one mirrored event with real values fetched on demand from the
273
- * device via the adapter's `getEventDetail`. Remote-mirror (desktop) only.
274
- */
275
- hydrateEvent(event) {
276
- const index = this.events.findIndex(e => e.id === event.id);
277
- if (index === -1) return;
278
- const current = this.events[index];
279
- const next = this.events.slice();
280
- next[index] = {
281
- ...current,
282
- ...event,
283
- timestamp: current.timestamp,
284
- data: {
285
- ...current.data,
286
- ...event.data
287
- }
288
- };
289
- super.replaceEvents(next);
290
- }
291
- }
292
-
293
- // Singleton instance
294
- export const storageEventStore = new StorageEventStore();
295
-
296
- // Convenience exports
297
- export const subscribeToStorageEvents = listener => storageEventStore.subscribeToEvents(listener);
298
- export const onStorageEvent = callback => storageEventStore.onEvent(callback);
299
- export const getStorageEvents = () => storageEventStore.getEvents();
300
- export const clearStorageEvents = () => storageEventStore.clearEvents();
301
- export const isStorageCapturing = () => storageEventStore.isCapturing();
1
+ "use strict";import{BaseEventStore as l}from"@buoy-gg/shared-ui";import{startListening as v,addListener as m,isListening as y}from"../utils/AsyncStorageListener";import{addMMKVListener as b}from"../utils/MMKVListener";import{mmkvInstanceRegistry as f}from"../utils/MMKVInstanceRegistry";import{detectMMKVType as h}from"../utils/mmkvTypeDetection";export function isValueOnDevice(s){return!!s&&typeof s==="object"&&s.__buoyValueOnDevice===true}export function batchPairsOf(s){if(s.storageType!=="async"||!s.data)return{};return{pairs:s.data.pairs,prevPairs:s.data.prevPairs}}function d(s){const t=s.data;if(!t)return false;const{pairs:n,prevPairs:a}=batchPairsOf(s);return isValueOnDevice(t.value)||isValueOnDevice(t.prevValue)||isValueOnDevice(n)||isValueOnDevice(a)}let S=0;function c(){return`se-${Date.now()}-${++S}`}class E extends l{asyncStorageUnsubscribe=null;mmkvUnsubscribe=null;hasScannedInitialState=false;constructor(){super({storeName:"storage",maxEvents:500})}async scanExistingState(){if(this.hasScannedInitialState)return;this.hasScannedInitialState=true;const t=new Date;const n=f.getAll();for(const{id:a,instance:e}of n){try{const r=e.getAllKeys();for(const i of r){const{value:u,type:o}=h(e,i);const p={string:"set.string",number:"set.number",boolean:"set.boolean",buffer:"set.buffer"};const g=p[o]??"set.string";this.addEvent({action:g,timestamp:t,instanceId:a,data:{key:i,value:u,valueType:o,initialScan:true},storageType:"mmkv",id:c()})}}catch{}}try{const{asyncStorage:a,readMany:e}=require("../utils/asyncStorageCompat");if(a){const r=await a.getAllKeys();if(r.length>0){const i=await e(r);for(const[u,o]of i){this.addEvent({action:"setItem",timestamp:t,data:{key:u,value:o??void 0,initialScan:true},storageType:"async",id:c()})}}}}catch{}}async startCapturing(){if(!y()){await v()}if(!this.asyncStorageUnsubscribe){this.asyncStorageUnsubscribe=m(t=>{const n={...t,storageType:"async",id:c()};this.addEvent(n)})}if(!this.mmkvUnsubscribe){this.mmkvUnsubscribe=b(t=>{const n={...t,storageType:"mmkv",id:c()};this.addEvent(n)})}await this.scanExistingState()}stopCapturing(){if(this.asyncStorageUnsubscribe){this.asyncStorageUnsubscribe();this.asyncStorageUnsubscribe=null}if(this.mmkvUnsubscribe){this.mmkvUnsubscribe();this.mmkvUnsubscribe=null}}isCapturing(){return this.asyncStorageUnsubscribe!==null||this.mmkvUnsubscribe!==null}getEventsByType(t){return this.events.filter(n=>n.storageType===t)}replaceEvents(t){const n=new Map;for(const e of this.events){if(!d(e)){n.set(e.id,e)}}const a=t.map(e=>{const r=n.get(e.id);if(!r||!d(e))return e;return{...e,data:{...e.data,value:r.data?.value,prevValue:r.data?.prevValue,...batchPairsOf(r)}}});super.replaceEvents(a)}hydrateEvent(t){const n=this.events.findIndex(r=>r.id===t.id);if(n===-1)return;const a=this.events[n];const e=this.events.slice();e[n]={...a,...t,timestamp:a.timestamp,data:{...a.data,...t.data}};super.replaceEvents(e)}}export const storageEventStore=new E;export const subscribeToStorageEvents=s=>storageEventStore.subscribeToEvents(s);export const onStorageEvent=s=>storageEventStore.onEvent(s);export const getStorageEvents=()=>storageEventStore.getEvents();export const clearStorageEvents=()=>storageEventStore.clearEvents();export const isStorageCapturing=()=>storageEventStore.isCapturing();