react-native-onyx 3.0.11 → 3.0.13

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
@@ -145,62 +145,7 @@ function disconnect(connection) {
145
145
  * @param options optional configuration object
146
146
  */
147
147
  function set(key, value, options) {
148
- // When we use Onyx.set to set a key we want to clear the current delta changes from Onyx.merge that were queued
149
- // before the value was set. If Onyx.merge is currently reading the old value from storage, it will then not apply the changes.
150
- if (OnyxUtils_1.default.hasPendingMergeForKey(key)) {
151
- delete OnyxUtils_1.default.getMergeQueue()[key];
152
- }
153
- const skippableCollectionMemberIDs = OnyxUtils_1.default.getSkippableCollectionMemberIDs();
154
- if (skippableCollectionMemberIDs.size) {
155
- try {
156
- const [, collectionMemberID] = OnyxUtils_1.default.splitCollectionMemberKey(key);
157
- if (skippableCollectionMemberIDs.has(collectionMemberID)) {
158
- // The key is a skippable one, so we set the new value to null.
159
- // eslint-disable-next-line no-param-reassign
160
- value = null;
161
- }
162
- }
163
- catch (e) {
164
- // The key is not a collection one or something went wrong during split, so we proceed with the function's logic.
165
- }
166
- }
167
- // Onyx.set will ignore `undefined` values as inputs, therefore we can return early.
168
- if (value === undefined) {
169
- return Promise.resolve();
170
- }
171
- const existingValue = OnyxCache_1.default.get(key, false);
172
- // If the existing value as well as the new value are null, we can return early.
173
- if (existingValue === undefined && value === null) {
174
- return Promise.resolve();
175
- }
176
- // Check if the value is compatible with the existing value in the storage
177
- const { isCompatible, existingValueType, newValueType } = utils_1.default.checkCompatibilityWithExistingValue(value, existingValue);
178
- if (!isCompatible) {
179
- Logger.logAlert(logMessages_1.default.incompatibleUpdateAlert(key, 'set', existingValueType, newValueType));
180
- return Promise.resolve();
181
- }
182
- // If the change is null, we can just delete the key.
183
- // Therefore, we don't need to further broadcast and update the value so we can return early.
184
- if (value === null) {
185
- OnyxUtils_1.default.remove(key);
186
- OnyxUtils_1.default.logKeyRemoved(OnyxUtils_1.default.METHOD.SET, key);
187
- return Promise.resolve();
188
- }
189
- const valueWithoutNestedNullValues = utils_1.default.removeNestedNullValues(value);
190
- const hasChanged = (options === null || options === void 0 ? void 0 : options.skipCacheCheck) ? true : OnyxCache_1.default.hasValueChanged(key, valueWithoutNestedNullValues);
191
- OnyxUtils_1.default.logKeyChanged(OnyxUtils_1.default.METHOD.SET, key, value, hasChanged);
192
- // This approach prioritizes fast UI changes without waiting for data to be stored in device storage.
193
- const updatePromise = OnyxUtils_1.default.broadcastUpdate(key, valueWithoutNestedNullValues, hasChanged);
194
- // If the value has not changed or the key got removed, calling Storage.setItem() would be redundant and a waste of performance, so return early instead.
195
- if (!hasChanged) {
196
- return updatePromise;
197
- }
198
- return storage_1.default.setItem(key, valueWithoutNestedNullValues)
199
- .catch((error) => OnyxUtils_1.default.evictStorageAndRetry(error, set, key, valueWithoutNestedNullValues))
200
- .then(() => {
201
- OnyxUtils_1.default.sendActionToDevTools(OnyxUtils_1.default.METHOD.SET, key, valueWithoutNestedNullValues);
202
- return updatePromise;
203
- });
148
+ return OnyxUtils_1.default.setWithRetry({ key, value, options });
204
149
  }
205
150
  /**
206
151
  * Sets multiple keys and values
@@ -210,42 +155,7 @@ function set(key, value, options) {
210
155
  * @param data object keyed by ONYXKEYS and the values to set
211
156
  */
212
157
  function multiSet(data) {
213
- let newData = data;
214
- const skippableCollectionMemberIDs = OnyxUtils_1.default.getSkippableCollectionMemberIDs();
215
- if (skippableCollectionMemberIDs.size) {
216
- newData = Object.keys(newData).reduce((result, key) => {
217
- try {
218
- const [, collectionMemberID] = OnyxUtils_1.default.splitCollectionMemberKey(key);
219
- // If the collection member key is a skippable one we set its value to null.
220
- // eslint-disable-next-line no-param-reassign
221
- result[key] = !skippableCollectionMemberIDs.has(collectionMemberID) ? newData[key] : null;
222
- }
223
- catch (_a) {
224
- // The key is not a collection one or something went wrong during split, so we assign the data to result anyway.
225
- // eslint-disable-next-line no-param-reassign
226
- result[key] = newData[key];
227
- }
228
- return result;
229
- }, {});
230
- }
231
- const keyValuePairsToSet = OnyxUtils_1.default.prepareKeyValuePairsForStorage(newData, true);
232
- const updatePromises = keyValuePairsToSet.map(([key, value]) => {
233
- // When we use multiSet to set a key we want to clear the current delta changes from Onyx.merge that were queued
234
- // before the value was set. If Onyx.merge is currently reading the old value from storage, it will then not apply the changes.
235
- if (OnyxUtils_1.default.hasPendingMergeForKey(key)) {
236
- delete OnyxUtils_1.default.getMergeQueue()[key];
237
- }
238
- // Update cache and optimistically inform subscribers on the next tick
239
- OnyxCache_1.default.set(key, value);
240
- return OnyxUtils_1.default.scheduleSubscriberUpdate(key, value);
241
- });
242
- return storage_1.default.multiSet(keyValuePairsToSet)
243
- .catch((error) => OnyxUtils_1.default.evictStorageAndRetry(error, multiSet, newData))
244
- .then(() => {
245
- OnyxUtils_1.default.sendActionToDevTools(OnyxUtils_1.default.METHOD.MULTI_SET, undefined, newData);
246
- return Promise.all(updatePromises);
247
- })
248
- .then(() => undefined);
158
+ return OnyxUtils_1.default.multiSetWithRetry(data);
249
159
  }
250
160
  /**
251
161
  * Merge a new value into an existing value at a key.
@@ -344,7 +254,7 @@ function merge(key, changes) {
344
254
  * @param collection Object collection keyed by individual collection member keys and values
345
255
  */
346
256
  function mergeCollection(collectionKey, collection) {
347
- return OnyxUtils_1.default.mergeCollectionWithPatches(collectionKey, collection, undefined, true);
257
+ return OnyxUtils_1.default.mergeCollectionWithPatches({ collectionKey, collection, isProcessingCollectionUpdate: true });
348
258
  }
349
259
  /**
350
260
  * Clear out all the data in the store
@@ -553,10 +463,15 @@ function update(data) {
553
463
  set: {},
554
464
  });
555
465
  if (!utils_1.default.isEmptyObject(batchedCollectionUpdates.merge)) {
556
- promises.push(() => OnyxUtils_1.default.mergeCollectionWithPatches(collectionKey, batchedCollectionUpdates.merge, batchedCollectionUpdates.mergeReplaceNullPatches, true));
466
+ promises.push(() => OnyxUtils_1.default.mergeCollectionWithPatches({
467
+ collectionKey,
468
+ collection: batchedCollectionUpdates.merge,
469
+ mergeReplaceNullPatches: batchedCollectionUpdates.mergeReplaceNullPatches,
470
+ isProcessingCollectionUpdate: true,
471
+ }));
557
472
  }
558
473
  if (!utils_1.default.isEmptyObject(batchedCollectionUpdates.set)) {
559
- promises.push(() => OnyxUtils_1.default.partialSetCollection(collectionKey, batchedCollectionUpdates.set));
474
+ promises.push(() => OnyxUtils_1.default.partialSetCollection({ collectionKey, collection: batchedCollectionUpdates.set }));
560
475
  }
561
476
  });
562
477
  Object.entries(updateQueue).forEach(([key, operations]) => {
@@ -588,54 +503,7 @@ function update(data) {
588
503
  * @param collection Object collection keyed by individual collection member keys and values
589
504
  */
590
505
  function setCollection(collectionKey, collection) {
591
- let resultCollection = collection;
592
- let resultCollectionKeys = Object.keys(resultCollection);
593
- // Confirm all the collection keys belong to the same parent
594
- if (!OnyxUtils_1.default.doAllCollectionItemsBelongToSameParent(collectionKey, resultCollectionKeys)) {
595
- Logger.logAlert(`setCollection called with keys that do not belong to the same parent ${collectionKey}. Skipping this update.`);
596
- return Promise.resolve();
597
- }
598
- const skippableCollectionMemberIDs = OnyxUtils_1.default.getSkippableCollectionMemberIDs();
599
- if (skippableCollectionMemberIDs.size) {
600
- resultCollection = resultCollectionKeys.reduce((result, key) => {
601
- try {
602
- const [, collectionMemberID] = OnyxUtils_1.default.splitCollectionMemberKey(key, collectionKey);
603
- // If the collection member key is a skippable one we set its value to null.
604
- // eslint-disable-next-line no-param-reassign
605
- result[key] = !skippableCollectionMemberIDs.has(collectionMemberID) ? resultCollection[key] : null;
606
- }
607
- catch (_a) {
608
- // Something went wrong during split, so we assign the data to result anyway.
609
- // eslint-disable-next-line no-param-reassign
610
- result[key] = resultCollection[key];
611
- }
612
- return result;
613
- }, {});
614
- }
615
- resultCollectionKeys = Object.keys(resultCollection);
616
- return OnyxUtils_1.default.getAllKeys().then((persistedKeys) => {
617
- const mutableCollection = Object.assign({}, resultCollection);
618
- persistedKeys.forEach((key) => {
619
- if (!key.startsWith(collectionKey)) {
620
- return;
621
- }
622
- if (resultCollectionKeys.includes(key)) {
623
- return;
624
- }
625
- mutableCollection[key] = null;
626
- });
627
- const keyValuePairs = OnyxUtils_1.default.prepareKeyValuePairsForStorage(mutableCollection, true, undefined, true);
628
- const previousCollection = OnyxUtils_1.default.getCachedCollection(collectionKey);
629
- // Preserve references for unchanged items in setCollection
630
- const preservedCollection = OnyxUtils_1.default.preserveCollectionReferences(keyValuePairs);
631
- const updatePromise = OnyxUtils_1.default.scheduleNotifyCollectionSubscribers(collectionKey, preservedCollection, previousCollection);
632
- return storage_1.default.multiSet(keyValuePairs)
633
- .catch((error) => OnyxUtils_1.default.evictStorageAndRetry(error, setCollection, collectionKey, collection))
634
- .then(() => {
635
- OnyxUtils_1.default.sendActionToDevTools(OnyxUtils_1.default.METHOD.SET_COLLECTION, undefined, mutableCollection);
636
- return updatePromise;
637
- });
638
- });
506
+ return OnyxUtils_1.default.setCollectionWithRetry({ collectionKey, collection });
639
507
  }
640
508
  const Onyx = {
641
509
  METHOD: OnyxUtils_1.default.METHOD,
@@ -1,6 +1,6 @@
1
1
  import type { ValueOf } from 'type-fest';
2
2
  import type Onyx from './Onyx';
3
- import type { CollectionKey, CollectionKeyBase, ConnectOptions, DeepRecord, KeyValueMapping, CallbackToStateMapping, MultiMergeReplaceNullPatches, OnyxCollection, OnyxEntry, OnyxInput, OnyxInputKeyValueMapping, OnyxKey, OnyxMergeCollectionInput, OnyxUpdate, OnyxValue, Selector, OnyxSetCollectionInput } from './types';
3
+ import type { CollectionKey, CollectionKeyBase, ConnectOptions, DeepRecord, KeyValueMapping, CallbackToStateMapping, MultiMergeReplaceNullPatches, OnyxCollection, OnyxEntry, OnyxInput, OnyxKey, OnyxMergeCollectionInput, OnyxUpdate, OnyxValue, Selector, MergeCollectionWithPatchesParams, SetCollectionParams, SetParams, OnyxMultiSetInput, RetriableOnyxOperation } from './types';
4
4
  import type { FastMergeResult } from './utils';
5
5
  import type { DeferredTask } from './createDeferredTask';
6
6
  import type { StorageKeyValuePair } from './storage/providers/types';
@@ -138,18 +138,6 @@ declare function getCollectionKey(key: CollectionKey): string;
138
138
  * If the requested key is a collection, it will return an object with all the collection members.
139
139
  */
140
140
  declare function tryGetCachedValue<TKey extends OnyxKey>(key: TKey): OnyxValue<OnyxKey>;
141
- /**
142
- * Utility function to preserve object references for unchanged items in collection operations.
143
- * Compares new values with cached values using deep equality and preserves references when data is identical.
144
- * @returns The preserved collection with unchanged references maintained
145
- */
146
- declare function preserveCollectionReferences(keyValuePairs: StorageKeyValuePair[]): OnyxInputKeyValueMapping;
147
- /**
148
- * Utility function for merge operations that preserves references after cache merge has been performed.
149
- * Compares merged values with original cached values and preserves references when data is unchanged.
150
- * @returns The preserved collection with unchanged references maintained
151
- */
152
- declare function preserveCollectionReferencesAfterMerge(collection: Record<string, OnyxValue<OnyxKey>>, originalCachedValues: Record<string, OnyxValue<OnyxKey>>): Record<string, OnyxValue<OnyxKey>>;
153
141
  declare function getCachedCollection<TKey extends CollectionKeyBase>(collectionKey: TKey, collectionMemberKeys?: string[]): NonNullable<OnyxCollection<KeyValueMapping[TKey]>>;
154
142
  /**
155
143
  * When a collection of keys change, search for any callbacks matching the collection key and trigger those callbacks
@@ -194,11 +182,12 @@ declare function scheduleNotifyCollectionSubscribers<TKey extends OnyxKey>(key:
194
182
  declare function remove<TKey extends OnyxKey>(key: TKey, isProcessingCollectionUpdate?: boolean): Promise<void>;
195
183
  declare function reportStorageQuota(): Promise<void>;
196
184
  /**
197
- * If we fail to set or merge we must handle this by
198
- * evicting some data from Onyx and then retrying to do
199
- * whatever it is we attempted to do.
185
+ * Handles storage operation failures based on the error type:
186
+ * - Storage capacity errors: evicts data and retries the operation
187
+ * - Invalid data errors: logs an alert and throws an error
188
+ * - Other errors: retries the operation
200
189
  */
201
- declare function evictStorageAndRetry<TMethod extends typeof Onyx.set | typeof Onyx.multiSet | typeof Onyx.mergeCollection | typeof Onyx.setCollection>(error: Error, onyxMethod: TMethod, ...args: Parameters<TMethod>): Promise<void>;
190
+ declare function retryOperation<TMethod extends RetriableOnyxOperation>(error: Error, onyxMethod: TMethod, defaultParams: Parameters<TMethod>[0], retryAttempt: number | undefined): Promise<void>;
202
191
  /**
203
192
  * Notifies subscribers and writes current value to cache
204
193
  */
@@ -253,25 +242,64 @@ declare function subscribeToKey<TKey extends OnyxKey>(connectOptions: ConnectOpt
253
242
  */
254
243
  declare function unsubscribeFromKey(subscriptionID: number): void;
255
244
  declare function updateSnapshots(data: OnyxUpdate[], mergeFn: typeof Onyx.merge): Array<() => Promise<void>>;
245
+ /**
246
+ * Writes a value to our store with the given key.
247
+ * Serves as core implementation for `Onyx.set()` public function, the difference being
248
+ * that this internal function allows passing an additional `retryAttempt` parameter to retry on failure.
249
+ *
250
+ * @param params - set parameters
251
+ * @param params.key ONYXKEY to set
252
+ * @param params.value value to store
253
+ * @param params.options optional configuration object
254
+ * @param retryAttempt retry attempt
255
+ */
256
+ declare function setWithRetry<TKey extends OnyxKey>({ key, value, options }: SetParams<TKey>, retryAttempt?: number): Promise<void>;
257
+ /**
258
+ * Sets multiple keys and values.
259
+ * Serves as core implementation for `Onyx.multiSet()` public function, the difference being
260
+ * that this internal function allows passing an additional `retryAttempt` parameter to retry on failure.
261
+ *
262
+ * @param data object keyed by ONYXKEYS and the values to set
263
+ * @param retryAttempt retry attempt
264
+ */
265
+ declare function multiSetWithRetry(data: OnyxMultiSetInput, retryAttempt?: number): Promise<void>;
266
+ /**
267
+ * Sets a collection by replacing all existing collection members with new values.
268
+ * Any existing collection members not included in the new data will be removed.
269
+ * Serves as core implementation for `Onyx.setCollection()` public function, the difference being
270
+ * that this internal function allows passing an additional `retryAttempt` parameter to retry on failure.
271
+ *
272
+ * @param params - collection parameters
273
+ * @param params.collectionKey e.g. `ONYXKEYS.COLLECTION.REPORT`
274
+ * @param params.collection Object collection keyed by individual collection member keys and values
275
+ * @param retryAttempt retry attempt
276
+ */
277
+ declare function setCollectionWithRetry<TKey extends CollectionKeyBase>({ collectionKey, collection }: SetCollectionParams<TKey>, retryAttempt?: number): Promise<void>;
256
278
  /**
257
279
  * Merges a collection based on their keys.
258
280
  * Serves as core implementation for `Onyx.mergeCollection()` public function, the difference being
259
- * that this internal function allows passing an additional `mergeReplaceNullPatches` parameter.
281
+ * that this internal function allows passing an additional `mergeReplaceNullPatches` parameter and retries on failure.
260
282
  *
261
- * @param collectionKey e.g. `ONYXKEYS.COLLECTION.REPORT`
262
- * @param collection Object collection keyed by individual collection member keys and values
263
- * @param mergeReplaceNullPatches Record where the key is a collection member key and the value is a list of
283
+ * @param params - mergeCollection parameters
284
+ * @param params.collectionKey e.g. `ONYXKEYS.COLLECTION.REPORT`
285
+ * @param params.collection Object collection keyed by individual collection member keys and values
286
+ * @param params.mergeReplaceNullPatches Record where the key is a collection member key and the value is a list of
264
287
  * tuples that we'll use to replace the nested objects of that collection member record with something else.
288
+ * @param params.isProcessingCollectionUpdate whether this is part of a collection update operation.
289
+ * @param retryAttempt retry attempt
265
290
  */
266
- declare function mergeCollectionWithPatches<TKey extends CollectionKeyBase>(collectionKey: TKey, collection: OnyxMergeCollectionInput<TKey>, mergeReplaceNullPatches?: MultiMergeReplaceNullPatches, isProcessingCollectionUpdate?: boolean): Promise<void>;
291
+ declare function mergeCollectionWithPatches<TKey extends CollectionKeyBase>({ collectionKey, collection, mergeReplaceNullPatches, isProcessingCollectionUpdate }: MergeCollectionWithPatchesParams<TKey>, retryAttempt?: number): Promise<void>;
267
292
  /**
268
293
  * Sets keys in a collection by replacing all targeted collection members with new values.
269
294
  * Any existing collection members not included in the new data will not be removed.
295
+ * Retries on failure.
270
296
  *
271
- * @param collectionKey e.g. `ONYXKEYS.COLLECTION.REPORT`
272
- * @param collection Object collection keyed by individual collection member keys and values
297
+ * @param params - collection parameters
298
+ * @param params.collectionKey e.g. `ONYXKEYS.COLLECTION.REPORT`
299
+ * @param params.collection Object collection keyed by individual collection member keys and values
300
+ * @param retryAttempt retry attempt
273
301
  */
274
- declare function partialSetCollection<TKey extends CollectionKeyBase>(collectionKey: TKey, collection: OnyxSetCollectionInput<TKey>): Promise<void>;
302
+ declare function partialSetCollection<TKey extends CollectionKeyBase>({ collectionKey, collection }: SetCollectionParams<TKey>, retryAttempt?: number): Promise<void>;
275
303
  declare function logKeyChanged(onyxMethod: Extract<OnyxMethod, 'set' | 'merge'>, key: OnyxKey, value: unknown, hasChanged: boolean): void;
276
304
  declare function logKeyRemoved(onyxMethod: Extract<OnyxMethod, 'set' | 'merge'>, key: OnyxKey): void;
277
305
  /**
@@ -313,7 +341,7 @@ declare const OnyxUtils: {
313
341
  scheduleNotifyCollectionSubscribers: typeof scheduleNotifyCollectionSubscribers;
314
342
  remove: typeof remove;
315
343
  reportStorageQuota: typeof reportStorageQuota;
316
- evictStorageAndRetry: typeof evictStorageAndRetry;
344
+ retryOperation: typeof retryOperation;
317
345
  broadcastUpdate: typeof broadcastUpdate;
318
346
  hasPendingMergeForKey: typeof hasPendingMergeForKey;
319
347
  prepareKeyValuePairsForStorage: typeof prepareKeyValuePairsForStorage;
@@ -336,10 +364,11 @@ declare const OnyxUtils: {
336
364
  updateSnapshots: typeof updateSnapshots;
337
365
  mergeCollectionWithPatches: typeof mergeCollectionWithPatches;
338
366
  partialSetCollection: typeof partialSetCollection;
339
- preserveCollectionReferences: typeof preserveCollectionReferences;
340
- preserveCollectionReferencesAfterMerge: typeof preserveCollectionReferencesAfterMerge;
341
367
  logKeyChanged: typeof logKeyChanged;
342
368
  logKeyRemoved: typeof logKeyRemoved;
369
+ setWithRetry: typeof setWithRetry;
370
+ multiSetWithRetry: typeof multiSetWithRetry;
371
+ setCollectionWithRetry: typeof setCollectionWithRetry;
343
372
  };
344
373
  export type { OnyxMethod };
345
374
  export default OnyxUtils;
package/dist/OnyxUtils.js CHANGED
@@ -61,6 +61,19 @@ const METHOD = {
61
61
  MULTI_SET: 'multiset',
62
62
  CLEAR: 'clear',
63
63
  };
64
+ // IndexedDB errors that indicate storage capacity issues where eviction can help
65
+ const IDB_STORAGE_ERRORS = [
66
+ 'quotaexceedederror', // Browser storage quota exceeded
67
+ ];
68
+ // SQLite errors that indicate storage capacity issues where eviction can help
69
+ const SQLITE_STORAGE_ERRORS = [
70
+ 'database or disk is full', // Device storage is full
71
+ 'disk I/O error', // File system I/O failure, often due to insufficient space or corrupted storage
72
+ 'out of memory', // Insufficient RAM or storage space to complete the operation
73
+ ];
74
+ const STORAGE_ERRORS = [...IDB_STORAGE_ERRORS, ...SQLITE_STORAGE_ERRORS];
75
+ // Max number of retries for failed storage operations
76
+ const MAX_STORAGE_OPERATION_RETRY_ATTEMPTS = 5;
64
77
  // Key/value store of Onyx key and arrays of values to merge
65
78
  let mergeQueue = {};
66
79
  let mergeQueuePromise = {};
@@ -452,51 +465,6 @@ function tryGetCachedValue(key) {
452
465
  }
453
466
  return val;
454
467
  }
455
- /**
456
- * Utility function to preserve object references for unchanged items in collection operations.
457
- * Compares new values with cached values using deep equality and preserves references when data is identical.
458
- * @returns The preserved collection with unchanged references maintained
459
- */
460
- function preserveCollectionReferences(keyValuePairs) {
461
- const preservedCollection = {};
462
- keyValuePairs.forEach(([key, value]) => {
463
- const cachedValue = OnyxCache_1.default.get(key, false);
464
- // If no cached value exists, we need to add the new value (skip expensive deep equality check)
465
- // Use deep equality check to preserve references for unchanged items
466
- if (cachedValue !== undefined && (0, fast_equals_1.deepEqual)(value, cachedValue)) {
467
- // Keep the existing reference
468
- preservedCollection[key] = cachedValue;
469
- }
470
- else {
471
- // Update cache only for changed items
472
- OnyxCache_1.default.set(key, value);
473
- preservedCollection[key] = value;
474
- }
475
- });
476
- return preservedCollection;
477
- }
478
- /**
479
- * Utility function for merge operations that preserves references after cache merge has been performed.
480
- * Compares merged values with original cached values and preserves references when data is unchanged.
481
- * @returns The preserved collection with unchanged references maintained
482
- */
483
- function preserveCollectionReferencesAfterMerge(collection, originalCachedValues) {
484
- const preservedCollection = {};
485
- Object.keys(collection).forEach((key) => {
486
- const newMergedValue = OnyxCache_1.default.get(key, false);
487
- const originalValue = originalCachedValues[key];
488
- // Use deep equality check to preserve references for unchanged items
489
- if (originalValue !== undefined && (0, fast_equals_1.deepEqual)(newMergedValue, originalValue)) {
490
- // Keep the existing reference and update cache
491
- preservedCollection[key] = originalValue;
492
- OnyxCache_1.default.set(key, originalValue);
493
- }
494
- else {
495
- preservedCollection[key] = newMergedValue;
496
- }
497
- });
498
- return preservedCollection;
499
- }
500
468
  function getCachedCollection(collectionKey, collectionMemberKeys) {
501
469
  // Use optimized collection data retrieval when cache is populated
502
470
  const collectionData = OnyxCache_1.default.getCollectionData(collectionKey);
@@ -757,16 +725,31 @@ function reportStorageQuota() {
757
725
  });
758
726
  }
759
727
  /**
760
- * If we fail to set or merge we must handle this by
761
- * evicting some data from Onyx and then retrying to do
762
- * whatever it is we attempted to do.
728
+ * Handles storage operation failures based on the error type:
729
+ * - Storage capacity errors: evicts data and retries the operation
730
+ * - Invalid data errors: logs an alert and throws an error
731
+ * - Other errors: retries the operation
763
732
  */
764
- function evictStorageAndRetry(error, onyxMethod, ...args) {
765
- Logger.logInfo(`Failed to save to storage. Error: ${error}. onyxMethod: ${onyxMethod.name}`);
733
+ function retryOperation(error, onyxMethod, defaultParams, retryAttempt) {
734
+ var _a, _b, _c, _d;
735
+ const currentRetryAttempt = retryAttempt !== null && retryAttempt !== void 0 ? retryAttempt : 0;
736
+ const nextRetryAttempt = currentRetryAttempt + 1;
737
+ Logger.logInfo(`Failed to save to storage. Error: ${error}. onyxMethod: ${onyxMethod.name}. retryAttempt: ${currentRetryAttempt}/${MAX_STORAGE_OPERATION_RETRY_ATTEMPTS}`);
766
738
  if (error && Str.startsWith(error.message, "Failed to execute 'put' on 'IDBObjectStore'")) {
767
739
  Logger.logAlert('Attempted to set invalid data set in Onyx. Please ensure all data is serializable.');
768
740
  throw error;
769
741
  }
742
+ const errorMessage = (_b = (_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.toLowerCase) === null || _b === void 0 ? void 0 : _b.call(_a);
743
+ const errorName = (_d = (_c = error === null || error === void 0 ? void 0 : error.name) === null || _c === void 0 ? void 0 : _c.toLowerCase) === null || _d === void 0 ? void 0 : _d.call(_c);
744
+ const isStorageCapacityError = STORAGE_ERRORS.some((storageError) => (errorName === null || errorName === void 0 ? void 0 : errorName.includes(storageError)) || (errorMessage === null || errorMessage === void 0 ? void 0 : errorMessage.includes(storageError)));
745
+ if (nextRetryAttempt > MAX_STORAGE_OPERATION_RETRY_ATTEMPTS) {
746
+ Logger.logAlert(`Storage operation failed after 5 retries. Error: ${error}. onyxMethod: ${onyxMethod.name}.`);
747
+ return Promise.resolve();
748
+ }
749
+ if (!isStorageCapacityError) {
750
+ // @ts-expect-error No overload matches this call.
751
+ return onyxMethod(defaultParams, nextRetryAttempt);
752
+ }
770
753
  // Find the first key that we can remove that has no subscribers in our blocklist
771
754
  const keyForRemoval = OnyxCache_1.default.getKeyForEviction();
772
755
  if (!keyForRemoval) {
@@ -780,7 +763,7 @@ function evictStorageAndRetry(error, onyxMethod, ...args) {
780
763
  Logger.logInfo(`Out of storage. Evicting least recently accessed key (${keyForRemoval}) and retrying.`);
781
764
  reportStorageQuota();
782
765
  // @ts-expect-error No overload matches this call.
783
- return remove(keyForRemoval).then(() => onyxMethod(...args));
766
+ return remove(keyForRemoval).then(() => onyxMethod(defaultParams, nextRetryAttempt));
784
767
  }
785
768
  /**
786
769
  * Notifies subscribers and writes current value to cache
@@ -1050,17 +1033,192 @@ function updateSnapshots(data, mergeFn) {
1050
1033
  });
1051
1034
  return promises;
1052
1035
  }
1036
+ /**
1037
+ * Writes a value to our store with the given key.
1038
+ * Serves as core implementation for `Onyx.set()` public function, the difference being
1039
+ * that this internal function allows passing an additional `retryAttempt` parameter to retry on failure.
1040
+ *
1041
+ * @param params - set parameters
1042
+ * @param params.key ONYXKEY to set
1043
+ * @param params.value value to store
1044
+ * @param params.options optional configuration object
1045
+ * @param retryAttempt retry attempt
1046
+ */
1047
+ function setWithRetry({ key, value, options }, retryAttempt) {
1048
+ // When we use Onyx.set to set a key we want to clear the current delta changes from Onyx.merge that were queued
1049
+ // before the value was set. If Onyx.merge is currently reading the old value from storage, it will then not apply the changes.
1050
+ if (OnyxUtils.hasPendingMergeForKey(key)) {
1051
+ delete OnyxUtils.getMergeQueue()[key];
1052
+ }
1053
+ if (skippableCollectionMemberIDs.size) {
1054
+ try {
1055
+ const [, collectionMemberID] = OnyxUtils.splitCollectionMemberKey(key);
1056
+ if (skippableCollectionMemberIDs.has(collectionMemberID)) {
1057
+ // The key is a skippable one, so we set the new value to null.
1058
+ // eslint-disable-next-line no-param-reassign
1059
+ value = null;
1060
+ }
1061
+ }
1062
+ catch (e) {
1063
+ // The key is not a collection one or something went wrong during split, so we proceed with the function's logic.
1064
+ }
1065
+ }
1066
+ // Onyx.set will ignore `undefined` values as inputs, therefore we can return early.
1067
+ if (value === undefined) {
1068
+ return Promise.resolve();
1069
+ }
1070
+ const existingValue = OnyxCache_1.default.get(key, false);
1071
+ // If the existing value as well as the new value are null, we can return early.
1072
+ if (existingValue === undefined && value === null) {
1073
+ return Promise.resolve();
1074
+ }
1075
+ // Check if the value is compatible with the existing value in the storage
1076
+ const { isCompatible, existingValueType, newValueType } = utils_1.default.checkCompatibilityWithExistingValue(value, existingValue);
1077
+ if (!isCompatible) {
1078
+ Logger.logAlert(logMessages_1.default.incompatibleUpdateAlert(key, 'set', existingValueType, newValueType));
1079
+ return Promise.resolve();
1080
+ }
1081
+ // If the change is null, we can just delete the key.
1082
+ // Therefore, we don't need to further broadcast and update the value so we can return early.
1083
+ if (value === null) {
1084
+ OnyxUtils.remove(key);
1085
+ OnyxUtils.logKeyRemoved(OnyxUtils.METHOD.SET, key);
1086
+ return Promise.resolve();
1087
+ }
1088
+ const valueWithoutNestedNullValues = utils_1.default.removeNestedNullValues(value);
1089
+ const hasChanged = (options === null || options === void 0 ? void 0 : options.skipCacheCheck) ? true : OnyxCache_1.default.hasValueChanged(key, valueWithoutNestedNullValues);
1090
+ OnyxUtils.logKeyChanged(OnyxUtils.METHOD.SET, key, value, hasChanged);
1091
+ // This approach prioritizes fast UI changes without waiting for data to be stored in device storage.
1092
+ const updatePromise = OnyxUtils.broadcastUpdate(key, valueWithoutNestedNullValues, hasChanged);
1093
+ // If the value has not changed and this isn't a retry attempt, calling Storage.setItem() would be redundant and a waste of performance, so return early instead.
1094
+ if (!hasChanged && !retryAttempt) {
1095
+ return updatePromise;
1096
+ }
1097
+ return storage_1.default.setItem(key, valueWithoutNestedNullValues)
1098
+ .catch((error) => OnyxUtils.retryOperation(error, setWithRetry, { key, value: valueWithoutNestedNullValues, options }, retryAttempt))
1099
+ .then(() => {
1100
+ OnyxUtils.sendActionToDevTools(OnyxUtils.METHOD.SET, key, valueWithoutNestedNullValues);
1101
+ return updatePromise;
1102
+ });
1103
+ }
1104
+ /**
1105
+ * Sets multiple keys and values.
1106
+ * Serves as core implementation for `Onyx.multiSet()` public function, the difference being
1107
+ * that this internal function allows passing an additional `retryAttempt` parameter to retry on failure.
1108
+ *
1109
+ * @param data object keyed by ONYXKEYS and the values to set
1110
+ * @param retryAttempt retry attempt
1111
+ */
1112
+ function multiSetWithRetry(data, retryAttempt) {
1113
+ let newData = data;
1114
+ if (skippableCollectionMemberIDs.size) {
1115
+ newData = Object.keys(newData).reduce((result, key) => {
1116
+ try {
1117
+ const [, collectionMemberID] = OnyxUtils.splitCollectionMemberKey(key);
1118
+ // If the collection member key is a skippable one we set its value to null.
1119
+ // eslint-disable-next-line no-param-reassign
1120
+ result[key] = !skippableCollectionMemberIDs.has(collectionMemberID) ? newData[key] : null;
1121
+ }
1122
+ catch (_a) {
1123
+ // The key is not a collection one or something went wrong during split, so we assign the data to result anyway.
1124
+ // eslint-disable-next-line no-param-reassign
1125
+ result[key] = newData[key];
1126
+ }
1127
+ return result;
1128
+ }, {});
1129
+ }
1130
+ const keyValuePairsToSet = OnyxUtils.prepareKeyValuePairsForStorage(newData, true);
1131
+ const updatePromises = keyValuePairsToSet.map(([key, value]) => {
1132
+ // When we use multiSet to set a key we want to clear the current delta changes from Onyx.merge that were queued
1133
+ // before the value was set. If Onyx.merge is currently reading the old value from storage, it will then not apply the changes.
1134
+ if (OnyxUtils.hasPendingMergeForKey(key)) {
1135
+ delete OnyxUtils.getMergeQueue()[key];
1136
+ }
1137
+ // Update cache and optimistically inform subscribers on the next tick
1138
+ OnyxCache_1.default.set(key, value);
1139
+ return OnyxUtils.scheduleSubscriberUpdate(key, value);
1140
+ });
1141
+ return storage_1.default.multiSet(keyValuePairsToSet)
1142
+ .catch((error) => OnyxUtils.retryOperation(error, multiSetWithRetry, newData, retryAttempt))
1143
+ .then(() => {
1144
+ OnyxUtils.sendActionToDevTools(OnyxUtils.METHOD.MULTI_SET, undefined, newData);
1145
+ return Promise.all(updatePromises);
1146
+ })
1147
+ .then(() => undefined);
1148
+ }
1149
+ /**
1150
+ * Sets a collection by replacing all existing collection members with new values.
1151
+ * Any existing collection members not included in the new data will be removed.
1152
+ * Serves as core implementation for `Onyx.setCollection()` public function, the difference being
1153
+ * that this internal function allows passing an additional `retryAttempt` parameter to retry on failure.
1154
+ *
1155
+ * @param params - collection parameters
1156
+ * @param params.collectionKey e.g. `ONYXKEYS.COLLECTION.REPORT`
1157
+ * @param params.collection Object collection keyed by individual collection member keys and values
1158
+ * @param retryAttempt retry attempt
1159
+ */
1160
+ function setCollectionWithRetry({ collectionKey, collection }, retryAttempt) {
1161
+ let resultCollection = collection;
1162
+ let resultCollectionKeys = Object.keys(resultCollection);
1163
+ // Confirm all the collection keys belong to the same parent
1164
+ if (!OnyxUtils.doAllCollectionItemsBelongToSameParent(collectionKey, resultCollectionKeys)) {
1165
+ Logger.logAlert(`setCollection called with keys that do not belong to the same parent ${collectionKey}. Skipping this update.`);
1166
+ return Promise.resolve();
1167
+ }
1168
+ if (skippableCollectionMemberIDs.size) {
1169
+ resultCollection = resultCollectionKeys.reduce((result, key) => {
1170
+ try {
1171
+ const [, collectionMemberID] = OnyxUtils.splitCollectionMemberKey(key, collectionKey);
1172
+ // If the collection member key is a skippable one we set its value to null.
1173
+ // eslint-disable-next-line no-param-reassign
1174
+ result[key] = !skippableCollectionMemberIDs.has(collectionMemberID) ? resultCollection[key] : null;
1175
+ }
1176
+ catch (_a) {
1177
+ // Something went wrong during split, so we assign the data to result anyway.
1178
+ // eslint-disable-next-line no-param-reassign
1179
+ result[key] = resultCollection[key];
1180
+ }
1181
+ return result;
1182
+ }, {});
1183
+ }
1184
+ resultCollectionKeys = Object.keys(resultCollection);
1185
+ return OnyxUtils.getAllKeys().then((persistedKeys) => {
1186
+ const mutableCollection = Object.assign({}, resultCollection);
1187
+ persistedKeys.forEach((key) => {
1188
+ if (!key.startsWith(collectionKey)) {
1189
+ return;
1190
+ }
1191
+ if (resultCollectionKeys.includes(key)) {
1192
+ return;
1193
+ }
1194
+ mutableCollection[key] = null;
1195
+ });
1196
+ const keyValuePairs = OnyxUtils.prepareKeyValuePairsForStorage(mutableCollection, true, undefined, true);
1197
+ const previousCollection = OnyxUtils.getCachedCollection(collectionKey);
1198
+ keyValuePairs.forEach(([key, value]) => OnyxCache_1.default.set(key, value));
1199
+ const updatePromise = OnyxUtils.scheduleNotifyCollectionSubscribers(collectionKey, mutableCollection, previousCollection);
1200
+ return storage_1.default.multiSet(keyValuePairs)
1201
+ .catch((error) => OnyxUtils.retryOperation(error, setCollectionWithRetry, { collectionKey, collection }, retryAttempt))
1202
+ .then(() => {
1203
+ OnyxUtils.sendActionToDevTools(OnyxUtils.METHOD.SET_COLLECTION, undefined, mutableCollection);
1204
+ return updatePromise;
1205
+ });
1206
+ });
1207
+ }
1053
1208
  /**
1054
1209
  * Merges a collection based on their keys.
1055
1210
  * Serves as core implementation for `Onyx.mergeCollection()` public function, the difference being
1056
- * that this internal function allows passing an additional `mergeReplaceNullPatches` parameter.
1211
+ * that this internal function allows passing an additional `mergeReplaceNullPatches` parameter and retries on failure.
1057
1212
  *
1058
- * @param collectionKey e.g. `ONYXKEYS.COLLECTION.REPORT`
1059
- * @param collection Object collection keyed by individual collection member keys and values
1060
- * @param mergeReplaceNullPatches Record where the key is a collection member key and the value is a list of
1213
+ * @param params - mergeCollection parameters
1214
+ * @param params.collectionKey e.g. `ONYXKEYS.COLLECTION.REPORT`
1215
+ * @param params.collection Object collection keyed by individual collection member keys and values
1216
+ * @param params.mergeReplaceNullPatches Record where the key is a collection member key and the value is a list of
1061
1217
  * tuples that we'll use to replace the nested objects of that collection member record with something else.
1218
+ * @param params.isProcessingCollectionUpdate whether this is part of a collection update operation.
1219
+ * @param retryAttempt retry attempt
1062
1220
  */
1063
- function mergeCollectionWithPatches(collectionKey, collection, mergeReplaceNullPatches, isProcessingCollectionUpdate = false) {
1221
+ function mergeCollectionWithPatches({ collectionKey, collection, mergeReplaceNullPatches, isProcessingCollectionUpdate = false }, retryAttempt) {
1064
1222
  if (!isValidNonEmptyCollectionForMerge(collection)) {
1065
1223
  Logger.logInfo('mergeCollection() called with invalid or empty value. Skipping this update.');
1066
1224
  return Promise.resolve();
@@ -1139,21 +1297,13 @@ function mergeCollectionWithPatches(collectionKey, collection, mergeReplaceNullP
1139
1297
  // finalMergedCollection contains all the keys that were merged, without the keys of incompatible updates
1140
1298
  const finalMergedCollection = Object.assign(Object.assign({}, existingKeyCollection), newCollection);
1141
1299
  // Prefill cache if necessary by calling get() on any existing keys and then merge original data to cache
1142
- // and update all subscribers with reference preservation for unchanged items
1300
+ // and update all subscribers
1143
1301
  const promiseUpdate = previousCollectionPromise.then((previousCollection) => {
1144
- // Capture the original cached values before merging
1145
- const originalCachedValues = {};
1146
- Object.keys(finalMergedCollection).forEach((key) => {
1147
- originalCachedValues[key] = OnyxCache_1.default.get(key, false);
1148
- });
1149
- // Then merge all the data into cache as normal
1150
1302
  OnyxCache_1.default.merge(finalMergedCollection);
1151
- // Finally, preserve references for items that didn't actually change
1152
- const preservedCollection = preserveCollectionReferencesAfterMerge(finalMergedCollection, originalCachedValues);
1153
- return scheduleNotifyCollectionSubscribers(collectionKey, preservedCollection, previousCollection);
1303
+ return scheduleNotifyCollectionSubscribers(collectionKey, finalMergedCollection, previousCollection);
1154
1304
  });
1155
1305
  return Promise.all(promises)
1156
- .catch((error) => evictStorageAndRetry(error, mergeCollectionWithPatches, collectionKey, resultCollection))
1306
+ .catch((error) => retryOperation(error, mergeCollectionWithPatches, { collectionKey, collection: resultCollection, mergeReplaceNullPatches, isProcessingCollectionUpdate }, retryAttempt))
1157
1307
  .then(() => {
1158
1308
  sendActionToDevTools(METHOD.MERGE_COLLECTION, undefined, resultCollection);
1159
1309
  return promiseUpdate;
@@ -1164,11 +1314,14 @@ function mergeCollectionWithPatches(collectionKey, collection, mergeReplaceNullP
1164
1314
  /**
1165
1315
  * Sets keys in a collection by replacing all targeted collection members with new values.
1166
1316
  * Any existing collection members not included in the new data will not be removed.
1317
+ * Retries on failure.
1167
1318
  *
1168
- * @param collectionKey e.g. `ONYXKEYS.COLLECTION.REPORT`
1169
- * @param collection Object collection keyed by individual collection member keys and values
1319
+ * @param params - collection parameters
1320
+ * @param params.collectionKey e.g. `ONYXKEYS.COLLECTION.REPORT`
1321
+ * @param params.collection Object collection keyed by individual collection member keys and values
1322
+ * @param retryAttempt retry attempt
1170
1323
  */
1171
- function partialSetCollection(collectionKey, collection) {
1324
+ function partialSetCollection({ collectionKey, collection }, retryAttempt) {
1172
1325
  let resultCollection = collection;
1173
1326
  let resultCollectionKeys = Object.keys(resultCollection);
1174
1327
  // Confirm all the collection keys belong to the same parent
@@ -1198,11 +1351,10 @@ function partialSetCollection(collectionKey, collection) {
1198
1351
  const existingKeys = resultCollectionKeys.filter((key) => persistedKeys.has(key));
1199
1352
  const previousCollection = getCachedCollection(collectionKey, existingKeys);
1200
1353
  const keyValuePairs = prepareKeyValuePairsForStorage(mutableCollection, true, undefined, true);
1201
- // Preserve references for unchanged items in partialSetCollection
1202
- const preservedCollection = preserveCollectionReferences(keyValuePairs);
1203
- const updatePromise = scheduleNotifyCollectionSubscribers(collectionKey, preservedCollection, previousCollection);
1354
+ keyValuePairs.forEach(([key, value]) => OnyxCache_1.default.set(key, value));
1355
+ const updatePromise = scheduleNotifyCollectionSubscribers(collectionKey, mutableCollection, previousCollection);
1204
1356
  return storage_1.default.multiSet(keyValuePairs)
1205
- .catch((error) => evictStorageAndRetry(error, partialSetCollection, collectionKey, collection))
1357
+ .catch((error) => retryOperation(error, partialSetCollection, { collectionKey, collection }, retryAttempt))
1206
1358
  .then(() => {
1207
1359
  sendActionToDevTools(METHOD.SET_COLLECTION, undefined, mutableCollection);
1208
1360
  return updatePromise;
@@ -1254,7 +1406,7 @@ const OnyxUtils = {
1254
1406
  scheduleNotifyCollectionSubscribers,
1255
1407
  remove,
1256
1408
  reportStorageQuota,
1257
- evictStorageAndRetry,
1409
+ retryOperation,
1258
1410
  broadcastUpdate,
1259
1411
  hasPendingMergeForKey,
1260
1412
  prepareKeyValuePairsForStorage,
@@ -1277,10 +1429,11 @@ const OnyxUtils = {
1277
1429
  updateSnapshots,
1278
1430
  mergeCollectionWithPatches,
1279
1431
  partialSetCollection,
1280
- preserveCollectionReferences,
1281
- preserveCollectionReferencesAfterMerge,
1282
1432
  logKeyChanged,
1283
1433
  logKeyRemoved,
1434
+ setWithRetry,
1435
+ multiSetWithRetry,
1436
+ setCollectionWithRetry,
1284
1437
  };
1285
1438
  GlobalSettings.addGlobalSettingsChangeListener(({ enablePerformanceMetrics }) => {
1286
1439
  if (!enablePerformanceMetrics) {
@@ -1314,7 +1467,7 @@ GlobalSettings.addGlobalSettingsChangeListener(({ enablePerformanceMetrics }) =>
1314
1467
  // @ts-expect-error Reassign
1315
1468
  reportStorageQuota = (0, metrics_1.default)(reportStorageQuota, 'OnyxUtils.reportStorageQuota');
1316
1469
  // @ts-expect-error Complex type signature
1317
- evictStorageAndRetry = (0, metrics_1.default)(evictStorageAndRetry, 'OnyxUtils.evictStorageAndRetry');
1470
+ retryOperation = (0, metrics_1.default)(retryOperation, 'OnyxUtils.retryOperation');
1318
1471
  // @ts-expect-error Reassign
1319
1472
  broadcastUpdate = (0, metrics_1.default)(broadcastUpdate, 'OnyxUtils.broadcastUpdate');
1320
1473
  // @ts-expect-error Reassign
@@ -1325,5 +1478,11 @@ GlobalSettings.addGlobalSettingsChangeListener(({ enablePerformanceMetrics }) =>
1325
1478
  tupleGet = (0, metrics_1.default)(tupleGet, 'OnyxUtils.tupleGet');
1326
1479
  // @ts-expect-error Reassign
1327
1480
  subscribeToKey = (0, metrics_1.default)(subscribeToKey, 'OnyxUtils.subscribeToKey');
1481
+ // @ts-expect-error Reassign
1482
+ setWithRetry = (0, metrics_1.default)(setWithRetry, 'OnyxUtils.setWithRetry');
1483
+ // @ts-expect-error Reassign
1484
+ multiSetWithRetry = (0, metrics_1.default)(multiSetWithRetry, 'OnyxUtils.multiSetWithRetry');
1485
+ // @ts-expect-error Reassign
1486
+ setCollectionWithRetry = (0, metrics_1.default)(setCollectionWithRetry, 'OnyxUtils.setCollectionWithRetry');
1328
1487
  });
1329
1488
  exports.default = OnyxUtils;
package/dist/types.d.ts CHANGED
@@ -311,6 +311,22 @@ type SetOptions = {
311
311
  /** Skip the deep equality check against the cached value. Improves performance for large objects. */
312
312
  skipCacheCheck?: boolean;
313
313
  };
314
+ type SetParams<TKey extends OnyxKey> = {
315
+ key: TKey;
316
+ value: OnyxSetInput<TKey>;
317
+ options?: SetOptions;
318
+ };
319
+ type SetCollectionParams<TKey extends CollectionKeyBase> = {
320
+ collectionKey: TKey;
321
+ collection: OnyxSetCollectionInput<TKey>;
322
+ };
323
+ type MergeCollectionWithPatchesParams<TKey extends CollectionKeyBase> = {
324
+ collectionKey: TKey;
325
+ collection: OnyxMergeCollectionInput<TKey>;
326
+ mergeReplaceNullPatches?: MultiMergeReplaceNullPatches;
327
+ isProcessingCollectionUpdate?: boolean;
328
+ };
329
+ type RetriableOnyxOperation = typeof OnyxUtils.setWithRetry | typeof OnyxUtils.multiSetWithRetry | typeof OnyxUtils.setCollectionWithRetry | typeof OnyxUtils.mergeCollectionWithPatches | typeof OnyxUtils.partialSetCollection;
314
330
  /**
315
331
  * Represents the options used in `Onyx.init()` method.
316
332
  */
@@ -368,4 +384,4 @@ type MixedOperationsQueue = {
368
384
  mergeReplaceNullPatches: MultiMergeReplaceNullPatches;
369
385
  set: OnyxInputKeyValueMapping;
370
386
  };
371
- export type { BaseConnectOptions, Collection, CollectionConnectCallback, CollectionConnectOptions, CollectionKey, CollectionKeyBase, ConnectOptions, CustomTypeOptions, DeepRecord, DefaultConnectCallback, DefaultConnectOptions, ExtractOnyxCollectionValue, GenericFunction, InitOptions, Key, KeyValueMapping, CallbackToStateMapping, NonNull, NonUndefined, OnyxInputKeyValueMapping, NullishDeep, OnyxCollection, OnyxEntry, OnyxKey, OnyxInputValue, OnyxCollectionInputValue, OnyxInput, OnyxSetInput, OnyxMultiSetInput, OnyxMergeInput, OnyxMergeCollectionInput, OnyxSetCollectionInput, OnyxMethod, OnyxMethodMap, OnyxUpdate, OnyxValue, Selector, SetOptions, MultiMergeReplaceNullPatches, MixedOperationsQueue, };
387
+ export type { BaseConnectOptions, Collection, CollectionConnectCallback, CollectionConnectOptions, CollectionKey, CollectionKeyBase, ConnectOptions, CustomTypeOptions, DeepRecord, DefaultConnectCallback, DefaultConnectOptions, ExtractOnyxCollectionValue, GenericFunction, InitOptions, Key, KeyValueMapping, CallbackToStateMapping, NonNull, NonUndefined, OnyxInputKeyValueMapping, NullishDeep, OnyxCollection, OnyxEntry, OnyxKey, OnyxInputValue, OnyxCollectionInputValue, OnyxInput, OnyxSetInput, OnyxMultiSetInput, OnyxMergeInput, OnyxMergeCollectionInput, OnyxSetCollectionInput, OnyxMethod, OnyxMethodMap, OnyxUpdate, OnyxValue, Selector, SetOptions, SetParams, SetCollectionParams, MergeCollectionWithPatchesParams, MultiMergeReplaceNullPatches, MixedOperationsQueue, RetriableOnyxOperation, };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "3.0.11",
3
+ "version": "3.0.13",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",