react-native-onyx 2.0.3 → 2.0.5
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 +31 -25
- package/dist/OnyxCache.d.ts +34 -80
- package/dist/OnyxCache.js +27 -83
- package/dist/storage/NativeStorage.d.ts +1 -1
- package/dist/storage/WebStorage.d.ts +2 -18
- package/dist/storage/WebStorage.js +4 -6
- package/dist/storage/__mocks__/index.d.ts +21 -21
- package/dist/storage/__mocks__/index.js +7 -8
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/index.native.d.ts +1 -1
- package/dist/storage/providers/IDBKeyVal.d.ts +2 -25
- package/dist/storage/providers/IDBKeyVal.js +14 -69
- package/dist/storage/providers/SQLiteStorage.d.ts +2 -51
- package/dist/storage/providers/SQLiteStorage.js +19 -81
- package/dist/storage/providers/types.d.ts +68 -0
- package/dist/storage/providers/types.js +2 -0
- package/dist/utils.d.ts +16 -9
- package/dist/utils.js +31 -42
- package/dist/withOnyx.js +1 -1
- package/package.json +3 -1
package/dist/Onyx.js
CHANGED
|
@@ -486,7 +486,7 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
|
|
|
486
486
|
const data = cachedCollection[subscriber.key];
|
|
487
487
|
const previousData = prevState[subscriber.statePropertyName];
|
|
488
488
|
// Avoids triggering unnecessary re-renders when feeding empty objects
|
|
489
|
-
if (utils_1.default.
|
|
489
|
+
if (utils_1.default.isEmptyObject(data) && utils_1.default.isEmptyObject(previousData)) {
|
|
490
490
|
return null;
|
|
491
491
|
}
|
|
492
492
|
if (data === previousData) {
|
|
@@ -510,11 +510,12 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
|
|
|
510
510
|
* @private
|
|
511
511
|
* @param {String} key
|
|
512
512
|
* @param {*} data
|
|
513
|
+
* @param {*} prevData
|
|
513
514
|
* @param {Function} [canUpdateSubscriber] only subscribers that pass this truth test will be updated
|
|
514
515
|
* @param {boolean} [notifyRegularSubscibers=true]
|
|
515
516
|
* @param {boolean} [notifyWithOnyxSubscibers=true]
|
|
516
517
|
*/
|
|
517
|
-
function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = true, notifyWithOnyxSubscibers = true) {
|
|
518
|
+
function keyChanged(key, data, prevData, canUpdateSubscriber = () => true, notifyRegularSubscibers = true, notifyWithOnyxSubscibers = true) {
|
|
518
519
|
// Add or remove this key from the recentlyAccessedKeys lists
|
|
519
520
|
if (!underscore_1.default.isNull(data)) {
|
|
520
521
|
addLastAccessedKey(key);
|
|
@@ -528,7 +529,7 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
|
|
|
528
529
|
const stateMappingKeys = underscore_1.default.keys(callbackToStateMapping);
|
|
529
530
|
for (let i = 0; i < stateMappingKeys.length; i++) {
|
|
530
531
|
const subscriber = callbackToStateMapping[stateMappingKeys[i]];
|
|
531
|
-
if (!subscriber || !isKeyMatch(subscriber.key, key) ||
|
|
532
|
+
if (!subscriber || !isKeyMatch(subscriber.key, key) || !canUpdateSubscriber(subscriber)) {
|
|
532
533
|
continue;
|
|
533
534
|
}
|
|
534
535
|
// Subscriber is a regular call to connect() and provided a callback
|
|
@@ -556,13 +557,13 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
|
|
|
556
557
|
// returned by the selector and only when the selected data has changed.
|
|
557
558
|
if (subscriber.selector) {
|
|
558
559
|
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
559
|
-
const
|
|
560
|
-
const
|
|
560
|
+
const prevWithOnyxData = prevState[subscriber.statePropertyName];
|
|
561
|
+
const newWithOnyxData = {
|
|
561
562
|
[key]: getSubsetOfData(data, subscriber.selector, subscriber.withOnyxInstance.state),
|
|
562
563
|
};
|
|
563
|
-
const prevDataWithNewData = Object.assign(Object.assign({},
|
|
564
|
-
if (!(0, fast_equals_1.deepEqual)(
|
|
565
|
-
PerformanceUtils.logSetStateCall(subscriber,
|
|
564
|
+
const prevDataWithNewData = Object.assign(Object.assign({}, prevWithOnyxData), newWithOnyxData);
|
|
565
|
+
if (!(0, fast_equals_1.deepEqual)(prevWithOnyxData, prevDataWithNewData)) {
|
|
566
|
+
PerformanceUtils.logSetStateCall(subscriber, prevWithOnyxData, newWithOnyxData, 'keyChanged', key);
|
|
566
567
|
return {
|
|
567
568
|
[subscriber.statePropertyName]: prevDataWithNewData,
|
|
568
569
|
};
|
|
@@ -584,8 +585,8 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
|
|
|
584
585
|
// If the subscriber has a selector, then the component's state must only be updated with the data
|
|
585
586
|
// returned by the selector and only if the selected data has changed.
|
|
586
587
|
if (subscriber.selector) {
|
|
587
|
-
subscriber.withOnyxInstance.setStateProxy((
|
|
588
|
-
const previousValue = getSubsetOfData(
|
|
588
|
+
subscriber.withOnyxInstance.setStateProxy(() => {
|
|
589
|
+
const previousValue = getSubsetOfData(prevData, subscriber.selector, subscriber.withOnyxInstance.state);
|
|
589
590
|
const newValue = getSubsetOfData(data, subscriber.selector, subscriber.withOnyxInstance.state);
|
|
590
591
|
if (!(0, fast_equals_1.deepEqual)(previousValue, newValue)) {
|
|
591
592
|
return {
|
|
@@ -598,15 +599,15 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
|
|
|
598
599
|
}
|
|
599
600
|
// If we did not match on a collection key then we just set the new data to the state property
|
|
600
601
|
subscriber.withOnyxInstance.setStateProxy((prevState) => {
|
|
601
|
-
const
|
|
602
|
+
const prevWithOnyxData = prevState[subscriber.statePropertyName];
|
|
602
603
|
// Avoids triggering unnecessary re-renders when feeding empty objects
|
|
603
|
-
if (utils_1.default.
|
|
604
|
+
if (utils_1.default.isEmptyObject(data) && utils_1.default.isEmptyObject(prevWithOnyxData)) {
|
|
604
605
|
return null;
|
|
605
606
|
}
|
|
606
|
-
if (
|
|
607
|
+
if (prevWithOnyxData === data) {
|
|
607
608
|
return null;
|
|
608
609
|
}
|
|
609
|
-
PerformanceUtils.logSetStateCall(subscriber,
|
|
610
|
+
PerformanceUtils.logSetStateCall(subscriber, prevData, data, 'keyChanged', key);
|
|
610
611
|
return {
|
|
611
612
|
[subscriber.statePropertyName]: data,
|
|
612
613
|
};
|
|
@@ -829,12 +830,13 @@ function disconnect(connectionID, keyToRemoveFromEvictionBlocklist) {
|
|
|
829
830
|
*
|
|
830
831
|
* @param {String} key
|
|
831
832
|
* @param {*} value
|
|
833
|
+
* @param {*} prevValue
|
|
832
834
|
* @param {Function} [canUpdateSubscriber] only subscribers that pass this truth test will be updated
|
|
833
835
|
* @returns {Promise}
|
|
834
836
|
*/
|
|
835
|
-
function scheduleSubscriberUpdate(key, value, canUpdateSubscriber) {
|
|
836
|
-
const promise = Promise.resolve().then(() => keyChanged(key, value, canUpdateSubscriber, true, false));
|
|
837
|
-
batchUpdates(() => keyChanged(key, value, canUpdateSubscriber, false, true));
|
|
837
|
+
function scheduleSubscriberUpdate(key, value, prevValue, canUpdateSubscriber = () => true) {
|
|
838
|
+
const promise = Promise.resolve().then(() => keyChanged(key, value, prevValue, canUpdateSubscriber, true, false));
|
|
839
|
+
batchUpdates(() => keyChanged(key, value, prevValue, canUpdateSubscriber, false, true));
|
|
838
840
|
return Promise.all([maybeFlushBatchUpdates(), promise]);
|
|
839
841
|
}
|
|
840
842
|
/**
|
|
@@ -859,8 +861,9 @@ function scheduleNotifyCollectionSubscribers(key, value) {
|
|
|
859
861
|
* @return {Promise}
|
|
860
862
|
*/
|
|
861
863
|
function remove(key) {
|
|
864
|
+
const prevValue = OnyxCache_1.default.getValue(key, false);
|
|
862
865
|
OnyxCache_1.default.drop(key);
|
|
863
|
-
scheduleSubscriberUpdate(key, null);
|
|
866
|
+
scheduleSubscriberUpdate(key, null, prevValue);
|
|
864
867
|
return storage_1.default.removeItem(key);
|
|
865
868
|
}
|
|
866
869
|
/**
|
|
@@ -920,6 +923,7 @@ function evictStorageAndRetry(error, onyxMethod, ...args) {
|
|
|
920
923
|
function broadcastUpdate(key, value, method, hasChanged, wasRemoved = false) {
|
|
921
924
|
// Logging properties only since values could be sensitive things we don't want to log
|
|
922
925
|
Logger.logInfo(`${method}() called for key: ${key}${underscore_1.default.isObject(value) ? ` properties: ${underscore_1.default.keys(value).join(',')}` : ''}`);
|
|
926
|
+
const prevValue = OnyxCache_1.default.getValue(key, false);
|
|
923
927
|
// Update subscribers if the cached value has changed, or when the subscriber specifically requires
|
|
924
928
|
// all updates regardless of value changes (indicated by initWithStoredValues set to false).
|
|
925
929
|
if (hasChanged && !wasRemoved) {
|
|
@@ -928,7 +932,7 @@ function broadcastUpdate(key, value, method, hasChanged, wasRemoved = false) {
|
|
|
928
932
|
else {
|
|
929
933
|
OnyxCache_1.default.addToAccessedKeys(key);
|
|
930
934
|
}
|
|
931
|
-
return scheduleSubscriberUpdate(key, value, (subscriber) => hasChanged || subscriber.initWithStoredValues === false);
|
|
935
|
+
return scheduleSubscriberUpdate(key, value, prevValue, (subscriber) => hasChanged || subscriber.initWithStoredValues === false);
|
|
932
936
|
}
|
|
933
937
|
/**
|
|
934
938
|
* @param {String} key
|
|
@@ -1011,9 +1015,10 @@ function prepareKeyValuePairsForStorage(data) {
|
|
|
1011
1015
|
function multiSet(data) {
|
|
1012
1016
|
const keyValuePairs = prepareKeyValuePairsForStorage(data);
|
|
1013
1017
|
const updatePromises = underscore_1.default.map(keyValuePairs, ([key, value]) => {
|
|
1018
|
+
const prevValue = OnyxCache_1.default.getValue(key, false);
|
|
1014
1019
|
// Update cache and optimistically inform subscribers on the next tick
|
|
1015
1020
|
OnyxCache_1.default.set(key, value);
|
|
1016
|
-
return scheduleSubscriberUpdate(key, value);
|
|
1021
|
+
return scheduleSubscriberUpdate(key, value, prevValue);
|
|
1017
1022
|
});
|
|
1018
1023
|
return storage_1.default.multiSet(keyValuePairs)
|
|
1019
1024
|
.catch((error) => evictStorageAndRetry(error, multiSet, data))
|
|
@@ -1130,10 +1135,10 @@ function merge(key, changes) {
|
|
|
1130
1135
|
*/
|
|
1131
1136
|
function initializeWithDefaultKeyStates() {
|
|
1132
1137
|
return storage_1.default.multiGet(underscore_1.default.keys(defaultKeyStates)).then((pairs) => {
|
|
1133
|
-
const
|
|
1134
|
-
const merged = utils_1.default.fastMerge(
|
|
1138
|
+
const existingDataAsObject = underscore_1.default.object(pairs);
|
|
1139
|
+
const merged = utils_1.default.fastMerge(existingDataAsObject, defaultKeyStates);
|
|
1135
1140
|
OnyxCache_1.default.merge(merged);
|
|
1136
|
-
underscore_1.default.each(merged, (val, key) => keyChanged(key, val));
|
|
1141
|
+
underscore_1.default.each(merged, (val, key) => keyChanged(key, val, existingDataAsObject));
|
|
1137
1142
|
});
|
|
1138
1143
|
}
|
|
1139
1144
|
/**
|
|
@@ -1201,7 +1206,7 @@ function clear(keysToPreserve = []) {
|
|
|
1201
1206
|
const updatePromises = [];
|
|
1202
1207
|
// Notify the subscribers for each key/value group so they can receive the new values
|
|
1203
1208
|
underscore_1.default.each(keyValuesToResetIndividually, (value, key) => {
|
|
1204
|
-
updatePromises.push(scheduleSubscriberUpdate(key, value));
|
|
1209
|
+
updatePromises.push(scheduleSubscriberUpdate(key, value, OnyxCache_1.default.getValue(key, false)));
|
|
1205
1210
|
});
|
|
1206
1211
|
underscore_1.default.each(keyValuesToResetAsCollection, (value, key) => {
|
|
1207
1212
|
updatePromises.push(scheduleNotifyCollectionSubscribers(key, value));
|
|
@@ -1401,8 +1406,9 @@ function init({ keys = {}, initialKeyStates = {}, safeEvictionKeys = [], maxCach
|
|
|
1401
1406
|
Promise.all([addAllSafeEvictionKeysToRecentlyAccessedList(), initializeWithDefaultKeyStates()]).then(deferredInitTask.resolve);
|
|
1402
1407
|
if (shouldSyncMultipleInstances && underscore_1.default.isFunction(storage_1.default.keepInstancesSync)) {
|
|
1403
1408
|
storage_1.default.keepInstancesSync((key, value) => {
|
|
1409
|
+
const prevValue = OnyxCache_1.default.getValue(key, false);
|
|
1404
1410
|
OnyxCache_1.default.set(key, value);
|
|
1405
|
-
keyChanged(key, value);
|
|
1411
|
+
keyChanged(key, value, prevValue);
|
|
1406
1412
|
});
|
|
1407
1413
|
}
|
|
1408
1414
|
}
|
package/dist/OnyxCache.d.ts
CHANGED
|
@@ -1,121 +1,75 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { Key, Value } from './storage/providers/types';
|
|
2
|
+
type StorageMap = Record<Key, Value>;
|
|
3
3
|
/**
|
|
4
4
|
* In memory cache providing data by reference
|
|
5
5
|
* Encapsulates Onyx cache related functionality
|
|
6
6
|
*/
|
|
7
7
|
declare class OnyxCache {
|
|
8
|
-
/**
|
|
9
|
-
* @private
|
|
10
|
-
* Cache of all the storage keys available in persistent storage
|
|
11
|
-
* @type {Set<string>}
|
|
12
|
-
*/
|
|
8
|
+
/** Cache of all the storage keys available in persistent storage */
|
|
13
9
|
private storageKeys;
|
|
14
|
-
/**
|
|
15
|
-
* @private
|
|
16
|
-
* Unique list of keys maintained in access order (most recent at the end)
|
|
17
|
-
* @type {Set<string>}
|
|
18
|
-
*/
|
|
10
|
+
/** Unique list of keys maintained in access order (most recent at the end) */
|
|
19
11
|
private recentKeys;
|
|
20
|
-
/**
|
|
21
|
-
* @private
|
|
22
|
-
* A map of cached values
|
|
23
|
-
* @type {Record<string, *>}
|
|
24
|
-
*/
|
|
12
|
+
/** A map of cached values */
|
|
25
13
|
private storageMap;
|
|
26
14
|
/**
|
|
27
|
-
* @private
|
|
28
15
|
* Captured pending tasks for already running storage methods
|
|
29
16
|
* Using a map yields better performance on operations such a delete
|
|
30
|
-
* https://www.zhenghao.io/posts/object-vs-map
|
|
31
|
-
* @type {Map<string, Promise>}
|
|
32
17
|
*/
|
|
33
18
|
private pendingPromises;
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
getAllKeys():
|
|
19
|
+
/** Maximum size of the keys store din cache */
|
|
20
|
+
private maxRecentKeysSize;
|
|
21
|
+
constructor();
|
|
22
|
+
/** Get all the storage keys */
|
|
23
|
+
getAllKeys(): Key[];
|
|
39
24
|
/**
|
|
40
25
|
* Get a cached value from storage
|
|
41
|
-
* @param
|
|
42
|
-
* @returns {*}
|
|
26
|
+
* @param [shouldReindexCache] – This is an LRU cache, and by default accessing a value will make it become last in line to be evicted. This flag can be used to skip that and just access the value directly without side-effects.
|
|
43
27
|
*/
|
|
44
|
-
getValue(key:
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
* @returns {boolean}
|
|
49
|
-
*/
|
|
50
|
-
hasCacheForKey(key: string): boolean;
|
|
51
|
-
/**
|
|
52
|
-
* Saves a key in the storage keys list
|
|
28
|
+
getValue(key: Key, shouldReindexCache?: boolean): Value;
|
|
29
|
+
/** Check whether cache has data for the given key */
|
|
30
|
+
hasCacheForKey(key: Key): boolean;
|
|
31
|
+
/** Saves a key in the storage keys list
|
|
53
32
|
* Serves to keep the result of `getAllKeys` up to date
|
|
54
|
-
* @param {string} key
|
|
55
33
|
*/
|
|
56
|
-
addKey(key:
|
|
34
|
+
addKey(key: Key): void;
|
|
57
35
|
/**
|
|
58
36
|
* Set's a key value in cache
|
|
59
37
|
* Adds the key to the storage keys list as well
|
|
60
|
-
* @param {string} key
|
|
61
|
-
* @param {*} value
|
|
62
|
-
* @returns {*} value - returns the cache value
|
|
63
38
|
*/
|
|
64
|
-
set(key:
|
|
65
|
-
/**
|
|
66
|
-
|
|
67
|
-
* @param {string} key
|
|
68
|
-
*/
|
|
69
|
-
drop(key: string): void;
|
|
39
|
+
set(key: Key, value: Value): Value;
|
|
40
|
+
/** Forget the cached value for the given key */
|
|
41
|
+
drop(key: Key): void;
|
|
70
42
|
/**
|
|
71
43
|
* Deep merge data to cache, any non existing keys will be created
|
|
72
|
-
* @param
|
|
44
|
+
* @param data - a map of (cache) key - values
|
|
73
45
|
*/
|
|
74
|
-
merge(data:
|
|
46
|
+
merge(data: StorageMap): void;
|
|
75
47
|
/**
|
|
76
48
|
* Check whether the given task is already running
|
|
77
|
-
* @param
|
|
78
|
-
* @returns {*}
|
|
49
|
+
* @param taskName - unique name given for the task
|
|
79
50
|
*/
|
|
80
|
-
hasPendingTask(taskName: string):
|
|
51
|
+
hasPendingTask(taskName: string): boolean;
|
|
81
52
|
/**
|
|
82
53
|
* Use this method to prevent concurrent calls for the same thing
|
|
83
54
|
* Instead of calling the same task again use the existing promise
|
|
84
55
|
* provided from this function
|
|
85
|
-
* @
|
|
86
|
-
* @param {string} taskName - unique name given for the task
|
|
87
|
-
* @returns {Promise<T>}
|
|
56
|
+
* @param taskName - unique name given for the task
|
|
88
57
|
*/
|
|
89
|
-
getTaskPromise
|
|
58
|
+
getTaskPromise(taskName: string): Promise<unknown> | undefined;
|
|
90
59
|
/**
|
|
91
60
|
* Capture a promise for a given task so other caller can
|
|
92
61
|
* hook up to the promise if it's still pending
|
|
93
|
-
* @
|
|
94
|
-
* @param {string} taskName - unique name for the task
|
|
95
|
-
* @param {Promise<T>} promise
|
|
96
|
-
* @returns {Promise<T>}
|
|
97
|
-
*/
|
|
98
|
-
captureTask<T_1>(taskName: string, promise: Promise<T_1>): Promise<T_1>;
|
|
99
|
-
/**
|
|
100
|
-
* @private
|
|
101
|
-
* Adds a key to the top of the recently accessed keys
|
|
102
|
-
* @param {string} key
|
|
62
|
+
* @param taskName - unique name for the task
|
|
103
63
|
*/
|
|
64
|
+
captureTask(taskName: string, promise: Promise<unknown>): Promise<unknown>;
|
|
65
|
+
/** Adds a key to the top of the recently accessed keys */
|
|
104
66
|
private addToAccessedKeys;
|
|
105
|
-
/**
|
|
106
|
-
* Remove keys that don't fall into the range of recently used keys
|
|
107
|
-
*/
|
|
67
|
+
/** Remove keys that don't fall into the range of recently used keys */
|
|
108
68
|
removeLeastRecentlyUsedKeys(): void;
|
|
109
|
-
/**
|
|
110
|
-
* Set the recent keys list size
|
|
111
|
-
* @param {number} limit
|
|
112
|
-
*/
|
|
69
|
+
/** Set the recent keys list size */
|
|
113
70
|
setRecentKeysLimit(limit: number): void;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
* @param {String} key
|
|
117
|
-
* @param {*} value
|
|
118
|
-
* @returns {Boolean}
|
|
119
|
-
*/
|
|
120
|
-
hasValueChanged(key: string, value: any): boolean;
|
|
71
|
+
/** Check if the value has changed */
|
|
72
|
+
hasValueChanged(key: Key, value: Value): boolean;
|
|
121
73
|
}
|
|
74
|
+
declare const instance: OnyxCache;
|
|
75
|
+
export default instance;
|
package/dist/OnyxCache.js
CHANGED
|
@@ -3,73 +3,44 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const underscore_1 = __importDefault(require("underscore"));
|
|
7
6
|
const fast_equals_1 = require("fast-equals");
|
|
7
|
+
const bindAll_1 = __importDefault(require("lodash/bindAll"));
|
|
8
8
|
const utils_1 = __importDefault(require("./utils"));
|
|
9
|
-
const isDefined = underscore_1.default.negate(underscore_1.default.isUndefined);
|
|
10
9
|
/**
|
|
11
10
|
* In memory cache providing data by reference
|
|
12
11
|
* Encapsulates Onyx cache related functionality
|
|
13
12
|
*/
|
|
14
13
|
class OnyxCache {
|
|
15
14
|
constructor() {
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
* Cache of all the storage keys available in persistent storage
|
|
19
|
-
* @type {Set<string>}
|
|
20
|
-
*/
|
|
15
|
+
/** Maximum size of the keys store din cache */
|
|
16
|
+
this.maxRecentKeysSize = 0;
|
|
21
17
|
this.storageKeys = new Set();
|
|
22
|
-
/**
|
|
23
|
-
* @private
|
|
24
|
-
* Unique list of keys maintained in access order (most recent at the end)
|
|
25
|
-
* @type {Set<string>}
|
|
26
|
-
*/
|
|
27
18
|
this.recentKeys = new Set();
|
|
28
|
-
/**
|
|
29
|
-
* @private
|
|
30
|
-
* A map of cached values
|
|
31
|
-
* @type {Record<string, *>}
|
|
32
|
-
*/
|
|
33
19
|
this.storageMap = {};
|
|
34
|
-
/**
|
|
35
|
-
* @private
|
|
36
|
-
* Captured pending tasks for already running storage methods
|
|
37
|
-
* Using a map yields better performance on operations such a delete
|
|
38
|
-
* https://www.zhenghao.io/posts/object-vs-map
|
|
39
|
-
* @type {Map<string, Promise>}
|
|
40
|
-
*/
|
|
41
20
|
this.pendingPromises = new Map();
|
|
42
21
|
// bind all public methods to prevent problems with `this`
|
|
43
|
-
|
|
22
|
+
(0, bindAll_1.default)(this, 'getAllKeys', 'getValue', 'hasCacheForKey', 'addKey', 'set', 'drop', 'merge', 'hasPendingTask', 'getTaskPromise', 'captureTask', 'removeLeastRecentlyUsedKeys', 'setRecentKeysLimit');
|
|
44
23
|
}
|
|
45
|
-
/**
|
|
46
|
-
* Get all the storage keys
|
|
47
|
-
* @returns {string[]}
|
|
48
|
-
*/
|
|
24
|
+
/** Get all the storage keys */
|
|
49
25
|
getAllKeys() {
|
|
50
26
|
return Array.from(this.storageKeys);
|
|
51
27
|
}
|
|
52
28
|
/**
|
|
53
29
|
* Get a cached value from storage
|
|
54
|
-
* @param
|
|
55
|
-
* @returns {*}
|
|
30
|
+
* @param [shouldReindexCache] – This is an LRU cache, and by default accessing a value will make it become last in line to be evicted. This flag can be used to skip that and just access the value directly without side-effects.
|
|
56
31
|
*/
|
|
57
|
-
getValue(key) {
|
|
58
|
-
|
|
32
|
+
getValue(key, shouldReindexCache = true) {
|
|
33
|
+
if (shouldReindexCache) {
|
|
34
|
+
this.addToAccessedKeys(key);
|
|
35
|
+
}
|
|
59
36
|
return this.storageMap[key];
|
|
60
37
|
}
|
|
61
|
-
/**
|
|
62
|
-
* Check whether cache has data for the given key
|
|
63
|
-
* @param {string} key
|
|
64
|
-
* @returns {boolean}
|
|
65
|
-
*/
|
|
38
|
+
/** Check whether cache has data for the given key */
|
|
66
39
|
hasCacheForKey(key) {
|
|
67
|
-
return
|
|
40
|
+
return this.storageMap[key] !== undefined;
|
|
68
41
|
}
|
|
69
|
-
/**
|
|
70
|
-
* Saves a key in the storage keys list
|
|
42
|
+
/** Saves a key in the storage keys list
|
|
71
43
|
* Serves to keep the result of `getAllKeys` up to date
|
|
72
|
-
* @param {string} key
|
|
73
44
|
*/
|
|
74
45
|
addKey(key) {
|
|
75
46
|
this.storageKeys.add(key);
|
|
@@ -77,9 +48,6 @@ class OnyxCache {
|
|
|
77
48
|
/**
|
|
78
49
|
* Set's a key value in cache
|
|
79
50
|
* Adds the key to the storage keys list as well
|
|
80
|
-
* @param {string} key
|
|
81
|
-
* @param {*} value
|
|
82
|
-
* @returns {*} value - returns the cache value
|
|
83
51
|
*/
|
|
84
52
|
set(key, value) {
|
|
85
53
|
this.addKey(key);
|
|
@@ -87,10 +55,7 @@ class OnyxCache {
|
|
|
87
55
|
this.storageMap[key] = value;
|
|
88
56
|
return value;
|
|
89
57
|
}
|
|
90
|
-
/**
|
|
91
|
-
* Forget the cached value for the given key
|
|
92
|
-
* @param {string} key
|
|
93
|
-
*/
|
|
58
|
+
/** Forget the cached value for the given key */
|
|
94
59
|
drop(key) {
|
|
95
60
|
delete this.storageMap[key];
|
|
96
61
|
this.storageKeys.delete(key);
|
|
@@ -98,35 +63,30 @@ class OnyxCache {
|
|
|
98
63
|
}
|
|
99
64
|
/**
|
|
100
65
|
* Deep merge data to cache, any non existing keys will be created
|
|
101
|
-
* @param
|
|
66
|
+
* @param data - a map of (cache) key - values
|
|
102
67
|
*/
|
|
103
68
|
merge(data) {
|
|
104
|
-
if (
|
|
69
|
+
if (typeof data !== 'object' || Array.isArray(data)) {
|
|
105
70
|
throw new Error('data passed to cache.merge() must be an Object of onyx key/value pairs');
|
|
106
71
|
}
|
|
107
|
-
// lodash adds a small overhead so we don't use it here
|
|
108
|
-
// eslint-disable-next-line prefer-object-spread, rulesdir/prefer-underscore-method
|
|
109
72
|
this.storageMap = Object.assign({}, utils_1.default.fastMerge(this.storageMap, data, false));
|
|
110
73
|
const storageKeys = this.getAllKeys();
|
|
111
|
-
const mergedKeys =
|
|
74
|
+
const mergedKeys = Object.keys(data);
|
|
112
75
|
this.storageKeys = new Set([...storageKeys, ...mergedKeys]);
|
|
113
|
-
|
|
76
|
+
mergedKeys.forEach((key) => this.addToAccessedKeys(key));
|
|
114
77
|
}
|
|
115
78
|
/**
|
|
116
79
|
* Check whether the given task is already running
|
|
117
|
-
* @param
|
|
118
|
-
* @returns {*}
|
|
80
|
+
* @param taskName - unique name given for the task
|
|
119
81
|
*/
|
|
120
82
|
hasPendingTask(taskName) {
|
|
121
|
-
return
|
|
83
|
+
return this.pendingPromises.get(taskName) !== undefined;
|
|
122
84
|
}
|
|
123
85
|
/**
|
|
124
86
|
* Use this method to prevent concurrent calls for the same thing
|
|
125
87
|
* Instead of calling the same task again use the existing promise
|
|
126
88
|
* provided from this function
|
|
127
|
-
* @
|
|
128
|
-
* @param {string} taskName - unique name given for the task
|
|
129
|
-
* @returns {Promise<T>}
|
|
89
|
+
* @param taskName - unique name given for the task
|
|
130
90
|
*/
|
|
131
91
|
getTaskPromise(taskName) {
|
|
132
92
|
return this.pendingPromises.get(taskName);
|
|
@@ -134,10 +94,7 @@ class OnyxCache {
|
|
|
134
94
|
/**
|
|
135
95
|
* Capture a promise for a given task so other caller can
|
|
136
96
|
* hook up to the promise if it's still pending
|
|
137
|
-
* @
|
|
138
|
-
* @param {string} taskName - unique name for the task
|
|
139
|
-
* @param {Promise<T>} promise
|
|
140
|
-
* @returns {Promise<T>}
|
|
97
|
+
* @param taskName - unique name for the task
|
|
141
98
|
*/
|
|
142
99
|
captureTask(taskName, promise) {
|
|
143
100
|
const returnPromise = promise.finally(() => {
|
|
@@ -146,19 +103,12 @@ class OnyxCache {
|
|
|
146
103
|
this.pendingPromises.set(taskName, returnPromise);
|
|
147
104
|
return returnPromise;
|
|
148
105
|
}
|
|
149
|
-
/**
|
|
150
|
-
* @private
|
|
151
|
-
* Adds a key to the top of the recently accessed keys
|
|
152
|
-
* @param {string} key
|
|
153
|
-
*/
|
|
106
|
+
/** Adds a key to the top of the recently accessed keys */
|
|
154
107
|
addToAccessedKeys(key) {
|
|
155
|
-
// Removing and re-adding a key ensures it's at the end of the list
|
|
156
108
|
this.recentKeys.delete(key);
|
|
157
109
|
this.recentKeys.add(key);
|
|
158
110
|
}
|
|
159
|
-
/**
|
|
160
|
-
* Remove keys that don't fall into the range of recently used keys
|
|
161
|
-
*/
|
|
111
|
+
/** Remove keys that don't fall into the range of recently used keys */
|
|
162
112
|
removeLeastRecentlyUsedKeys() {
|
|
163
113
|
let numKeysToRemove = this.recentKeys.size - this.maxRecentKeysSize;
|
|
164
114
|
if (numKeysToRemove <= 0) {
|
|
@@ -171,23 +121,17 @@ class OnyxCache {
|
|
|
171
121
|
temp.push(value);
|
|
172
122
|
numKeysToRemove--;
|
|
173
123
|
}
|
|
124
|
+
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
|
174
125
|
for (let i = 0; i < temp.length; ++i) {
|
|
175
126
|
delete this.storageMap[temp[i]];
|
|
176
127
|
this.recentKeys.delete(temp[i]);
|
|
177
128
|
}
|
|
178
129
|
}
|
|
179
|
-
/**
|
|
180
|
-
* Set the recent keys list size
|
|
181
|
-
* @param {number} limit
|
|
182
|
-
*/
|
|
130
|
+
/** Set the recent keys list size */
|
|
183
131
|
setRecentKeysLimit(limit) {
|
|
184
132
|
this.maxRecentKeysSize = limit;
|
|
185
133
|
}
|
|
186
|
-
/**
|
|
187
|
-
* @param {String} key
|
|
188
|
-
* @param {*} value
|
|
189
|
-
* @returns {Boolean}
|
|
190
|
-
*/
|
|
134
|
+
/** Check if the value has changed */
|
|
191
135
|
hasValueChanged(key, value) {
|
|
192
136
|
return !(0, fast_equals_1.deepEqual)(this.storageMap[key], value);
|
|
193
137
|
}
|
|
@@ -1,19 +1,3 @@
|
|
|
1
|
+
import type StorageProvider from './providers/types';
|
|
2
|
+
declare const webStorage: StorageProvider;
|
|
1
3
|
export default webStorage;
|
|
2
|
-
declare const webStorage: {
|
|
3
|
-
/**
|
|
4
|
-
* @param {Function} onStorageKeyChanged Storage synchronization mechanism keeping all opened tabs in sync
|
|
5
|
-
*/
|
|
6
|
-
keepInstancesSync(onStorageKeyChanged: Function): void;
|
|
7
|
-
setItem: (key: string, value: any) => Promise<void>;
|
|
8
|
-
multiGet: (keysParam: string[]) => Promise<[key, value][]>;
|
|
9
|
-
multiMerge: (pairs: [key, value][]) => Promise<void>;
|
|
10
|
-
mergeItem(key: string, _changes: any, modifiedData: any): Promise<void>;
|
|
11
|
-
multiSet: (pairs: [key, value][]) => Promise<void>;
|
|
12
|
-
clear: () => Promise<void>;
|
|
13
|
-
setMemoryOnlyKeys: () => void;
|
|
14
|
-
getAllKeys: () => Promise<string[]>;
|
|
15
|
-
getItem: (key: string) => Promise<any>;
|
|
16
|
-
removeItem: (key: string) => Promise<void>;
|
|
17
|
-
removeItems: (keysParam: any[]) => Promise<any>;
|
|
18
|
-
getDatabaseSize(): Promise<number>;
|
|
19
|
-
};
|
|
@@ -8,25 +8,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
8
8
|
* when using LocalStorage APIs in the browser. These events are great because multiple tabs can listen for when
|
|
9
9
|
* data changes and then stay up-to-date with everything happening in Onyx.
|
|
10
10
|
*/
|
|
11
|
-
const underscore_1 = __importDefault(require("underscore"));
|
|
12
11
|
const IDBKeyVal_1 = __importDefault(require("./providers/IDBKeyVal"));
|
|
13
12
|
const SYNC_ONYX = 'SYNC_ONYX';
|
|
14
13
|
/**
|
|
15
14
|
* Raise an event thorough `localStorage` to let other tabs know a value changed
|
|
16
|
-
* @param {String} onyxKey
|
|
17
15
|
*/
|
|
18
16
|
function raiseStorageSyncEvent(onyxKey) {
|
|
19
17
|
global.localStorage.setItem(SYNC_ONYX, onyxKey);
|
|
20
|
-
global.localStorage.removeItem(SYNC_ONYX
|
|
18
|
+
global.localStorage.removeItem(SYNC_ONYX);
|
|
21
19
|
}
|
|
22
20
|
function raiseStorageSyncManyKeysEvent(onyxKeys) {
|
|
23
|
-
|
|
21
|
+
onyxKeys.forEach((onyxKey) => {
|
|
24
22
|
raiseStorageSyncEvent(onyxKey);
|
|
25
23
|
});
|
|
26
24
|
}
|
|
27
25
|
const webStorage = Object.assign(Object.assign({}, IDBKeyVal_1.default), {
|
|
28
26
|
/**
|
|
29
|
-
* @param
|
|
27
|
+
* @param onStorageKeyChanged Storage synchronization mechanism keeping all opened tabs in sync
|
|
30
28
|
*/
|
|
31
29
|
keepInstancesSync(onStorageKeyChanged) {
|
|
32
30
|
// Override set, remove and clear to raise storage events that we intercept in other tabs
|
|
@@ -48,7 +46,7 @@ const webStorage = Object.assign(Object.assign({}, IDBKeyVal_1.default), {
|
|
|
48
46
|
.then(() => {
|
|
49
47
|
// Now that storage is cleared, the storage sync event can happen which is a more atomic action
|
|
50
48
|
// for other browser tabs
|
|
51
|
-
|
|
49
|
+
allKeys.forEach(raiseStorageSyncEvent);
|
|
52
50
|
});
|
|
53
51
|
};
|
|
54
52
|
// This listener will only be triggered by events coming from other tabs
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
bytesRemaining: number;
|
|
1
|
+
/// <reference types="jest" />
|
|
2
|
+
import type { KeyValuePairList } from '../providers/types';
|
|
3
|
+
declare const idbKeyvalMockSpy: {
|
|
4
|
+
idbKeyvalSet: jest.Mock<Promise<any>, [key: any, value: any]>;
|
|
5
|
+
setItem: jest.Mock<Promise<void | import("react-native-quick-sqlite").QueryResult>, [key: string, value: IDBValidKey]>;
|
|
6
|
+
getItem: jest.Mock<Promise<IDBValidKey | null>, [key: string]>;
|
|
7
|
+
removeItem: jest.Mock<Promise<void | import("react-native-quick-sqlite").QueryResult>, [key: string]>;
|
|
8
|
+
removeItems: jest.Mock<Promise<void | import("react-native-quick-sqlite").QueryResult>, [keys: import("../providers/types").KeyList]>;
|
|
9
|
+
clear: jest.Mock<Promise<void | import("react-native-quick-sqlite").QueryResult>, []>;
|
|
10
|
+
getAllKeys: jest.Mock<Promise<import("../providers/types").KeyList>, []>;
|
|
11
|
+
multiGet: jest.Mock<Promise<KeyValuePairList>, [keys: import("../providers/types").KeyList]>;
|
|
12
|
+
multiSet: jest.Mock<Promise<void | import("react-native-quick-sqlite").BatchQueryResult>, [pairs: KeyValuePairList]>;
|
|
13
|
+
multiMerge: jest.Mock<Promise<import("react-native-quick-sqlite").BatchQueryResult | IDBValidKey[]>, [pairs: KeyValuePairList]>;
|
|
14
|
+
mergeItem: jest.Mock<Promise<void | import("react-native-quick-sqlite").BatchQueryResult>, [key: string, changes: IDBValidKey, modifiedData: IDBValidKey]>;
|
|
15
|
+
getStorageMap: jest.Mock<Record<string, IDBValidKey>, []>;
|
|
16
|
+
setInitialMockData: jest.Mock<void, [data: any]>;
|
|
17
|
+
getDatabaseSize: jest.Mock<Promise<{
|
|
19
18
|
bytesUsed: number;
|
|
19
|
+
bytesRemaining: number;
|
|
20
20
|
}>, []>;
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
21
|
+
setMemoryOnlyKeys: jest.Mock<void, []>;
|
|
22
|
+
};
|
|
23
|
+
export default idbKeyvalMockSpy;
|