react-native-onyx 1.0.130 → 2.0.1

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.
Files changed (71) hide show
  1. package/API.md +83 -22
  2. package/README.md +3 -13
  3. package/dist/DevTools.d.ts +23 -0
  4. package/{lib → dist}/DevTools.js +16 -18
  5. package/dist/Logger.js +32 -0
  6. package/dist/MDTable.d.ts +36 -0
  7. package/{lib → dist}/MDTable.js +12 -17
  8. package/{lib → dist}/Onyx.d.ts +0 -10
  9. package/{lib → dist}/Onyx.js +279 -583
  10. package/dist/OnyxCache.d.ts +121 -0
  11. package/{lib → dist}/OnyxCache.js +16 -53
  12. package/dist/Str.d.ts +18 -0
  13. package/dist/Str.js +31 -0
  14. package/dist/SyncQueue.d.ts +32 -0
  15. package/{lib → dist}/SyncQueue.js +9 -11
  16. package/dist/batch.d.ts +2 -0
  17. package/dist/batch.js +4 -0
  18. package/dist/batch.native.d.ts +2 -0
  19. package/dist/batch.native.js +4 -0
  20. package/dist/compose.d.ts +19 -0
  21. package/{lib → dist}/compose.js +5 -8
  22. package/dist/createDeferredTask.d.ts +12 -0
  23. package/{lib → dist}/createDeferredTask.js +4 -2
  24. package/dist/index.d.ts +6 -0
  25. package/dist/index.js +10 -0
  26. package/dist/metrics/PerformanceUtils.d.ts +14 -0
  27. package/{lib → dist}/metrics/PerformanceUtils.js +16 -16
  28. package/dist/metrics/index.d.ts +4 -0
  29. package/dist/metrics/index.js +14 -0
  30. package/dist/metrics/index.native.d.ts +43 -0
  31. package/{lib → dist}/metrics/index.native.js +80 -102
  32. package/{lib/storage/NativeStorage.js → dist/storage/NativeStorage.d.ts} +1 -2
  33. package/dist/storage/NativeStorage.js +7 -0
  34. package/dist/storage/WebStorage.d.ts +19 -0
  35. package/{lib → dist}/storage/WebStorage.js +24 -34
  36. package/dist/storage/__mocks__/index.d.ts +23 -0
  37. package/{lib → dist}/storage/__mocks__/index.js +17 -19
  38. package/{lib/storage/index.web.js → dist/storage/index.d.ts} +1 -2
  39. package/dist/storage/index.js +7 -0
  40. package/{lib/storage/index.native.js → dist/storage/index.native.d.ts} +1 -2
  41. package/dist/storage/index.native.js +7 -0
  42. package/dist/storage/providers/IDBKeyVal.d.ts +26 -0
  43. package/{lib → dist}/storage/providers/IDBKeyVal.js +38 -52
  44. package/dist/storage/providers/SQLiteStorage.d.ts +52 -0
  45. package/{lib → dist}/storage/providers/SQLiteStorage.js +27 -42
  46. package/{lib → dist}/utils.d.ts +2 -2
  47. package/{lib → dist}/utils.js +14 -27
  48. package/{lib → dist}/withOnyx.js +122 -159
  49. package/package.json +23 -58
  50. package/dist/web.development.js +0 -4593
  51. package/dist/web.development.js.map +0 -1
  52. package/dist/web.min.js +0 -2
  53. package/dist/web.min.js.map +0 -1
  54. package/lib/ActiveClientManager/index.d.ts +0 -22
  55. package/lib/ActiveClientManager/index.native.js +0 -18
  56. package/lib/ActiveClientManager/index.web.js +0 -94
  57. package/lib/Logger.js +0 -31
  58. package/lib/Str.js +0 -42
  59. package/lib/batch.js +0 -3
  60. package/lib/batch.native.js +0 -3
  61. package/lib/broadcast/index.d.ts +0 -17
  62. package/lib/broadcast/index.native.js +0 -12
  63. package/lib/broadcast/index.web.js +0 -33
  64. package/lib/index.d.ts +0 -6
  65. package/lib/index.js +0 -5
  66. package/lib/metrics/index.web.js +0 -10
  67. package/native.js +0 -11
  68. package/web.js +0 -12
  69. /package/{lib → dist}/Logger.d.ts +0 -0
  70. /package/{lib → dist}/types.d.ts +0 -0
  71. /package/{lib → dist}/withOnyx.d.ts +0 -0
@@ -1,18 +1,43 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
1
29
  /* eslint-disable no-continue */
2
- import {deepEqual} from 'fast-equals';
3
- import _ from 'underscore';
4
- import * as Logger from './Logger';
5
- import cache from './OnyxCache';
6
- import * as Str from './Str';
7
- import createDeferredTask from './createDeferredTask';
8
- import * as PerformanceUtils from './metrics/PerformanceUtils';
9
- import Storage from './storage';
10
- import * as Broadcast from './broadcast';
11
- import * as ActiveClientManager from './ActiveClientManager';
12
- import utils from './utils';
13
- import unstable_batchedUpdates from './batch';
14
- import DevTools from './DevTools';
15
-
30
+ const fast_equals_1 = require("fast-equals");
31
+ const underscore_1 = __importDefault(require("underscore"));
32
+ const Logger = __importStar(require("./Logger"));
33
+ const OnyxCache_1 = __importDefault(require("./OnyxCache"));
34
+ const Str = __importStar(require("./Str"));
35
+ const createDeferredTask_1 = __importDefault(require("./createDeferredTask"));
36
+ const PerformanceUtils = __importStar(require("./metrics/PerformanceUtils"));
37
+ const storage_1 = __importDefault(require("./storage"));
38
+ const utils_1 = __importDefault(require("./utils"));
39
+ const batch_1 = __importDefault(require("./batch"));
40
+ const DevTools_1 = __importDefault(require("./DevTools"));
16
41
  // Method constants
17
42
  const METHOD = {
18
43
  SET: 'set',
@@ -21,48 +46,29 @@ const METHOD = {
21
46
  MULTI_SET: 'multiset',
22
47
  CLEAR: 'clear',
23
48
  };
24
-
25
- const ON_CLEAR = 'on_clear';
26
-
27
49
  // Key/value store of Onyx key and arrays of values to merge
28
50
  const mergeQueue = {};
29
51
  const mergeQueuePromise = {};
30
-
31
52
  // Keeps track of the last connectionID that was used so we can keep incrementing it
32
53
  let lastConnectionID = 0;
33
-
34
54
  // Holds a mapping of all the react components that want their state subscribed to a store key
35
55
  const callbackToStateMapping = {};
36
-
37
56
  // Keeps a copy of the values of the onyx collection keys as a map for faster lookups
38
57
  let onyxCollectionKeyMap = new Map();
39
-
40
58
  // Holds a list of keys that have been directly subscribed to or recently modified from least to most recent
41
59
  let recentlyAccessedKeys = [];
42
-
43
60
  // Holds a list of keys that are safe to remove when we reach max storage. If a key does not match with
44
61
  // whatever appears in this list it will NEVER be a candidate for eviction.
45
62
  let evictionAllowList = [];
46
-
47
63
  // Holds a map of keys and connectionID arrays whose keys will never be automatically evicted as
48
64
  // long as we have at least one subscriber that returns false for the canEvict property.
49
65
  const evictionBlocklist = {};
50
-
51
66
  // Optional user-provided key value states set when Onyx initializes or clears
52
67
  let defaultKeyStates = {};
53
-
54
68
  // Connections can be made before `Onyx.init`. They would wait for this task before resolving
55
- const deferredInitTask = createDeferredTask();
56
-
57
- // The promise of the clear function, saved so that no writes happen while it's executing
58
- let isClearing = false;
59
-
60
- // Callback to be executed after the clear execution ends
61
- let onClearCallback = null;
62
-
69
+ const deferredInitTask = (0, createDeferredTask_1.default)();
63
70
  let batchUpdatesPromise = null;
64
71
  let batchUpdatesQueue = [];
65
-
66
72
  /**
67
73
  * Sends an action to DevTools extension
68
74
  *
@@ -72,9 +78,8 @@ let batchUpdatesQueue = [];
72
78
  * @param {any} mergedValue - (optional) value that was written in the storage after a merge method was executed.
73
79
  */
74
80
  function sendActionToDevTools(method, key, value, mergedValue = undefined) {
75
- DevTools.registerAction(utils.formatActionName(method, key), value, key ? {[key]: mergedValue || value} : value);
81
+ DevTools_1.default.registerAction(utils_1.default.formatActionName(method, key), value, key ? { [key]: mergedValue || value } : value);
76
82
  }
77
-
78
83
  /**
79
84
  * We are batching together onyx updates. This helps with use cases where we schedule onyx updates after each other.
80
85
  * This happens for example in the Onyx.update function, where we process API responses that might contain a lot of
@@ -86,7 +91,6 @@ function maybeFlushBatchUpdates() {
86
91
  if (batchUpdatesPromise) {
87
92
  return batchUpdatesPromise;
88
93
  }
89
-
90
94
  batchUpdatesPromise = new Promise((resolve) => {
91
95
  /* We use (setTimeout, 0) here which should be called once native module calls are flushed (usually at the end of the frame)
92
96
  * We may investigate if (setTimeout, 1) (which in React Native is equal to requestAnimationFrame) works even better
@@ -96,23 +100,20 @@ function maybeFlushBatchUpdates() {
96
100
  const updatesCopy = batchUpdatesQueue;
97
101
  batchUpdatesQueue = [];
98
102
  batchUpdatesPromise = null;
99
- unstable_batchedUpdates(() => {
103
+ (0, batch_1.default)(() => {
100
104
  updatesCopy.forEach((applyUpdates) => {
101
105
  applyUpdates();
102
106
  });
103
107
  });
104
-
105
108
  resolve();
106
109
  }, 0);
107
110
  });
108
111
  return batchUpdatesPromise;
109
112
  }
110
-
111
113
  function batchUpdates(updates) {
112
114
  batchUpdatesQueue.push(updates);
113
115
  return maybeFlushBatchUpdates();
114
116
  }
115
-
116
117
  /**
117
118
  * Uses a selector function to return a simplified version of sourceData
118
119
  * @param {Mixed} sourceData
@@ -121,7 +122,6 @@ function batchUpdates(updates) {
121
122
  * @returns {Mixed}
122
123
  */
123
124
  const getSubsetOfData = (sourceData, selector, withOnyxInstanceState) => selector(sourceData, withOnyxInstanceState);
124
-
125
125
  /**
126
126
  * Takes a collection of items (eg. {testKey_1:{a:'a'}, testKey_2:{b:'b'}})
127
127
  * and runs it through a reducer function to return a subset of the data according to a selector.
@@ -131,18 +131,11 @@ const getSubsetOfData = (sourceData, selector, withOnyxInstanceState) => selecto
131
131
  * @param {Object} [withOnyxInstanceState]
132
132
  * @returns {Object}
133
133
  */
134
- const reduceCollectionWithSelector = (collection, selector, withOnyxInstanceState) =>
135
- _.reduce(
136
- collection,
137
- (finalCollection, item, key) => {
138
- // eslint-disable-next-line no-param-reassign
139
- finalCollection[key] = getSubsetOfData(item, selector, withOnyxInstanceState);
140
-
141
- return finalCollection;
142
- },
143
- {},
144
- );
145
-
134
+ const reduceCollectionWithSelector = (collection, selector, withOnyxInstanceState) => underscore_1.default.reduce(collection, (finalCollection, item, key) => {
135
+ // eslint-disable-next-line no-param-reassign
136
+ finalCollection[key] = getSubsetOfData(item, selector, withOnyxInstanceState);
137
+ return finalCollection;
138
+ }, {});
146
139
  /**
147
140
  * Get some data from the store
148
141
  *
@@ -152,28 +145,23 @@ const reduceCollectionWithSelector = (collection, selector, withOnyxInstanceStat
152
145
  */
153
146
  function get(key) {
154
147
  // When we already have the value in cache - resolve right away
155
- if (cache.hasCacheForKey(key)) {
156
- return Promise.resolve(cache.getValue(key));
148
+ if (OnyxCache_1.default.hasCacheForKey(key)) {
149
+ return Promise.resolve(OnyxCache_1.default.getValue(key));
157
150
  }
158
-
159
151
  const taskName = `get:${key}`;
160
-
161
152
  // When a value retrieving task for this key is still running hook to it
162
- if (cache.hasPendingTask(taskName)) {
163
- return cache.getTaskPromise(taskName);
153
+ if (OnyxCache_1.default.hasPendingTask(taskName)) {
154
+ return OnyxCache_1.default.getTaskPromise(taskName);
164
155
  }
165
-
166
156
  // Otherwise retrieve the value from storage and capture a promise to aid concurrent usages
167
- const promise = Storage.getItem(key)
157
+ const promise = storage_1.default.getItem(key)
168
158
  .then((val) => {
169
- cache.set(key, val);
170
- return val;
171
- })
159
+ OnyxCache_1.default.set(key, val);
160
+ return val;
161
+ })
172
162
  .catch((err) => Logger.logInfo(`Unable to get item from persistent storage. Key: ${key} Error: ${err}`));
173
-
174
- return cache.captureTask(taskName, promise);
163
+ return OnyxCache_1.default.captureTask(taskName, promise);
175
164
  }
176
-
177
165
  /**
178
166
  * Returns current key names stored in persisted storage
179
167
  * @private
@@ -181,27 +169,22 @@ function get(key) {
181
169
  */
182
170
  function getAllKeys() {
183
171
  // When we've already read stored keys, resolve right away
184
- const storedKeys = cache.getAllKeys();
172
+ const storedKeys = OnyxCache_1.default.getAllKeys();
185
173
  if (storedKeys.length > 0) {
186
174
  return Promise.resolve(storedKeys);
187
175
  }
188
-
189
176
  const taskName = 'getAllKeys';
190
-
191
177
  // When a value retrieving task for all keys is still running hook to it
192
- if (cache.hasPendingTask(taskName)) {
193
- return cache.getTaskPromise(taskName);
178
+ if (OnyxCache_1.default.hasPendingTask(taskName)) {
179
+ return OnyxCache_1.default.getTaskPromise(taskName);
194
180
  }
195
-
196
181
  // Otherwise retrieve the keys from storage and capture a promise to aid concurrent usages
197
- const promise = Storage.getAllKeys().then((keys) => {
198
- _.each(keys, (key) => cache.addKey(key));
182
+ const promise = storage_1.default.getAllKeys().then((keys) => {
183
+ underscore_1.default.each(keys, (key) => OnyxCache_1.default.addKey(key));
199
184
  return keys;
200
185
  });
201
-
202
- return cache.captureTask(taskName, promise);
186
+ return OnyxCache_1.default.captureTask(taskName, promise);
203
187
  }
204
-
205
188
  /**
206
189
  * Checks to see if the a subscriber's supplied key
207
190
  * is associated with a collection of keys.
@@ -213,7 +196,6 @@ function getAllKeys() {
213
196
  function isCollectionKey(key) {
214
197
  return onyxCollectionKeyMap.has(key);
215
198
  }
216
-
217
199
  /**
218
200
  * @param {String} collectionKey
219
201
  * @param {String} key
@@ -222,7 +204,6 @@ function isCollectionKey(key) {
222
204
  function isCollectionMemberKey(collectionKey, key) {
223
205
  return Str.startsWith(key, collectionKey) && key.length > collectionKey.length;
224
206
  }
225
-
226
207
  /**
227
208
  * Checks to see if a provided key is the exact configured key of our connected subscriber
228
209
  * or if the provided key is a collection member key (in case our configured key is a "collection key")
@@ -235,7 +216,6 @@ function isCollectionMemberKey(collectionKey, key) {
235
216
  function isKeyMatch(configKey, key) {
236
217
  return isCollectionKey(configKey) ? Str.startsWith(key, configKey) : configKey === key;
237
218
  }
238
-
239
219
  /**
240
220
  * Checks to see if this key has been flagged as
241
221
  * safe for removal.
@@ -245,9 +225,8 @@ function isKeyMatch(configKey, key) {
245
225
  * @returns {Boolean}
246
226
  */
247
227
  function isSafeEvictionKey(testKey) {
248
- return _.some(evictionAllowList, (key) => isKeyMatch(key, testKey));
228
+ return underscore_1.default.some(evictionAllowList, (key) => isKeyMatch(key, testKey));
249
229
  }
250
-
251
230
  /**
252
231
  * Tries to get a value from the cache. If the value is not present in cache it will return the default value or undefined.
253
232
  * If the requested key is a collection, it will return an object with all the collection members.
@@ -257,34 +236,26 @@ function isSafeEvictionKey(testKey) {
257
236
  * @returns {Mixed}
258
237
  */
259
238
  function tryGetCachedValue(key, mapping = {}) {
260
- let val = cache.getValue(key);
261
-
239
+ let val = OnyxCache_1.default.getValue(key);
262
240
  if (isCollectionKey(key)) {
263
- const allCacheKeys = cache.getAllKeys();
264
-
241
+ const allCacheKeys = OnyxCache_1.default.getAllKeys();
265
242
  // It is possible we haven't loaded all keys yet so we do not know if the
266
243
  // collection actually exists.
267
244
  if (allCacheKeys.length === 0) {
268
245
  return;
269
246
  }
270
- const matchingKeys = _.filter(allCacheKeys, (k) => k.startsWith(key));
271
- const values = _.reduce(
272
- matchingKeys,
273
- (finalObject, matchedKey) => {
274
- const cachedValue = cache.getValue(matchedKey);
275
- if (cachedValue) {
276
- // This is permissible because we're in the process of constructing the final object in a reduce function.
277
- // eslint-disable-next-line no-param-reassign
278
- finalObject[matchedKey] = cachedValue;
279
- }
280
- return finalObject;
281
- },
282
- {},
283
- );
284
-
247
+ const matchingKeys = underscore_1.default.filter(allCacheKeys, (k) => k.startsWith(key));
248
+ const values = underscore_1.default.reduce(matchingKeys, (finalObject, matchedKey) => {
249
+ const cachedValue = OnyxCache_1.default.getValue(matchedKey);
250
+ if (cachedValue) {
251
+ // This is permissible because we're in the process of constructing the final object in a reduce function.
252
+ // eslint-disable-next-line no-param-reassign
253
+ finalObject[matchedKey] = cachedValue;
254
+ }
255
+ return finalObject;
256
+ }, {});
285
257
  val = values;
286
258
  }
287
-
288
259
  if (mapping.selector) {
289
260
  const state = mapping.withOnyxInstance ? mapping.withOnyxInstance.state : undefined;
290
261
  if (isCollectionKey(key)) {
@@ -292,10 +263,8 @@ function tryGetCachedValue(key, mapping = {}) {
292
263
  }
293
264
  return getSubsetOfData(val, mapping.selector, state);
294
265
  }
295
-
296
266
  return val;
297
267
  }
298
-
299
268
  /**
300
269
  * Remove a key from the recently accessed key list.
301
270
  *
@@ -303,9 +272,8 @@ function tryGetCachedValue(key, mapping = {}) {
303
272
  * @param {String} key
304
273
  */
305
274
  function removeLastAccessedKey(key) {
306
- recentlyAccessedKeys = _.without(recentlyAccessedKeys, key);
275
+ recentlyAccessedKeys = underscore_1.default.without(recentlyAccessedKeys, key);
307
276
  }
308
-
309
277
  /**
310
278
  * Add a key to the list of recently accessed keys. The least
311
279
  * recently accessed key should be at the head and the most
@@ -319,11 +287,9 @@ function addLastAccessedKey(key) {
319
287
  if (isCollectionKey(key) || !isSafeEvictionKey(key)) {
320
288
  return;
321
289
  }
322
-
323
290
  removeLastAccessedKey(key);
324
291
  recentlyAccessedKeys.push(key);
325
292
  }
326
-
327
293
  /**
328
294
  * Removes a key previously added to this list
329
295
  * which will enable it to be deleted again.
@@ -333,14 +299,12 @@ function addLastAccessedKey(key) {
333
299
  * @param {Number} connectionID
334
300
  */
335
301
  function removeFromEvictionBlockList(key, connectionID) {
336
- evictionBlocklist[key] = _.without(evictionBlocklist[key] || [], connectionID);
337
-
302
+ evictionBlocklist[key] = underscore_1.default.without(evictionBlocklist[key] || [], connectionID);
338
303
  // Remove the key if there are no more subscribers
339
304
  if (evictionBlocklist[key].length === 0) {
340
305
  delete evictionBlocklist[key];
341
306
  }
342
307
  }
343
-
344
308
  /**
345
309
  * Keys added to this list can never be deleted.
346
310
  *
@@ -350,14 +314,11 @@ function removeFromEvictionBlockList(key, connectionID) {
350
314
  */
351
315
  function addToEvictionBlockList(key, connectionID) {
352
316
  removeFromEvictionBlockList(key, connectionID);
353
-
354
317
  if (!evictionBlocklist[key]) {
355
318
  evictionBlocklist[key] = [];
356
319
  }
357
-
358
320
  evictionBlocklist[key].push(connectionID);
359
321
  }
360
-
361
322
  /**
362
323
  * Take all the keys that are safe to evict and add them to
363
324
  * the recently accessed list when initializing the app. This
@@ -369,8 +330,8 @@ function addToEvictionBlockList(key, connectionID) {
369
330
  */
370
331
  function addAllSafeEvictionKeysToRecentlyAccessedList() {
371
332
  return getAllKeys().then((keys) => {
372
- _.each(evictionAllowList, (safeEvictionKey) => {
373
- _.each(keys, (key) => {
333
+ underscore_1.default.each(evictionAllowList, (safeEvictionKey) => {
334
+ underscore_1.default.each(keys, (key) => {
374
335
  if (!isKeyMatch(safeEvictionKey, key)) {
375
336
  return;
376
337
  }
@@ -379,31 +340,23 @@ function addAllSafeEvictionKeysToRecentlyAccessedList() {
379
340
  });
380
341
  });
381
342
  }
382
-
383
343
  /**
384
344
  * @private
385
345
  * @param {String} collectionKey
386
346
  * @returns {Object}
387
347
  */
388
348
  function getCachedCollection(collectionKey) {
389
- const collectionMemberKeys = _.filter(cache.getAllKeys(), (storedKey) => isCollectionMemberKey(collectionKey, storedKey));
390
-
391
- return _.reduce(
392
- collectionMemberKeys,
393
- (prev, curr) => {
394
- const cachedValue = cache.getValue(curr);
395
- if (!cachedValue) {
396
- return prev;
397
- }
398
-
399
- // eslint-disable-next-line no-param-reassign
400
- prev[curr] = cachedValue;
349
+ const collectionMemberKeys = underscore_1.default.filter(OnyxCache_1.default.getAllKeys(), (storedKey) => isCollectionMemberKey(collectionKey, storedKey));
350
+ return underscore_1.default.reduce(collectionMemberKeys, (prev, curr) => {
351
+ const cachedValue = OnyxCache_1.default.getValue(curr);
352
+ if (!cachedValue) {
401
353
  return prev;
402
- },
403
- {},
404
- );
354
+ }
355
+ // eslint-disable-next-line no-param-reassign
356
+ prev[curr] = cachedValue;
357
+ return prev;
358
+ }, {});
405
359
  }
406
-
407
360
  /**
408
361
  * When a collection of keys change, search for any callbacks matching the collection key and trigger those callbacks
409
362
  *
@@ -417,38 +370,32 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
417
370
  // We are iterating over all subscribers similar to keyChanged(). However, we are looking for subscribers who are subscribing to either a collection key or
418
371
  // individual collection key member for the collection that is being updated. It is important to note that the collection parameter cane be a PARTIAL collection
419
372
  // and does not represent all of the combined keys and values for a collection key. It is just the "new" data that was merged in via mergeCollection().
420
- const stateMappingKeys = _.keys(callbackToStateMapping);
373
+ const stateMappingKeys = underscore_1.default.keys(callbackToStateMapping);
421
374
  for (let i = 0; i < stateMappingKeys.length; i++) {
422
375
  const subscriber = callbackToStateMapping[stateMappingKeys[i]];
423
376
  if (!subscriber) {
424
377
  continue;
425
378
  }
426
-
427
379
  // Skip iteration if we do not have a collection key or a collection member key on this subscriber
428
380
  if (!Str.startsWith(subscriber.key, collectionKey)) {
429
381
  continue;
430
382
  }
431
-
432
383
  /**
433
384
  * e.g. Onyx.connect({key: ONYXKEYS.COLLECTION.REPORT, callback: ...});
434
385
  */
435
386
  const isSubscribedToCollectionKey = subscriber.key === collectionKey;
436
-
437
387
  /**
438
388
  * e.g. Onyx.connect({key: `${ONYXKEYS.COLLECTION.REPORT}{reportID}`, callback: ...});
439
389
  */
440
390
  const isSubscribedToCollectionMemberKey = isCollectionMemberKey(collectionKey, subscriber.key);
441
-
442
391
  // We prepare the "cached collection" which is the entire collection + the new partial data that
443
392
  // was merged in via mergeCollection().
444
393
  const cachedCollection = getCachedCollection(collectionKey);
445
-
446
394
  // Regular Onyx.connect() subscriber found.
447
- if (_.isFunction(subscriber.callback)) {
395
+ if (underscore_1.default.isFunction(subscriber.callback)) {
448
396
  if (!notifyRegularSubscibers) {
449
397
  continue;
450
398
  }
451
-
452
399
  // If they are subscribed to the collection key and using waitForCollectionCallback then we'll
453
400
  // send the whole cached collection.
454
401
  if (isSubscribedToCollectionKey) {
@@ -456,33 +403,28 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
456
403
  subscriber.callback(cachedCollection);
457
404
  continue;
458
405
  }
459
-
460
406
  // If they are not using waitForCollectionCallback then we notify the subscriber with
461
407
  // the new merged data but only for any keys in the partial collection.
462
- const dataKeys = _.keys(partialCollection);
408
+ const dataKeys = underscore_1.default.keys(partialCollection);
463
409
  for (let j = 0; j < dataKeys.length; j++) {
464
410
  const dataKey = dataKeys[j];
465
411
  subscriber.callback(cachedCollection[dataKey], dataKey);
466
412
  }
467
413
  continue;
468
414
  }
469
-
470
415
  // And if the subscriber is specifically only tracking a particular collection member key then we will
471
416
  // notify them with the cached data for that key only.
472
417
  if (isSubscribedToCollectionMemberKey) {
473
418
  subscriber.callback(cachedCollection[subscriber.key], subscriber.key);
474
419
  continue;
475
420
  }
476
-
477
421
  continue;
478
422
  }
479
-
480
423
  // React component subscriber found.
481
424
  if (subscriber.withOnyxInstance) {
482
425
  if (!notifyWithOnyxSubscibers) {
483
426
  continue;
484
427
  }
485
-
486
428
  // We are subscribed to a collection key so we must update the data in state with the new
487
429
  // collection member key values from the partial update.
488
430
  if (isSubscribedToCollectionKey) {
@@ -492,8 +434,7 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
492
434
  subscriber.withOnyxInstance.setStateProxy((prevState) => {
493
435
  const previousData = prevState[subscriber.statePropertyName];
494
436
  const newData = reduceCollectionWithSelector(cachedCollection, subscriber.selector, subscriber.withOnyxInstance.state);
495
-
496
- if (!deepEqual(previousData, newData)) {
437
+ if (!(0, fast_equals_1.deepEqual)(previousData, newData)) {
497
438
  return {
498
439
  [subscriber.statePropertyName]: newData,
499
440
  };
@@ -502,15 +443,13 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
502
443
  });
503
444
  continue;
504
445
  }
505
-
506
446
  subscriber.withOnyxInstance.setStateProxy((prevState) => {
507
- const finalCollection = _.clone(prevState[subscriber.statePropertyName] || {});
508
- const dataKeys = _.keys(partialCollection);
447
+ const finalCollection = underscore_1.default.clone(prevState[subscriber.statePropertyName] || {});
448
+ const dataKeys = underscore_1.default.keys(partialCollection);
509
449
  for (let j = 0; j < dataKeys.length; j++) {
510
450
  const dataKey = dataKeys[j];
511
451
  finalCollection[dataKey] = cachedCollection[dataKey];
512
452
  }
513
-
514
453
  PerformanceUtils.logSetStateCall(subscriber, prevState[subscriber.statePropertyName], finalCollection, 'keysChanged', collectionKey);
515
454
  return {
516
455
  [subscriber.statePropertyName]: finalCollection,
@@ -518,16 +457,14 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
518
457
  });
519
458
  continue;
520
459
  }
521
-
522
460
  // If a React component is only interested in a single key then we can set the cached value directly to the state name.
523
461
  if (isSubscribedToCollectionMemberKey) {
524
462
  // However, we only want to update this subscriber if the partial data contains a change.
525
463
  // Otherwise, we would update them with a value they already have and trigger an unnecessary re-render.
526
464
  const dataFromCollection = partialCollection[subscriber.key];
527
- if (_.isUndefined(dataFromCollection)) {
465
+ if (underscore_1.default.isUndefined(dataFromCollection)) {
528
466
  continue;
529
467
  }
530
-
531
468
  // If the subscriber has a selector, then the component's state must only be updated with the data
532
469
  // returned by the selector and the state should only change when the subset of data changes from what
533
470
  // it was previously.
@@ -535,30 +472,26 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
535
472
  subscriber.withOnyxInstance.setStateProxy((prevState) => {
536
473
  const prevData = prevState[subscriber.statePropertyName];
537
474
  const newData = getSubsetOfData(cachedCollection[subscriber.key], subscriber.selector, subscriber.withOnyxInstance.state);
538
- if (!deepEqual(prevData, newData)) {
475
+ if (!(0, fast_equals_1.deepEqual)(prevData, newData)) {
539
476
  PerformanceUtils.logSetStateCall(subscriber, prevData, newData, 'keysChanged', collectionKey);
540
477
  return {
541
478
  [subscriber.statePropertyName]: newData,
542
479
  };
543
480
  }
544
-
545
481
  return null;
546
482
  });
547
483
  continue;
548
484
  }
549
-
550
485
  subscriber.withOnyxInstance.setStateProxy((prevState) => {
551
486
  const data = cachedCollection[subscriber.key];
552
487
  const previousData = prevState[subscriber.statePropertyName];
553
-
554
488
  // Avoids triggering unnecessary re-renders when feeding empty objects
555
- if (utils.areObjectsEmpty(data, previousData)) {
489
+ if (utils_1.default.areObjectsEmpty(data, previousData)) {
556
490
  return null;
557
491
  }
558
492
  if (data === previousData) {
559
493
  return null;
560
494
  }
561
-
562
495
  PerformanceUtils.logSetStateCall(subscriber, previousData, data, 'keysChanged', collectionKey);
563
496
  return {
564
497
  [subscriber.statePropertyName]: data,
@@ -568,7 +501,6 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
568
501
  }
569
502
  }
570
503
  }
571
-
572
504
  /**
573
505
  * When a key change happens, search for any callbacks matching the key or collection key and trigger those callbacks
574
506
  *
@@ -584,24 +516,23 @@ function keysChanged(collectionKey, partialCollection, notifyRegularSubscibers =
584
516
  */
585
517
  function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = true, notifyWithOnyxSubscibers = true) {
586
518
  // Add or remove this key from the recentlyAccessedKeys lists
587
- if (!_.isNull(data)) {
519
+ if (!underscore_1.default.isNull(data)) {
588
520
  addLastAccessedKey(key);
589
- } else {
521
+ }
522
+ else {
590
523
  removeLastAccessedKey(key);
591
524
  }
592
-
593
525
  // We are iterating over all subscribers to see if they are interested in the key that has just changed. If the subscriber's key is a collection key then we will
594
526
  // 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. Depending on whether the subscriber
595
527
  // was connected via withOnyx we will call setState() directly on the withOnyx instance. If it is a regular connection we will pass the data to the provided callback.
596
- const stateMappingKeys = _.keys(callbackToStateMapping);
528
+ const stateMappingKeys = underscore_1.default.keys(callbackToStateMapping);
597
529
  for (let i = 0; i < stateMappingKeys.length; i++) {
598
530
  const subscriber = callbackToStateMapping[stateMappingKeys[i]];
599
- if (!subscriber || !isKeyMatch(subscriber.key, key) || (_.isFunction(canUpdateSubscriber) && !canUpdateSubscriber(subscriber))) {
531
+ if (!subscriber || !isKeyMatch(subscriber.key, key) || (underscore_1.default.isFunction(canUpdateSubscriber) && !canUpdateSubscriber(subscriber))) {
600
532
  continue;
601
533
  }
602
-
603
534
  // Subscriber is a regular call to connect() and provided a callback
604
- if (_.isFunction(subscriber.callback)) {
535
+ if (underscore_1.default.isFunction(subscriber.callback)) {
605
536
  if (!notifyRegularSubscibers) {
606
537
  continue;
607
538
  }
@@ -611,17 +542,14 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
611
542
  subscriber.callback(cachedCollection);
612
543
  continue;
613
544
  }
614
-
615
545
  subscriber.callback(data, key);
616
546
  continue;
617
547
  }
618
-
619
548
  // Subscriber connected via withOnyx() HOC
620
549
  if (subscriber.withOnyxInstance) {
621
550
  if (!notifyWithOnyxSubscibers) {
622
551
  continue;
623
552
  }
624
-
625
553
  // Check if we are subscribing to a collection key and overwrite the collection member key value in state
626
554
  if (isCollectionKey(subscriber.key)) {
627
555
  // If the subscriber has a selector, then the consumer of this data must only be given the data
@@ -632,11 +560,8 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
632
560
  const newData = {
633
561
  [key]: getSubsetOfData(data, subscriber.selector, subscriber.withOnyxInstance.state),
634
562
  };
635
- const prevDataWithNewData = {
636
- ...prevData,
637
- ...newData,
638
- };
639
- if (!deepEqual(prevData, prevDataWithNewData)) {
563
+ const prevDataWithNewData = Object.assign(Object.assign({}, prevData), newData);
564
+ if (!(0, fast_equals_1.deepEqual)(prevData, prevDataWithNewData)) {
640
565
  PerformanceUtils.logSetStateCall(subscriber, prevData, newData, 'keyChanged', key);
641
566
  return {
642
567
  [subscriber.statePropertyName]: prevDataWithNewData,
@@ -646,13 +571,9 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
646
571
  });
647
572
  continue;
648
573
  }
649
-
650
574
  subscriber.withOnyxInstance.setStateProxy((prevState) => {
651
575
  const collection = prevState[subscriber.statePropertyName] || {};
652
- const newCollection = {
653
- ...collection,
654
- [key]: data,
655
- };
576
+ const newCollection = Object.assign(Object.assign({}, collection), { [key]: data });
656
577
  PerformanceUtils.logSetStateCall(subscriber, collection, newCollection, 'keyChanged', key);
657
578
  return {
658
579
  [subscriber.statePropertyName]: newCollection,
@@ -660,14 +581,13 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
660
581
  });
661
582
  continue;
662
583
  }
663
-
664
584
  // If the subscriber has a selector, then the component's state must only be updated with the data
665
585
  // returned by the selector and only if the selected data has changed.
666
586
  if (subscriber.selector) {
667
587
  subscriber.withOnyxInstance.setStateProxy((prevState) => {
668
588
  const previousValue = getSubsetOfData(prevState[subscriber.statePropertyName], subscriber.selector, subscriber.withOnyxInstance.state);
669
589
  const newValue = getSubsetOfData(data, subscriber.selector, subscriber.withOnyxInstance.state);
670
- if (!deepEqual(previousValue, newValue)) {
590
+ if (!(0, fast_equals_1.deepEqual)(previousValue, newValue)) {
671
591
  return {
672
592
  [subscriber.statePropertyName]: newValue,
673
593
  };
@@ -676,19 +596,16 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
676
596
  });
677
597
  continue;
678
598
  }
679
-
680
599
  // If we did not match on a collection key then we just set the new data to the state property
681
600
  subscriber.withOnyxInstance.setStateProxy((prevState) => {
682
601
  const previousData = prevState[subscriber.statePropertyName];
683
-
684
602
  // Avoids triggering unnecessary re-renders when feeding empty objects
685
- if (utils.areObjectsEmpty(data, previousData)) {
603
+ if (utils_1.default.areObjectsEmpty(data, previousData)) {
686
604
  return null;
687
605
  }
688
606
  if (previousData === data) {
689
607
  return null;
690
608
  }
691
-
692
609
  PerformanceUtils.logSetStateCall(subscriber, previousData, data, 'keyChanged', key);
693
610
  return {
694
611
  [subscriber.statePropertyName]: data,
@@ -696,11 +613,9 @@ function keyChanged(key, data, canUpdateSubscriber, notifyRegularSubscibers = tr
696
613
  });
697
614
  continue;
698
615
  }
699
-
700
616
  console.error('Warning: Found a matching subscriber to a key that changed, but no callback or withOnyxInstance could be found.');
701
617
  }
702
618
  }
703
-
704
619
  /**
705
620
  * Sends the data obtained from the keys to the connection. It either:
706
621
  * - sets state on the withOnyxInstances
@@ -722,36 +637,33 @@ function sendDataToConnection(mapping, val, matchedKey, isBatched) {
722
637
  if (!callbackToStateMapping[mapping.connectionID]) {
723
638
  return;
724
639
  }
725
-
726
640
  if (mapping.withOnyxInstance) {
727
641
  let newData = val;
728
-
729
642
  // If the mapping has a selector, then the component's state must only be updated with the data
730
643
  // returned by the selector.
731
644
  if (mapping.selector) {
732
645
  if (isCollectionKey(mapping.key)) {
733
646
  newData = reduceCollectionWithSelector(val, mapping.selector, mapping.withOnyxInstance.state);
734
- } else {
647
+ }
648
+ else {
735
649
  newData = getSubsetOfData(val, mapping.selector, mapping.withOnyxInstance.state);
736
650
  }
737
651
  }
738
-
739
652
  PerformanceUtils.logSetStateCall(mapping, null, newData, 'sendDataToConnection');
740
653
  if (isBatched) {
741
654
  batchUpdates(() => {
742
655
  mapping.withOnyxInstance.setWithOnyxState(mapping.statePropertyName, newData);
743
656
  });
744
- } else {
657
+ }
658
+ else {
745
659
  mapping.withOnyxInstance.setWithOnyxState(mapping.statePropertyName, newData);
746
660
  }
747
661
  return;
748
662
  }
749
-
750
- if (_.isFunction(mapping.callback)) {
663
+ if (underscore_1.default.isFunction(mapping.callback)) {
751
664
  mapping.callback(val, matchedKey);
752
665
  }
753
666
  }
754
-
755
667
  /**
756
668
  * We check to see if this key is flagged as safe for eviction and add it to the recentlyAccessedKeys list so that when we
757
669
  * run out of storage the least recently accessed key can be removed.
@@ -763,20 +675,16 @@ function addKeyToRecentlyAccessedIfNeeded(mapping) {
763
675
  if (!isSafeEvictionKey(mapping.key)) {
764
676
  return;
765
677
  }
766
-
767
678
  // Try to free some cache whenever we connect to a safe eviction key
768
- cache.removeLeastRecentlyUsedKeys();
769
-
679
+ OnyxCache_1.default.removeLeastRecentlyUsedKeys();
770
680
  if (mapping.withOnyxInstance && !isCollectionKey(mapping.key)) {
771
681
  // All React components subscribing to a key flagged as a safe eviction key must implement the canEvict property.
772
- if (_.isUndefined(mapping.canEvict)) {
682
+ if (underscore_1.default.isUndefined(mapping.canEvict)) {
773
683
  throw new Error(`Cannot subscribe to safe eviction key '${mapping.key}' without providing a canEvict value.`);
774
684
  }
775
-
776
685
  addLastAccessedKey(mapping.key);
777
686
  }
778
687
  }
779
-
780
688
  /**
781
689
  * Gets the data for a given an array of matching keys, combines them into an object, and sends the result back to the subscriber.
782
690
  *
@@ -785,21 +693,14 @@ function addKeyToRecentlyAccessedIfNeeded(mapping) {
785
693
  * @param {Object} mapping
786
694
  */
787
695
  function getCollectionDataAndSendAsObject(matchingKeys, mapping) {
788
- Promise.all(_.map(matchingKeys, (key) => get(key)))
789
- .then((values) =>
790
- _.reduce(
791
- values,
792
- (finalObject, value, i) => {
793
- // eslint-disable-next-line no-param-reassign
794
- finalObject[matchingKeys[i]] = value;
795
- return finalObject;
796
- },
797
- {},
798
- ),
799
- )
696
+ Promise.all(underscore_1.default.map(matchingKeys, (key) => get(key)))
697
+ .then((values) => underscore_1.default.reduce(values, (finalObject, value, i) => {
698
+ // eslint-disable-next-line no-param-reassign
699
+ finalObject[matchingKeys[i]] = value;
700
+ return finalObject;
701
+ }, {}))
800
702
  .then((val) => sendDataToConnection(mapping, val, undefined, true));
801
703
  }
802
-
803
704
  /**
804
705
  * Subscribes a react component's state directly to a store key
805
706
  *
@@ -832,87 +733,75 @@ function connect(mapping) {
832
733
  const connectionID = lastConnectionID++;
833
734
  callbackToStateMapping[connectionID] = mapping;
834
735
  callbackToStateMapping[connectionID].connectionID = connectionID;
835
-
836
736
  if (mapping.initWithStoredValues === false) {
837
737
  return connectionID;
838
738
  }
839
-
840
739
  // Commit connection only after init passes
841
740
  deferredInitTask.promise
842
741
  .then(() => addKeyToRecentlyAccessedIfNeeded(mapping))
843
742
  .then(() => {
844
- // Performance improvement
845
- // If the mapping is connected to an onyx key that is not a collection
846
- // we can skip the call to getAllKeys() and return an array with a single item
847
- if (Boolean(mapping.key) && typeof mapping.key === 'string' && !mapping.key.endsWith('_') && cache.storageKeys.has(mapping.key)) {
848
- return [mapping.key];
849
- }
850
- return getAllKeys();
851
- })
743
+ // Performance improvement
744
+ // If the mapping is connected to an onyx key that is not a collection
745
+ // we can skip the call to getAllKeys() and return an array with a single item
746
+ if (Boolean(mapping.key) && typeof mapping.key === 'string' && !mapping.key.endsWith('_') && OnyxCache_1.default.storageKeys.has(mapping.key)) {
747
+ return [mapping.key];
748
+ }
749
+ return getAllKeys();
750
+ })
852
751
  .then((keys) => {
853
- // We search all the keys in storage to see if any are a "match" for the subscriber we are connecting so that we
854
- // can send data back to the subscriber. Note that multiple keys can match as a subscriber could either be
855
- // subscribed to a "collection key" or a single key.
856
- const matchingKeys = _.filter(keys, (key) => isKeyMatch(mapping.key, key));
857
-
858
- // If the key being connected to does not exist we initialize the value with null. For subscribers that connected
859
- // directly via connect() they will simply get a null value sent to them without any information about which key matched
860
- // since there are none matched. In withOnyx() we wait for all connected keys to return a value before rendering the child
861
- // component. This null value will be filtered out so that the connected component can utilize defaultProps.
862
- if (matchingKeys.length === 0) {
863
- if (mapping.key && !isCollectionKey(mapping.key)) {
864
- cache.set(mapping.key, null);
865
- }
866
-
867
- // Here we cannot use batching because the null value is expected to be set immediately for default props
868
- // or they will be undefined.
869
- sendDataToConnection(mapping, null, undefined, false);
870
- return;
752
+ // We search all the keys in storage to see if any are a "match" for the subscriber we are connecting so that we
753
+ // can send data back to the subscriber. Note that multiple keys can match as a subscriber could either be
754
+ // subscribed to a "collection key" or a single key.
755
+ const matchingKeys = underscore_1.default.filter(keys, (key) => isKeyMatch(mapping.key, key));
756
+ // If the key being connected to does not exist we initialize the value with null. For subscribers that connected
757
+ // directly via connect() they will simply get a null value sent to them without any information about which key matched
758
+ // since there are none matched. In withOnyx() we wait for all connected keys to return a value before rendering the child
759
+ // component. This null value will be filtered out so that the connected component can utilize defaultProps.
760
+ if (matchingKeys.length === 0) {
761
+ if (mapping.key && !isCollectionKey(mapping.key)) {
762
+ OnyxCache_1.default.set(mapping.key, null);
871
763
  }
872
-
873
- // When using a callback subscriber we will either trigger the provided callback for each key we find or combine all values
874
- // into an object and just make a single call. The latter behavior is enabled by providing a waitForCollectionCallback key
875
- // combined with a subscription to a collection key.
876
- if (_.isFunction(mapping.callback)) {
877
- if (isCollectionKey(mapping.key)) {
878
- if (mapping.waitForCollectionCallback) {
879
- getCollectionDataAndSendAsObject(matchingKeys, mapping);
880
- return;
881
- }
882
-
883
- // We did not opt into using waitForCollectionCallback mode so the callback is called for every matching key.
884
- for (let i = 0; i < matchingKeys.length; i++) {
885
- get(matchingKeys[i]).then((val) => sendDataToConnection(mapping, val, matchingKeys[i], true));
886
- }
764
+ // Here we cannot use batching because the null value is expected to be set immediately for default props
765
+ // or they will be undefined.
766
+ sendDataToConnection(mapping, null, undefined, false);
767
+ return;
768
+ }
769
+ // When using a callback subscriber we will either trigger the provided callback for each key we find or combine all values
770
+ // into an object and just make a single call. The latter behavior is enabled by providing a waitForCollectionCallback key
771
+ // combined with a subscription to a collection key.
772
+ if (underscore_1.default.isFunction(mapping.callback)) {
773
+ if (isCollectionKey(mapping.key)) {
774
+ if (mapping.waitForCollectionCallback) {
775
+ getCollectionDataAndSendAsObject(matchingKeys, mapping);
887
776
  return;
888
777
  }
889
-
890
- // If we are not subscribed to a collection key then there's only a single key to send an update for.
891
- get(mapping.key).then((val) => sendDataToConnection(mapping, val, mapping.key, true));
778
+ // We did not opt into using waitForCollectionCallback mode so the callback is called for every matching key.
779
+ for (let i = 0; i < matchingKeys.length; i++) {
780
+ get(matchingKeys[i]).then((val) => sendDataToConnection(mapping, val, matchingKeys[i], true));
781
+ }
892
782
  return;
893
783
  }
894
-
895
- // If we have a withOnyxInstance that means a React component has subscribed via the withOnyx() HOC and we need to
896
- // group collection key member data into an object.
897
- if (mapping.withOnyxInstance) {
898
- if (isCollectionKey(mapping.key)) {
899
- getCollectionDataAndSendAsObject(matchingKeys, mapping);
900
- return;
901
- }
902
-
903
- // If the subscriber is not using a collection key then we just send a single value back to the subscriber
904
- get(mapping.key).then((val) => sendDataToConnection(mapping, val, mapping.key, true));
784
+ // If we are not subscribed to a collection key then there's only a single key to send an update for.
785
+ get(mapping.key).then((val) => sendDataToConnection(mapping, val, mapping.key, true));
786
+ return;
787
+ }
788
+ // If we have a withOnyxInstance that means a React component has subscribed via the withOnyx() HOC and we need to
789
+ // group collection key member data into an object.
790
+ if (mapping.withOnyxInstance) {
791
+ if (isCollectionKey(mapping.key)) {
792
+ getCollectionDataAndSendAsObject(matchingKeys, mapping);
905
793
  return;
906
794
  }
907
-
908
- console.error('Warning: Onyx.connect() was found without a callback or withOnyxInstance');
909
- });
910
-
795
+ // If the subscriber is not using a collection key then we just send a single value back to the subscriber
796
+ get(mapping.key).then((val) => sendDataToConnection(mapping, val, mapping.key, true));
797
+ return;
798
+ }
799
+ console.error('Warning: Onyx.connect() was found without a callback or withOnyxInstance');
800
+ });
911
801
  // The connectionID is returned back to the caller so that it can be used to clean up the connection when it's no longer needed
912
802
  // by calling Onyx.disconnect(connectionID).
913
803
  return connectionID;
914
804
  }
915
-
916
805
  /**
917
806
  * Remove the listener for a react component
918
807
  * @example
@@ -925,16 +814,13 @@ function disconnect(connectionID, keyToRemoveFromEvictionBlocklist) {
925
814
  if (!callbackToStateMapping[connectionID]) {
926
815
  return;
927
816
  }
928
-
929
817
  // Remove this key from the eviction block list as we are no longer
930
818
  // subscribing to it and it should be safe to delete again
931
819
  if (keyToRemoveFromEvictionBlocklist) {
932
820
  removeFromEvictionBlockList(keyToRemoveFromEvictionBlocklist, connectionID);
933
821
  }
934
-
935
822
  delete callbackToStateMapping[connectionID];
936
823
  }
937
-
938
824
  /**
939
825
  * Schedules an update that will be appended to the macro task queue (so it doesn't update the subscribers immediately).
940
826
  *
@@ -951,7 +837,6 @@ function scheduleSubscriberUpdate(key, value, canUpdateSubscriber) {
951
837
  batchUpdates(() => keyChanged(key, value, canUpdateSubscriber, false, true));
952
838
  return Promise.all([maybeFlushBatchUpdates(), promise]);
953
839
  }
954
-
955
840
  /**
956
841
  * This method is similar to notifySubscribersOnNextTick but it is built for working specifically with collections
957
842
  * so that keysChanged() is triggered for the collection and not keyChanged(). If this was not done, then the
@@ -966,7 +851,6 @@ function scheduleNotifyCollectionSubscribers(key, value) {
966
851
  batchUpdates(() => keysChanged(key, value, false, true));
967
852
  return Promise.all([maybeFlushBatchUpdates(), promise]);
968
853
  }
969
-
970
854
  /**
971
855
  * Remove a key from Onyx and update the subscribers
972
856
  *
@@ -975,25 +859,23 @@ function scheduleNotifyCollectionSubscribers(key, value) {
975
859
  * @return {Promise}
976
860
  */
977
861
  function remove(key) {
978
- cache.drop(key);
862
+ OnyxCache_1.default.drop(key);
979
863
  scheduleSubscriberUpdate(key, null);
980
- return Storage.removeItem(key);
864
+ return storage_1.default.removeItem(key);
981
865
  }
982
-
983
866
  /**
984
867
  * @private
985
868
  * @returns {Promise<void>}
986
869
  */
987
870
  function reportStorageQuota() {
988
- return Storage.getDatabaseSize()
989
- .then(({bytesUsed, bytesRemaining}) => {
990
- Logger.logInfo(`Storage Quota Check -- bytesUsed: ${bytesUsed} bytesRemaining: ${bytesRemaining}`);
991
- })
871
+ return storage_1.default.getDatabaseSize()
872
+ .then(({ bytesUsed, bytesRemaining }) => {
873
+ Logger.logInfo(`Storage Quota Check -- bytesUsed: ${bytesUsed} bytesRemaining: ${bytesRemaining}`);
874
+ })
992
875
  .catch((dbSizeError) => {
993
- Logger.logAlert(`Unable to get database size. Error: ${dbSizeError}`);
994
- });
876
+ Logger.logAlert(`Unable to get database size. Error: ${dbSizeError}`);
877
+ });
995
878
  }
996
-
997
879
  /**
998
880
  * If we fail to set or merge we must handle this by
999
881
  * evicting some data from Onyx and then retrying to do
@@ -1007,14 +889,12 @@ function reportStorageQuota() {
1007
889
  */
1008
890
  function evictStorageAndRetry(error, onyxMethod, ...args) {
1009
891
  Logger.logInfo(`Failed to save to storage. Error: ${error}. onyxMethod: ${onyxMethod.name}`);
1010
-
1011
892
  if (error && Str.startsWith(error.message, "Failed to execute 'put' on 'IDBObjectStore'")) {
1012
893
  Logger.logAlert('Attempted to set invalid data set in Onyx. Please ensure all data is serializable.');
1013
894
  throw error;
1014
895
  }
1015
-
1016
896
  // Find the first key that we can remove that has no subscribers in our blocklist
1017
- const keyForRemoval = _.find(recentlyAccessedKeys, (key) => !evictionBlocklist[key]);
897
+ const keyForRemoval = underscore_1.default.find(recentlyAccessedKeys, (key) => !evictionBlocklist[key]);
1018
898
  if (!keyForRemoval) {
1019
899
  // If we have no acceptable keys to remove then we are possibly trying to save mission critical data. If this is the case,
1020
900
  // then we should stop retrying as there is not much the user can do to fix this. Instead of getting them stuck in an infinite loop we
@@ -1022,13 +902,11 @@ function evictStorageAndRetry(error, onyxMethod, ...args) {
1022
902
  Logger.logAlert('Out of storage. But found no acceptable keys to remove.');
1023
903
  return reportStorageQuota();
1024
904
  }
1025
-
1026
905
  // Remove the least recently viewed key that is not currently being accessed and retry.
1027
906
  Logger.logInfo(`Out of storage. Evicting least recently accessed key (${keyForRemoval}) and retrying.`);
1028
907
  reportStorageQuota();
1029
908
  return remove(keyForRemoval).then(() => onyxMethod(...args));
1030
909
  }
1031
-
1032
910
  /**
1033
911
  * Notifys subscribers and writes current value to cache
1034
912
  *
@@ -1041,19 +919,17 @@ function evictStorageAndRetry(error, onyxMethod, ...args) {
1041
919
  */
1042
920
  function broadcastUpdate(key, value, method, hasChanged, wasRemoved = false) {
1043
921
  // Logging properties only since values could be sensitive things we don't want to log
1044
- Logger.logInfo(`${method}() called for key: ${key}${_.isObject(value) ? ` properties: ${_.keys(value).join(',')}` : ''}`);
1045
-
922
+ Logger.logInfo(`${method}() called for key: ${key}${underscore_1.default.isObject(value) ? ` properties: ${underscore_1.default.keys(value).join(',')}` : ''}`);
1046
923
  // Update subscribers if the cached value has changed, or when the subscriber specifically requires
1047
924
  // all updates regardless of value changes (indicated by initWithStoredValues set to false).
1048
925
  if (hasChanged && !wasRemoved) {
1049
- cache.set(key, value);
1050
- } else {
1051
- cache.addToAccessedKeys(key);
926
+ OnyxCache_1.default.set(key, value);
927
+ }
928
+ else {
929
+ OnyxCache_1.default.addToAccessedKeys(key);
1052
930
  }
1053
-
1054
931
  return scheduleSubscriberUpdate(key, value, (subscriber) => hasChanged || subscriber.initWithStoredValues === false);
1055
932
  }
1056
-
1057
933
  /**
1058
934
  * @param {String} key
1059
935
  * @returns {Boolean}
@@ -1061,7 +937,6 @@ function broadcastUpdate(key, value, method, hasChanged, wasRemoved = false) {
1061
937
  function hasPendingMergeForKey(key) {
1062
938
  return Boolean(mergeQueue[key]);
1063
939
  }
1064
-
1065
940
  /**
1066
941
  * Removes a key from storage if the value is null.
1067
942
  * Otherwise removes all nested null values in objects and returns the object
@@ -1070,17 +945,15 @@ function hasPendingMergeForKey(key) {
1070
945
  * @returns {Mixed} The value without null values and a boolean "wasRemoved", which indicates if the key got removed completely
1071
946
  */
1072
947
  function removeNullValues(key, value) {
1073
- if (_.isNull(value)) {
948
+ if (underscore_1.default.isNull(value)) {
1074
949
  remove(key);
1075
- return {value, wasRemoved: true};
950
+ return { value, wasRemoved: true };
1076
951
  }
1077
-
1078
952
  // We can remove all null values in an object by merging it with itself
1079
953
  // utils.fastMerge recursively goes through the object and removes all null values
1080
954
  // Passing two identical objects as source and target to fastMerge will not change it, but only remove the null values
1081
- return {value: utils.removeNestedNullValues(value), wasRemoved: false};
955
+ return { value: utils_1.default.removeNestedNullValues(value), wasRemoved: false };
1082
956
  }
1083
-
1084
957
  /**
1085
958
  * Write a value to our store with the given key
1086
959
  *
@@ -1090,40 +963,25 @@ function removeNullValues(key, value) {
1090
963
  * @returns {Promise}
1091
964
  */
1092
965
  function set(key, value) {
1093
- if (!ActiveClientManager.isClientTheLeader()) {
1094
- Broadcast.sendMessage({type: METHOD.SET, key, value});
1095
- return Promise.resolve();
1096
- }
1097
-
1098
- if (isClearing) {
1099
- return Promise.resolve();
1100
- }
1101
-
1102
966
  // If the value is null, we remove the key from storage
1103
- const {value: valueAfterRemoving, wasRemoved} = removeNullValues(key, value);
1104
-
967
+ const { value: valueAfterRemoving, wasRemoved } = removeNullValues(key, value);
1105
968
  if (hasPendingMergeForKey(key)) {
1106
969
  delete mergeQueue[key];
1107
970
  }
1108
-
1109
- const hasChanged = cache.hasValueChanged(key, valueAfterRemoving);
1110
-
971
+ const hasChanged = OnyxCache_1.default.hasValueChanged(key, valueAfterRemoving);
1111
972
  // This approach prioritizes fast UI changes without waiting for data to be stored in device storage.
1112
973
  const updatePromise = broadcastUpdate(key, valueAfterRemoving, 'set', hasChanged, wasRemoved);
1113
-
1114
974
  // 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.
1115
975
  if (!hasChanged || wasRemoved) {
1116
976
  return updatePromise;
1117
977
  }
1118
-
1119
- return Storage.setItem(key, valueAfterRemoving)
978
+ return storage_1.default.setItem(key, valueAfterRemoving)
1120
979
  .catch((error) => evictStorageAndRetry(error, set, key, valueAfterRemoving))
1121
980
  .then(() => {
1122
- sendActionToDevTools(METHOD.SET, key, valueAfterRemoving);
1123
- return updatePromise;
1124
- });
981
+ sendActionToDevTools(METHOD.SET, key, valueAfterRemoving);
982
+ return updatePromise;
983
+ });
1125
984
  }
1126
-
1127
985
  /**
1128
986
  * Storage expects array like: [["@MyApp_user", value_1], ["@MyApp_key", value_2]]
1129
987
  * This method transforms an object like {'@MyApp_user': myUserValue, '@MyApp_key': myKeyValue}
@@ -1134,18 +992,14 @@ function set(key, value) {
1134
992
  */
1135
993
  function prepareKeyValuePairsForStorage(data) {
1136
994
  const keyValuePairs = [];
1137
-
1138
- _.forEach(data, (value, key) => {
1139
- const {value: valueAfterRemoving, wasRemoved} = removeNullValues(key, value);
1140
-
1141
- if (wasRemoved) return;
1142
-
995
+ underscore_1.default.forEach(data, (value, key) => {
996
+ const { value: valueAfterRemoving, wasRemoved } = removeNullValues(key, value);
997
+ if (wasRemoved)
998
+ return;
1143
999
  keyValuePairs.push([key, valueAfterRemoving]);
1144
1000
  });
1145
-
1146
1001
  return keyValuePairs;
1147
1002
  }
1148
-
1149
1003
  /**
1150
1004
  * Sets multiple keys and values
1151
1005
  *
@@ -1155,31 +1009,19 @@ function prepareKeyValuePairsForStorage(data) {
1155
1009
  * @returns {Promise}
1156
1010
  */
1157
1011
  function multiSet(data) {
1158
- if (!ActiveClientManager.isClientTheLeader()) {
1159
- Broadcast.sendMessage({type: METHOD.MULTI_SET, data});
1160
- return Promise.resolve();
1161
- }
1162
-
1163
- if (isClearing) {
1164
- return Promise.resolve();
1165
- }
1166
-
1167
1012
  const keyValuePairs = prepareKeyValuePairsForStorage(data);
1168
-
1169
- const updatePromises = _.map(keyValuePairs, ([key, value]) => {
1013
+ const updatePromises = underscore_1.default.map(keyValuePairs, ([key, value]) => {
1170
1014
  // Update cache and optimistically inform subscribers on the next tick
1171
- cache.set(key, value);
1015
+ OnyxCache_1.default.set(key, value);
1172
1016
  return scheduleSubscriberUpdate(key, value);
1173
1017
  });
1174
-
1175
- return Storage.multiSet(keyValuePairs)
1018
+ return storage_1.default.multiSet(keyValuePairs)
1176
1019
  .catch((error) => evictStorageAndRetry(error, multiSet, data))
1177
1020
  .then(() => {
1178
- sendActionToDevTools(METHOD.MULTI_SET, undefined, data);
1179
- return Promise.all(updatePromises);
1180
- });
1021
+ sendActionToDevTools(METHOD.MULTI_SET, undefined, data);
1022
+ return Promise.all(updatePromises);
1023
+ });
1181
1024
  }
1182
-
1183
1025
  /**
1184
1026
  * Merges an array of changes with an existing value
1185
1027
  *
@@ -1190,22 +1032,18 @@ function multiSet(data) {
1190
1032
  * @returns {*}
1191
1033
  */
1192
1034
  function applyMerge(existingValue, changes, shouldRemoveNullObjectValues) {
1193
- const lastChange = _.last(changes);
1194
-
1195
- if (_.isArray(lastChange)) {
1035
+ const lastChange = underscore_1.default.last(changes);
1036
+ if (underscore_1.default.isArray(lastChange)) {
1196
1037
  return lastChange;
1197
1038
  }
1198
-
1199
- if (_.some(changes, _.isObject)) {
1039
+ if (underscore_1.default.some(changes, underscore_1.default.isObject)) {
1200
1040
  // Object values are then merged one after the other
1201
- return _.reduce(changes, (modifiedData, change) => utils.fastMerge(modifiedData, change, shouldRemoveNullObjectValues), existingValue || {});
1041
+ return underscore_1.default.reduce(changes, (modifiedData, change) => utils_1.default.fastMerge(modifiedData, change, shouldRemoveNullObjectValues), existingValue || {});
1202
1042
  }
1203
-
1204
1043
  // If we have anything else we can't merge it so we'll
1205
1044
  // simply return the last value that was queued
1206
1045
  return lastChange;
1207
1046
  }
1208
-
1209
1047
  /**
1210
1048
  * Merge a new value into an existing value at a key.
1211
1049
  *
@@ -1227,21 +1065,11 @@ function applyMerge(existingValue, changes, shouldRemoveNullObjectValues) {
1227
1065
  * @returns {Promise}
1228
1066
  */
1229
1067
  function merge(key, changes) {
1230
- if (!ActiveClientManager.isClientTheLeader()) {
1231
- Broadcast.sendMessage({type: METHOD.MERGE, key, changes});
1232
- return Promise.resolve();
1233
- }
1234
-
1235
- if (isClearing) {
1236
- return Promise.resolve();
1237
- }
1238
-
1239
1068
  // Top-level undefined values are ignored
1240
1069
  // Therefore we need to prevent adding them to the merge queue
1241
- if (_.isUndefined(changes)) {
1070
+ if (underscore_1.default.isUndefined(changes)) {
1242
1071
  return mergeQueue[key] ? mergeQueuePromise[key] : Promise.resolve();
1243
1072
  }
1244
-
1245
1073
  // Merge attempts are batched together. The delta should be applied after a single call to get() to prevent a race condition.
1246
1074
  // Using the initial value from storage in subsequent merge attempts will lead to an incorrect final merged value.
1247
1075
  if (mergeQueue[key]) {
@@ -1249,32 +1077,26 @@ function merge(key, changes) {
1249
1077
  return mergeQueuePromise[key];
1250
1078
  }
1251
1079
  mergeQueue[key] = [changes];
1252
-
1253
1080
  mergeQueuePromise[key] = get(key).then((existingValue) => {
1254
1081
  // Calls to Onyx.set after a merge will terminate the current merge process and clear the merge queue
1255
- if (mergeQueue[key] == null) return;
1256
-
1082
+ if (mergeQueue[key] == null)
1083
+ return;
1257
1084
  try {
1258
1085
  // We first only merge the changes, so we can provide these to the native implementation (SQLite uses only delta changes in "JSON_PATCH" to merge)
1259
1086
  // We don't want to remove null values from the "batchedChanges", because SQLite uses them to remove keys from storage natively.
1260
1087
  let batchedChanges = applyMerge(undefined, mergeQueue[key], false);
1261
-
1262
1088
  // The presence of a `null` in the merge queue instructs us to drop the existing value.
1263
1089
  // In this case, we can't simply merge the batched changes with the existing value, because then the null in the merge queue would have no effect
1264
- const shouldOverwriteExistingValue = _.includes(mergeQueue[key], null);
1265
-
1090
+ const shouldOverwriteExistingValue = underscore_1.default.includes(mergeQueue[key], null);
1266
1091
  // Clean up the write queue, so we don't apply these changes again
1267
1092
  delete mergeQueue[key];
1268
1093
  delete mergeQueuePromise[key];
1269
-
1270
1094
  // If the batched changes equal null, we want to remove the key from storage, to reduce storage size
1271
- const {wasRemoved} = removeNullValues(key, batchedChanges);
1272
-
1095
+ const { wasRemoved } = removeNullValues(key, batchedChanges);
1273
1096
  // After that we merge the batched changes with the existing value
1274
1097
  // We can remove null values from the "modifiedData", because "null" implicates that the user wants to remove a value from storage.
1275
1098
  // The "modifiedData" will be directly "set" in storage instead of being merged
1276
1099
  const modifiedData = shouldOverwriteExistingValue ? batchedChanges : applyMerge(existingValue, [batchedChanges], true);
1277
-
1278
1100
  // On native platforms we use SQLite which utilises JSON_PATCH to merge changes.
1279
1101
  // JSON_PATCH generally removes null values from the stored object.
1280
1102
  // When there is no existing value though, SQLite will just insert the changes as a new value and thus the null values won't be removed.
@@ -1282,45 +1104,38 @@ function merge(key, changes) {
1282
1104
  if (!existingValue) {
1283
1105
  batchedChanges = applyMerge(undefined, [batchedChanges], true);
1284
1106
  }
1285
-
1286
- const hasChanged = cache.hasValueChanged(key, modifiedData);
1287
-
1107
+ const hasChanged = OnyxCache_1.default.hasValueChanged(key, modifiedData);
1288
1108
  // This approach prioritizes fast UI changes without waiting for data to be stored in device storage.
1289
1109
  const updatePromise = broadcastUpdate(key, modifiedData, 'merge', hasChanged, wasRemoved);
1290
-
1291
1110
  // If the value has not changed, calling Storage.setItem() would be redundant and a waste of performance, so return early instead.
1292
- if (!hasChanged || isClearing || wasRemoved) {
1111
+ if (!hasChanged || wasRemoved) {
1293
1112
  return updatePromise;
1294
1113
  }
1295
-
1296
- return Storage.mergeItem(key, batchedChanges, modifiedData).then(() => {
1114
+ return storage_1.default.mergeItem(key, batchedChanges, modifiedData).then(() => {
1297
1115
  sendActionToDevTools(METHOD.MERGE, key, changes, modifiedData);
1298
1116
  return updatePromise;
1299
1117
  });
1300
- } catch (error) {
1118
+ }
1119
+ catch (error) {
1301
1120
  Logger.logAlert(`An error occurred while applying merge for key: ${key}, Error: ${error}`);
1302
1121
  return Promise.resolve();
1303
1122
  }
1304
1123
  });
1305
-
1306
1124
  return mergeQueuePromise[key];
1307
1125
  }
1308
-
1309
1126
  /**
1310
1127
  * Merge user provided default key value pairs.
1311
1128
  * @private
1312
1129
  * @returns {Promise}
1313
1130
  */
1314
1131
  function initializeWithDefaultKeyStates() {
1315
- return Storage.multiGet(_.keys(defaultKeyStates)).then((pairs) => {
1316
- const asObject = _.object(pairs);
1317
-
1318
- const merged = utils.fastMerge(asObject, defaultKeyStates);
1319
- cache.merge(merged);
1320
- _.each(merged, (val, key) => keyChanged(key, val));
1132
+ return storage_1.default.multiGet(underscore_1.default.keys(defaultKeyStates)).then((pairs) => {
1133
+ const asObject = underscore_1.default.object(pairs);
1134
+ const merged = utils_1.default.fastMerge(asObject, defaultKeyStates);
1135
+ OnyxCache_1.default.merge(merged);
1136
+ underscore_1.default.each(merged, (val, key) => keyChanged(key, val));
1321
1137
  });
1322
1138
  }
1323
-
1324
1139
  /**
1325
1140
  * Clear out all the data in the store
1326
1141
  *
@@ -1344,85 +1159,64 @@ function initializeWithDefaultKeyStates() {
1344
1159
  * @returns {Promise<void>}
1345
1160
  */
1346
1161
  function clear(keysToPreserve = []) {
1347
- if (!ActiveClientManager.isClientTheLeader()) {
1348
- Broadcast.sendMessage({type: METHOD.CLEAR, keysToPreserve});
1349
- return Promise.resolve();
1350
- }
1351
-
1352
- if (isClearing) {
1353
- return Promise.resolve();
1354
- }
1355
-
1356
- isClearing = true;
1357
-
1358
1162
  return getAllKeys().then((keys) => {
1359
1163
  const keysToBeClearedFromStorage = [];
1360
1164
  const keyValuesToResetAsCollection = {};
1361
1165
  const keyValuesToResetIndividually = {};
1362
-
1363
1166
  // The only keys that should not be cleared are:
1364
1167
  // 1. Anything specifically passed in keysToPreserve (because some keys like language preferences, offline
1365
1168
  // status, or activeClients need to remain in Onyx even when signed out)
1366
1169
  // 2. Any keys with a default state (because they need to remain in Onyx as their default, and setting them
1367
1170
  // to null would cause unknown behavior)
1368
- _.each(keys, (key) => {
1369
- const isKeyToPreserve = _.contains(keysToPreserve, key);
1370
- const isDefaultKey = _.has(defaultKeyStates, key);
1371
-
1171
+ underscore_1.default.each(keys, (key) => {
1172
+ const isKeyToPreserve = underscore_1.default.contains(keysToPreserve, key);
1173
+ const isDefaultKey = underscore_1.default.has(defaultKeyStates, key);
1372
1174
  // If the key is being removed or reset to default:
1373
1175
  // 1. Update it in the cache
1374
1176
  // 2. Figure out whether it is a collection key or not,
1375
1177
  // since collection key subscribers need to be updated differently
1376
1178
  if (!isKeyToPreserve) {
1377
- const oldValue = cache.getValue(key);
1378
- const newValue = _.get(defaultKeyStates, key, null);
1179
+ const oldValue = OnyxCache_1.default.getValue(key);
1180
+ const newValue = underscore_1.default.get(defaultKeyStates, key, null);
1379
1181
  if (newValue !== oldValue) {
1380
- cache.set(key, newValue);
1182
+ OnyxCache_1.default.set(key, newValue);
1381
1183
  const collectionKey = key.substring(0, key.indexOf('_') + 1);
1382
1184
  if (collectionKey) {
1383
1185
  if (!keyValuesToResetAsCollection[collectionKey]) {
1384
1186
  keyValuesToResetAsCollection[collectionKey] = {};
1385
1187
  }
1386
1188
  keyValuesToResetAsCollection[collectionKey][key] = newValue;
1387
- } else {
1189
+ }
1190
+ else {
1388
1191
  keyValuesToResetIndividually[key] = newValue;
1389
1192
  }
1390
1193
  }
1391
1194
  }
1392
-
1393
1195
  if (isKeyToPreserve || isDefaultKey) {
1394
1196
  return;
1395
1197
  }
1396
-
1397
1198
  // If it isn't preserved and doesn't have a default, we'll remove it
1398
1199
  keysToBeClearedFromStorage.push(key);
1399
1200
  });
1400
-
1401
1201
  const updatePromises = [];
1402
-
1403
1202
  // Notify the subscribers for each key/value group so they can receive the new values
1404
- _.each(keyValuesToResetIndividually, (value, key) => {
1203
+ underscore_1.default.each(keyValuesToResetIndividually, (value, key) => {
1405
1204
  updatePromises.push(scheduleSubscriberUpdate(key, value));
1406
1205
  });
1407
- _.each(keyValuesToResetAsCollection, (value, key) => {
1206
+ underscore_1.default.each(keyValuesToResetAsCollection, (value, key) => {
1408
1207
  updatePromises.push(scheduleNotifyCollectionSubscribers(key, value));
1409
1208
  });
1410
-
1411
- const defaultKeyValuePairs = _.pairs(_.omit(defaultKeyStates, keysToPreserve));
1412
-
1209
+ const defaultKeyValuePairs = underscore_1.default.pairs(underscore_1.default.omit(defaultKeyStates, keysToPreserve));
1413
1210
  // Remove only the items that we want cleared from storage, and reset others to default
1414
- _.each(keysToBeClearedFromStorage, (key) => cache.drop(key));
1415
- return Storage.removeItems(keysToBeClearedFromStorage)
1416
- .then(() => Storage.multiSet(defaultKeyValuePairs))
1211
+ underscore_1.default.each(keysToBeClearedFromStorage, (key) => OnyxCache_1.default.drop(key));
1212
+ return storage_1.default.removeItems(keysToBeClearedFromStorage)
1213
+ .then(() => storage_1.default.multiSet(defaultKeyValuePairs))
1417
1214
  .then(() => {
1418
- isClearing = false;
1419
- Broadcast.sendMessage({type: METHOD.CLEAR, keysToPreserve});
1420
- DevTools.clearState(keysToPreserve);
1421
- return Promise.all(updatePromises);
1422
- });
1215
+ DevTools_1.default.clearState(keysToPreserve);
1216
+ return Promise.all(updatePromises);
1217
+ });
1423
1218
  });
1424
1219
  }
1425
-
1426
1220
  /**
1427
1221
  * Merges a collection based on their keys
1428
1222
  *
@@ -1438,78 +1232,66 @@ function clear(keysToPreserve = []) {
1438
1232
  * @returns {Promise}
1439
1233
  */
1440
1234
  function mergeCollection(collectionKey, collection) {
1441
- if (!_.isObject(collection) || _.isArray(collection) || _.isEmpty(collection)) {
1235
+ if (!underscore_1.default.isObject(collection) || underscore_1.default.isArray(collection) || underscore_1.default.isEmpty(collection)) {
1442
1236
  Logger.logInfo('mergeCollection() called with invalid or empty value. Skipping this update.');
1443
1237
  return Promise.resolve();
1444
1238
  }
1445
-
1446
1239
  // Confirm all the collection keys belong to the same parent
1447
1240
  let hasCollectionKeyCheckFailed = false;
1448
- _.each(collection, (_data, dataKey) => {
1241
+ underscore_1.default.each(collection, (_data, dataKey) => {
1449
1242
  if (isKeyMatch(collectionKey, dataKey)) {
1450
1243
  return;
1451
1244
  }
1452
-
1453
1245
  if (process.env.NODE_ENV === 'development') {
1454
1246
  throw new Error(`Provided collection doesn't have all its data belonging to the same parent. CollectionKey: ${collectionKey}, DataKey: ${dataKey}`);
1455
1247
  }
1456
-
1457
1248
  hasCollectionKeyCheckFailed = true;
1458
1249
  Logger.logAlert(`Provided collection doesn't have all its data belonging to the same parent. CollectionKey: ${collectionKey}, DataKey: ${dataKey}`);
1459
1250
  });
1460
-
1461
1251
  // Gracefully handle bad mergeCollection updates so it doesn't block the merge queue
1462
1252
  if (hasCollectionKeyCheckFailed) {
1463
1253
  return Promise.resolve();
1464
1254
  }
1465
-
1466
1255
  return getAllKeys().then((persistedKeys) => {
1467
1256
  // Split to keys that exist in storage and keys that don't
1468
- const [existingKeys, newKeys] = _.chain(collection)
1257
+ const [existingKeys, newKeys] = underscore_1.default.chain(collection)
1469
1258
  .pick((value, key) => {
1470
- if (_.isNull(value)) {
1471
- remove(key);
1472
- return false;
1473
- }
1474
- return true;
1475
- })
1259
+ if (underscore_1.default.isNull(value)) {
1260
+ remove(key);
1261
+ return false;
1262
+ }
1263
+ return true;
1264
+ })
1476
1265
  .keys()
1477
1266
  .partition((key) => persistedKeys.includes(key))
1478
1267
  .value();
1479
-
1480
- const existingKeyCollection = _.pick(collection, existingKeys);
1481
- const newCollection = _.pick(collection, newKeys);
1268
+ const existingKeyCollection = underscore_1.default.pick(collection, existingKeys);
1269
+ const newCollection = underscore_1.default.pick(collection, newKeys);
1482
1270
  const keyValuePairsForExistingCollection = prepareKeyValuePairsForStorage(existingKeyCollection);
1483
1271
  const keyValuePairsForNewCollection = prepareKeyValuePairsForStorage(newCollection);
1484
-
1485
1272
  const promises = [];
1486
-
1487
1273
  // New keys will be added via multiSet while existing keys will be updated using multiMerge
1488
1274
  // This is because setting a key that doesn't exist yet with multiMerge will throw errors
1489
1275
  if (keyValuePairsForExistingCollection.length > 0) {
1490
- promises.push(Storage.multiMerge(keyValuePairsForExistingCollection));
1276
+ promises.push(storage_1.default.multiMerge(keyValuePairsForExistingCollection));
1491
1277
  }
1492
-
1493
1278
  if (keyValuePairsForNewCollection.length > 0) {
1494
- promises.push(Storage.multiSet(keyValuePairsForNewCollection));
1279
+ promises.push(storage_1.default.multiSet(keyValuePairsForNewCollection));
1495
1280
  }
1496
-
1497
1281
  // Prefill cache if necessary by calling get() on any existing keys and then merge original data to cache
1498
1282
  // and update all subscribers
1499
- const promiseUpdate = Promise.all(_.map(existingKeys, get)).then(() => {
1500
- cache.merge(collection);
1283
+ const promiseUpdate = Promise.all(underscore_1.default.map(existingKeys, get)).then(() => {
1284
+ OnyxCache_1.default.merge(collection);
1501
1285
  return scheduleNotifyCollectionSubscribers(collectionKey, collection);
1502
1286
  });
1503
-
1504
1287
  return Promise.all(promises)
1505
1288
  .catch((error) => evictStorageAndRetry(error, mergeCollection, collection))
1506
1289
  .then(() => {
1507
- sendActionToDevTools(METHOD.MERGE_COLLECTION, undefined, collection);
1508
- return promiseUpdate;
1509
- });
1290
+ sendActionToDevTools(METHOD.MERGE_COLLECTION, undefined, collection);
1291
+ return promiseUpdate;
1292
+ });
1510
1293
  });
1511
1294
  }
1512
-
1513
1295
  /**
1514
1296
  * Insert API responses and lifecycle data into Onyx
1515
1297
  *
@@ -1518,24 +1300,23 @@ function mergeCollection(collectionKey, collection) {
1518
1300
  */
1519
1301
  function update(data) {
1520
1302
  // First, validate the Onyx object is in the format we expect
1521
- _.each(data, ({onyxMethod, key, value}) => {
1522
- if (!_.contains([METHOD.CLEAR, METHOD.SET, METHOD.MERGE, METHOD.MERGE_COLLECTION, METHOD.MULTI_SET], onyxMethod)) {
1303
+ underscore_1.default.each(data, ({ onyxMethod, key, value }) => {
1304
+ if (!underscore_1.default.contains([METHOD.CLEAR, METHOD.SET, METHOD.MERGE, METHOD.MERGE_COLLECTION, METHOD.MULTI_SET], onyxMethod)) {
1523
1305
  throw new Error(`Invalid onyxMethod ${onyxMethod} in Onyx update.`);
1524
1306
  }
1525
1307
  if (onyxMethod === METHOD.MULTI_SET) {
1526
1308
  // For multiset, we just expect the value to be an object
1527
- if (!_.isObject(value) || _.isArray(value) || _.isFunction(value)) {
1309
+ if (!underscore_1.default.isObject(value) || underscore_1.default.isArray(value) || underscore_1.default.isFunction(value)) {
1528
1310
  throw new Error('Invalid value provided in Onyx multiSet. Onyx multiSet value must be of type object.');
1529
1311
  }
1530
- } else if (onyxMethod !== METHOD.CLEAR && !_.isString(key)) {
1312
+ }
1313
+ else if (onyxMethod !== METHOD.CLEAR && !underscore_1.default.isString(key)) {
1531
1314
  throw new Error(`Invalid ${typeof key} key provided in Onyx update. Onyx key must be of type string.`);
1532
1315
  }
1533
1316
  });
1534
-
1535
1317
  const promises = [];
1536
1318
  let clearPromise = Promise.resolve();
1537
-
1538
- _.each(data, ({onyxMethod, key, value}) => {
1319
+ underscore_1.default.each(data, ({ onyxMethod, key, value }) => {
1539
1320
  switch (onyxMethod) {
1540
1321
  case METHOD.SET:
1541
1322
  promises.push(() => set(key, value));
@@ -1556,63 +1337,17 @@ function update(data) {
1556
1337
  break;
1557
1338
  }
1558
1339
  });
1559
-
1560
- return clearPromise.then(() => Promise.all(_.map(promises, (p) => p())));
1340
+ return clearPromise.then(() => Promise.all(underscore_1.default.map(promises, (p) => p())));
1561
1341
  }
1562
-
1563
1342
  /**
1564
1343
  * When set these keys will not be persisted to storage
1565
1344
  * @param {string[]} keyList
1566
1345
  */
1567
1346
  function setMemoryOnlyKeys(keyList) {
1568
- Storage.setMemoryOnlyKeys(keyList);
1569
-
1347
+ storage_1.default.setMemoryOnlyKeys(keyList);
1570
1348
  // When in memory only mode for certain keys we do not want to ever drop items from the cache as the user will have no way to recover them again via storage.
1571
- cache.setRecentKeysLimit(Infinity);
1349
+ OnyxCache_1.default.setRecentKeysLimit(Infinity);
1572
1350
  }
1573
-
1574
- /**
1575
- * Sets the callback to be called when the clear finishes executing.
1576
- * @param {Function} callback
1577
- */
1578
- function onClear(callback) {
1579
- onClearCallback = callback;
1580
- }
1581
-
1582
- /**
1583
- * Subscribes to the Broadcast channel and executes actions based on the
1584
- * types of events.
1585
- */
1586
- function subscribeToEvents() {
1587
- Broadcast.subscribe(({data}) => {
1588
- if (!ActiveClientManager.isClientTheLeader()) {
1589
- return;
1590
- }
1591
- switch (data.type) {
1592
- case METHOD.CLEAR:
1593
- clear(data.keysToPreserve);
1594
- break;
1595
- case METHOD.SET:
1596
- set(data.key, data.value);
1597
- break;
1598
- case METHOD.MULTI_SET:
1599
- multiSet(data.key, data.value);
1600
- break;
1601
- case METHOD.MERGE:
1602
- merge(data.key, data.changes);
1603
- break;
1604
- case ON_CLEAR:
1605
- if (!onClearCallback) {
1606
- break;
1607
- }
1608
- onClearCallback();
1609
- break;
1610
- default:
1611
- break;
1612
- }
1613
- });
1614
- }
1615
-
1616
1351
  /**
1617
1352
  * Initialize the store with actions and listening for storage events
1618
1353
  *
@@ -1638,69 +1373,39 @@ function subscribeToEvents() {
1638
1373
  * },
1639
1374
  * });
1640
1375
  */
1641
- function init({
1642
- keys = {},
1643
- initialKeyStates = {},
1644
- safeEvictionKeys = [],
1645
- maxCachedKeysCount = 1000,
1646
- captureMetrics = false,
1647
- shouldSyncMultipleInstances = Boolean(global.localStorage),
1648
- debugSetState = false,
1649
- } = {}) {
1650
- ActiveClientManager.init();
1651
-
1652
- ActiveClientManager.isReady().then(() => {
1653
- if (!ActiveClientManager.isClientTheLeader()) {
1654
- return;
1655
- }
1656
- subscribeToEvents();
1657
- });
1658
-
1376
+ function init({ keys = {}, initialKeyStates = {}, safeEvictionKeys = [], maxCachedKeysCount = 1000, captureMetrics = false, shouldSyncMultipleInstances = Boolean(global.localStorage), debugSetState = false, } = {}) {
1659
1377
  if (captureMetrics) {
1660
1378
  // The code here is only bundled and applied when the captureMetrics is set
1661
1379
  // eslint-disable-next-line no-use-before-define
1662
1380
  applyDecorators();
1663
1381
  }
1664
-
1665
1382
  if (debugSetState) {
1666
1383
  PerformanceUtils.setShouldDebugSetState(true);
1667
1384
  }
1668
-
1669
1385
  if (maxCachedKeysCount > 0) {
1670
- cache.setRecentKeysLimit(maxCachedKeysCount);
1386
+ OnyxCache_1.default.setRecentKeysLimit(maxCachedKeysCount);
1671
1387
  }
1672
-
1673
1388
  // We need the value of the collection keys later for checking if a
1674
1389
  // key is a collection. We store it in a map for faster lookup.
1675
- const collectionValues = _.values(keys.COLLECTION);
1676
- onyxCollectionKeyMap = _.reduce(
1677
- collectionValues,
1678
- (acc, val) => {
1679
- acc.set(val, true);
1680
- return acc;
1681
- },
1682
- new Map(),
1683
- );
1684
-
1390
+ const collectionValues = underscore_1.default.values(keys.COLLECTION);
1391
+ onyxCollectionKeyMap = underscore_1.default.reduce(collectionValues, (acc, val) => {
1392
+ acc.set(val, true);
1393
+ return acc;
1394
+ }, new Map());
1685
1395
  // Set our default key states to use when initializing and clearing Onyx data
1686
1396
  defaultKeyStates = initialKeyStates;
1687
-
1688
- DevTools.initState(initialKeyStates);
1689
-
1397
+ DevTools_1.default.initState(initialKeyStates);
1690
1398
  // Let Onyx know about which keys are safe to evict
1691
1399
  evictionAllowList = safeEvictionKeys;
1692
-
1693
1400
  // Initialize all of our keys with data provided then give green light to any pending connections
1694
1401
  Promise.all([addAllSafeEvictionKeysToRecentlyAccessedList(), initializeWithDefaultKeyStates()]).then(deferredInitTask.resolve);
1695
-
1696
- if (shouldSyncMultipleInstances && _.isFunction(Storage.keepInstancesSync)) {
1697
- Storage.keepInstancesSync((key, value) => {
1698
- cache.set(key, value);
1402
+ if (shouldSyncMultipleInstances && underscore_1.default.isFunction(storage_1.default.keepInstancesSync)) {
1403
+ storage_1.default.keepInstancesSync((key, value) => {
1404
+ OnyxCache_1.default.set(key, value);
1699
1405
  keyChanged(key, value);
1700
1406
  });
1701
1407
  }
1702
1408
  }
1703
-
1704
1409
  const Onyx = {
1705
1410
  connect,
1706
1411
  disconnect,
@@ -1720,12 +1425,7 @@ const Onyx = {
1720
1425
  setMemoryOnlyKeys,
1721
1426
  tryGetCachedValue,
1722
1427
  hasPendingMergeForKey,
1723
- onClear,
1724
- isClientManagerReady: ActiveClientManager.isReady,
1725
- isClientTheLeader: ActiveClientManager.isClientTheLeader,
1726
- subscribeToClientChange: ActiveClientManager.subscribeToClientChange,
1727
1428
  };
1728
-
1729
1429
  /**
1730
1430
  * Apply calls statistic decorators to benchmark Onyx
1731
1431
  *
@@ -1734,7 +1434,6 @@ const Onyx = {
1734
1434
  function applyDecorators() {
1735
1435
  // We're requiring the script dynamically here so that it's only evaluated when decorators are used
1736
1436
  const decorate = require('./metrics');
1737
-
1738
1437
  // Re-assign with decorated functions
1739
1438
  /* eslint-disable no-func-assign */
1740
1439
  get = decorate.decorateWithMetrics(get, 'Onyx:get');
@@ -1747,7 +1446,6 @@ function applyDecorators() {
1747
1446
  initializeWithDefaultKeyStates = decorate.decorateWithMetrics(initializeWithDefaultKeyStates, 'Onyx:defaults');
1748
1447
  update = decorate.decorateWithMetrics(update, 'Onyx:update');
1749
1448
  /* eslint-enable */
1750
-
1751
1449
  // Re-expose decorated methods
1752
1450
  /* eslint-disable rulesdir/prefer-actions-set-data */
1753
1451
  Onyx.set = set;
@@ -1757,11 +1455,9 @@ function applyDecorators() {
1757
1455
  Onyx.mergeCollection = mergeCollection;
1758
1456
  Onyx.update = update;
1759
1457
  /* eslint-enable */
1760
-
1761
1458
  // Expose stats methods on Onyx
1762
1459
  Onyx.getMetrics = decorate.getMetrics;
1763
1460
  Onyx.resetMetrics = decorate.resetMetrics;
1764
1461
  Onyx.printMetrics = decorate.printMetrics;
1765
1462
  }
1766
-
1767
- export default Onyx;
1463
+ exports.default = Onyx;