react-native-onyx 2.0.10 → 2.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/Onyx.d.ts CHANGED
@@ -97,7 +97,6 @@ type InitOptions = {
97
97
  initialKeyStates?: Partial<NullableKeyValueMapping>;
98
98
  safeEvictionKeys?: OnyxKey[];
99
99
  maxCachedKeysCount?: number;
100
- captureMetrics?: boolean;
101
100
  shouldSyncMultipleInstances?: boolean;
102
101
  debugSetState?: boolean;
103
102
  };
@@ -267,7 +266,6 @@ declare function update(data: OnyxUpdate[]): Promise<void>;
267
266
  * @param [options.maxCachedKeysCount=55] Sets how many recent keys should we try to keep in cache
268
267
  * Setting this to 0 would practically mean no cache
269
268
  * We try to free cache when we connect to a safe eviction key
270
- * @param [options.captureMetrics] Enables Onyx benchmarking and exposes the get/print/reset functions
271
269
  * @param [options.shouldSyncMultipleInstances] Auto synchronize storage events between multiple instances
272
270
  * of Onyx running in different tabs/windows. Defaults to true for platforms that support local storage (web/desktop)
273
271
  * @param [options.debugSetState] Enables debugging setState() calls to connected components.
package/dist/Onyx.js CHANGED
@@ -33,7 +33,7 @@ const Logger = __importStar(require("./Logger"));
33
33
  const OnyxCache_1 = __importDefault(require("./OnyxCache"));
34
34
  const Str = __importStar(require("./Str"));
35
35
  const createDeferredTask_1 = __importDefault(require("./createDeferredTask"));
36
- const PerformanceUtils = __importStar(require("./metrics/PerformanceUtils"));
36
+ const PerformanceUtils = __importStar(require("./PerformanceUtils"));
37
37
  const storage_1 = __importDefault(require("./storage"));
38
38
  const utils_1 = __importDefault(require("./utils"));
39
39
  const batch_1 = __importDefault(require("./batch"));
@@ -1378,7 +1378,7 @@ function setMemoryOnlyKeys(keyList) {
1378
1378
  * },
1379
1379
  * });
1380
1380
  */
1381
- function init({ keys = {}, initialKeyStates = {}, safeEvictionKeys = [], maxCachedKeysCount = 1000, captureMetrics = false, shouldSyncMultipleInstances = Boolean(global.localStorage), debugSetState = false, } = {}) {
1381
+ function init({ keys = {}, initialKeyStates = {}, safeEvictionKeys = [], maxCachedKeysCount = 1000, shouldSyncMultipleInstances = Boolean(global.localStorage), debugSetState = false } = {}) {
1382
1382
  storage_1.default.init();
1383
1383
  if (shouldSyncMultipleInstances) {
1384
1384
  storage_1.default.keepInstancesSync((key, value) => {
@@ -1387,11 +1387,6 @@ function init({ keys = {}, initialKeyStates = {}, safeEvictionKeys = [], maxCach
1387
1387
  keyChanged(key, value, prevValue);
1388
1388
  });
1389
1389
  }
1390
- if (captureMetrics) {
1391
- // The code here is only bundled and applied when the captureMetrics is set
1392
- // eslint-disable-next-line no-use-before-define
1393
- applyDecorators();
1394
- }
1395
1390
  if (debugSetState) {
1396
1391
  PerformanceUtils.setShouldDebugSetState(true);
1397
1392
  }
@@ -1433,38 +1428,4 @@ const Onyx = {
1433
1428
  tryGetCachedValue,
1434
1429
  hasPendingMergeForKey,
1435
1430
  };
1436
- /**
1437
- * Apply calls statistic decorators to benchmark Onyx
1438
- *
1439
- * @private
1440
- */
1441
- function applyDecorators() {
1442
- // We're requiring the script dynamically here so that it's only evaluated when decorators are used
1443
- const decorate = require('./metrics');
1444
- // Re-assign with decorated functions
1445
- /* eslint-disable no-func-assign */
1446
- get = decorate.decorateWithMetrics(get, 'Onyx:get');
1447
- set = decorate.decorateWithMetrics(set, 'Onyx:set');
1448
- multiSet = decorate.decorateWithMetrics(multiSet, 'Onyx:multiSet');
1449
- clear = decorate.decorateWithMetrics(clear, 'Onyx:clear');
1450
- merge = decorate.decorateWithMetrics(merge, 'Onyx:merge');
1451
- mergeCollection = decorate.decorateWithMetrics(mergeCollection, 'Onyx:mergeCollection');
1452
- getAllKeys = decorate.decorateWithMetrics(getAllKeys, 'Onyx:getAllKeys');
1453
- initializeWithDefaultKeyStates = decorate.decorateWithMetrics(initializeWithDefaultKeyStates, 'Onyx:defaults');
1454
- update = decorate.decorateWithMetrics(update, 'Onyx:update');
1455
- /* eslint-enable */
1456
- // Re-expose decorated methods
1457
- /* eslint-disable rulesdir/prefer-actions-set-data */
1458
- Onyx.set = set;
1459
- Onyx.multiSet = multiSet;
1460
- Onyx.clear = clear;
1461
- Onyx.merge = merge;
1462
- Onyx.mergeCollection = mergeCollection;
1463
- Onyx.update = update;
1464
- /* eslint-enable */
1465
- // Expose stats methods on Onyx
1466
- Onyx.getMetrics = decorate.getMetrics;
1467
- Onyx.resetMetrics = decorate.resetMetrics;
1468
- Onyx.printMetrics = decorate.printMetrics;
1469
- }
1470
1431
  exports.default = Onyx;
@@ -0,0 +1,10 @@
1
+ type Mapping = Record<string, unknown> & {
2
+ key: string;
3
+ displayName: string;
4
+ };
5
+ declare function setShouldDebugSetState(debug: boolean): void;
6
+ /**
7
+ * Provide insights into why a setState() call occurred by diffing the before and after values.
8
+ */
9
+ declare function logSetStateCall(mapping: Mapping, previousValue: unknown, newValue: unknown, caller: string, keyThatChanged: string): void;
10
+ export { logSetStateCall, setShouldDebugSetState };
@@ -5,11 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.setShouldDebugSetState = exports.logSetStateCall = void 0;
7
7
  const transform_1 = __importDefault(require("lodash/transform"));
8
- const underscore_1 = __importDefault(require("underscore"));
8
+ const fast_equals_1 = require("fast-equals");
9
9
  let debugSetState = false;
10
- /**
11
- * @param {Boolean} debug
12
- */
13
10
  function setShouldDebugSetState(debug) {
14
11
  debugSetState = debug;
15
12
  }
@@ -17,31 +14,24 @@ exports.setShouldDebugSetState = setShouldDebugSetState;
17
14
  /**
18
15
  * Deep diff between two objects. Useful for figuring out what changed about an object from one render to the next so
19
16
  * that state and props updates can be optimized.
20
- *
21
- * @param {Object} object
22
- * @param {Object} base
23
- * @return {Object}
24
17
  */
25
18
  function diffObject(object, base) {
26
- function changes(obj, comparisonObject) {
27
- return (0, transform_1.default)(obj, (result, value, key) => {
28
- if (underscore_1.default.isEqual(value, comparisonObject[key])) {
29
- return;
30
- }
19
+ return (0, transform_1.default)(object, (result, value, key) => {
20
+ if ((0, fast_equals_1.deepEqual)(value, base[key])) {
21
+ return;
22
+ }
23
+ if (typeof value === 'object' && typeof base[key] === 'object') {
31
24
  // eslint-disable-next-line no-param-reassign
32
- result[key] = underscore_1.default.isObject(value) && underscore_1.default.isObject(comparisonObject[key]) ? changes(value, comparisonObject[key]) : value;
33
- });
34
- }
35
- return changes(object, base);
25
+ result[key] = diffObject(value, base[key]);
26
+ }
27
+ else {
28
+ // eslint-disable-next-line no-param-reassign
29
+ result[key] = value;
30
+ }
31
+ });
36
32
  }
37
33
  /**
38
34
  * Provide insights into why a setState() call occurred by diffing the before and after values.
39
- *
40
- * @param {Object} mapping
41
- * @param {*} previousValue
42
- * @param {*} newValue
43
- * @param {String} caller
44
- * @param {String} [keyThatChanged]
45
35
  */
46
36
  function logSetStateCall(mapping, previousValue, newValue, caller, keyThatChanged) {
47
37
  if (!debugSetState) {
@@ -51,7 +41,7 @@ function logSetStateCall(mapping, previousValue, newValue, caller, keyThatChange
51
41
  if (keyThatChanged) {
52
42
  logParams.keyThatChanged = keyThatChanged;
53
43
  }
54
- if (underscore_1.default.isObject(newValue) && underscore_1.default.isObject(previousValue)) {
44
+ if (newValue && previousValue && typeof newValue === 'object' && typeof previousValue === 'object') {
55
45
  logParams.difference = diffObject(previousValue, newValue);
56
46
  }
57
47
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-onyx",
3
- "version": "2.0.10",
3
+ "version": "2.0.11",
4
4
  "author": "Expensify, Inc.",
5
5
  "homepage": "https://expensify.com",
6
6
  "description": "State management for React Native",
@@ -1,14 +0,0 @@
1
- /**
2
- * Provide insights into why a setState() call occurred by diffing the before and after values.
3
- *
4
- * @param {Object} mapping
5
- * @param {*} previousValue
6
- * @param {*} newValue
7
- * @param {String} caller
8
- * @param {String} [keyThatChanged]
9
- */
10
- export function logSetStateCall(mapping: Object, previousValue: any, newValue: any, caller: string, keyThatChanged?: string | undefined): void;
11
- /**
12
- * @param {Boolean} debug
13
- */
14
- export function setShouldDebugSetState(debug: boolean): void;
@@ -1,4 +0,0 @@
1
- export function decorateWithMetrics(func: any): any;
2
- export function getMetrics(): void;
3
- export function resetMetrics(): void;
4
- export function printMetrics(): void;
@@ -1,14 +0,0 @@
1
- "use strict";
2
- // For web-only implementations of Onyx, this module will just be a no-op
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.printMetrics = exports.resetMetrics = exports.getMetrics = exports.decorateWithMetrics = void 0;
5
- function decorateWithMetrics(func) {
6
- return func;
7
- }
8
- exports.decorateWithMetrics = decorateWithMetrics;
9
- function getMetrics() { }
10
- exports.getMetrics = getMetrics;
11
- function printMetrics() { }
12
- exports.printMetrics = printMetrics;
13
- function resetMetrics() { }
14
- exports.resetMetrics = resetMetrics;
@@ -1,43 +0,0 @@
1
- /**
2
- * Wraps a function with metrics capturing logic
3
- * @param {function} func
4
- * @param {String} [alias]
5
- * @returns {function} The wrapped function
6
- */
7
- export function decorateWithMetrics(func: Function, alias?: string | undefined): Function;
8
- /**
9
- * Aggregates and returns benchmark information
10
- * @returns {{summaries: Record<string, Object>, totalTime: number, lastCompleteCall: *}}
11
- * An object with
12
- * - `totalTime` - total time spent by decorated methods
13
- * - `lastCompleteCall` - millisecond since launch the last call completed at
14
- * - `summaries` - mapping of all captured stats: summaries.methodName -> method stats
15
- */
16
- export function getMetrics(): {
17
- summaries: Record<string, Object>;
18
- totalTime: number;
19
- lastCompleteCall: any;
20
- };
21
- /**
22
- * Clears all collected metrics.
23
- */
24
- export function resetMetrics(): void;
25
- /**
26
- * Print extensive information on the dev console
27
- * max, min, average, total time for each method
28
- * and a table of individual calls
29
- *
30
- * @param {Object} [options]
31
- * @param {boolean} [options.raw=false] - setting this to true will print raw instead of human friendly times
32
- * Useful when you copy the printed table to excel and let excel do the number formatting
33
- * @param {'console'|'csv'|'json'|'string'} [options.format=console] The output format of this function
34
- * `string` is useful when __DEV__ is set to `false` as writing to the console is disabled, but the result of this
35
- * method would still get printed as output
36
- * @param {string[]} [options.methods] Print stats only for these method names
37
- * @returns {string|undefined}
38
- */
39
- export function printMetrics({ raw, format, methods }?: {
40
- raw?: boolean | undefined;
41
- format?: "string" | "console" | "csv" | "json" | undefined;
42
- methods?: string[] | undefined;
43
- } | undefined): string | undefined;
@@ -1,229 +0,0 @@
1
- "use strict";
2
- var __rest = (this && this.__rest) || function (s, e) {
3
- var t = {};
4
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
- t[p] = s[p];
6
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
- t[p[i]] = s[p[i]];
10
- }
11
- return t;
12
- };
13
- var __importDefault = (this && this.__importDefault) || function (mod) {
14
- return (mod && mod.__esModule) ? mod : { "default": mod };
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.printMetrics = exports.resetMetrics = exports.getMetrics = exports.decorateWithMetrics = void 0;
18
- const underscore_1 = __importDefault(require("underscore"));
19
- const react_native_performance_1 = __importDefault(require("react-native-performance"));
20
- const MDTable_1 = __importDefault(require("../MDTable"));
21
- const decoratedAliases = new Set();
22
- /**
23
- * Capture a start mark to performance entries
24
- * @param {string} alias
25
- * @param {Array<*>} args
26
- * @returns {{name: string, startTime:number, detail: {args: [], alias: string}}}
27
- */
28
- function addMark(alias, args) {
29
- return react_native_performance_1.default.mark(alias, { detail: { args, alias } });
30
- }
31
- /**
32
- * Capture a measurement between the start mark and now
33
- * @param {{name: string, startTime:number, detail: {args: []}}} startMark
34
- * @param {*} detail
35
- */
36
- function measureMarkToNow(startMark, detail) {
37
- react_native_performance_1.default.measure(`${startMark.name} [${startMark.detail.args.toString()}]`, {
38
- start: startMark.startTime,
39
- end: react_native_performance_1.default.now(),
40
- detail: Object.assign(Object.assign({}, startMark.detail), detail),
41
- });
42
- }
43
- /**
44
- * Wraps a function with metrics capturing logic
45
- * @param {function} func
46
- * @param {String} [alias]
47
- * @returns {function} The wrapped function
48
- */
49
- function decorateWithMetrics(func, alias = func.name) {
50
- if (decoratedAliases.has(alias)) {
51
- throw new Error(`"${alias}" is already decorated`);
52
- }
53
- decoratedAliases.add(alias);
54
- function decorated(...args) {
55
- const mark = addMark(alias, args);
56
- // eslint-disable-next-line no-invalid-this
57
- const originalPromise = func.apply(this, args);
58
- /*
59
- * Then handlers added here are not affecting the original promise
60
- * They create a separate chain that's not exposed (returned) to the original caller
61
- * */
62
- originalPromise
63
- .then((result) => {
64
- measureMarkToNow(mark, { result });
65
- })
66
- .catch((error) => {
67
- measureMarkToNow(mark, { error });
68
- });
69
- return originalPromise;
70
- }
71
- return decorated;
72
- }
73
- exports.decorateWithMetrics = decorateWithMetrics;
74
- /**
75
- * Calculate the total sum of a given key in a list
76
- * @param {Array<Record<prop, Number>>} list
77
- * @param {string} prop
78
- * @returns {number}
79
- */
80
- function sum(list, prop) {
81
- return underscore_1.default.reduce(list, (memo, next) => memo + next[prop], 0);
82
- }
83
- /**
84
- * Aggregates and returns benchmark information
85
- * @returns {{summaries: Record<string, Object>, totalTime: number, lastCompleteCall: *}}
86
- * An object with
87
- * - `totalTime` - total time spent by decorated methods
88
- * - `lastCompleteCall` - millisecond since launch the last call completed at
89
- * - `summaries` - mapping of all captured stats: summaries.methodName -> method stats
90
- */
91
- function getMetrics() {
92
- const summaries = underscore_1.default.chain(react_native_performance_1.default.getEntriesByType('measure'))
93
- .filter((entry) => entry.detail && decoratedAliases.has(entry.detail.alias))
94
- .groupBy((entry) => entry.detail.alias)
95
- .map((calls, methodName) => {
96
- const total = sum(calls, 'duration');
97
- const avg = total / calls.length || 0;
98
- const max = underscore_1.default.max(calls, 'duration').duration || 0;
99
- const min = underscore_1.default.min(calls, 'duration').duration || 0;
100
- // Latest complete call (by end time) for all the calls made to the current method
101
- const lastCall = underscore_1.default.max(calls, (call) => call.startTime + call.duration);
102
- return [
103
- methodName,
104
- {
105
- methodName,
106
- total,
107
- max,
108
- min,
109
- avg,
110
- lastCall,
111
- calls,
112
- },
113
- ];
114
- })
115
- .object() // Create a map like methodName -> StatSummary
116
- .value();
117
- const totalTime = sum(underscore_1.default.values(summaries), 'total');
118
- // Latest complete call (by end time) of all methods up to this point
119
- const lastCompleteCall = underscore_1.default.max(underscore_1.default.values(summaries), (summary) => summary.lastCall.startTime + summary.lastCall.duration).lastCall;
120
- return {
121
- totalTime,
122
- summaries,
123
- lastCompleteCall,
124
- };
125
- }
126
- exports.getMetrics = getMetrics;
127
- /**
128
- * Convert milliseconds to human readable time
129
- * @param {number} millis
130
- * @param {boolean} [raw=false]
131
- * @returns {string|number}
132
- */
133
- function toDuration(millis, raw = false) {
134
- if (raw) {
135
- return millis;
136
- }
137
- const minute = 60 * 1000;
138
- if (millis > minute) {
139
- return `${(millis / minute).toFixed(1)}min`;
140
- }
141
- const second = 1000;
142
- if (millis > second) {
143
- return `${(millis / second).toFixed(2)}sec`;
144
- }
145
- return `${millis.toFixed(3)}ms`;
146
- }
147
- /**
148
- * Print extensive information on the dev console
149
- * max, min, average, total time for each method
150
- * and a table of individual calls
151
- *
152
- * @param {Object} [options]
153
- * @param {boolean} [options.raw=false] - setting this to true will print raw instead of human friendly times
154
- * Useful when you copy the printed table to excel and let excel do the number formatting
155
- * @param {'console'|'csv'|'json'|'string'} [options.format=console] The output format of this function
156
- * `string` is useful when __DEV__ is set to `false` as writing to the console is disabled, but the result of this
157
- * method would still get printed as output
158
- * @param {string[]} [options.methods] Print stats only for these method names
159
- * @returns {string|undefined}
160
- */
161
- function printMetrics({ raw = false, format = 'console', methods } = {}) {
162
- const { totalTime, summaries, lastCompleteCall } = getMetrics();
163
- const tableSummary = MDTable_1.default.factory({
164
- heading: ['method', 'total time spent', 'max', 'min', 'avg', 'time last call completed', 'calls made'],
165
- leftAlignedCols: [0],
166
- });
167
- /* Performance marks (startTimes) are relative to system uptime
168
- * timeOrigin is the point at which the app started to init
169
- * We use timeOrigin to display times relative to app launch time
170
- * See: https://github.com/oblador/react-native-performance/issues/50 */
171
- const timeOrigin = react_native_performance_1.default.timeOrigin;
172
- const methodNames = underscore_1.default.isArray(methods) ? methods : underscore_1.default.keys(summaries);
173
- const methodCallTables = underscore_1.default.chain(methodNames)
174
- .filter((methodName) => summaries[methodName] && summaries[methodName].avg > 0)
175
- .map((methodName) => {
176
- const _a = summaries[methodName], { calls } = _a, methodStats = __rest(_a, ["calls"]);
177
- tableSummary.addRow(methodName, toDuration(methodStats.total, raw), toDuration(methodStats.max, raw), toDuration(methodStats.min, raw), toDuration(methodStats.avg, raw), toDuration(methodStats.lastCall.startTime + methodStats.lastCall.duration - timeOrigin, raw), calls.length);
178
- return MDTable_1.default.factory({
179
- title: methodName,
180
- heading: ['start time', 'end time', 'duration', 'args'],
181
- leftAlignedCols: [3],
182
- rows: underscore_1.default.map(calls, (call) => [
183
- toDuration(call.startTime - react_native_performance_1.default.timeOrigin, raw),
184
- toDuration(call.startTime + call.duration - timeOrigin, raw),
185
- toDuration(call.duration, raw),
186
- underscore_1.default.map(call.detail.args, String).join(', ').slice(0, 60), // Restrict cell width to 60 chars max
187
- ]),
188
- });
189
- })
190
- .value();
191
- if (/csv|json|string/i.test(format)) {
192
- const allTables = [tableSummary, ...methodCallTables];
193
- return underscore_1.default.map(allTables, (table) => {
194
- switch (format.toLowerCase()) {
195
- case 'csv':
196
- return table.toCSV();
197
- case 'json':
198
- return table.toJSON();
199
- default:
200
- return table.toString();
201
- }
202
- }).join('\n\n');
203
- }
204
- const lastComplete = lastCompleteCall && toDuration(lastCompleteCall.startTime + lastCompleteCall.duration - timeOrigin, raw);
205
- const mainOutput = ['### Onyx Benchmark', ` - Total: ${toDuration(totalTime, raw)}`, ` - Last call finished at: ${lastComplete || 'N/A'}`, '', tableSummary.toString()];
206
- /* eslint-disable no-console */
207
- console.info(mainOutput.join('\n'));
208
- methodCallTables.forEach((table) => {
209
- console.groupCollapsed(table.getTitle());
210
- console.info(table.toString());
211
- console.groupEnd();
212
- });
213
- /* eslint-enable */
214
- }
215
- exports.printMetrics = printMetrics;
216
- /**
217
- * Clears all collected metrics.
218
- */
219
- function resetMetrics() {
220
- const { summaries } = getMetrics();
221
- underscore_1.default.chain(summaries)
222
- .map((summary) => summary.calls)
223
- .flatten()
224
- .each((measure) => {
225
- react_native_performance_1.default.clearMarks(measure.detail.alias);
226
- react_native_performance_1.default.clearMeasures(measure.name);
227
- });
228
- }
229
- exports.resetMetrics = resetMetrics;