react-native-onyx 3.0.48 → 3.0.49

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
@@ -221,7 +221,13 @@ function merge(key, changes) {
221
221
  }
222
222
  try {
223
223
  const validChanges = mergeQueue[key].filter((change) => {
224
- const { isCompatible, existingValueType, newValueType } = utils_1.default.checkCompatibilityWithExistingValue(change, existingValue);
224
+ const { isCompatible, existingValueType, newValueType, isEmptyArrayCoercion } = utils_1.default.checkCompatibilityWithExistingValue(change, existingValue);
225
+ if (isEmptyArrayCoercion) {
226
+ // Merging an object into an empty array isn't semantically correct, but we allow it
227
+ // in case we accidentally encoded an empty object as an empty array in PHP. If you're
228
+ // looking at a bugbot from this message, we're probably missing that key in OnyxKeys::KEYS_REQUIRING_EMPTY_OBJECT
229
+ Logger.logAlert(`[ENSURE_BUGBOT] Onyx merge called on key "${key}" whose existing value is an empty array. Will coerce to object.`);
230
+ }
225
231
  if (!isCompatible) {
226
232
  Logger.logAlert(logMessages_1.default.incompatibleUpdateAlert(key, 'merge', existingValueType, newValueType));
227
233
  }
package/dist/OnyxUtils.js CHANGED
@@ -1111,7 +1111,13 @@ function setWithRetry({ key, value, options }, retryAttempt) {
1111
1111
  return Promise.resolve();
1112
1112
  }
1113
1113
  // Check if the value is compatible with the existing value in the storage
1114
- const { isCompatible, existingValueType, newValueType } = utils_1.default.checkCompatibilityWithExistingValue(value, existingValue);
1114
+ const { isCompatible, existingValueType, newValueType, isEmptyArrayCoercion } = utils_1.default.checkCompatibilityWithExistingValue(value, existingValue);
1115
+ if (isEmptyArrayCoercion) {
1116
+ // Setting an object over empty array isn't semantically correct, but we allow it
1117
+ // in case we accidentally encoded an empty object as an empty array in PHP. If you're
1118
+ // looking at a bugbot from this message, we're probably missing that key in OnyxKeys::KEYS_REQUIRING_EMPTY_OBJECT
1119
+ Logger.logAlert(`[ENSURE_BUGBOT] Onyx setWithRetry called on key "${key}" whose existing value is an empty array. Will coerce to object.`);
1120
+ }
1115
1121
  if (!isCompatible) {
1116
1122
  Logger.logAlert(logMessages_1.default.incompatibleUpdateAlert(key, 'set', existingValueType, newValueType));
1117
1123
  return Promise.resolve();
@@ -1309,7 +1315,13 @@ function mergeCollectionWithPatches({ collectionKey, collection, mergeReplaceNul
1309
1315
  const existingKeys = keys.filter((key) => persistedKeys.has(key));
1310
1316
  const cachedCollectionForExistingKeys = getCachedCollection(collectionKey, existingKeys);
1311
1317
  const existingKeyCollection = existingKeys.reduce((obj, key) => {
1312
- const { isCompatible, existingValueType, newValueType } = utils_1.default.checkCompatibilityWithExistingValue(resultCollection[key], cachedCollectionForExistingKeys[key]);
1318
+ const { isCompatible, existingValueType, newValueType, isEmptyArrayCoercion } = utils_1.default.checkCompatibilityWithExistingValue(resultCollection[key], cachedCollectionForExistingKeys[key]);
1319
+ if (isEmptyArrayCoercion) {
1320
+ // Merging an object into an empty array isn't semantically correct, but we allow it
1321
+ // in case we accidentally encoded an empty object as an empty array in PHP. If you're
1322
+ // looking at a bugbot from this message, we're probably missing that key in OnyxKeys::KEYS_REQUIRING_EMPTY_OBJECT
1323
+ Logger.logAlert(`[ENSURE_BUGBOT] Onyx mergeCollection called on key "${key}" whose existing value is an empty array. Will coerce to object.`);
1324
+ }
1313
1325
  if (!isCompatible) {
1314
1326
  Logger.logAlert(logMessages_1.default.incompatibleUpdateAlert(key, 'mergeCollection', existingValueType, newValueType));
1315
1327
  return obj;
package/dist/utils.d.ts CHANGED
@@ -46,6 +46,7 @@ declare function checkCompatibilityWithExistingValue(value: unknown, existingVal
46
46
  isCompatible: boolean;
47
47
  existingValueType?: string;
48
48
  newValueType?: string;
49
+ isEmptyArrayCoercion?: boolean;
49
50
  };
50
51
  /**
51
52
  * Picks entries from an object based on a condition.
package/dist/utils.js CHANGED
@@ -142,6 +142,16 @@ function checkCompatibilityWithExistingValue(value, existingValue) {
142
142
  isCompatible: true,
143
143
  };
144
144
  }
145
+ // PHP's associative arrays cannot distinguish between an empty list and an
146
+ // empty object, so it encodes both as []. A key that should hold an
147
+ // object may arrive from the server as [] and be stored that way. If
148
+ // we then try to MERGE an object into that key, the array-vs-object type check
149
+ // would normally block it. Since an empty array carries no data worth
150
+ // preserving, we treat it as compatible with an object update and coerce it.
151
+ const isObjectValue = typeof value === 'object' && !Array.isArray(value);
152
+ if (Array.isArray(existingValue) && existingValue.length === 0 && isObjectValue) {
153
+ return { isCompatible: true, isEmptyArrayCoercion: true };
154
+ }
145
155
  const existingValueType = Array.isArray(existingValue) ? 'array' : 'non-array';
146
156
  const newValueType = Array.isArray(value) ? 'array' : 'non-array';
147
157
  if (existingValueType !== newValueType) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "3.0.48",
3
+ "version": "3.0.49",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",