react-native-onyx 3.0.115 → 3.0.117
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/API.md +15 -5
- package/README.md +8 -8
- package/dist/Onyx.d.ts +17 -14
- package/dist/Onyx.js +132 -41
- package/dist/OnyxCache.d.ts +2 -2
- package/dist/OnyxCache.js +4 -9
- package/dist/OnyxUtils.d.ts +24 -55
- package/dist/OnyxUtils.js +125 -352
- package/dist/PendingWrites.d.ts +35 -0
- package/dist/PendingWrites.js +103 -0
- package/dist/index.d.ts +1 -2
- package/dist/types.d.ts +10 -13
- package/dist/useOnyx.d.ts +11 -10
- package/dist/useOnyx.js +68 -193
- package/package.json +4 -2
- package/dist/OnyxConnectionManager.d.ts +0 -78
- package/dist/OnyxConnectionManager.js +0 -189
- package/dist/OnyxSnapshotCache.d.ts +0 -75
- package/dist/OnyxSnapshotCache.js +0 -125
- package/dist/createMemoizedSelector.d.ts +0 -18
- package/dist/createMemoizedSelector.js +0 -37
- package/dist/memoizedShallowEqual.d.ts +0 -5
- package/dist/memoizedShallowEqual.js +0 -34
package/dist/OnyxUtils.js
CHANGED
|
@@ -37,13 +37,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.clearOnyxUtilsInternals = clearOnyxUtilsInternals;
|
|
40
|
-
|
|
40
|
+
exports.resetDeferredInitTask = resetDeferredInitTask;
|
|
41
41
|
const underscore_1 = __importDefault(require("underscore"));
|
|
42
42
|
const DevTools_1 = __importDefault(require("./DevTools"));
|
|
43
43
|
const Logger = __importStar(require("./Logger"));
|
|
44
44
|
const OnyxCache_1 = __importStar(require("./OnyxCache"));
|
|
45
45
|
const OnyxKeys_1 = __importDefault(require("./OnyxKeys"));
|
|
46
|
+
const PendingWrites_1 = __importDefault(require("./PendingWrites"));
|
|
46
47
|
const StorageCircuitBreaker_1 = __importDefault(require("./StorageCircuitBreaker"));
|
|
48
|
+
const OnyxSubscriptionManager_1 = __importDefault(require("./OnyxSubscriptionManager"));
|
|
47
49
|
const storage_1 = __importDefault(require("./storage"));
|
|
48
50
|
const errors_1 = require("./storage/errors");
|
|
49
51
|
const utils_1 = __importDefault(require("./utils"));
|
|
@@ -68,25 +70,24 @@ let lastDiskPressureLogTime = 0;
|
|
|
68
70
|
function resetDiskPressureLogThrottle() {
|
|
69
71
|
lastDiskPressureLogTime = 0;
|
|
70
72
|
}
|
|
71
|
-
function formatCaughtError(error) {
|
|
72
|
-
return error instanceof Error ? error.toString() : String(error);
|
|
73
|
-
}
|
|
74
73
|
// Key/value store of Onyx key and arrays of values to merge
|
|
75
74
|
let mergeQueue = {};
|
|
76
75
|
let mergeQueuePromise = {};
|
|
77
|
-
|
|
78
|
-
let callbackToStateMapping = {};
|
|
79
|
-
// Holds a mapping of the connected key to the subscriptionID for faster lookups
|
|
80
|
-
let onyxKeyToSubscriptionIDs = new Map();
|
|
76
|
+
const mergeQueuesWithStaleRead = new WeakSet();
|
|
81
77
|
// Optional user-provided key value states set when Onyx initializes or clears
|
|
82
78
|
let defaultKeyStates = {};
|
|
83
|
-
// Used for comparison with a new update to avoid invoking the Onyx.connect callback with the same data.
|
|
84
|
-
let lastConnectionCallbackData = new Map();
|
|
85
79
|
let snapshotKey = null;
|
|
86
|
-
//
|
|
87
|
-
let
|
|
88
|
-
|
|
89
|
-
|
|
80
|
+
// Connections can be made before `Onyx.init`. They would wait for this task before resolving.
|
|
81
|
+
let deferredInitTask = (0, createDeferredTask_1.default)();
|
|
82
|
+
/**
|
|
83
|
+
* Sentinel for "nothing delivered yet" in `connect()`'s per-subscription dedup. A Symbol
|
|
84
|
+
* can't collide with any real Onyx value, so the first `Object.is` check never matches and
|
|
85
|
+
* the initial fire runs even when a key's genuine first value is `undefined`. It only needs
|
|
86
|
+
* to be distinct from real values, not unique per subscription, so one module-level instance
|
|
87
|
+
* is reused by every connection.
|
|
88
|
+
*/
|
|
89
|
+
// eslint-disable-next-line rulesdir/no-negated-variables
|
|
90
|
+
const NOT_DELIVERED = Symbol('NOT_DELIVERED');
|
|
90
91
|
// Collection member IDs that Onyx should silently ignore across all operations — reads, writes, cache, and subscriber
|
|
91
92
|
// notifications. This is used to filter out keys formed from invalid/default IDs (e.g. "-1", "0",
|
|
92
93
|
// "undefined", "null", "NaN") that can appear when an ID variable is accidentally coerced to string.
|
|
@@ -348,31 +349,6 @@ function multiGet(keys) {
|
|
|
348
349
|
function tupleGet(keys) {
|
|
349
350
|
return Promise.all(keys.map((key) => get(key)));
|
|
350
351
|
}
|
|
351
|
-
/**
|
|
352
|
-
* Stores a subscription ID associated with a given key.
|
|
353
|
-
*
|
|
354
|
-
* @param subscriptionID - A subscription ID of the subscriber.
|
|
355
|
-
* @param key - A key that the subscriber is subscribed to.
|
|
356
|
-
*/
|
|
357
|
-
function storeKeyBySubscriptions(key, subscriptionID) {
|
|
358
|
-
if (!onyxKeyToSubscriptionIDs.has(key)) {
|
|
359
|
-
onyxKeyToSubscriptionIDs.set(key, []);
|
|
360
|
-
}
|
|
361
|
-
onyxKeyToSubscriptionIDs.get(key).push(subscriptionID);
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* Deletes a subscription ID associated with its corresponding key.
|
|
365
|
-
*
|
|
366
|
-
* @param subscriptionID - The subscription ID to be deleted.
|
|
367
|
-
*/
|
|
368
|
-
function deleteKeyBySubscriptions(subscriptionID) {
|
|
369
|
-
const subscriber = callbackToStateMapping[subscriptionID];
|
|
370
|
-
if (subscriber && onyxKeyToSubscriptionIDs.has(subscriber.key)) {
|
|
371
|
-
const updatedSubscriptionsIDs = onyxKeyToSubscriptionIDs.get(subscriber.key).filter((id) => id !== subscriptionID);
|
|
372
|
-
onyxKeyToSubscriptionIDs.set(subscriber.key, updatedSubscriptionsIDs);
|
|
373
|
-
}
|
|
374
|
-
lastConnectionCallbackData.delete(subscriptionID);
|
|
375
|
-
}
|
|
376
352
|
/** Returns current key names stored in persisted storage */
|
|
377
353
|
function getAllKeys() {
|
|
378
354
|
// When we've already read stored keys, resolve right away
|
|
@@ -395,28 +371,6 @@ function getAllKeys() {
|
|
|
395
371
|
});
|
|
396
372
|
return OnyxCache_1.default.captureTask(OnyxCache_1.TASK.GET_ALL_KEYS, promise);
|
|
397
373
|
}
|
|
398
|
-
/**
|
|
399
|
-
* Tries to get a value from the cache. If the value is not present in cache it will return the default value or undefined.
|
|
400
|
-
* If the requested key is a collection, it will return an object with all the collection members.
|
|
401
|
-
*/
|
|
402
|
-
function tryGetCachedValue(key) {
|
|
403
|
-
let val = OnyxCache_1.default.get(key);
|
|
404
|
-
if (OnyxKeys_1.default.isCollectionKey(key)) {
|
|
405
|
-
const collectionData = OnyxCache_1.default.getCollectionData(key);
|
|
406
|
-
if (collectionData !== undefined) {
|
|
407
|
-
val = collectionData;
|
|
408
|
-
}
|
|
409
|
-
else {
|
|
410
|
-
// If we haven't loaded all keys yet, we can't determine if the collection exists
|
|
411
|
-
if (OnyxCache_1.default.getAllKeys().size === 0) {
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
// Set an empty collection object for collections that exist but have no data
|
|
415
|
-
val = {};
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
return val;
|
|
419
|
-
}
|
|
420
374
|
function getCachedCollection(collectionKey, collectionMemberKeys) {
|
|
421
375
|
// Use optimized collection data retrieval when cache is populated
|
|
422
376
|
const collectionData = OnyxCache_1.default.getCollectionData(collectionKey);
|
|
@@ -457,187 +411,42 @@ function getCachedCollection(collectionKey, collectionMemberKeys) {
|
|
|
457
411
|
return collection;
|
|
458
412
|
}
|
|
459
413
|
/**
|
|
460
|
-
*
|
|
461
|
-
|
|
462
|
-
function keysChanged(collectionKey, partialCollection, partialPreviousCollection) {
|
|
463
|
-
var _a;
|
|
464
|
-
const cachedCollection = getCachedCollection(collectionKey);
|
|
465
|
-
const previousCollection = partialPreviousCollection !== null && partialPreviousCollection !== void 0 ? partialPreviousCollection : {};
|
|
466
|
-
const changedMemberKeys = Object.keys(partialCollection !== null && partialCollection !== void 0 ? partialCollection : {});
|
|
467
|
-
// Add or remove the keys from the recentlyAccessedKeys list
|
|
468
|
-
for (const memberKey of changedMemberKeys) {
|
|
469
|
-
const value = partialCollection === null || partialCollection === void 0 ? void 0 : partialCollection[memberKey];
|
|
470
|
-
if (value !== null && value !== undefined) {
|
|
471
|
-
OnyxCache_1.default.addLastAccessedKey(memberKey, false);
|
|
472
|
-
}
|
|
473
|
-
else {
|
|
474
|
-
OnyxCache_1.default.removeLastAccessedKey(memberKey);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
// Use indexed lookup instead of scanning all subscribers.
|
|
478
|
-
// We need subscribers for: (1) the collection key itself, and (2) individual changed member keys.
|
|
479
|
-
const collectionSubscriberIDs = (_a = onyxKeyToSubscriptionIDs.get(collectionKey)) !== null && _a !== void 0 ? _a : [];
|
|
480
|
-
const memberSubscriberIDs = [];
|
|
481
|
-
for (const memberKey of changedMemberKeys) {
|
|
482
|
-
const ids = onyxKeyToSubscriptionIDs.get(memberKey);
|
|
483
|
-
if (ids) {
|
|
484
|
-
for (const id of ids) {
|
|
485
|
-
memberSubscriberIDs.push(id);
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
// Notify collection-level subscribers
|
|
490
|
-
for (const subID of collectionSubscriberIDs) {
|
|
491
|
-
const subscriber = callbackToStateMapping[subID];
|
|
492
|
-
if (!subscriber || typeof subscriber.callback !== 'function') {
|
|
493
|
-
continue;
|
|
494
|
-
}
|
|
495
|
-
try {
|
|
496
|
-
lastConnectionCallbackData.set(subscriber.subscriptionID, { value: cachedCollection, matchedKey: subscriber.key });
|
|
497
|
-
subscriber.callback(cachedCollection, subscriber.key);
|
|
498
|
-
}
|
|
499
|
-
catch (error) {
|
|
500
|
-
Logger.logAlert(`[OnyxUtils.keysChanged] Subscriber callback threw an error for key '${collectionKey}': ${formatCaughtError(error)}`);
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
// Notify member-level subscribers (e.g. subscribed to `report_123`)
|
|
504
|
-
for (const subID of memberSubscriberIDs) {
|
|
505
|
-
const subscriber = callbackToStateMapping[subID];
|
|
506
|
-
if (!subscriber || typeof subscriber.callback !== 'function') {
|
|
507
|
-
continue;
|
|
508
|
-
}
|
|
509
|
-
if (cachedCollection[subscriber.key] === previousCollection[subscriber.key]) {
|
|
510
|
-
continue;
|
|
511
|
-
}
|
|
512
|
-
try {
|
|
513
|
-
const subscriberCallback = subscriber.callback;
|
|
514
|
-
subscriberCallback(cachedCollection[subscriber.key], subscriber.key);
|
|
515
|
-
lastConnectionCallbackData.set(subscriber.subscriptionID, {
|
|
516
|
-
value: cachedCollection[subscriber.key],
|
|
517
|
-
matchedKey: subscriber.key,
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
catch (error) {
|
|
521
|
-
Logger.logAlert(`[OnyxUtils.keysChanged] Subscriber callback threw an error for key '${collectionKey}': ${formatCaughtError(error)}`);
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
/**
|
|
526
|
-
* When a key change happens, search for any callbacks matching the key or collection key and trigger those callbacks
|
|
414
|
+
* Notify subscribers of a single-key write. Wrapper over `onyxSubscriptionManager.notifyKey()`
|
|
415
|
+
* that also performs LRU bookkeeping for eviction.
|
|
527
416
|
*/
|
|
528
|
-
function
|
|
529
|
-
var _a, _b;
|
|
530
|
-
// Add or remove this key from the recentlyAccessedKeys list
|
|
417
|
+
function notifyKey(key, value) {
|
|
531
418
|
if (value !== null && value !== undefined) {
|
|
532
419
|
OnyxCache_1.default.addLastAccessedKey(key, OnyxKeys_1.default.isCollectionKey(key));
|
|
533
420
|
}
|
|
534
421
|
else {
|
|
535
422
|
OnyxCache_1.default.removeLastAccessedKey(key);
|
|
536
423
|
}
|
|
537
|
-
|
|
538
|
-
// notify them if the key that changed is a collection member. Or if it is a regular key notify them when there is an exact match.
|
|
539
|
-
// Given the amount of times this function is called we need to make sure we are not iterating over all subscribers every time. On the other hand, we don't need to
|
|
540
|
-
// do the same in keysChanged, because we only call that function when a collection key changes, and it doesn't happen that often.
|
|
541
|
-
// For performance reason, we look for the given key and later if don't find it we look for the collection key, instead of checking if it is a collection key first.
|
|
542
|
-
let stateMappingKeys = (_a = onyxKeyToSubscriptionIDs.get(key)) !== null && _a !== void 0 ? _a : [];
|
|
543
|
-
const collectionKey = OnyxKeys_1.default.getCollectionKey(key);
|
|
544
|
-
if (collectionKey) {
|
|
545
|
-
// Getting the collection key from the specific key because only collection keys were stored in the mapping.
|
|
546
|
-
stateMappingKeys = [...stateMappingKeys, ...((_b = onyxKeyToSubscriptionIDs.get(collectionKey)) !== null && _b !== void 0 ? _b : [])];
|
|
547
|
-
if (stateMappingKeys.length === 0) {
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
// Cache the collection snapshot per dispatch so all subscribers to the same collection
|
|
552
|
-
// see a consistent view, even if an earlier subscriber's callback synchronously writes
|
|
553
|
-
// to the same collection.
|
|
554
|
-
const cachedCollections = {};
|
|
555
|
-
for (const stateMappingKey of stateMappingKeys) {
|
|
556
|
-
const subscriber = callbackToStateMapping[stateMappingKey];
|
|
557
|
-
if (!subscriber || !OnyxKeys_1.default.isKeyMatch(subscriber.key, key) || !canUpdateSubscriber(subscriber)) {
|
|
558
|
-
continue;
|
|
559
|
-
}
|
|
560
|
-
// Subscriber is a regular call to connect() and provided a callback
|
|
561
|
-
if (typeof subscriber.callback === 'function') {
|
|
562
|
-
try {
|
|
563
|
-
const lastData = lastConnectionCallbackData.get(subscriber.subscriptionID);
|
|
564
|
-
if (lastData && lastData.matchedKey === key && lastData.value === value) {
|
|
565
|
-
continue;
|
|
566
|
-
}
|
|
567
|
-
if (OnyxKeys_1.default.isCollectionKey(subscriber.key)) {
|
|
568
|
-
// Cache once per dispatch to ensure all subscribers see a consistent snapshot
|
|
569
|
-
// even if a previous callback synchronously wrote to the same collection.
|
|
570
|
-
let cachedCollection = cachedCollections[subscriber.key];
|
|
571
|
-
if (!cachedCollection) {
|
|
572
|
-
cachedCollection = getCachedCollection(subscriber.key);
|
|
573
|
-
cachedCollections[subscriber.key] = cachedCollection;
|
|
574
|
-
}
|
|
575
|
-
lastConnectionCallbackData.set(subscriber.subscriptionID, { value: cachedCollection, matchedKey: subscriber.key });
|
|
576
|
-
subscriber.callback(cachedCollection, subscriber.key);
|
|
577
|
-
continue;
|
|
578
|
-
}
|
|
579
|
-
const subscriberCallback = subscriber.callback;
|
|
580
|
-
subscriberCallback(value, key);
|
|
581
|
-
lastConnectionCallbackData.set(subscriber.subscriptionID, {
|
|
582
|
-
value,
|
|
583
|
-
matchedKey: key,
|
|
584
|
-
});
|
|
585
|
-
continue;
|
|
586
|
-
}
|
|
587
|
-
catch (error) {
|
|
588
|
-
Logger.logAlert(`[OnyxUtils.keyChanged] Subscriber callback threw an error for key '${key}': ${formatCaughtError(error)}`);
|
|
589
|
-
}
|
|
590
|
-
continue;
|
|
591
|
-
}
|
|
592
|
-
console.error('Warning: Found a matching subscriber to a key that changed, but no callback could be found.');
|
|
593
|
-
}
|
|
424
|
+
OnyxSubscriptionManager_1.default.notifyKey(key, value);
|
|
594
425
|
}
|
|
595
426
|
/**
|
|
596
|
-
*
|
|
427
|
+
* Notify subscribers of a batch collection update. Wrapper over
|
|
428
|
+
* `onyxSubscriptionManager.notifyCollection()` that also performs LRU bookkeeping per
|
|
429
|
+
* changed member.
|
|
597
430
|
*/
|
|
598
|
-
function
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
let value;
|
|
609
|
-
if (OnyxKeys_1.default.isCollectionKey(mapping.key)) {
|
|
610
|
-
const collection = getCachedCollection(mapping.key);
|
|
611
|
-
value = Object.keys(collection).length > 0 ? collection : undefined;
|
|
612
|
-
}
|
|
613
|
-
else {
|
|
614
|
-
value = OnyxCache_1.default.get(matchedKey !== null && matchedKey !== void 0 ? matchedKey : mapping.key);
|
|
615
|
-
}
|
|
616
|
-
// For regular callbacks, we never want to pass null values, but always just undefined if a value is not set in cache or storage.
|
|
617
|
-
value = value === null ? undefined : value;
|
|
618
|
-
const lastData = lastConnectionCallbackData.get(mapping.subscriptionID);
|
|
619
|
-
// If the value has not changed for the same key we do not need to trigger the callback.
|
|
620
|
-
// We compare matchedKey to avoid suppressing callbacks for different collection members
|
|
621
|
-
// that happen to have shallow-equal values (e.g. during hydration racing with set()).
|
|
622
|
-
if (lastData && lastData.matchedKey === matchedKey && (0, fast_equals_1.shallowEqual)(lastData.value, value)) {
|
|
623
|
-
return;
|
|
431
|
+
function notifyCollection(collectionKey, partialCollection, partialPreviousCollection) {
|
|
432
|
+
const changedKeys = Object.keys(partialCollection !== null && partialCollection !== void 0 ? partialCollection : {});
|
|
433
|
+
for (const memberKey of changedKeys) {
|
|
434
|
+
const value = partialCollection === null || partialCollection === void 0 ? void 0 : partialCollection[memberKey];
|
|
435
|
+
if (value !== null && value !== undefined) {
|
|
436
|
+
OnyxCache_1.default.addLastAccessedKey(memberKey, false);
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
OnyxCache_1.default.removeLastAccessedKey(memberKey);
|
|
440
|
+
}
|
|
624
441
|
}
|
|
625
|
-
|
|
626
|
-
}
|
|
627
|
-
/**
|
|
628
|
-
* Gets the data for a given an array of matching keys, combines them into an object, and sends the result back to the subscriber.
|
|
629
|
-
*/
|
|
630
|
-
function getCollectionDataAndSendAsObject(matchingKeys, mapping) {
|
|
631
|
-
multiGet(matchingKeys).then(() => {
|
|
632
|
-
sendDataToConnection(mapping, mapping.key);
|
|
633
|
-
});
|
|
442
|
+
OnyxSubscriptionManager_1.default.notifyCollection(collectionKey, partialCollection, partialPreviousCollection);
|
|
634
443
|
}
|
|
635
444
|
/**
|
|
636
|
-
* Remove a key from Onyx and update the subscribers
|
|
445
|
+
* Remove a key from Onyx and update the subscribers.
|
|
637
446
|
*/
|
|
638
447
|
function remove(key) {
|
|
639
448
|
OnyxCache_1.default.drop(key);
|
|
640
|
-
|
|
449
|
+
notifyKey(key, undefined);
|
|
641
450
|
if (OnyxKeys_1.default.isRamOnlyKey(key)) {
|
|
642
451
|
return Promise.resolve();
|
|
643
452
|
}
|
|
@@ -768,11 +577,57 @@ function broadcastUpdate(key, value, hasChanged) {
|
|
|
768
577
|
return;
|
|
769
578
|
}
|
|
770
579
|
OnyxCache_1.default.set(key, value);
|
|
771
|
-
|
|
580
|
+
notifyKey(key, value);
|
|
772
581
|
}
|
|
773
582
|
function hasPendingMergeForKey(key) {
|
|
774
583
|
return !!mergeQueue[key];
|
|
775
584
|
}
|
|
585
|
+
function cancelPendingMergesForKey(key) {
|
|
586
|
+
delete mergeQueue[key];
|
|
587
|
+
delete mergeQueuePromise[key];
|
|
588
|
+
}
|
|
589
|
+
function cancelPendingMergesForCollection(collectionKey) {
|
|
590
|
+
for (const key of Object.keys(mergeQueue)) {
|
|
591
|
+
if (!OnyxKeys_1.default.isCollectionMemberKey(collectionKey, key)) {
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
cancelPendingMergesForKey(key);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function getPendingMergeEntries(keysToPreserve) {
|
|
598
|
+
return Object.entries(mergeQueue)
|
|
599
|
+
.filter(([key]) => !keysToPreserve.some((preserveKey) => OnyxKeys_1.default.isKeyMatch(preserveKey, key)))
|
|
600
|
+
.map(([key, queuedChanges]) => [key, queuedChanges, queuedChanges.length]);
|
|
601
|
+
}
|
|
602
|
+
function cancelPendingMerges(entries) {
|
|
603
|
+
for (const [key, queuedChanges, capturedLength] of entries) {
|
|
604
|
+
if (mergeQueue[key] !== queuedChanges) {
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
607
|
+
if (queuedChanges.length === capturedLength) {
|
|
608
|
+
cancelPendingMergesForKey(key);
|
|
609
|
+
continue;
|
|
610
|
+
}
|
|
611
|
+
queuedChanges.splice(0, capturedLength);
|
|
612
|
+
mergeQueuesWithStaleRead.add(queuedChanges);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
function hasStaleMergeRead(queuedChanges) {
|
|
616
|
+
return mergeQueuesWithStaleRead.has(queuedChanges);
|
|
617
|
+
}
|
|
618
|
+
function cancelPendingMergesForKeys(keys) {
|
|
619
|
+
for (const key of keys) {
|
|
620
|
+
cancelPendingMergesForKey(key);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
function cancelPendingMergesForNullMembers(collection) {
|
|
624
|
+
for (const [key, value] of Object.entries(collection)) {
|
|
625
|
+
if (value !== null) {
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
cancelPendingMergesForKey(key);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
776
631
|
/**
|
|
777
632
|
* Storage expects array like: [["@MyApp_user", value_1], ["@MyApp_key", value_2]]
|
|
778
633
|
* This method transforms an object like {'@MyApp_user': myUserValue, '@MyApp_key': myKeyValue}
|
|
@@ -892,7 +747,7 @@ function initializeWithDefaultKeyStates() {
|
|
|
892
747
|
// Notify subscribers about default key states so that any subscriber that connected
|
|
893
748
|
// before init (e.g. during module load) receives the merged default values immediately
|
|
894
749
|
for (const [key, value] of Object.entries(merged !== null && merged !== void 0 ? merged : {})) {
|
|
895
|
-
|
|
750
|
+
notifyKey(key, value);
|
|
896
751
|
}
|
|
897
752
|
})
|
|
898
753
|
.catch((error) => {
|
|
@@ -907,7 +762,7 @@ function initializeWithDefaultKeyStates() {
|
|
|
907
762
|
// Notify subscribers about default key states so that any subscriber that connected
|
|
908
763
|
// before init (e.g. during module load) receives the merged default values immediately
|
|
909
764
|
for (const [key, value] of Object.entries(defaultKeyStates)) {
|
|
910
|
-
|
|
765
|
+
notifyKey(key, value);
|
|
911
766
|
}
|
|
912
767
|
});
|
|
913
768
|
}
|
|
@@ -934,97 +789,6 @@ function doAllCollectionItemsBelongToSameParent(collectionKey, collectionKeys) {
|
|
|
934
789
|
}
|
|
935
790
|
return !hasCollectionKeyCheckFailed;
|
|
936
791
|
}
|
|
937
|
-
/**
|
|
938
|
-
* Subscribes to an Onyx key and listens to its changes.
|
|
939
|
-
*
|
|
940
|
-
* @param connectOptions The options object that will define the behavior of the connection.
|
|
941
|
-
* @returns The subscription ID to use when calling `OnyxUtils.unsubscribeFromKey()`.
|
|
942
|
-
*/
|
|
943
|
-
function subscribeToKey(connectOptions) {
|
|
944
|
-
const mapping = connectOptions;
|
|
945
|
-
const subscriptionID = lastSubscriptionID++;
|
|
946
|
-
callbackToStateMapping[subscriptionID] = mapping;
|
|
947
|
-
callbackToStateMapping[subscriptionID].subscriptionID = subscriptionID;
|
|
948
|
-
// When keyChanged is called, a key is passed and the method looks through all the Subscribers in callbackToStateMapping for the matching key to get the subscriptionID
|
|
949
|
-
// to avoid having to loop through all the Subscribers all the time (even when just one connection belongs to one key),
|
|
950
|
-
// We create a mapping from key to lists of subscriptionIDs to access the specific list of subscriptionIDs.
|
|
951
|
-
storeKeyBySubscriptions(mapping.key, callbackToStateMapping[subscriptionID].subscriptionID);
|
|
952
|
-
// Commit connection only after init passes
|
|
953
|
-
deferredInitTask.promise
|
|
954
|
-
// This first .then() adds a microtask tick for compatibility reasons and
|
|
955
|
-
// to ensure subscribers don't receive an extra initial callback before Onyx.update() data arrives.
|
|
956
|
-
.then(() => undefined)
|
|
957
|
-
.then(() => {
|
|
958
|
-
// Performance improvement
|
|
959
|
-
// If the mapping is connected to an onyx key that is not a collection
|
|
960
|
-
// we can skip the call to getAllKeys() and return an array with a single item
|
|
961
|
-
if (!!mapping.key && typeof mapping.key === 'string' && !OnyxKeys_1.default.isCollectionKey(mapping.key) && OnyxCache_1.default.getAllKeys().has(mapping.key)) {
|
|
962
|
-
return new Set([mapping.key]);
|
|
963
|
-
}
|
|
964
|
-
return getAllKeys();
|
|
965
|
-
})
|
|
966
|
-
.then((keys) => {
|
|
967
|
-
// We search all the keys in storage to see if any are a "match" for the subscriber we are connecting so that we
|
|
968
|
-
// can send data back to the subscriber. Note that multiple keys can match as a subscriber could either be
|
|
969
|
-
// subscribed to a "collection key" or a single key.
|
|
970
|
-
const matchingKeys = [];
|
|
971
|
-
// Performance optimization: For single key subscriptions, avoid O(n) iteration
|
|
972
|
-
if (!OnyxKeys_1.default.isCollectionKey(mapping.key)) {
|
|
973
|
-
if (keys.has(mapping.key)) {
|
|
974
|
-
matchingKeys.push(mapping.key);
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
else {
|
|
978
|
-
// Collection case - need to iterate through all keys to find matches (O(n))
|
|
979
|
-
for (const key of keys) {
|
|
980
|
-
if (!OnyxKeys_1.default.isKeyMatch(mapping.key, key)) {
|
|
981
|
-
continue;
|
|
982
|
-
}
|
|
983
|
-
matchingKeys.push(key);
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
// If the key being connected to does not exist we initialize the value with null. For subscribers that connected
|
|
987
|
-
// directly via connect() they will simply get a null value sent to them without any information about which key matched
|
|
988
|
-
// since there are none matched.
|
|
989
|
-
if (matchingKeys.length === 0) {
|
|
990
|
-
if (mapping.key) {
|
|
991
|
-
OnyxCache_1.default.addNullishStorageKey(mapping.key);
|
|
992
|
-
}
|
|
993
|
-
const matchedKey = OnyxKeys_1.default.isCollectionKey(mapping.key) ? mapping.key : undefined;
|
|
994
|
-
// Here we cannot use batching because the nullish value is expected to be set immediately for default props
|
|
995
|
-
// or they will be undefined.
|
|
996
|
-
sendDataToConnection(mapping, matchedKey);
|
|
997
|
-
return;
|
|
998
|
-
}
|
|
999
|
-
// When using a callback subscriber, a subscription to a collection key combines all matching
|
|
1000
|
-
// member values into a single object and makes one call with the whole collection object.
|
|
1001
|
-
if (typeof mapping.callback === 'function') {
|
|
1002
|
-
if (OnyxKeys_1.default.isCollectionKey(mapping.key)) {
|
|
1003
|
-
getCollectionDataAndSendAsObject(matchingKeys, mapping);
|
|
1004
|
-
return;
|
|
1005
|
-
}
|
|
1006
|
-
// If we are not subscribed to a collection key then there's only a single key to send an update for.
|
|
1007
|
-
get(mapping.key).then(() => sendDataToConnection(mapping, mapping.key));
|
|
1008
|
-
return;
|
|
1009
|
-
}
|
|
1010
|
-
console.error('Warning: Onyx.connect() was found without a callback');
|
|
1011
|
-
});
|
|
1012
|
-
// The subscriptionID is returned back to the caller so that it can be used to clean up the connection when it's no longer needed
|
|
1013
|
-
// by calling OnyxUtils.unsubscribeFromKey(subscriptionID).
|
|
1014
|
-
return subscriptionID;
|
|
1015
|
-
}
|
|
1016
|
-
/**
|
|
1017
|
-
* Disconnects and removes the listener from the Onyx key.
|
|
1018
|
-
*
|
|
1019
|
-
* @param subscriptionID Subscription ID returned by calling `OnyxUtils.subscribeToKey()`.
|
|
1020
|
-
*/
|
|
1021
|
-
function unsubscribeFromKey(subscriptionID) {
|
|
1022
|
-
if (!callbackToStateMapping[subscriptionID]) {
|
|
1023
|
-
return;
|
|
1024
|
-
}
|
|
1025
|
-
deleteKeyBySubscriptions(subscriptionID);
|
|
1026
|
-
delete callbackToStateMapping[subscriptionID];
|
|
1027
|
-
}
|
|
1028
792
|
function updateSnapshots(data, mergeFn) {
|
|
1029
793
|
const snapshotCollectionKey = getSnapshotKey();
|
|
1030
794
|
if (!snapshotCollectionKey)
|
|
@@ -1197,14 +961,17 @@ function multiSetWithRetry(data, retryAttempt) {
|
|
|
1197
961
|
}, {});
|
|
1198
962
|
}
|
|
1199
963
|
const { pairs: keyValuePairsToSet, keysToRemove: removalCandidates } = OnyxUtils.prepareKeyValuePairsForStorage(newData, true);
|
|
964
|
+
if (!retryAttempt) {
|
|
965
|
+
cancelPendingMergesForKeys(removalCandidates);
|
|
966
|
+
}
|
|
1200
967
|
// Removals of keys that are neither cached nor persisted are no-ops and skipped. When the key
|
|
1201
968
|
// index has not been loaded yet (empty set), keep the removal to be safe.
|
|
1202
969
|
const persistedKeys = OnyxCache_1.default.getAllKeys();
|
|
1203
970
|
const keysToRemove = removalCandidates.filter((key) => OnyxCache_1.default.get(key) !== undefined || persistedKeys.size === 0 || persistedKeys.has(key));
|
|
1204
971
|
// Group collection members by their parent collection key so each collection can be notified
|
|
1205
|
-
// via a single batched
|
|
972
|
+
// via a single batched notifyCollection() call instead of one notifyKey() per member. For each
|
|
1206
973
|
// collection, `partial` holds the new values being set and `previous` holds the cached values
|
|
1207
|
-
// from before the set, which
|
|
974
|
+
// from before the set, which notifyCollection() uses to skip subscribers whose value didn't change.
|
|
1208
975
|
const collectionBatches = new Map();
|
|
1209
976
|
for (const [key, value] of keyValuePairsToSet) {
|
|
1210
977
|
// When we use multiSet to set a key we want to clear the current delta changes from Onyx.merge that were queued
|
|
@@ -1214,7 +981,7 @@ function multiSetWithRetry(data, retryAttempt) {
|
|
|
1214
981
|
}
|
|
1215
982
|
const collectionKey = OnyxKeys_1.default.getCollectionKey(key);
|
|
1216
983
|
if (collectionKey && OnyxKeys_1.default.isCollectionMemberKey(collectionKey, key)) {
|
|
1217
|
-
// Capture the previous cached value
|
|
984
|
+
// Capture the previous cached value before calling cache.set() so notifyCollection()
|
|
1218
985
|
// can diff old vs new per-member.
|
|
1219
986
|
const previousValue = OnyxCache_1.default.get(key);
|
|
1220
987
|
OnyxCache_1.default.set(key, value);
|
|
@@ -1227,14 +994,13 @@ function multiSetWithRetry(data, retryAttempt) {
|
|
|
1227
994
|
batch.previous[key] = previousValue;
|
|
1228
995
|
}
|
|
1229
996
|
else {
|
|
1230
|
-
// Non-collection keys are notified inline (cache.set +
|
|
997
|
+
// Non-collection keys are notified inline (cache.set + notifyKey in iteration order)
|
|
1231
998
|
// so re-entrant callbacks (e.g. Onyx.set inside a callback) see consistent cache
|
|
1232
999
|
// and subscriber state, matching the original per-key notification semantics.
|
|
1233
1000
|
OnyxCache_1.default.set(key, value);
|
|
1234
1001
|
// Skip subscriber notification on retry — already notified on attempt 0.
|
|
1235
|
-
// Collection-root subscribers re-fire on every keyChanged by contract.
|
|
1236
1002
|
if (!retryAttempt) {
|
|
1237
|
-
|
|
1003
|
+
notifyKey(key, value);
|
|
1238
1004
|
}
|
|
1239
1005
|
}
|
|
1240
1006
|
}
|
|
@@ -1255,15 +1021,15 @@ function multiSetWithRetry(data, retryAttempt) {
|
|
|
1255
1021
|
}
|
|
1256
1022
|
else if (!retryAttempt) {
|
|
1257
1023
|
// Skip subscriber notification on retry — already notified on attempt 0.
|
|
1258
|
-
|
|
1024
|
+
notifyKey(key, undefined);
|
|
1259
1025
|
}
|
|
1260
1026
|
}
|
|
1261
|
-
// One
|
|
1262
|
-
//
|
|
1027
|
+
// One notifyCollection() per collection: fires each collection-level subscriber once and lets
|
|
1028
|
+
// notifyCollection() internally decide which individual member subscribers need notification.
|
|
1263
1029
|
// Skip on retry — already notified on attempt 0 (see same-reason comment above).
|
|
1264
1030
|
if (!retryAttempt) {
|
|
1265
1031
|
for (const [collectionKey, batch] of collectionBatches) {
|
|
1266
|
-
|
|
1032
|
+
notifyCollection(collectionKey, batch.partial, batch.previous);
|
|
1267
1033
|
}
|
|
1268
1034
|
}
|
|
1269
1035
|
const keyValuePairsToStore = keyValuePairsToSet.filter((keyValuePair) => {
|
|
@@ -1321,6 +1087,9 @@ function setCollectionWithRetry({ collectionKey, collection }, retryAttempt) {
|
|
|
1321
1087
|
}, {});
|
|
1322
1088
|
}
|
|
1323
1089
|
resultCollectionKeys = Object.keys(resultCollection);
|
|
1090
|
+
if (!retryAttempt) {
|
|
1091
|
+
cancelPendingMergesForCollection(collectionKey);
|
|
1092
|
+
}
|
|
1324
1093
|
return OnyxUtils.getAllKeys().then((persistedKeys) => {
|
|
1325
1094
|
const mutableCollection = Object.assign({}, resultCollection);
|
|
1326
1095
|
for (const key of persistedKeys) {
|
|
@@ -1335,18 +1104,17 @@ function setCollectionWithRetry({ collectionKey, collection }, retryAttempt) {
|
|
|
1335
1104
|
const { pairs: keyValuePairs, keysToRemove: removalCandidates } = OnyxUtils.prepareKeyValuePairsForStorage(mutableCollection, true);
|
|
1336
1105
|
// Removals of keys that are neither cached nor persisted are no-ops and skipped.
|
|
1337
1106
|
const keysToRemove = removalCandidates.filter((key) => OnyxCache_1.default.get(key) !== undefined || persistedKeys.has(key));
|
|
1338
|
-
//
|
|
1107
|
+
// Capture the previous collection before cache mutations so notifyCollection() can diff removed members.
|
|
1339
1108
|
const previousCollection = OnyxUtils.getCachedCollection(collectionKey);
|
|
1340
1109
|
for (const [key, value] of keyValuePairs)
|
|
1341
1110
|
OnyxCache_1.default.set(key, value);
|
|
1342
1111
|
for (const key of keysToRemove)
|
|
1343
1112
|
OnyxCache_1.default.drop(key);
|
|
1344
1113
|
// Skip subscriber notification on retry — already notified on attempt 0.
|
|
1345
|
-
// Collection-root subscribers re-fire on every keysChanged by contract.
|
|
1346
1114
|
if (!retryAttempt) {
|
|
1347
1115
|
// Removed members are notified as undefined, matching mergeCollection/multiSet.
|
|
1348
1116
|
const partialForNotify = Object.fromEntries(Object.entries(mutableCollection).map(([key, value]) => [key, value !== null && value !== void 0 ? value : undefined]));
|
|
1349
|
-
|
|
1117
|
+
notifyCollection(collectionKey, partialForNotify, previousCollection);
|
|
1350
1118
|
}
|
|
1351
1119
|
// RAM-only keys are not supposed to be saved to storage
|
|
1352
1120
|
if (OnyxKeys_1.default.isRamOnlyKey(collectionKey)) {
|
|
@@ -1408,6 +1176,9 @@ function mergeCollectionWithPatches({ collectionKey, collection, mergeReplaceNul
|
|
|
1408
1176
|
}, {});
|
|
1409
1177
|
}
|
|
1410
1178
|
resultCollectionKeys = Object.keys(resultCollection);
|
|
1179
|
+
if (!retryAttempt) {
|
|
1180
|
+
cancelPendingMergesForNullMembers(resultCollection);
|
|
1181
|
+
}
|
|
1411
1182
|
return getAllKeys()
|
|
1412
1183
|
.then((persistedKeys) => {
|
|
1413
1184
|
// Split to keys that exist in storage and keys that don't. Null members are collected
|
|
@@ -1473,13 +1244,13 @@ function mergeCollectionWithPatches({ collectionKey, collection, mergeReplaceNul
|
|
|
1473
1244
|
// promise-chain depth; slow path batches the misses into one Storage.multiGet.
|
|
1474
1245
|
const hasColdExistingKey = existingKeys.some((key) => !OnyxCache_1.default.hasCacheForKey(key));
|
|
1475
1246
|
// Swallow pre-warm read failures so a transient Storage.multiGet rejection doesn't
|
|
1476
|
-
// skip the cache.merge() +
|
|
1247
|
+
// skip the cache.merge() + notifyCollection() below. Subscribers still see the merge even
|
|
1477
1248
|
// when storage reads fail.
|
|
1478
1249
|
const prewarmPromise = hasColdExistingKey
|
|
1479
1250
|
? multiGet(existingKeys).catch((err) => Logger.logInfo(`mergeCollectionWithPatches pre-warm failed; proceeding with cache-only merge. Error: ${err}`))
|
|
1480
1251
|
: Promise.resolve();
|
|
1481
1252
|
return prewarmPromise.then(() => {
|
|
1482
|
-
//
|
|
1253
|
+
// Capture previous values from the (now-warm) cache for the subscriber diff, then update
|
|
1483
1254
|
// cache and notify subscribers synchronously BEFORE issuing storage writes. This matches
|
|
1484
1255
|
// the cache-first / storage-second invariant followed by every other Onyx write method
|
|
1485
1256
|
// (setWithRetry, applyMerge, setCollectionWithRetry, partialSetCollection, clear),
|
|
@@ -1488,12 +1259,11 @@ function mergeCollectionWithPatches({ collectionKey, collection, mergeReplaceNul
|
|
|
1488
1259
|
const previousCollection = getCachedCollection(collectionKey, existingKeys);
|
|
1489
1260
|
OnyxCache_1.default.merge(finalMergedCollection);
|
|
1490
1261
|
// Skip subscriber notification on retry — already notified on attempt 0.
|
|
1491
|
-
// Collection-root subscribers re-fire on every keysChanged by contract.
|
|
1492
1262
|
if (!retryAttempt) {
|
|
1493
1263
|
const partialForNotify = keysToRemove.length > 0 ? Object.assign(Object.assign({}, finalMergedCollection), Object.fromEntries(keysToRemove.map((key) => [key, undefined]))) : finalMergedCollection;
|
|
1494
1264
|
const previousForNotify = keysToRemove.length > 0 ? Object.assign(Object.assign({}, previousCollection), removedPreviousValues) : previousCollection;
|
|
1495
1265
|
if (Object.keys(partialForNotify).length > 0) {
|
|
1496
|
-
|
|
1266
|
+
notifyCollection(collectionKey, partialForNotify, previousForNotify);
|
|
1497
1267
|
}
|
|
1498
1268
|
}
|
|
1499
1269
|
const promises = [];
|
|
@@ -1561,24 +1331,26 @@ function partialSetCollection({ collectionKey, collection }, retryAttempt) {
|
|
|
1561
1331
|
}, {});
|
|
1562
1332
|
}
|
|
1563
1333
|
resultCollectionKeys = Object.keys(resultCollection);
|
|
1334
|
+
if (!retryAttempt) {
|
|
1335
|
+
cancelPendingMergesForKeys(resultCollectionKeys);
|
|
1336
|
+
}
|
|
1564
1337
|
return getAllKeys().then((persistedKeys) => {
|
|
1565
1338
|
const mutableCollection = Object.assign({}, resultCollection);
|
|
1566
1339
|
const existingKeys = resultCollectionKeys.filter((key) => persistedKeys.has(key));
|
|
1567
1340
|
const { pairs: keyValuePairs, keysToRemove: removalCandidates } = prepareKeyValuePairsForStorage(mutableCollection, true);
|
|
1568
1341
|
// Removals of keys that are neither cached nor persisted are no-ops and skipped.
|
|
1569
1342
|
const keysToRemove = removalCandidates.filter((key) => OnyxCache_1.default.get(key) !== undefined || persistedKeys.has(key));
|
|
1570
|
-
//
|
|
1343
|
+
// Capture the previous collection before cache mutations so notifyCollection() can diff removed members.
|
|
1571
1344
|
const previousCollection = getCachedCollection(collectionKey, existingKeys);
|
|
1572
1345
|
for (const [key, value] of keyValuePairs)
|
|
1573
1346
|
OnyxCache_1.default.set(key, value);
|
|
1574
1347
|
for (const key of keysToRemove)
|
|
1575
1348
|
OnyxCache_1.default.drop(key);
|
|
1576
1349
|
// Skip subscriber notification on retry — already notified on attempt 0.
|
|
1577
|
-
// Collection-root subscribers re-fire on every keysChanged by contract.
|
|
1578
1350
|
if (!retryAttempt) {
|
|
1579
1351
|
// Removed members are notified as undefined, matching mergeCollection/multiSet.
|
|
1580
1352
|
const partialForNotify = Object.fromEntries(Object.entries(mutableCollection).map(([key, value]) => [key, value !== null && value !== void 0 ? value : undefined]));
|
|
1581
|
-
|
|
1353
|
+
notifyCollection(collectionKey, partialForNotify, previousCollection);
|
|
1582
1354
|
}
|
|
1583
1355
|
if (OnyxKeys_1.default.isRamOnlyKey(collectionKey)) {
|
|
1584
1356
|
sendActionToDevTools(METHOD.SET_COLLECTION, undefined, mutableCollection);
|
|
@@ -1605,18 +1377,23 @@ function logKeyChanged(onyxMethod, key, value, hasChanged) {
|
|
|
1605
1377
|
function logKeyRemoved(onyxMethod, key) {
|
|
1606
1378
|
Logger.logInfo(`${onyxMethod} called for key: ${key} => null passed, so key was removed`);
|
|
1607
1379
|
}
|
|
1380
|
+
/**
|
|
1381
|
+
* Put Onyx back to "not initialised" state, useful for tests that need a cold start.
|
|
1382
|
+
*/
|
|
1383
|
+
function resetDeferredInitTask() {
|
|
1384
|
+
deferredInitTask = (0, createDeferredTask_1.default)();
|
|
1385
|
+
}
|
|
1608
1386
|
/**
|
|
1609
1387
|
* Clear internal variables used in this file, useful in test environments.
|
|
1610
1388
|
*/
|
|
1611
1389
|
function clearOnyxUtilsInternals() {
|
|
1612
1390
|
mergeQueue = {};
|
|
1613
1391
|
mergeQueuePromise = {};
|
|
1614
|
-
|
|
1615
|
-
onyxKeyToSubscriptionIDs = new Map();
|
|
1616
|
-
lastConnectionCallbackData = new Map();
|
|
1392
|
+
PendingWrites_1.default.clearPendingWrites();
|
|
1617
1393
|
}
|
|
1618
1394
|
const OnyxUtils = {
|
|
1619
1395
|
METHOD,
|
|
1396
|
+
NOT_DELIVERED,
|
|
1620
1397
|
getMergeQueue,
|
|
1621
1398
|
getMergeQueuePromise,
|
|
1622
1399
|
getDefaultKeyStates,
|
|
@@ -1626,18 +1403,18 @@ const OnyxUtils = {
|
|
|
1626
1403
|
sendActionToDevTools,
|
|
1627
1404
|
get,
|
|
1628
1405
|
getAllKeys,
|
|
1629
|
-
tryGetCachedValue,
|
|
1630
1406
|
getCachedCollection,
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
sendDataToConnection,
|
|
1634
|
-
getCollectionDataAndSendAsObject,
|
|
1407
|
+
notifyKey,
|
|
1408
|
+
notifyCollection,
|
|
1635
1409
|
remove,
|
|
1636
1410
|
reportStorageQuota,
|
|
1637
1411
|
resetDiskPressureLogThrottle,
|
|
1638
1412
|
retryOperation,
|
|
1639
1413
|
broadcastUpdate,
|
|
1640
1414
|
hasPendingMergeForKey,
|
|
1415
|
+
getPendingMergeEntries,
|
|
1416
|
+
cancelPendingMerges,
|
|
1417
|
+
hasStaleMergeRead,
|
|
1641
1418
|
prepareKeyValuePairsForStorage,
|
|
1642
1419
|
mergeChanges,
|
|
1643
1420
|
mergeAndMarkChanges,
|
|
@@ -1647,14 +1424,10 @@ const OnyxUtils = {
|
|
|
1647
1424
|
tupleGet,
|
|
1648
1425
|
isValidNonEmptyCollectionForMerge,
|
|
1649
1426
|
doAllCollectionItemsBelongToSameParent,
|
|
1650
|
-
subscribeToKey,
|
|
1651
|
-
unsubscribeFromKey,
|
|
1652
1427
|
getSkippableCollectionMemberIDs,
|
|
1653
1428
|
setSkippableCollectionMemberIDs,
|
|
1654
1429
|
getSnapshotMergeKeys,
|
|
1655
1430
|
setSnapshotMergeKeys,
|
|
1656
|
-
storeKeyBySubscriptions,
|
|
1657
|
-
deleteKeyBySubscriptions,
|
|
1658
1431
|
reduceCollectionWithSelector,
|
|
1659
1432
|
updateSnapshots,
|
|
1660
1433
|
mergeCollectionWithPatches,
|