@buoy-gg/storage 7.0.41 → 7.0.43

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 +6 -10
  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,486 +1 @@
1
- "use strict";
2
-
3
- import AsyncStorage from "@react-native-async-storage/async-storage";
4
- import { approxJsonSize, isDevToolsStorageKey, isOverWireBudget, isJsonSerializable } from "@buoy-gg/shared-ui";
5
- import { readMany, writeMany, removeMany } from "../utils/asyncStorageCompat";
6
- import { batchPairsOf, storageEventStore } from "../stores/storageEventStore";
7
- import { clearAllAppStorage } from "../utils/clearAllStorage";
8
- import { undoOperation, jumpToState, canUndo } from "../utils/storageTimeTravelUtils";
9
- import { mmkvInstanceRegistry } from "../utils/MMKVInstanceRegistry";
10
- import { detectMMKVType } from "../utils/mmkvTypeDetection";
11
- import { secureStoreRegistry } from "../utils/SecureStoreRegistry";
12
-
13
- /**
14
- * Sentinel used in WIRE-FORM snapshots in place of oversized values.
15
- * Storage events, MMKV browse dumps, and SecureStore dumps all reuse this
16
- * marker. Dashboards fetch the real payload via `getEventDetail` / `mmkv.get`
17
- * / `secure.get`.
18
- */
19
- export const VALUE_ON_DEVICE = Object.freeze({
20
- __buoyValueOnDevice: true,
21
- note: "Value stays on the device — fetched on demand via getEventDetail / mmkv.get / secure.get."
22
- });
23
-
24
- /** Max serialized bytes of one storage value on the list snapshot. */
25
- export const SNAPSHOT_VALUE_INLINE_LIMIT = 16 * 1024;
26
-
27
- /**
28
- * Total inline budget for one snapshot's event list, held under the emit
29
- * layer's 2MB so a snapshot is never DROPPED (a drop leaves the whole panel
30
- * stale). Per-value caps alone don't get there: the store keeps 500 events, so
31
- * 500 writes of an individually-legal 9KB value still total ~4.7MB. Newest
32
- * events keep their values inline; once the budget is spent, older ones
33
- * degrade to metadata + fetch-on-demand.
34
- */
35
- export const SNAPSHOT_EVENTS_BUDGET = 1.25 * 1024 * 1024;
36
- const MAX_EVENT_DETAIL_BYTES = 8 * 1024 * 1024;
37
- function toWireValue(value) {
38
- if (value === undefined) return value;
39
- return isOverWireBudget(value, SNAPSHOT_VALUE_INLINE_LIMIT) ? VALUE_ON_DEVICE : value;
40
- }
41
-
42
- /**
43
- * Apply `map` to every payload field an event carries, preserving object
44
- * identity when nothing changed (the desktop mirror feeds these into effect
45
- * dependencies, so a fresh object per snapshot would re-run them).
46
- *
47
- * The cast is deliberate and load-bearing: the wire form intentionally puts a
48
- * marker OBJECT where the domain type declares a string, because the real
49
- * value stayed on the device. `StorageEvent` describes on-device truth; this
50
- * is its wire projection.
51
- */
52
- function mapEventValues(event, map) {
53
- const data = event.data;
54
- if (!data) return event;
55
- const value = map(data.value);
56
- const prevValue = map(data.prevValue);
57
- const batch = batchPairsOf(event);
58
- const pairs = map(batch.pairs);
59
- const prevPairs = map(batch.prevPairs);
60
- if (value === data.value && prevValue === data.prevValue && pairs === batch.pairs && prevPairs === batch.prevPairs) {
61
- return event;
62
- }
63
- return {
64
- ...event,
65
- data: {
66
- ...data,
67
- value,
68
- prevValue,
69
- // Only AsyncStorage events have these; see batchPairsOf.
70
- ...(event.storageType === "async" ? {
71
- pairs,
72
- prevPairs
73
- } : {})
74
- }
75
- };
76
- }
77
- function capDetailValue(value) {
78
- if (value === undefined) return value;
79
- if (!isOverWireBudget(value, MAX_EVENT_DETAIL_BYTES)) return value;
80
- return isJsonSerializable(value) ? {
81
- __buoyTruncated: true,
82
- note: `Exceeds the ${MAX_EVENT_DETAIL_BYTES / (1024 * 1024)}MB detail limit — inspect it on-device.`
83
- } : VALUE_ON_DEVICE;
84
- }
85
-
86
- /** One event's wire form plus its measured cost, so the budget pass is free. */
87
-
88
- /**
89
- * Per-event wire cache. The store never mutates an event in place — `addEvent`
90
- * prepends and `hydrateEvent` replaces — so keying on identity is correct and
91
- * self-invalidating. Without it every value is re-measured on all ~5 snapshots
92
- * per second for as long as it stays in the list, which for storage means
93
- * re-walking megabytes of strings per tick.
94
- */
95
- const wireCache = new WeakMap();
96
- const strippedCache = new WeakMap();
97
- function wireFor(event) {
98
- const cached = wireCache.get(event);
99
- if (cached) return cached;
100
- let entry;
101
- try {
102
- const wire = mapEventValues(event, toWireValue);
103
- entry = {
104
- event: wire,
105
- bytes: approxJsonSize(wire, SNAPSHOT_EVENTS_BUDGET).bytes
106
- };
107
- } catch {
108
- // A hostile getter can blow up the walk itself. Degrading this ONE event
109
- // beats letting getSnapshot throw and taking the whole panel with it.
110
- const wire = mapEventValues(event, () => VALUE_ON_DEVICE);
111
- entry = {
112
- event: wire,
113
- bytes: 1024
114
- };
115
- }
116
- wireCache.set(event, entry);
117
- return entry;
118
- }
119
-
120
- /** Metadata-only form for the tail of the list once the budget is spent. */
121
- function strippedFor(event) {
122
- const cached = strippedCache.get(event);
123
- if (cached) return cached;
124
- const wire = mapEventValues(event, v => v === undefined ? undefined : VALUE_ON_DEVICE);
125
- const entry = {
126
- event: wire,
127
- bytes: approxJsonSize(wire, SNAPSHOT_EVENTS_BUDGET).bytes
128
- };
129
- strippedCache.set(event, entry);
130
- return entry;
131
- }
132
-
133
- /**
134
- * Spend the snapshot budget newest-first. The store keeps events newest-first,
135
- * so the developer keeps full detail on what they are actually looking at and
136
- * the tail degrades rather than the whole panel being dropped.
137
- */
138
- function fitEventsToBudget(events) {
139
- const out = new Array(events.length);
140
- let spent = 0;
141
- for (let i = 0; i < events.length; i++) {
142
- const full = wireFor(events[i]);
143
- if (spent + full.bytes <= SNAPSHOT_EVENTS_BUDGET) {
144
- out[i] = full.event;
145
- spent += full.bytes;
146
- } else {
147
- const lean = strippedFor(events[i]);
148
- out[i] = lean.event;
149
- spent += lean.bytes;
150
- }
151
- }
152
- return out;
153
- }
154
-
155
- /** One MMKV entry in a `mmkv.snapshot` result. */
156
-
157
- /** One MMKV instance in a `mmkv.snapshot` result. */
158
-
159
- /** One registered key in a `secure.snapshot` result. */
160
-
161
- /**
162
- * Resolve an MMKV instance for WRITING, refusing the ones the app registered
163
- * read-only.
164
- *
165
- * The registry has always tracked `readOnly`, but nothing enforced it: any sync
166
- * client — dashboard or MCP — could write to an instance the app explicitly
167
- * declared immutable. That was harmless while the tool could only read; it
168
- * stops being harmless the moment there's an edit UI pointed at it.
169
- */
170
- function assertMMKVWritable(instanceId) {
171
- const registered = mmkvInstanceRegistry.get(instanceId);
172
- if (!registered) {
173
- throw new Error(`No MMKV instance registered as "${instanceId}"`);
174
- }
175
- if (registered.instance?.isReadOnly) {
176
- throw new Error(`MMKV instance "${instanceId}" is registered read-only`);
177
- }
178
- return registered.instance;
179
- }
180
-
181
- /**
182
- * Sync adapter for the storage tool, consumed by @buoy-gg/external-sync's
183
- * `useExternalSync` (structurally matches its ToolSyncAdapter interface so
184
- * this package doesn't need a dependency on it).
185
- *
186
- * Subscribing starts the underlying capture lifecycle (including the initial
187
- * key scan), so storage events are only recorded while a dashboard is
188
- * watching.
189
- *
190
- * The `async.*` actions mirror the AsyncStorage API so the desktop dashboard
191
- * can proxy its browse/edit mode against the device's real storage.
192
- *
193
- * v2: oversized / unserializable event values are replaced with
194
- * VALUE_ON_DEVICE. Dashboards fetch them via `getEventDetail`.
195
- */
196
- let toolRequiredStorageKeys = [];
197
-
198
- /**
199
- * Called by `createStorageTool` so remote dashboards validate against the
200
- * same required keys as the on-device tool. The desktop panel used to have no
201
- * way to learn them and showed every key as unchecked.
202
- */
203
- export function setToolRequiredStorageKeys(keys) {
204
- toolRequiredStorageKeys = keys;
205
- }
206
- export const storageSyncAdapter = {
207
- // v3: a total snapshot budget, so a burst of individually-legal values
208
- // degrades the oldest events instead of the emit layer dropping the panel.
209
- version: 3,
210
- // Strip Buoy's OWN devtool keys (@react_buoy*, @buoy*, buoy-*, legacy) at the
211
- // source so no sync client (desktop, MCP, raw socket) ever sees them as if
212
- // they were app data — matching the tools' default key filters, but
213
- // authoritative instead of UI-only.
214
- getSnapshot: () => fitEventsToBudget(storageEventStore.getEvents().filter(e => {
215
- const key = e.data?.key;
216
- return !(typeof key === "string" && isDevToolsStorageKey(key));
217
- })),
218
- subscribe: onChange => storageEventStore.subscribeToEvents(onChange),
219
- actions: {
220
- clearEvents: () => {
221
- storageEventStore.clearEvents();
222
- },
223
- /** The app's required storage keys, as given to `createStorageTool`. */
224
- getRequiredKeys: () => toolRequiredStorageKeys,
225
- /**
226
- * On-demand detail: one event's real value/prevValue/pairs.
227
- * The per-snapshot stream stays payload-light; this is the explicit,
228
- * size-guarded channel for the detail pane (and for time-travel undo,
229
- * which needs `prevValue`).
230
- */
231
- getEventDetail: params => {
232
- const {
233
- id
234
- } = params ?? {};
235
- if (!id) return {
236
- found: false,
237
- reason: "missing id"
238
- };
239
- const event = storageEventStore.getEvents().find(e => e.id === id);
240
- if (!event) return {
241
- found: false,
242
- reason: "unknown id"
243
- };
244
- const detailed = mapEventValues(event, capDetailValue);
245
- return {
246
- found: true,
247
- id,
248
- event: detailed
249
- };
250
- },
251
- /** Clears all app storage keys, preserving dev tools settings. */
252
- clearAppStorage: () => clearAllAppStorage(),
253
- // ── Time travel (same code path as the modal's UNDO / JUMP buttons) ──
254
- // The UI swallows failures silently; these THROW so a caller (desktop, MCP)
255
- // gets the actual reason back instead of a no-op.
256
-
257
- /** Undo one event, addressed by the `id` the store assigned it. */
258
- "timeTravel.undo": async params => {
259
- const id = params?.id;
260
- if (!id) throw new Error("timeTravel.undo requires an event `id`");
261
- const event = storageEventStore.getEvents().find(e => e.id === id);
262
- if (!event) throw new Error(`No storage event with id "${id}"`);
263
- if (event.storageType === "mmkv") {
264
- throw new Error("Time travel supports AsyncStorage events only");
265
- }
266
- const asyncEvent = event;
267
- if (!canUndo(asyncEvent)) {
268
- throw new Error(`Event "${id}" (${asyncEvent.action}) has no previous value captured, so it cannot be undone`);
269
- }
270
- await undoOperation(asyncEvent);
271
- return {
272
- undone: true,
273
- action: asyncEvent.action,
274
- key: asyncEvent.data?.key
275
- };
276
- },
277
- /**
278
- * Jump storage back to the state as of one event. `scope: "key"` (default)
279
- * replays only that key's history, matching the modal's JUMP button;
280
- * `scope: "all"` replays every captured AsyncStorage event.
281
- */
282
- "timeTravel.jump": async params => {
283
- const {
284
- id,
285
- scope = "key"
286
- } = params ?? {};
287
- if (!id) throw new Error("timeTravel.jump requires an event `id`");
288
-
289
- // Oldest-first, which is the order jumpToState replays in.
290
- const all = storageEventStore.getEvents().filter(e => e.storageType !== "mmkv").slice().reverse();
291
- const target = all.find(e => e.id === id);
292
- if (!target) throw new Error(`No AsyncStorage event with id "${id}"`);
293
- const timeline = scope === "all" ? all : all.filter(e => e.data?.key === target.data?.key);
294
- const index = timeline.findIndex(e => e.id === id);
295
- if (index === -1) throw new Error(`Event "${id}" is not in the timeline`);
296
- await jumpToState(timeline, index);
297
- return {
298
- jumped: true,
299
- scope,
300
- replayed: index + 1,
301
- of: timeline.length,
302
- key: target.data?.key
303
- };
304
- },
305
- // ── Remote AsyncStorage proxy (desktop browse/edit mode) ──
306
- // Single-item methods share the same signature across async-storage v2/v3;
307
- // batch methods go through the compat helpers (which translate to v3's
308
- // getMany/setMany/removeMany) and keep the v2 tuple/pairs shape on the wire.
309
- // Read paths filter out Buoy's own devtool keys so they're never exposed.
310
- "async.getAllKeys": async () => (await AsyncStorage.getAllKeys()).filter(k => !isDevToolsStorageKey(k)),
311
- "async.multiGet": async params => (await readMany(params.keys)).filter(([k]) => !isDevToolsStorageKey(k)),
312
- "async.getItem": params => {
313
- const {
314
- key
315
- } = params;
316
- return isDevToolsStorageKey(key) ? Promise.resolve(null) : AsyncStorage.getItem(key);
317
- },
318
- "async.setItem": params => {
319
- const {
320
- key,
321
- value
322
- } = params;
323
- return AsyncStorage.setItem(key, value);
324
- },
325
- "async.removeItem": params => AsyncStorage.removeItem(params.key),
326
- "async.multiRemove": params => removeMany(params.keys),
327
- "async.multiSet": params => writeMany(params.pairs),
328
- "async.clear": () => AsyncStorage.clear(),
329
- // ── Remote MMKV proxy (desktop browse mode + MCP) ──
330
- // MMKV has no remote presence off-device; these read/write the registered
331
- // instances so sync clients can browse them like AsyncStorage.
332
- "mmkv.snapshot": () => mmkvInstanceRegistry.getAll().map(({
333
- id,
334
- instance,
335
- encrypted,
336
- readOnly
337
- }) => {
338
- let entries = [];
339
- try {
340
- entries = instance.getAllKeys().filter(key => !isDevToolsStorageKey(key)).map(key => {
341
- const {
342
- value,
343
- type: valueType
344
- } = detectMMKVType(instance, key);
345
- return {
346
- key,
347
- value: toWireValue(value),
348
- valueType
349
- };
350
- });
351
- } catch {
352
- // Instance unreadable — return it with no entries rather than failing
353
- }
354
- return {
355
- id,
356
- encrypted,
357
- readOnly,
358
- entries
359
- };
360
- }),
361
- /**
362
- * On-demand MMKV value. `mmkv.snapshot` omits oversized strings so a
363
- * browse poll cannot blow the action-result budget; this is the
364
- * size-guarded channel for one key.
365
- */
366
- "mmkv.get": params => {
367
- const {
368
- instanceId,
369
- key
370
- } = params ?? {};
371
- if (!instanceId || !key) {
372
- return {
373
- found: false,
374
- reason: "missing instanceId/key"
375
- };
376
- }
377
- const registered = mmkvInstanceRegistry.get(instanceId);
378
- if (!registered) {
379
- return {
380
- found: false,
381
- reason: "unknown instance"
382
- };
383
- }
384
- const {
385
- value,
386
- type: valueType
387
- } = detectMMKVType(registered.instance, key);
388
- return {
389
- found: true,
390
- instanceId,
391
- key,
392
- value: capDetailValue(value),
393
- valueType
394
- };
395
- },
396
- "mmkv.set": params => {
397
- const {
398
- instanceId,
399
- key,
400
- value
401
- } = params;
402
- assertMMKVWritable(instanceId).set(key, value);
403
- },
404
- "mmkv.remove": params => {
405
- const {
406
- instanceId,
407
- key
408
- } = params;
409
- const instance = assertMMKVWritable(instanceId);
410
- // v4 renamed delete() to remove() — support both
411
- if (instance.remove) instance.remove(key);else instance.delete?.(key);
412
- },
413
- // ── Remote SecureStore proxy ──
414
- // Only registered keys are known (SecureStore has no enumeration API).
415
- // Biometric-protected keys are never read — reads would fire an auth prompt.
416
- "secure.keys": () => secureStoreRegistry.getAll().map(({
417
- key,
418
- options,
419
- description
420
- }) => ({
421
- key,
422
- description,
423
- keychainService: options.keychainService,
424
- requireAuthentication: options.requireAuthentication ?? false
425
- })),
426
- /**
427
- * Every registered key WITH its value, in one round trip — the SecureStore
428
- * counterpart to `mmkv.snapshot`. The dashboard polls this (the keychain
429
- * has no change feed) and serves the browser from the result, instead of
430
- * firing one `secure.get` per key per refresh.
431
- */
432
- "secure.snapshot": async () => {
433
- const module = secureStoreRegistry.getModule();
434
- const entries = secureStoreRegistry.getAll();
435
- const result = [];
436
- for (const {
437
- key,
438
- options,
439
- description
440
- } of entries) {
441
- let value = null;
442
- // Biometric-protected keys are never read — that would fire an auth prompt.
443
- if (module && !options.requireAuthentication) {
444
- try {
445
- value = await module.getItemAsync(key, options);
446
- } catch {
447
- // Unreadable (e.g. keychain state changed) — report as unset
448
- }
449
- }
450
- result.push({
451
- key,
452
- description,
453
- keychainService: options.keychainService,
454
- requireAuthentication: options.requireAuthentication ?? false,
455
- value: value == null ? value : toWireValue(value)
456
- });
457
- }
458
- return result;
459
- },
460
- "secure.get": params => {
461
- const {
462
- key
463
- } = params;
464
- const entry = secureStoreRegistry.getAll().find(e => e.key === key);
465
- const module = secureStoreRegistry.getModule();
466
- if (!entry || !module || entry.options.requireAuthentication) {
467
- return Promise.resolve(null);
468
- }
469
- return module.getItemAsync(key, entry.options);
470
- },
471
- /**
472
- * Write a registered SecureStore key. Goes through the registry so the
473
- * value is written with the SAME options it was registered with — writing
474
- * with different `keychainService` options creates a second entry the app
475
- * can't read. Biometric-protected keys throw rather than prompting.
476
- */
477
- "secure.set": params => {
478
- const {
479
- key,
480
- value
481
- } = params;
482
- return secureStoreRegistry.setValue(key, value);
483
- },
484
- "secure.delete": params => secureStoreRegistry.deleteValue(params.key)
485
- }
486
- };
1
+ "use strict";import l from"@react-native-async-storage/async-storage";import{approxJsonSize as p,isDevToolsStorageKey as y,isOverWireBudget as v,isJsonSerializable as T}from"@buoy-gg/shared-ui";import{readMany as b,writeMany as V,removeMany as I}from"../utils/asyncStorageCompat";import{batchPairsOf as M,storageEventStore as u}from"../stores/storageEventStore";import{clearAllAppStorage as _}from"../utils/clearAllStorage";import{undoOperation as q,jumpToState as K,canUndo as N}from"../utils/storageTimeTravelUtils";import{mmkvInstanceRegistry as f}from"../utils/MMKVInstanceRegistry";import{detectMMKVType as g}from"../utils/mmkvTypeDetection";import{secureStoreRegistry as c}from"../utils/SecureStoreRegistry";export const VALUE_ON_DEVICE=Object.freeze({__buoyValueOnDevice:true,note:"Value stays on the device \u2014 fetched on demand via getEventDetail / mmkv.get / secure.get."});export const SNAPSHOT_VALUE_INLINE_LIMIT=16*1024;export const SNAPSHOT_EVENTS_BUDGET=1.25*1024*1024;const h=8*1024*1024;function m(e){if(e===void 0)return e;return v(e,SNAPSHOT_VALUE_INLINE_LIMIT)?VALUE_ON_DEVICE:e}function d(e,t){const r=e.data;if(!r)return e;const n=t(r.value);const s=t(r.prevValue);const o=M(e);const i=t(o.pairs);const a=t(o.prevPairs);if(n===r.value&&s===r.prevValue&&i===o.pairs&&a===o.prevPairs){return e}return{...e,data:{...r,value:n,prevValue:s,...e.storageType==="async"?{pairs:i,prevPairs:a}:{}}}}function k(e){if(e===void 0)return e;if(!v(e,h))return e;return T(e)?{__buoyTruncated:true,note:`Exceeds the ${h/(1024*1024)}MB detail limit \u2014 inspect it on-device.`}:VALUE_ON_DEVICE}const E=new WeakMap;const w=new WeakMap;function x(e){const t=E.get(e);if(t)return t;let r;try{const n=d(e,m);r={event:n,bytes:p(n,SNAPSHOT_EVENTS_BUDGET).bytes}}catch{const n=d(e,()=>VALUE_ON_DEVICE);r={event:n,bytes:1024}}E.set(e,r);return r}function O(e){const t=w.get(e);if(t)return t;const r=d(e,s=>s===void 0?void 0:VALUE_ON_DEVICE);const n={event:r,bytes:p(r,SNAPSHOT_EVENTS_BUDGET).bytes};w.set(e,n);return n}function D(e){const t=new Array(e.length);let r=0;for(let n=0;n<e.length;n++){const s=x(e[n]);if(r+s.bytes<=SNAPSHOT_EVENTS_BUDGET){t[n]=s.event;r+=s.bytes}else{const o=O(e[n]);t[n]=o.event;r+=o.bytes}}return t}function A(e){const t=f.get(e);if(!t){throw new Error(`No MMKV instance registered as "${e}"`)}if(t.instance?.isReadOnly){throw new Error(`MMKV instance "${e}" is registered read-only`)}return t.instance}let S=[];export function setToolRequiredStorageKeys(e){S=e}export const storageSyncAdapter={version:3,getSnapshot:()=>D(u.getEvents().filter(e=>{const t=e.data?.key;return!(typeof t==="string"&&y(t))})),subscribe:e=>u.subscribeToEvents(e),actions:{clearEvents:()=>{u.clearEvents()},getRequiredKeys:()=>S,getEventDetail:e=>{const{id:t}=e??{};if(!t)return{found:false,reason:"missing id"};const r=u.getEvents().find(s=>s.id===t);if(!r)return{found:false,reason:"unknown id"};const n=d(r,k);return{found:true,id:t,event:n}},clearAppStorage:()=>_(),"timeTravel.undo":async e=>{const t=e?.id;if(!t)throw new Error("timeTravel.undo requires an event `id`");const r=u.getEvents().find(s=>s.id===t);if(!r)throw new Error(`No storage event with id "${t}"`);if(r.storageType==="mmkv"){throw new Error("Time travel supports AsyncStorage events only")}const n=r;if(!N(n)){throw new Error(`Event "${t}" (${n.action}) has no previous value captured, so it cannot be undone`)}await q(n);return{undone:true,action:n.action,key:n.data?.key}},"timeTravel.jump":async e=>{const{id:t,scope:r="key"}=e??{};if(!t)throw new Error("timeTravel.jump requires an event `id`");const n=u.getEvents().filter(a=>a.storageType!=="mmkv").slice().reverse();const s=n.find(a=>a.id===t);if(!s)throw new Error(`No AsyncStorage event with id "${t}"`);const o=r==="all"?n:n.filter(a=>a.data?.key===s.data?.key);const i=o.findIndex(a=>a.id===t);if(i===-1)throw new Error(`Event "${t}" is not in the timeline`);await K(o,i);return{jumped:true,scope:r,replayed:i+1,of:o.length,key:s.data?.key}},"async.getAllKeys":async()=>(await l.getAllKeys()).filter(e=>!y(e)),"async.multiGet":async e=>(await b(e.keys)).filter(([t])=>!y(t)),"async.getItem":e=>{const{key:t}=e;return y(t)?Promise.resolve(null):l.getItem(t)},"async.setItem":e=>{const{key:t,value:r}=e;return l.setItem(t,r)},"async.removeItem":e=>l.removeItem(e.key),"async.multiRemove":e=>I(e.keys),"async.multiSet":e=>V(e.pairs),"async.clear":()=>l.clear(),"mmkv.snapshot":()=>f.getAll().map(({id:e,instance:t,encrypted:r,readOnly:n})=>{let s=[];try{s=t.getAllKeys().filter(o=>!y(o)).map(o=>{const{value:i,type:a}=g(t,o);return{key:o,value:m(i),valueType:a}})}catch{}return{id:e,encrypted:r,readOnly:n,entries:s}}),"mmkv.get":e=>{const{instanceId:t,key:r}=e??{};if(!t||!r){return{found:false,reason:"missing instanceId/key"}}const n=f.get(t);if(!n){return{found:false,reason:"unknown instance"}}const{value:s,type:o}=g(n.instance,r);return{found:true,instanceId:t,key:r,value:k(s),valueType:o}},"mmkv.set":e=>{const{instanceId:t,key:r,value:n}=e;A(t).set(r,n)},"mmkv.remove":e=>{const{instanceId:t,key:r}=e;const n=A(t);if(n.remove)n.remove(r);else n.delete?.(r)},"secure.keys":()=>c.getAll().map(({key:e,options:t,description:r})=>({key:e,description:r,keychainService:t.keychainService,requireAuthentication:t.requireAuthentication??false})),"secure.snapshot":async()=>{const e=c.getModule();const t=c.getAll();const r=[];for(const{key:n,options:s,description:o}of t){let i=null;if(e&&!s.requireAuthentication){try{i=await e.getItemAsync(n,s)}catch{}}r.push({key:n,description:o,keychainService:s.keychainService,requireAuthentication:s.requireAuthentication??false,value:i==null?i:m(i)})}return r},"secure.get":e=>{const{key:t}=e;const r=c.getAll().find(s=>s.key===t);const n=c.getModule();if(!r||!n||r.options.requireAuthentication){return Promise.resolve(null)}return n.getItemAsync(t,r.options)},"secure.set":e=>{const{key:t,value:r}=e;return c.setValue(t,r)},"secure.delete":e=>c.deleteValue(e.key)}};
@@ -1,3 +1 @@
1
- "use strict";
2
-
3
- export {};
1
+ "use strict";export{};