@buoy-gg/storage 3.0.1 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/commonjs/index.js +7 -0
- package/lib/commonjs/storage/components/GameUIStorageBrowser.js +25 -4
- package/lib/commonjs/storage/components/GameUIStorageStats.js +2 -2
- package/lib/commonjs/storage/components/SelectionActionBar.js +16 -3
- package/lib/commonjs/storage/components/StorageBrowserMode.js +6 -2
- package/lib/commonjs/storage/components/StorageEventDetailContent.js +2 -2
- package/lib/commonjs/storage/components/StorageKeyCard.js +5 -5
- package/lib/commonjs/storage/components/StorageKeyRow.js +97 -8
- package/lib/commonjs/storage/components/StorageKeySection.js +10 -4
- package/lib/commonjs/storage/components/StorageModalWithTabs.js +47 -1
- package/lib/commonjs/storage/hooks/useAsyncStorageKeys.js +15 -3
- package/lib/commonjs/storage/hooks/useMMKVKeys.js +20 -2
- package/lib/commonjs/storage/stores/storageEventStore.js +84 -0
- package/lib/commonjs/storage/sync/storageSyncAdapter.js +53 -0
- package/lib/commonjs/storage/utils/AsyncStorageListener.js +148 -160
- package/lib/commonjs/storage/utils/asyncStorageCompat.js +89 -0
- package/lib/commonjs/storage/utils/clearAllStorage.js +2 -1
- package/lib/commonjs/storage/utils/mmkvTypeDetection.js +20 -5
- package/lib/commonjs/storage/utils/storageTimeTravelUtils.js +3 -2
- package/lib/commonjs/storage/utils/valueType.js +41 -0
- package/lib/module/index.js +5 -0
- package/lib/module/storage/components/GameUIStorageBrowser.js +26 -4
- package/lib/module/storage/components/GameUIStorageStats.js +3 -2
- package/lib/module/storage/components/SelectionActionBar.js +17 -3
- package/lib/module/storage/components/StorageBrowserMode.js +6 -2
- package/lib/module/storage/components/StorageEventDetailContent.js +2 -2
- package/lib/module/storage/components/StorageKeyCard.js +5 -5
- package/lib/module/storage/components/StorageKeyRow.js +99 -10
- package/lib/module/storage/components/StorageKeySection.js +10 -4
- package/lib/module/storage/components/StorageModalWithTabs.js +47 -1
- package/lib/module/storage/hooks/useAsyncStorageKeys.js +16 -4
- package/lib/module/storage/hooks/useMMKVKeys.js +21 -3
- package/lib/module/storage/stores/storageEventStore.js +84 -0
- package/lib/module/storage/sync/storageSyncAdapter.js +48 -0
- package/lib/module/storage/utils/AsyncStorageListener.js +124 -135
- package/lib/module/storage/utils/asyncStorageCompat.js +81 -0
- package/lib/module/storage/utils/clearAllStorage.js +2 -1
- package/lib/module/storage/utils/mmkvTypeDetection.js +20 -5
- package/lib/module/storage/utils/storageTimeTravelUtils.js +3 -2
- package/lib/module/storage/utils/valueType.js +39 -0
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts +5 -1
- package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts.map +1 -1
- package/lib/typescript/storage/components/GameUIStorageStats.d.ts.map +1 -1
- package/lib/typescript/storage/components/SelectionActionBar.d.ts +3 -1
- package/lib/typescript/storage/components/SelectionActionBar.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageBrowserMode.d.ts +3 -1
- package/lib/typescript/storage/components/StorageBrowserMode.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageKeyRow.d.ts +7 -1
- package/lib/typescript/storage/components/StorageKeyRow.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageKeySection.d.ts +7 -1
- package/lib/typescript/storage/components/StorageKeySection.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageModalWithTabs.d.ts.map +1 -1
- package/lib/typescript/storage/hooks/useAsyncStorageKeys.d.ts.map +1 -1
- package/lib/typescript/storage/hooks/useMMKVKeys.d.ts.map +1 -1
- package/lib/typescript/storage/stores/storageEventStore.d.ts +8 -0
- package/lib/typescript/storage/stores/storageEventStore.d.ts.map +1 -1
- package/lib/typescript/storage/sync/storageSyncAdapter.d.ts +31 -0
- package/lib/typescript/storage/sync/storageSyncAdapter.d.ts.map +1 -0
- package/lib/typescript/storage/utils/AsyncStorageListener.d.ts +20 -0
- package/lib/typescript/storage/utils/AsyncStorageListener.d.ts.map +1 -1
- package/lib/typescript/storage/utils/asyncStorageCompat.d.ts +30 -0
- package/lib/typescript/storage/utils/asyncStorageCompat.d.ts.map +1 -0
- package/lib/typescript/storage/utils/clearAllStorage.d.ts.map +1 -1
- package/lib/typescript/storage/utils/mmkvTypeDetection.d.ts.map +1 -1
- package/lib/typescript/storage/utils/storageTimeTravelUtils.d.ts.map +1 -1
- package/lib/typescript/storage/utils/valueType.d.ts +13 -0
- package/lib/typescript/storage/utils/valueType.d.ts.map +1 -1
- package/package.json +6 -6
|
@@ -7,6 +7,8 @@ exports.subscribeToStorageEvents = exports.storageEventStore = exports.onStorage
|
|
|
7
7
|
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
8
8
|
var _AsyncStorageListener = require("../utils/AsyncStorageListener");
|
|
9
9
|
var _MMKVListener = require("../utils/MMKVListener");
|
|
10
|
+
var _MMKVInstanceRegistry = require("../utils/MMKVInstanceRegistry");
|
|
11
|
+
var _mmkvTypeDetection = require("../utils/mmkvTypeDetection");
|
|
10
12
|
/**
|
|
11
13
|
* Storage Event Store
|
|
12
14
|
*
|
|
@@ -53,6 +55,7 @@ class StorageEventStore extends _sharedUi.BaseEventStore {
|
|
|
53
55
|
// Unsubscribe functions for raw listeners
|
|
54
56
|
asyncStorageUnsubscribe = null;
|
|
55
57
|
mmkvUnsubscribe = null;
|
|
58
|
+
hasScannedInitialState = false;
|
|
56
59
|
constructor() {
|
|
57
60
|
super({
|
|
58
61
|
storeName: "storage",
|
|
@@ -60,6 +63,84 @@ class StorageEventStore extends _sharedUi.BaseEventStore {
|
|
|
60
63
|
});
|
|
61
64
|
}
|
|
62
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Scan all registered MMKV instances and AsyncStorage for existing keys,
|
|
68
|
+
* creating synthetic events so that getEvents() includes the current state
|
|
69
|
+
* (not just changes made after capture started). Only runs once per store
|
|
70
|
+
* lifetime to avoid duplicates.
|
|
71
|
+
*/
|
|
72
|
+
async scanExistingState() {
|
|
73
|
+
if (this.hasScannedInitialState) return;
|
|
74
|
+
this.hasScannedInitialState = true;
|
|
75
|
+
const now = new Date();
|
|
76
|
+
|
|
77
|
+
// ── MMKV: synchronous scan ──
|
|
78
|
+
const instances = _MMKVInstanceRegistry.mmkvInstanceRegistry.getAll();
|
|
79
|
+
for (const {
|
|
80
|
+
id: instanceId,
|
|
81
|
+
instance
|
|
82
|
+
} of instances) {
|
|
83
|
+
try {
|
|
84
|
+
const keys = instance.getAllKeys();
|
|
85
|
+
for (const key of keys) {
|
|
86
|
+
const {
|
|
87
|
+
value,
|
|
88
|
+
type: valueType
|
|
89
|
+
} = (0, _mmkvTypeDetection.detectMMKVType)(instance, key);
|
|
90
|
+
const actionMap = {
|
|
91
|
+
string: "set.string",
|
|
92
|
+
number: "set.number",
|
|
93
|
+
boolean: "set.boolean",
|
|
94
|
+
buffer: "set.buffer"
|
|
95
|
+
};
|
|
96
|
+
const action = actionMap[valueType] ?? "set.string";
|
|
97
|
+
this.addEvent({
|
|
98
|
+
action,
|
|
99
|
+
timestamp: now,
|
|
100
|
+
instanceId,
|
|
101
|
+
data: {
|
|
102
|
+
key,
|
|
103
|
+
value,
|
|
104
|
+
valueType
|
|
105
|
+
},
|
|
106
|
+
storageType: "mmkv",
|
|
107
|
+
id: nextStorageEventId()
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
// Instance may not be accessible — skip silently
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ── AsyncStorage: async scan ──
|
|
116
|
+
try {
|
|
117
|
+
const {
|
|
118
|
+
asyncStorage,
|
|
119
|
+
readMany
|
|
120
|
+
} = require("../utils/asyncStorageCompat");
|
|
121
|
+
if (asyncStorage) {
|
|
122
|
+
const allKeys = await asyncStorage.getAllKeys();
|
|
123
|
+
if (allKeys.length > 0) {
|
|
124
|
+
const pairs = await readMany(allKeys);
|
|
125
|
+
for (const [key, rawValue] of pairs) {
|
|
126
|
+
this.addEvent({
|
|
127
|
+
action: "setItem",
|
|
128
|
+
timestamp: now,
|
|
129
|
+
data: {
|
|
130
|
+
key,
|
|
131
|
+
value: rawValue ?? undefined
|
|
132
|
+
},
|
|
133
|
+
storageType: "async",
|
|
134
|
+
id: nextStorageEventId()
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
} catch {
|
|
140
|
+
// AsyncStorage not available — skip silently
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
63
144
|
/**
|
|
64
145
|
* Start capturing storage events from both AsyncStorage and MMKV
|
|
65
146
|
*/
|
|
@@ -92,6 +173,9 @@ class StorageEventStore extends _sharedUi.BaseEventStore {
|
|
|
92
173
|
this.addEvent(storageEvent);
|
|
93
174
|
});
|
|
94
175
|
}
|
|
176
|
+
|
|
177
|
+
// Scan existing keys so getEvents() includes pre-existing state
|
|
178
|
+
await this.scanExistingState();
|
|
95
179
|
}
|
|
96
180
|
|
|
97
181
|
/**
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.storageSyncAdapter = void 0;
|
|
7
|
+
var _asyncStorage = _interopRequireDefault(require("@react-native-async-storage/async-storage"));
|
|
8
|
+
var _asyncStorageCompat = require("../utils/asyncStorageCompat");
|
|
9
|
+
var _storageEventStore = require("../stores/storageEventStore");
|
|
10
|
+
var _clearAllStorage = require("../utils/clearAllStorage");
|
|
11
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
/**
|
|
13
|
+
* Sync adapter for the storage tool, consumed by @buoy-gg/external-sync's
|
|
14
|
+
* `useExternalSync` (structurally matches its ToolSyncAdapter interface so
|
|
15
|
+
* this package doesn't need a dependency on it).
|
|
16
|
+
*
|
|
17
|
+
* Subscribing starts the underlying capture lifecycle (including the initial
|
|
18
|
+
* key scan), so storage events are only recorded while a dashboard is
|
|
19
|
+
* watching.
|
|
20
|
+
*
|
|
21
|
+
* The `async.*` actions mirror the AsyncStorage API so the desktop dashboard
|
|
22
|
+
* can proxy its browse/edit mode against the device's real storage.
|
|
23
|
+
*/
|
|
24
|
+
const storageSyncAdapter = exports.storageSyncAdapter = {
|
|
25
|
+
version: 1,
|
|
26
|
+
getSnapshot: () => _storageEventStore.storageEventStore.getEvents(),
|
|
27
|
+
subscribe: onChange => _storageEventStore.storageEventStore.subscribeToEvents(onChange),
|
|
28
|
+
actions: {
|
|
29
|
+
clearEvents: () => {
|
|
30
|
+
_storageEventStore.storageEventStore.clearEvents();
|
|
31
|
+
},
|
|
32
|
+
/** Clears all app storage keys, preserving dev tools settings. */
|
|
33
|
+
clearAppStorage: () => (0, _clearAllStorage.clearAllAppStorage)(),
|
|
34
|
+
// ── Remote AsyncStorage proxy (desktop browse/edit mode) ──
|
|
35
|
+
// Single-item methods share the same signature across async-storage v2/v3;
|
|
36
|
+
// batch methods go through the compat helpers (which translate to v3's
|
|
37
|
+
// getMany/setMany/removeMany) and keep the v2 tuple/pairs shape on the wire.
|
|
38
|
+
"async.getAllKeys": () => _asyncStorage.default.getAllKeys(),
|
|
39
|
+
"async.multiGet": params => (0, _asyncStorageCompat.readMany)(params.keys),
|
|
40
|
+
"async.getItem": params => _asyncStorage.default.getItem(params.key),
|
|
41
|
+
"async.setItem": params => {
|
|
42
|
+
const {
|
|
43
|
+
key,
|
|
44
|
+
value
|
|
45
|
+
} = params;
|
|
46
|
+
return _asyncStorage.default.setItem(key, value);
|
|
47
|
+
},
|
|
48
|
+
"async.removeItem": params => _asyncStorage.default.removeItem(params.key),
|
|
49
|
+
"async.multiRemove": params => (0, _asyncStorageCompat.removeMany)(params.keys),
|
|
50
|
+
"async.multiSet": params => (0, _asyncStorageCompat.writeMany)(params.pairs),
|
|
51
|
+
"async.clear": () => _asyncStorage.default.clear()
|
|
52
|
+
}
|
|
53
|
+
};
|
|
@@ -4,10 +4,11 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.stopListening = exports.startListening = exports.resumeCapture = exports.removeAllListeners = exports.pauseCapture = exports.isPaused = exports.isListening = exports.getListenerCount = exports.default = exports.addListener = void 0;
|
|
7
|
-
var
|
|
8
|
-
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
+
var _asyncStorageCompat = require("./asyncStorageCompat");
|
|
9
8
|
// AsyncStorage method signatures - matching the actual AsyncStorage API
|
|
10
9
|
|
|
10
|
+
// v3-only batch methods (replace multiSet/multiRemove)
|
|
11
|
+
|
|
11
12
|
// Event types for AsyncStorage operations
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -18,6 +19,12 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
18
19
|
* and emits events to registered listeners. It maintains the original functionality
|
|
19
20
|
* while providing observability for debugging and development tools.
|
|
20
21
|
*
|
|
22
|
+
* Supports both async-storage v2 (multiGet/multiSet/multiRemove/mergeItem) and
|
|
23
|
+
* v3 (getMany/setMany/removeMany, no merge). On v3 the renamed batch methods are
|
|
24
|
+
* swizzled and normalized back to the same v2-shaped events, so the rest of the
|
|
25
|
+
* tool — event store, time-travel, desktop sync — is version-agnostic. v3 dropped
|
|
26
|
+
* the merge API entirely, so no merge events can occur there.
|
|
27
|
+
*
|
|
21
28
|
* @example
|
|
22
29
|
* ```typescript
|
|
23
30
|
* // Start listening to all AsyncStorage operations
|
|
@@ -58,9 +65,13 @@ class AsyncStorageListener {
|
|
|
58
65
|
originalRemoveItem = null;
|
|
59
66
|
originalMergeItem = null;
|
|
60
67
|
originalClear = null;
|
|
68
|
+
// v2 batch originals
|
|
61
69
|
originalMultiSet = null;
|
|
62
70
|
originalMultiRemove = null;
|
|
63
71
|
originalMultiMerge = null;
|
|
72
|
+
// v3 batch originals
|
|
73
|
+
originalSetMany = null;
|
|
74
|
+
originalRemoveMany = null;
|
|
64
75
|
|
|
65
76
|
/**
|
|
66
77
|
* Determines if a storage key should be ignored to prevent infinite loops
|
|
@@ -99,19 +110,27 @@ class AsyncStorageListener {
|
|
|
99
110
|
}
|
|
100
111
|
|
|
101
112
|
// Check if methods are already swizzled by checking the function name
|
|
102
|
-
if (
|
|
113
|
+
if (_asyncStorageCompat.asyncStorage.setItem.name === "swizzled_setItem") {
|
|
103
114
|
// Don't store swizzled methods as originals
|
|
104
115
|
return false;
|
|
105
116
|
}
|
|
106
117
|
|
|
107
|
-
// Store original methods (these should be the real AsyncStorage methods)
|
|
108
|
-
|
|
109
|
-
this.
|
|
110
|
-
this.
|
|
111
|
-
this.originalClear =
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
118
|
+
// Store original methods (these should be the real AsyncStorage methods).
|
|
119
|
+
// Single-item + clear are identical across v2/v3.
|
|
120
|
+
this.originalSetItem = _asyncStorageCompat.asyncStorage.setItem.bind(_asyncStorageCompat.asyncStorage);
|
|
121
|
+
this.originalRemoveItem = _asyncStorageCompat.asyncStorage.removeItem.bind(_asyncStorageCompat.asyncStorage);
|
|
122
|
+
this.originalClear = _asyncStorageCompat.asyncStorage.clear.bind(_asyncStorageCompat.asyncStorage);
|
|
123
|
+
if (_asyncStorageCompat.asyncStorageCaps.hasLegacyMultiApi) {
|
|
124
|
+
// v2: multiSet/multiRemove + merge API
|
|
125
|
+
this.originalMultiSet = _asyncStorageCompat.asyncStorage.multiSet.bind(_asyncStorageCompat.asyncStorage);
|
|
126
|
+
this.originalMultiRemove = _asyncStorageCompat.asyncStorage.multiRemove.bind(_asyncStorageCompat.asyncStorage);
|
|
127
|
+
this.originalMergeItem = _asyncStorageCompat.asyncStorage.mergeItem.bind(_asyncStorageCompat.asyncStorage);
|
|
128
|
+
this.originalMultiMerge = _asyncStorageCompat.asyncStorage.multiMerge ? _asyncStorageCompat.asyncStorage.multiMerge.bind(_asyncStorageCompat.asyncStorage) : null;
|
|
129
|
+
} else {
|
|
130
|
+
// v3: setMany/removeMany; merge API was removed (nothing to capture)
|
|
131
|
+
this.originalSetMany = _asyncStorageCompat.asyncStorage.setMany.bind(_asyncStorageCompat.asyncStorage);
|
|
132
|
+
this.originalRemoveMany = _asyncStorageCompat.asyncStorage.removeMany.bind(_asyncStorageCompat.asyncStorage);
|
|
133
|
+
}
|
|
115
134
|
|
|
116
135
|
// Original methods stored successfully
|
|
117
136
|
this.isInitialized = true;
|
|
@@ -125,27 +144,35 @@ class AsyncStorageListener {
|
|
|
125
144
|
* AsyncStorage methods that were saved during initialization.
|
|
126
145
|
*/
|
|
127
146
|
restoreOriginalMethods() {
|
|
128
|
-
if (!
|
|
147
|
+
if (!_asyncStorageCompat.asyncStorage || !this.originalSetItem) {
|
|
129
148
|
return;
|
|
130
149
|
}
|
|
131
|
-
|
|
150
|
+
_asyncStorageCompat.asyncStorage.setItem = this.originalSetItem;
|
|
132
151
|
if (this.originalRemoveItem) {
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
if (this.originalMergeItem) {
|
|
136
|
-
_asyncStorage.default.mergeItem = this.originalMergeItem;
|
|
152
|
+
_asyncStorageCompat.asyncStorage.removeItem = this.originalRemoveItem;
|
|
137
153
|
}
|
|
138
154
|
if (this.originalClear) {
|
|
139
|
-
|
|
155
|
+
_asyncStorageCompat.asyncStorage.clear = this.originalClear;
|
|
156
|
+
}
|
|
157
|
+
// v2 batch + merge
|
|
158
|
+
if (this.originalMergeItem) {
|
|
159
|
+
_asyncStorageCompat.asyncStorage.mergeItem = this.originalMergeItem;
|
|
140
160
|
}
|
|
141
161
|
if (this.originalMultiSet) {
|
|
142
|
-
|
|
162
|
+
_asyncStorageCompat.asyncStorage.multiSet = this.originalMultiSet;
|
|
143
163
|
}
|
|
144
164
|
if (this.originalMultiRemove) {
|
|
145
|
-
|
|
165
|
+
_asyncStorageCompat.asyncStorage.multiRemove = this.originalMultiRemove;
|
|
146
166
|
}
|
|
147
167
|
if (this.originalMultiMerge) {
|
|
148
|
-
|
|
168
|
+
_asyncStorageCompat.asyncStorage.multiMerge = this.originalMultiMerge;
|
|
169
|
+
}
|
|
170
|
+
// v3 batch
|
|
171
|
+
if (this.originalSetMany) {
|
|
172
|
+
_asyncStorageCompat.asyncStorage.setMany = this.originalSetMany;
|
|
173
|
+
}
|
|
174
|
+
if (this.originalRemoveMany) {
|
|
175
|
+
_asyncStorageCompat.asyncStorage.removeMany = this.originalRemoveMany;
|
|
149
176
|
}
|
|
150
177
|
}
|
|
151
178
|
|
|
@@ -175,6 +202,77 @@ class AsyncStorageListener {
|
|
|
175
202
|
});
|
|
176
203
|
}
|
|
177
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Shared core for batch writes (v2 multiSet/multiMerge, v3 setMany).
|
|
207
|
+
*
|
|
208
|
+
* Captures previous values for the affected keys, runs the original native
|
|
209
|
+
* operation, and emits a normalized event. `action` distinguishes a plain set
|
|
210
|
+
* from a merge so time-travel can replay them differently.
|
|
211
|
+
*/
|
|
212
|
+
async handleBatchSet(pairs, runOriginal, action = "multiSet") {
|
|
213
|
+
// Filter out ignored keys
|
|
214
|
+
const filteredPairs = pairs.filter(([key]) => !this.shouldIgnoreKey(key));
|
|
215
|
+
if (filteredPairs.length === 0) {
|
|
216
|
+
return runOriginal();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Capture previous values for all keys being written
|
|
220
|
+
const keysToSet = filteredPairs.map(([key]) => key);
|
|
221
|
+
let prevPairs = [];
|
|
222
|
+
try {
|
|
223
|
+
prevPairs = await (0, _asyncStorageCompat.readMany)(keysToSet);
|
|
224
|
+
} catch {
|
|
225
|
+
// Failed to capture previous values
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Execute the operation
|
|
229
|
+
const result = await runOriginal();
|
|
230
|
+
|
|
231
|
+
// Emit event with previous values
|
|
232
|
+
this.emit({
|
|
233
|
+
action,
|
|
234
|
+
timestamp: new Date(),
|
|
235
|
+
data: {
|
|
236
|
+
pairs: filteredPairs,
|
|
237
|
+
prevPairs
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
return result;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Shared core for batch removals (v2 multiRemove, v3 removeMany).
|
|
245
|
+
*/
|
|
246
|
+
async handleBatchRemove(keys, runOriginal) {
|
|
247
|
+
// Filter out ignored keys
|
|
248
|
+
const filteredKeys = keys.filter(key => !this.shouldIgnoreKey(key));
|
|
249
|
+
if (filteredKeys.length === 0) {
|
|
250
|
+
return runOriginal();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Capture previous values for all keys being removed
|
|
254
|
+
let prevPairs = [];
|
|
255
|
+
try {
|
|
256
|
+
prevPairs = await (0, _asyncStorageCompat.readMany)(filteredKeys);
|
|
257
|
+
} catch {
|
|
258
|
+
// Failed to capture previous values
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Execute the operation
|
|
262
|
+
const result = await runOriginal();
|
|
263
|
+
|
|
264
|
+
// Emit event with previous values
|
|
265
|
+
this.emit({
|
|
266
|
+
action: "multiRemove",
|
|
267
|
+
timestamp: new Date(),
|
|
268
|
+
data: {
|
|
269
|
+
keys: filteredKeys,
|
|
270
|
+
prevPairs
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
return result;
|
|
274
|
+
}
|
|
275
|
+
|
|
178
276
|
/**
|
|
179
277
|
* Start intercepting AsyncStorage operations by swizzling methods
|
|
180
278
|
*
|
|
@@ -196,7 +294,7 @@ class AsyncStorageListener {
|
|
|
196
294
|
}
|
|
197
295
|
|
|
198
296
|
// Check if methods are already swizzled (this can happen if initialize was called twice somehow)
|
|
199
|
-
if (
|
|
297
|
+
if (_asyncStorageCompat.asyncStorage && _asyncStorageCompat.asyncStorage.setItem.name === "swizzled_setItem") {
|
|
200
298
|
this.restoreOriginalMethods();
|
|
201
299
|
}
|
|
202
300
|
|
|
@@ -207,7 +305,7 @@ class AsyncStorageListener {
|
|
|
207
305
|
// Only capture and emit if key is not ignored
|
|
208
306
|
if (!this.shouldIgnoreKey(key)) {
|
|
209
307
|
// Capture previous value before the operation
|
|
210
|
-
const prevValue = this.originalSetItem ? await
|
|
308
|
+
const prevValue = this.originalSetItem ? await _asyncStorageCompat.asyncStorage.getItem(key) : null;
|
|
211
309
|
|
|
212
310
|
// Execute the operation
|
|
213
311
|
const result = this.originalSetItem ? await this.originalSetItem(key, value) : undefined;
|
|
@@ -229,17 +327,17 @@ class AsyncStorageListener {
|
|
|
229
327
|
Object.defineProperty(swizzled_setItem, "name", {
|
|
230
328
|
value: "swizzled_setItem"
|
|
231
329
|
});
|
|
232
|
-
if (
|
|
233
|
-
|
|
330
|
+
if (_asyncStorageCompat.asyncStorage) {
|
|
331
|
+
_asyncStorageCompat.asyncStorage.setItem = swizzled_setItem;
|
|
234
332
|
}
|
|
235
333
|
|
|
236
334
|
// Swizzle removeItem
|
|
237
|
-
if (
|
|
238
|
-
|
|
335
|
+
if (_asyncStorageCompat.asyncStorage) {
|
|
336
|
+
_asyncStorageCompat.asyncStorage.removeItem = async key => {
|
|
239
337
|
// Only capture and emit if key is not ignored
|
|
240
338
|
if (!this.shouldIgnoreKey(key)) {
|
|
241
339
|
// Capture previous value before the operation
|
|
242
|
-
const prevValue = this.originalRemoveItem ? await
|
|
340
|
+
const prevValue = this.originalRemoveItem ? await _asyncStorageCompat.asyncStorage.getItem(key) : null;
|
|
243
341
|
|
|
244
342
|
// Execute the operation
|
|
245
343
|
const result = this.originalRemoveItem ? await this.originalRemoveItem(key) : undefined;
|
|
@@ -259,13 +357,13 @@ class AsyncStorageListener {
|
|
|
259
357
|
};
|
|
260
358
|
}
|
|
261
359
|
|
|
262
|
-
// Swizzle mergeItem
|
|
263
|
-
if (
|
|
264
|
-
|
|
360
|
+
// Swizzle mergeItem (v2 only — v3 removed the merge API)
|
|
361
|
+
if (_asyncStorageCompat.asyncStorageCaps.hasMergeApi && _asyncStorageCompat.asyncStorage) {
|
|
362
|
+
_asyncStorageCompat.asyncStorage.mergeItem = async (key, value) => {
|
|
265
363
|
// Only capture and emit if key is not ignored
|
|
266
364
|
if (!this.shouldIgnoreKey(key)) {
|
|
267
365
|
// Capture previous value before the operation
|
|
268
|
-
const prevValue = this.originalMergeItem ? await
|
|
366
|
+
const prevValue = this.originalMergeItem ? await _asyncStorageCompat.asyncStorage.getItem(key) : null;
|
|
269
367
|
|
|
270
368
|
// Execute the operation
|
|
271
369
|
const result = this.originalMergeItem ? await this.originalMergeItem(key, value) : undefined;
|
|
@@ -287,17 +385,16 @@ class AsyncStorageListener {
|
|
|
287
385
|
}
|
|
288
386
|
|
|
289
387
|
// Swizzle clear
|
|
290
|
-
if (
|
|
291
|
-
|
|
388
|
+
if (_asyncStorageCompat.asyncStorage) {
|
|
389
|
+
_asyncStorageCompat.asyncStorage.clear = async () => {
|
|
292
390
|
// Capture all key-value pairs before clearing
|
|
293
391
|
let prevPairs = [];
|
|
294
392
|
try {
|
|
295
|
-
const allKeys = await
|
|
393
|
+
const allKeys = await _asyncStorageCompat.asyncStorage.getAllKeys();
|
|
296
394
|
// Filter out ignored keys
|
|
297
395
|
const keysToCapture = allKeys.filter(key => !this.shouldIgnoreKey(key));
|
|
298
396
|
if (keysToCapture.length > 0) {
|
|
299
|
-
|
|
300
|
-
prevPairs = keyValuePairs;
|
|
397
|
+
prevPairs = await (0, _asyncStorageCompat.readMany)(keysToCapture);
|
|
301
398
|
}
|
|
302
399
|
} catch {
|
|
303
400
|
// Failed to capture previous state
|
|
@@ -318,105 +415,18 @@ class AsyncStorageListener {
|
|
|
318
415
|
};
|
|
319
416
|
}
|
|
320
417
|
|
|
321
|
-
// Swizzle
|
|
322
|
-
if (
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
} catch {
|
|
334
|
-
// Failed to capture previous values
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// Execute the operation
|
|
338
|
-
const result = this.originalMultiSet ? await this.originalMultiSet(keyValuePairs) : undefined;
|
|
339
|
-
|
|
340
|
-
// Emit event with previous values
|
|
341
|
-
this.emit({
|
|
342
|
-
action: "multiSet",
|
|
343
|
-
timestamp: new Date(),
|
|
344
|
-
data: {
|
|
345
|
-
pairs: filteredPairs,
|
|
346
|
-
prevPairs
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
return result;
|
|
350
|
-
}
|
|
351
|
-
return this.originalMultiSet ? this.originalMultiSet(keyValuePairs) : Promise.resolve();
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// Swizzle multiRemove
|
|
356
|
-
if (_asyncStorage.default) {
|
|
357
|
-
_asyncStorage.default.multiRemove = async keys => {
|
|
358
|
-
// Filter out ignored keys
|
|
359
|
-
const filteredKeys = keys.filter(key => !this.shouldIgnoreKey(key));
|
|
360
|
-
if (filteredKeys.length > 0) {
|
|
361
|
-
// Capture previous values for all keys being removed
|
|
362
|
-
let prevPairs = [];
|
|
363
|
-
try {
|
|
364
|
-
const existingValues = await _asyncStorage.default.multiGet(filteredKeys);
|
|
365
|
-
prevPairs = existingValues;
|
|
366
|
-
} catch {
|
|
367
|
-
// Failed to capture previous values
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
// Execute the operation
|
|
371
|
-
const result = this.originalMultiRemove ? await this.originalMultiRemove(keys) : undefined;
|
|
372
|
-
|
|
373
|
-
// Emit event with previous values
|
|
374
|
-
this.emit({
|
|
375
|
-
action: "multiRemove",
|
|
376
|
-
timestamp: new Date(),
|
|
377
|
-
data: {
|
|
378
|
-
keys: filteredKeys,
|
|
379
|
-
prevPairs
|
|
380
|
-
}
|
|
381
|
-
});
|
|
382
|
-
return result;
|
|
383
|
-
}
|
|
384
|
-
return this.originalMultiRemove ? this.originalMultiRemove(keys) : Promise.resolve();
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// Swizzle multiMerge if available
|
|
389
|
-
if (this.originalMultiMerge && _asyncStorage.default) {
|
|
390
|
-
_asyncStorage.default.multiMerge = async keyValuePairs => {
|
|
391
|
-
// Filter out ignored keys
|
|
392
|
-
const filteredPairs = keyValuePairs.filter(([key]) => !this.shouldIgnoreKey(key));
|
|
393
|
-
if (filteredPairs.length > 0) {
|
|
394
|
-
// Capture previous values for all keys being merged
|
|
395
|
-
const keysToMerge = filteredPairs.map(([key]) => key);
|
|
396
|
-
let prevPairs = [];
|
|
397
|
-
try {
|
|
398
|
-
const existingValues = await _asyncStorage.default.multiGet(keysToMerge);
|
|
399
|
-
prevPairs = existingValues;
|
|
400
|
-
} catch {
|
|
401
|
-
// Failed to capture previous values
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// Execute the operation
|
|
405
|
-
const result = this.originalMultiMerge ? await this.originalMultiMerge(keyValuePairs) : undefined;
|
|
406
|
-
|
|
407
|
-
// Emit event with previous values
|
|
408
|
-
this.emit({
|
|
409
|
-
action: "multiMerge",
|
|
410
|
-
timestamp: new Date(),
|
|
411
|
-
data: {
|
|
412
|
-
pairs: filteredPairs,
|
|
413
|
-
prevPairs
|
|
414
|
-
}
|
|
415
|
-
});
|
|
416
|
-
return result;
|
|
417
|
-
}
|
|
418
|
-
return this.originalMultiMerge ? this.originalMultiMerge(keyValuePairs) : Promise.resolve();
|
|
419
|
-
};
|
|
418
|
+
// Swizzle the batch methods for whichever API the installed version exposes.
|
|
419
|
+
if (_asyncStorageCompat.asyncStorageCaps.hasLegacyMultiApi && _asyncStorageCompat.asyncStorage) {
|
|
420
|
+
// ── v2: multiSet / multiRemove / multiMerge ──
|
|
421
|
+
_asyncStorageCompat.asyncStorage.multiSet = keyValuePairs => this.handleBatchSet(keyValuePairs, () => this.originalMultiSet ? this.originalMultiSet(keyValuePairs) : Promise.resolve());
|
|
422
|
+
_asyncStorageCompat.asyncStorage.multiRemove = keys => this.handleBatchRemove(keys, () => this.originalMultiRemove ? this.originalMultiRemove(keys) : Promise.resolve());
|
|
423
|
+
if (this.originalMultiMerge) {
|
|
424
|
+
_asyncStorageCompat.asyncStorage.multiMerge = keyValuePairs => this.handleBatchSet(keyValuePairs, () => this.originalMultiMerge ? this.originalMultiMerge(keyValuePairs) : Promise.resolve(), "multiMerge");
|
|
425
|
+
}
|
|
426
|
+
} else if (_asyncStorageCompat.asyncStorage) {
|
|
427
|
+
// ── v3: setMany / removeMany (normalized back to multiSet/multiRemove events) ──
|
|
428
|
+
_asyncStorageCompat.asyncStorage.setMany = entries => this.handleBatchSet(Object.entries(entries), () => this.originalSetMany ? this.originalSetMany(entries) : Promise.resolve());
|
|
429
|
+
_asyncStorageCompat.asyncStorage.removeMany = keys => this.handleBatchRemove(keys, () => this.originalRemoveMany ? this.originalRemoveMany(keys) : Promise.resolve());
|
|
420
430
|
}
|
|
421
431
|
this.isListening = true;
|
|
422
432
|
// Started listening successfully
|
|
@@ -432,34 +442,12 @@ class AsyncStorageListener {
|
|
|
432
442
|
if (!this.isListening) {
|
|
433
443
|
return;
|
|
434
444
|
}
|
|
435
|
-
if (!
|
|
445
|
+
if (!_asyncStorageCompat.asyncStorage) {
|
|
436
446
|
return;
|
|
437
447
|
}
|
|
438
448
|
|
|
439
449
|
// Stopping listener and restoring original methods
|
|
440
|
-
|
|
441
|
-
// Restore original methods
|
|
442
|
-
if (this.originalSetItem) {
|
|
443
|
-
_asyncStorage.default.setItem = this.originalSetItem;
|
|
444
|
-
}
|
|
445
|
-
if (this.originalRemoveItem) {
|
|
446
|
-
_asyncStorage.default.removeItem = this.originalRemoveItem;
|
|
447
|
-
}
|
|
448
|
-
if (this.originalMergeItem) {
|
|
449
|
-
_asyncStorage.default.mergeItem = this.originalMergeItem;
|
|
450
|
-
}
|
|
451
|
-
if (this.originalClear) {
|
|
452
|
-
_asyncStorage.default.clear = this.originalClear;
|
|
453
|
-
}
|
|
454
|
-
if (this.originalMultiSet) {
|
|
455
|
-
_asyncStorage.default.multiSet = this.originalMultiSet;
|
|
456
|
-
}
|
|
457
|
-
if (this.originalMultiRemove) {
|
|
458
|
-
_asyncStorage.default.multiRemove = this.originalMultiRemove;
|
|
459
|
-
}
|
|
460
|
-
if (this.originalMultiMerge) {
|
|
461
|
-
_asyncStorage.default.multiMerge = this.originalMultiMerge;
|
|
462
|
-
}
|
|
450
|
+
this.restoreOriginalMethods();
|
|
463
451
|
this.isListening = false;
|
|
464
452
|
// Stopped listening successfully
|
|
465
453
|
}
|