react-native-onyx 2.0.10 → 2.0.12
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 +0 -2
- package/dist/Onyx.js +66 -48
- package/dist/PerformanceUtils.d.ts +10 -0
- package/dist/{metrics/PerformanceUtils.js → PerformanceUtils.js} +14 -24
- package/package.json +1 -1
- package/dist/metrics/PerformanceUtils.d.ts +0 -14
- package/dist/metrics/index.d.ts +0 -4
- package/dist/metrics/index.js +0 -14
- package/dist/metrics/index.native.d.ts +0 -43
- package/dist/metrics/index.native.js +0 -229
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("./
|
|
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"));
|
|
@@ -694,13 +694,70 @@ function addKeyToRecentlyAccessedIfNeeded(mapping) {
|
|
|
694
694
|
* @param {Object} mapping
|
|
695
695
|
*/
|
|
696
696
|
function getCollectionDataAndSendAsObject(matchingKeys, mapping) {
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
697
|
+
// Keys that are not in the cache
|
|
698
|
+
const missingKeys = [];
|
|
699
|
+
// Tasks that are pending
|
|
700
|
+
const pendingTasks = [];
|
|
701
|
+
// Keys for the tasks that are pending
|
|
702
|
+
const pendingKeys = [];
|
|
703
|
+
// We are going to combine all the data from the matching keys into a single object
|
|
704
|
+
const data = {};
|
|
705
|
+
/**
|
|
706
|
+
* We are going to iterate over all the matching keys and check if we have the data in the cache.
|
|
707
|
+
* If we do then we add it to the data object. If we do not then we check if there is a pending task
|
|
708
|
+
* for the key. If there is then we add the promise to the pendingTasks array and the key to the pendingKeys
|
|
709
|
+
* array. If there is no pending task then we add the key to the missingKeys array.
|
|
710
|
+
*
|
|
711
|
+
* These missingKeys will be later to use to multiGet the data from the storage.
|
|
712
|
+
*/
|
|
713
|
+
matchingKeys.forEach((key) => {
|
|
714
|
+
const cacheValue = OnyxCache_1.default.getValue(key);
|
|
715
|
+
if (cacheValue) {
|
|
716
|
+
data[key] = cacheValue;
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
const pendingKey = `get:${key}`;
|
|
720
|
+
if (OnyxCache_1.default.hasPendingTask(pendingKey)) {
|
|
721
|
+
pendingTasks.push(OnyxCache_1.default.getTaskPromise(pendingKey));
|
|
722
|
+
pendingKeys.push(key);
|
|
723
|
+
}
|
|
724
|
+
else {
|
|
725
|
+
missingKeys.push(key);
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
Promise.all(pendingTasks)
|
|
729
|
+
// We are going to wait for all the pending tasks to resolve and then add the data to the data object.
|
|
730
|
+
.then((values) => {
|
|
731
|
+
values.forEach((value, index) => {
|
|
732
|
+
data[pendingKeys[index]] = value;
|
|
733
|
+
});
|
|
734
|
+
return Promise.resolve();
|
|
735
|
+
})
|
|
736
|
+
// We are going to get the missing keys using multiGet from the storage.
|
|
737
|
+
.then(() => {
|
|
738
|
+
if (missingKeys.length === 0) {
|
|
739
|
+
return Promise.resolve();
|
|
740
|
+
}
|
|
741
|
+
return storage_1.default.multiGet(missingKeys);
|
|
742
|
+
})
|
|
743
|
+
// We are going to add the data from the missing keys to the data object and also merge it to the cache.
|
|
744
|
+
.then((values) => {
|
|
745
|
+
if (!values || values.length === 0) {
|
|
746
|
+
return Promise.resolve();
|
|
747
|
+
}
|
|
748
|
+
// temp object is used to merge the missing data into the cache
|
|
749
|
+
const temp = {};
|
|
750
|
+
values.forEach((value) => {
|
|
751
|
+
data[value[0]] = value[1];
|
|
752
|
+
temp[value[0]] = value[1];
|
|
753
|
+
});
|
|
754
|
+
OnyxCache_1.default.merge(temp);
|
|
755
|
+
return Promise.resolve();
|
|
756
|
+
})
|
|
757
|
+
// We are going to send the data to the subscriber.
|
|
758
|
+
.finally(() => {
|
|
759
|
+
sendDataToConnection(mapping, data, undefined, true);
|
|
760
|
+
});
|
|
704
761
|
}
|
|
705
762
|
/**
|
|
706
763
|
* Subscribes a react component's state directly to a store key
|
|
@@ -1378,7 +1435,7 @@ function setMemoryOnlyKeys(keyList) {
|
|
|
1378
1435
|
* },
|
|
1379
1436
|
* });
|
|
1380
1437
|
*/
|
|
1381
|
-
function init({ keys = {}, initialKeyStates = {}, safeEvictionKeys = [], maxCachedKeysCount = 1000,
|
|
1438
|
+
function init({ keys = {}, initialKeyStates = {}, safeEvictionKeys = [], maxCachedKeysCount = 1000, shouldSyncMultipleInstances = Boolean(global.localStorage), debugSetState = false } = {}) {
|
|
1382
1439
|
storage_1.default.init();
|
|
1383
1440
|
if (shouldSyncMultipleInstances) {
|
|
1384
1441
|
storage_1.default.keepInstancesSync((key, value) => {
|
|
@@ -1387,11 +1444,6 @@ function init({ keys = {}, initialKeyStates = {}, safeEvictionKeys = [], maxCach
|
|
|
1387
1444
|
keyChanged(key, value, prevValue);
|
|
1388
1445
|
});
|
|
1389
1446
|
}
|
|
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
1447
|
if (debugSetState) {
|
|
1396
1448
|
PerformanceUtils.setShouldDebugSetState(true);
|
|
1397
1449
|
}
|
|
@@ -1433,38 +1485,4 @@ const Onyx = {
|
|
|
1433
1485
|
tryGetCachedValue,
|
|
1434
1486
|
hasPendingMergeForKey,
|
|
1435
1487
|
};
|
|
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
1488
|
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
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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] =
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
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 (
|
|
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,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;
|
package/dist/metrics/index.d.ts
DELETED
package/dist/metrics/index.js
DELETED
|
@@ -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;
|