react-native-onyx 1.0.1 → 1.0.2

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/lib/Onyx.js CHANGED
@@ -2,8 +2,7 @@ import _ from 'underscore';
2
2
  import Str from 'expensify-common/lib/str';
3
3
  import lodashMerge from 'lodash/merge';
4
4
  import Storage from './storage';
5
-
6
- import {registerLogger, logInfo, logAlert} from './Logger';
5
+ import * as Logger from './Logger';
7
6
  import cache from './OnyxCache';
8
7
  import createDeferredTask from './createDeferredTask';
9
8
 
@@ -59,7 +58,7 @@ function get(key) {
59
58
  cache.set(key, val);
60
59
  return val;
61
60
  })
62
- .catch(err => logInfo(`Unable to get item from persistent storage. Key: ${key} Error: ${err}`));
61
+ .catch(err => Logger.logInfo(`Unable to get item from persistent storage. Key: ${key} Error: ${err}`));
63
62
 
64
63
  return cache.captureTask(taskName, promise);
65
64
  }
@@ -208,9 +207,10 @@ function addAllSafeEvictionKeysToRecentlyAccessedList() {
208
207
  .then((keys) => {
209
208
  _.each(evictionAllowList, (safeEvictionKey) => {
210
209
  _.each(keys, (key) => {
211
- if (isKeyMatch(safeEvictionKey, key)) {
212
- addLastAccessedKey(key);
210
+ if (!isKeyMatch(safeEvictionKey, key)) {
211
+ return;
213
212
  }
213
+ addLastAccessedKey(key);
214
214
  });
215
215
  });
216
216
  });
@@ -258,7 +258,6 @@ function keysChanged(collectionKey, collection) {
258
258
 
259
259
  if (isSubscribedToCollectionKey) {
260
260
  if (_.isFunction(subscriber.callback)) {
261
- // eslint-disable-next-line no-use-before-define
262
261
  const cachedCollection = getCachedCollection(collectionKey);
263
262
  _.each(collection, (data, dataKey) => {
264
263
  subscriber.callback(cachedCollection[dataKey], dataKey);
@@ -317,29 +316,31 @@ function keyChanged(key, data) {
317
316
 
318
317
  // Find all subscribers that were added with connect() and trigger the callback or setState() with the new data
319
318
  _.each(callbackToStateMapping, (subscriber) => {
320
- if (subscriber && isKeyMatch(subscriber.key, key)) {
321
- if (_.isFunction(subscriber.callback)) {
322
- subscriber.callback(data, key);
323
- }
319
+ if (!subscriber || !isKeyMatch(subscriber.key, key)) {
320
+ return;
321
+ }
324
322
 
325
- if (!subscriber.withOnyxInstance) {
326
- return;
327
- }
323
+ if (_.isFunction(subscriber.callback)) {
324
+ subscriber.callback(data, key);
325
+ }
328
326
 
329
- // Check if we are subscribing to a collection key and add this item as a collection
330
- if (isCollectionKey(subscriber.key)) {
331
- subscriber.withOnyxInstance.setState((prevState) => {
332
- const collection = _.clone(prevState[subscriber.statePropertyName] || {});
333
- collection[key] = data;
334
- return {
335
- [subscriber.statePropertyName]: collection,
336
- };
337
- });
338
- } else {
339
- subscriber.withOnyxInstance.setState({
340
- [subscriber.statePropertyName]: data,
341
- });
342
- }
327
+ if (!subscriber.withOnyxInstance) {
328
+ return;
329
+ }
330
+
331
+ // Check if we are subscribing to a collection key and add this item as a collection
332
+ if (isCollectionKey(subscriber.key)) {
333
+ subscriber.withOnyxInstance.setState((prevState) => {
334
+ const collection = _.clone(prevState[subscriber.statePropertyName] || {});
335
+ collection[key] = data;
336
+ return {
337
+ [subscriber.statePropertyName]: collection,
338
+ };
339
+ });
340
+ } else {
341
+ subscriber.withOnyxInstance.setState({
342
+ [subscriber.statePropertyName]: data,
343
+ });
343
344
  }
344
345
  });
345
346
  }
@@ -404,21 +405,23 @@ function connect(mapping) {
404
405
  deferredInitTask.promise
405
406
  .then(() => {
406
407
  // Check to see if this key is flagged as a safe eviction key and add it to the recentlyAccessedKeys list
407
- if (isSafeEvictionKey(mapping.key)) {
408
- // Try to free some cache whenever we connect to a safe eviction key
409
- cache.removeLeastRecentlyUsedKeys();
410
-
411
- if (mapping.withOnyxInstance && !isCollectionKey(mapping.key)) {
412
- // All React components subscribing to a key flagged as a safe eviction
413
- // key must implement the canEvict property.
414
- if (_.isUndefined(mapping.canEvict)) {
415
- throw new Error(
416
- `Cannot subscribe to safe eviction key '${mapping.key}' without providing a canEvict value.`
417
- );
418
- }
408
+ if (!isSafeEvictionKey(mapping.key)) {
409
+ return;
410
+ }
419
411
 
420
- addLastAccessedKey(mapping.key);
412
+ // Try to free some cache whenever we connect to a safe eviction key
413
+ cache.removeLeastRecentlyUsedKeys();
414
+
415
+ if (mapping.withOnyxInstance && !isCollectionKey(mapping.key)) {
416
+ // All React components subscribing to a key flagged as a safe eviction
417
+ // key must implement the canEvict property.
418
+ if (_.isUndefined(mapping.canEvict)) {
419
+ throw new Error(
420
+ `Cannot subscribe to safe eviction key '${mapping.key}' without providing a canEvict value.`,
421
+ );
421
422
  }
423
+
424
+ addLastAccessedKey(mapping.key);
422
425
  }
423
426
  })
424
427
  .then(getAllKeys)
@@ -505,10 +508,10 @@ function remove(key) {
505
508
  * @return {Promise}
506
509
  */
507
510
  function evictStorageAndRetry(error, onyxMethod, ...args) {
508
- logInfo(`Handled error: ${error}`);
511
+ Logger.logInfo(`Handled error: ${error}`);
509
512
 
510
513
  if (error && Str.startsWith(error.message, 'Failed to execute \'put\' on \'IDBObjectStore\'')) {
511
- logAlert('Attempted to set invalid data set in Onyx. Please ensure all data is serializable.');
514
+ Logger.logAlert('Attempted to set invalid data set in Onyx. Please ensure all data is serializable.');
512
515
  throw error;
513
516
  }
514
517
 
@@ -516,12 +519,12 @@ function evictStorageAndRetry(error, onyxMethod, ...args) {
516
519
  const keyForRemoval = _.find(recentlyAccessedKeys, key => !evictionBlocklist[key]);
517
520
 
518
521
  if (!keyForRemoval) {
519
- logAlert('Out of storage. But found no acceptable keys to remove.');
522
+ Logger.logAlert('Out of storage. But found no acceptable keys to remove.');
520
523
  throw error;
521
524
  }
522
525
 
523
526
  // Remove the least recently viewed key that is not currently being accessed and retry.
524
- logInfo(`Out of storage. Evicting least recently accessed key (${keyForRemoval}) and retrying.`);
527
+ Logger.logInfo(`Out of storage. Evicting least recently accessed key (${keyForRemoval}) and retrying.`);
525
528
  return remove(keyForRemoval)
526
529
  .then(() => onyxMethod(...args));
527
530
  }
@@ -536,12 +539,11 @@ function evictStorageAndRetry(error, onyxMethod, ...args) {
536
539
  */
537
540
  function set(key, value) {
538
541
  // Logging properties only since values could be sensitive things we don't want to log
539
- logInfo(`set() called for key: ${key}${_.isObject(value) ? ` properties: ${_.keys(value).join(',')}` : ''}`);
542
+ Logger.logInfo(`set() called for key: ${key}${_.isObject(value) ? ` properties: ${_.keys(value).join(',')}` : ''}`);
540
543
 
541
544
  // eslint-disable-next-line no-use-before-define
542
545
  if (hasPendingMergeForKey(key)) {
543
- // eslint-disable-next-line max-len
544
- logAlert(`Onyx.set() called after Onyx.merge() for key: ${key}. It is recommended to use set() or merge() not both.`);
546
+ Logger.logAlert(`Onyx.set() called after Onyx.merge() for key: ${key}. It is recommended to use set() or merge() not both.`);
545
547
  }
546
548
 
547
549
  // Adds the key to cache when it's not available
@@ -680,7 +682,7 @@ function merge(key, value) {
680
682
 
681
683
  return set(key, modifiedData);
682
684
  } catch (error) {
683
- logAlert(`An error occurred while applying merge for key: ${key}, Error: ${error}`);
685
+ Logger.logAlert(`An error occurred while applying merge for key: ${key}, Error: ${error}`);
684
686
  }
685
687
 
686
688
  return Promise.resolve();
@@ -737,10 +739,11 @@ function clear() {
737
739
  function mergeCollection(collectionKey, collection) {
738
740
  // Confirm all the collection keys belong to the same parent
739
741
  _.each(collection, (data, dataKey) => {
740
- if (!isKeyMatch(collectionKey, dataKey)) {
741
- // eslint-disable-next-line max-len
742
- throw new Error(`Provided collection does not have all its data belonging to the same parent. CollectionKey: ${collectionKey}, DataKey: ${dataKey}`);
742
+ if (isKeyMatch(collectionKey, dataKey)) {
743
+ return;
743
744
  }
745
+
746
+ throw new Error(`Provided collection doesn't have all its data belonging to the same parent. CollectionKey: ${collectionKey}, DataKey: ${dataKey}`);
744
747
  });
745
748
 
746
749
  return getAllKeys()
@@ -780,6 +783,36 @@ function mergeCollection(collectionKey, collection) {
780
783
  });
781
784
  }
782
785
 
786
+ /**
787
+ * Insert API responses and lifecycle data into Onyx
788
+ *
789
+ * @param {Array} data An array of objects with shape {onyxMethod: oneOf('set', 'merge'), key: string, value: *}
790
+ */
791
+ function update(data) {
792
+ // First, validate the Onyx object is in the format we expect
793
+ _.each(data, ({onyxMethod, key}) => {
794
+ if (!_.contains(['set', 'merge'], onyxMethod)) {
795
+ throw new Error(`Invalid onyxMethod ${onyxMethod} in Onyx update.`);
796
+ }
797
+ if (!_.isString(key)) {
798
+ throw new Error(`Invalid ${typeof key} key provided in Onyx update. Onyx key must be of type string.`);
799
+ }
800
+ });
801
+
802
+ _.each(data, ({onyxMethod, key, value}) => {
803
+ switch (onyxMethod) {
804
+ case 'set':
805
+ set(key, value);
806
+ break;
807
+ case 'merge':
808
+ merge(key, value);
809
+ break;
810
+ default:
811
+ break;
812
+ }
813
+ });
814
+ }
815
+
783
816
  /**
784
817
  * Initialize the store with actions and listening for storage events
785
818
  *
@@ -837,7 +870,7 @@ function init({
837
870
  // Initialize all of our keys with data provided then give green light to any pending connections
838
871
  Promise.all([
839
872
  addAllSafeEvictionKeysToRecentlyAccessedList(),
840
- initializeWithDefaultKeyStates()
873
+ initializeWithDefaultKeyStates(),
841
874
  ])
842
875
  .then(deferredInitTask.resolve);
843
876
 
@@ -856,9 +889,10 @@ const Onyx = {
856
889
  multiSet,
857
890
  merge,
858
891
  mergeCollection,
892
+ update,
859
893
  clear,
860
894
  init,
861
- registerLogger,
895
+ registerLogger: Logger.registerLogger,
862
896
  addToEvictionBlockList,
863
897
  removeFromEvictionBlockList,
864
898
  isSafeEvictionKey,
@@ -883,14 +917,18 @@ function applyDecorators() {
883
917
  mergeCollection = decorate.decorateWithMetrics(mergeCollection, 'Onyx:mergeCollection');
884
918
  getAllKeys = decorate.decorateWithMetrics(getAllKeys, 'Onyx:getAllKeys');
885
919
  initializeWithDefaultKeyStates = decorate.decorateWithMetrics(initializeWithDefaultKeyStates, 'Onyx:defaults');
920
+ update = decorate.decorateWithMetrics(update, 'Onyx:update');
886
921
  /* eslint-enable */
887
922
 
888
923
  // Re-expose decorated methods
924
+ /* eslint-disable rulesdir/prefer-actions-set-data */
889
925
  Onyx.set = set;
890
926
  Onyx.multiSet = multiSet;
891
927
  Onyx.clear = clear;
892
928
  Onyx.merge = merge;
893
929
  Onyx.mergeCollection = mergeCollection;
930
+ Onyx.update = update;
931
+ /* eslint-enable */
894
932
 
895
933
  // Expose stats methods on Onyx
896
934
  Onyx.getMetrics = decorate.getMetrics;
package/lib/OnyxCache.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import _ from 'underscore';
2
2
  import lodashMerge from 'lodash/merge';
3
3
 
4
-
5
4
  const isDefined = _.negate(_.isUndefined);
6
5
 
7
6
  /**
@@ -43,7 +42,7 @@ class OnyxCache {
43
42
  this,
44
43
  'getAllKeys', 'getValue', 'hasCacheForKey', 'addKey', 'set', 'drop', 'merge',
45
44
  'hasPendingTask', 'getTaskPromise', 'captureTask', 'removeLeastRecentlyUsedKeys',
46
- 'setRecentKeysLimit'
45
+ 'setRecentKeysLimit',
47
46
  );
48
47
  }
49
48
 
@@ -171,14 +170,16 @@ class OnyxCache {
171
170
  * Remove keys that don't fall into the range of recently used keys
172
171
  */
173
172
  removeLeastRecentlyUsedKeys() {
174
- if (this.recentKeys.size > this.maxRecentKeysSize) {
175
- // Get the last N keys by doing a negative slice
176
- const recentlyAccessed = [...this.recentKeys].slice(-this.maxRecentKeysSize);
177
- const storageKeys = _.keys(this.storageMap);
178
- const keysToRemove = _.difference(storageKeys, recentlyAccessed);
179
-
180
- _.each(keysToRemove, this.drop);
173
+ if (this.recentKeys.size <= this.maxRecentKeysSize) {
174
+ return;
181
175
  }
176
+
177
+ // Get the last N keys by doing a negative slice
178
+ const recentlyAccessed = [...this.recentKeys].slice(-this.maxRecentKeysSize);
179
+ const storageKeys = _.keys(this.storageMap);
180
+ const keysToRemove = _.difference(storageKeys, recentlyAccessed);
181
+
182
+ _.each(keysToRemove, this.drop);
182
183
  }
183
184
 
184
185
  /**
package/lib/compose.js CHANGED
@@ -25,5 +25,6 @@ export default function compose(...funcs) {
25
25
  return funcs[0];
26
26
  }
27
27
 
28
+ // eslint-disable-next-line rulesdir/prefer-underscore-method
28
29
  return funcs.reduce((a, b) => (...args) => a(b(...args)));
29
30
  }
@@ -23,7 +23,7 @@ function measureMarkToNow(startMark, detail) {
23
23
  performance.measure(`${startMark.name} [${startMark.detail.args.toString()}]`, {
24
24
  start: startMark.startTime,
25
25
  end: performance.now(),
26
- detail: {...startMark.detail, ...detail}
26
+ detail: {...startMark.detail, ...detail},
27
27
  });
28
28
  }
29
29
 
@@ -193,12 +193,12 @@ function printMetrics({raw = false, format = 'console', methods} = {}) {
193
193
  title: methodName,
194
194
  heading: ['start time', 'end time', 'duration', 'args'],
195
195
  leftAlignedCols: [3],
196
- rows: calls.map(call => ([
196
+ rows: _.map(calls, call => ([
197
197
  toDuration(call.startTime - performance.timeOrigin, raw),
198
198
  toDuration((call.startTime + call.duration) - timeOrigin, raw),
199
199
  toDuration(call.duration, raw),
200
- call.detail.args.map(String).join(', ').slice(0, 60), // Restrict cell width to 60 chars max
201
- ]))
200
+ _.map(call.detail.args, String).join(', ').slice(0, 60), // Restrict cell width to 60 chars max
201
+ ])),
202
202
  });
203
203
  })
204
204
  .value();
@@ -206,7 +206,7 @@ function printMetrics({raw = false, format = 'console', methods} = {}) {
206
206
  if (/csv|json|string/i.test(format)) {
207
207
  const allTables = [tableSummary, ...methodCallTables];
208
208
 
209
- return allTables.map((table) => {
209
+ return _.map(allTables, (table) => {
210
210
  switch (format.toLowerCase()) {
211
211
  case 'csv':
212
212
  return table.toCSV();
@@ -219,7 +219,7 @@ function printMetrics({raw = false, format = 'console', methods} = {}) {
219
219
  }
220
220
 
221
221
  const lastComplete = lastCompleteCall && toDuration(
222
- (lastCompleteCall.startTime + lastCompleteCall.duration) - timeOrigin, raw
222
+ (lastCompleteCall.startTime + lastCompleteCall.duration) - timeOrigin, raw,
223
223
  );
224
224
 
225
225
  const mainOutput = [
@@ -227,7 +227,7 @@ function printMetrics({raw = false, format = 'console', methods} = {}) {
227
227
  ` - Total: ${toDuration(totalTime, raw)}`,
228
228
  ` - Last call finished at: ${lastComplete || 'N/A'}`,
229
229
  '',
230
- tableSummary.toString()
230
+ tableSummary.toString(),
231
231
  ];
232
232
 
233
233
  /* eslint-disable no-console */
@@ -10,7 +10,7 @@ import lodashMerge from 'lodash/merge';
10
10
  import SyncQueue from '../../SyncQueue';
11
11
 
12
12
  localforage.config({
13
- name: 'OnyxDB'
13
+ name: 'OnyxDB',
14
14
  });
15
15
 
16
16
  const provider = {
@@ -42,7 +42,7 @@ const provider = {
42
42
  const pairs = _.map(
43
43
  keys,
44
44
  key => localforage.getItem(key)
45
- .then(value => [key, value])
45
+ .then(value => [key, value]),
46
46
  );
47
47
 
48
48
  return Promise.all(pairs);
package/lib/withOnyx.js CHANGED
@@ -122,8 +122,7 @@ export default function (mapOnyxToState) {
122
122
  const key = Str.result(mapping.key, this.props);
123
123
 
124
124
  if (!Onyx.isSafeEvictionKey(key)) {
125
- // eslint-disable-next-line max-len
126
- throw new Error(`canEvict cannot be used on key '${key}'. This key must explicitly be flagged as safe for removal by adding it to Onyx.init({safeEvictionKeys: []}).`);
125
+ throw new Error(`canEvict can't be used on key '${key}'. This key must explicitly be flagged as safe for removal by adding it to Onyx.init({safeEvictionKeys: []}).`);
127
126
  }
128
127
 
129
128
  if (canEvict) {
@@ -147,6 +146,7 @@ export default function (mapOnyxToState) {
147
146
  connectMappingToOnyx(mapping, statePropertyName) {
148
147
  const key = Str.result(mapping.key, this.props);
149
148
 
149
+ // eslint-disable-next-line rulesdir/prefer-onyx-connect-in-libs
150
150
  this.activeConnectionIDs[key] = Onyx.connect({
151
151
  ...mapping,
152
152
  key,
package/package.json CHANGED
@@ -1,11 +1,24 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",
7
7
  "license": "MIT",
8
8
  "private": false,
9
+ "keywords": [
10
+ "React Native",
11
+ "React",
12
+ "Persistant storage",
13
+ "Pub/Sub"
14
+ ],
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/Expensify/react-native-onyx.git"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/Expensify/react-native-onyx/issues"
21
+ },
9
22
  "files": [
10
23
  "dist/**/*",
11
24
  "lib/**/*",
@@ -22,7 +35,7 @@
22
35
  "lint": "eslint .",
23
36
  "lint-tests": "eslint tests/**",
24
37
  "test": "jest",
25
- "build:web": "webpack --config webpack.config.js",
38
+ "build": "webpack --config webpack.config.js",
26
39
  "build:docs": "node buildDocs.js"
27
40
  },
28
41
  "dependencies": {
@@ -45,7 +58,7 @@
45
58
  "babel-plugin-react-native-web": "^0.13.5",
46
59
  "babel-plugin-transform-class-properties": "^6.24.1",
47
60
  "eslint": "^7.6.0",
48
- "eslint-config-expensify": "^2.0.11",
61
+ "eslint-config-expensify": "^2.0.24",
49
62
  "expensify-common": "git+https://github.com/Expensify/expensify-common.git#427295da130a4eacc184d38693664280d020dffd",
50
63
  "jest": "^26.5.2",
51
64
  "jest-cli": "^26.5.2",
package/web.js CHANGED
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  if (process.env.NODE_ENV === 'production') {
9
- module.exports = require('./dist/web.min.js');
9
+ module.exports = require('./dist/web.min');
10
10
  } else {
11
- module.exports = require('./dist/web.development.js');
11
+ module.exports = require('./dist/web.development');
12
12
  }