react-native-onyx 3.0.40 → 3.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.
package/dist/Onyx.js CHANGED
@@ -61,6 +61,11 @@ function init({ keys = {}, initialKeyStates = {}, evictableKeys = [], maxCachedK
61
61
  OnyxCache_1.default.setRamOnlyKeys(new Set(ramOnlyKeys));
62
62
  if (shouldSyncMultipleInstances) {
63
63
  (_a = storage_1.default.keepInstancesSync) === null || _a === void 0 ? void 0 : _a.call(storage_1.default, (key, value) => {
64
+ // RAM-only keys should never sync from storage as they may have stale persisted data
65
+ // from before the key was migrated to RAM-only.
66
+ if (OnyxUtils_1.default.isRamOnlyKey(key)) {
67
+ return;
68
+ }
64
69
  OnyxCache_1.default.set(key, value);
65
70
  // Check if this is a collection member key to prevent duplicate callbacks
66
71
  // When a collection is updated, individual members sync separately to other tabs
package/dist/OnyxUtils.js CHANGED
@@ -208,6 +208,13 @@ function get(key) {
208
208
  if (OnyxCache_1.default.hasCacheForKey(key)) {
209
209
  return Promise.resolve(OnyxCache_1.default.get(key));
210
210
  }
211
+ // RAM-only keys should never read from storage (they may have stale persisted data
212
+ // from before the key was migrated to RAM-only). Mark as nullish so future get() calls
213
+ // short-circuit via hasCacheForKey and avoid re-running this branch.
214
+ if (isRamOnlyKey(key)) {
215
+ OnyxCache_1.default.addNullishStorageKey(key);
216
+ return Promise.resolve(undefined);
217
+ }
211
218
  const taskName = `${OnyxCache_1.TASK.GET}:${key}`;
212
219
  // When a value retrieving task for this key is still running hook to it
213
220
  if (OnyxCache_1.default.hasPendingTask(taskName)) {
@@ -258,6 +265,14 @@ function multiGet(keys) {
258
265
  * These missingKeys will be later used to multiGet the data from the storage.
259
266
  */
260
267
  for (const key of keys) {
268
+ // RAM-only keys should never read from storage as they may have stale persisted data
269
+ // from before the key was migrated to RAM-only.
270
+ if (isRamOnlyKey(key)) {
271
+ if (OnyxCache_1.default.hasCacheForKey(key)) {
272
+ dataMap.set(key, OnyxCache_1.default.get(key));
273
+ }
274
+ continue;
275
+ }
261
276
  const cacheValue = OnyxCache_1.default.get(key);
262
277
  if (cacheValue) {
263
278
  dataMap.set(key, cacheValue);
@@ -361,7 +376,10 @@ function getAllKeys() {
361
376
  }
362
377
  // Otherwise retrieve the keys from storage and capture a promise to aid concurrent usages
363
378
  const promise = storage_1.default.getAllKeys().then((keys) => {
364
- OnyxCache_1.default.setAllKeys(keys);
379
+ // Filter out RAM-only keys from storage results as they may be stale entries
380
+ // from before the key was migrated to RAM-only.
381
+ const filteredKeys = keys.filter((key) => !isRamOnlyKey(key));
382
+ OnyxCache_1.default.setAllKeys(filteredKeys);
365
383
  // return the updated set of keys
366
384
  return OnyxCache_1.default.getAllKeys();
367
385
  });
@@ -888,7 +906,10 @@ function mergeInternal(mode, changes, existingValue) {
888
906
  * Merge user provided default key value pairs.
889
907
  */
890
908
  function initializeWithDefaultKeyStates() {
891
- return storage_1.default.multiGet(Object.keys(defaultKeyStates)).then((pairs) => {
909
+ // Filter out RAM-only keys from storage reads as they may have stale persisted data
910
+ // from before the key was migrated to RAM-only.
911
+ const keysToFetch = Object.keys(defaultKeyStates).filter((key) => !isRamOnlyKey(key));
912
+ return storage_1.default.multiGet(keysToFetch).then((pairs) => {
892
913
  const existingDataAsObject = Object.fromEntries(pairs);
893
914
  const merged = utils_1.default.fastMerge(existingDataAsObject, defaultKeyStates, {
894
915
  shouldRemoveNestedNulls: true,
@@ -86,7 +86,7 @@ const provider = {
86
86
  if (!provider.store) {
87
87
  throw new Error('Store is not initialized!');
88
88
  }
89
- const query = 'REPLACE INTO keyvaluepairs (record_key, valueJSON) VALUES (?, json(?));';
89
+ const query = 'REPLACE INTO keyvaluepairs (record_key, valueJSON) VALUES (?, ?);';
90
90
  const params = pairs.map((pair) => [pair[0], JSON.stringify(pair[1] === undefined ? null : pair[1])]);
91
91
  if (utils_1.default.isEmptyObject(params)) {
92
92
  return Promise.resolve();
@@ -100,12 +100,16 @@ const provider = {
100
100
  const commands = [];
101
101
  // Query to merge the change into the DB value.
102
102
  const patchQuery = `INSERT INTO keyvaluepairs (record_key, valueJSON)
103
- VALUES (:key, JSON(:value))
103
+ VALUES (:key, :value)
104
104
  ON CONFLICT DO UPDATE
105
- SET valueJSON = JSON_PATCH(valueJSON, JSON(:value));
105
+ SET valueJSON = JSON_PATCH(valueJSON, :value);
106
106
  `;
107
107
  const patchQueryArguments = [];
108
108
  // Query to fully replace the nested objects of the DB value.
109
+ // NOTE: The JSON() wrapper around the replacement value is required here. Unlike JSON_PATCH (which
110
+ // parses both arguments as JSON internally), JSON_REPLACE treats a plain TEXT binding as a quoted
111
+ // JSON string. Without JSON(), objects would be stored as string values (e.g. "{...}") instead of
112
+ // actual JSON objects, corrupting the stored data.
109
113
  const replaceQuery = `UPDATE keyvaluepairs
110
114
  SET valueJSON = JSON_REPLACE(valueJSON, ?, JSON(?))
111
115
  WHERE record_key = ?;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "3.0.40",
3
+ "version": "3.0.42",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",