@atlaskit/react-ufo 5.1.3 → 5.2.0
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/AGENTS.md +426 -0
- package/CHANGELOG.md +22 -0
- package/dist/cjs/create-payload/index.js +3 -3
- package/dist/cjs/hidden-timing/index.js +135 -0
- package/dist/cjs/interaction-metrics/index.js +11 -3
- package/dist/cjs/interaction-metrics-init/index.js +3 -0
- package/dist/cjs/set-terminal-error/index.js +11 -3
- package/dist/cjs/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +39 -27
- package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +22 -7
- package/dist/es2019/create-payload/index.js +3 -3
- package/dist/es2019/hidden-timing/index.js +128 -0
- package/dist/es2019/interaction-metrics/index.js +10 -2
- package/dist/es2019/interaction-metrics-init/index.js +4 -1
- package/dist/es2019/set-terminal-error/index.js +12 -4
- package/dist/es2019/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +17 -4
- package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +22 -7
- package/dist/esm/create-payload/index.js +4 -4
- package/dist/esm/hidden-timing/index.js +130 -0
- package/dist/esm/interaction-metrics/index.js +10 -2
- package/dist/esm/interaction-metrics-init/index.js +4 -1
- package/dist/esm/set-terminal-error/index.js +12 -4
- package/dist/esm/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +39 -27
- package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +22 -7
- package/dist/types/common/react-ufo-payload-schema.d.ts +1 -0
- package/dist/types/common/vc/types.d.ts +2 -0
- package/dist/types/create-terminal-error-payload/index.d.ts +4 -0
- package/dist/types/hidden-timing/index.d.ts +42 -0
- package/dist/types/interaction-metrics/index.d.ts +8 -0
- package/dist/types/set-terminal-error/index.d.ts +4 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.d.ts +3 -3
- package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/types.d.ts +12 -0
- package/dist/types-ts4.5/common/react-ufo-payload-schema.d.ts +1 -0
- package/dist/types-ts4.5/common/vc/types.d.ts +2 -0
- package/dist/types-ts4.5/create-terminal-error-payload/index.d.ts +4 -0
- package/dist/types-ts4.5/hidden-timing/index.d.ts +42 -0
- package/dist/types-ts4.5/interaction-metrics/index.d.ts +8 -0
- package/dist/types-ts4.5/set-terminal-error/index.d.ts +4 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.d.ts +3 -3
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/types.d.ts +12 -0
- package/package.json +10 -1
|
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
-
exports.ModuleLoadingProfiler = void 0;
|
|
7
|
+
exports.PreviousInteractionLog = exports.ModuleLoadingProfiler = void 0;
|
|
8
8
|
exports.abort = abort;
|
|
9
9
|
exports.abortAll = abortAll;
|
|
10
10
|
exports.abortByNewInteraction = abortByNewInteraction;
|
|
@@ -71,9 +71,12 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
71
71
|
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
72
72
|
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
73
73
|
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
|
|
74
|
-
var PreviousInteractionLog = {
|
|
74
|
+
var PreviousInteractionLog = exports.PreviousInteractionLog = {
|
|
75
|
+
id: undefined,
|
|
75
76
|
name: undefined,
|
|
76
|
-
|
|
77
|
+
type: undefined,
|
|
78
|
+
isAborted: undefined,
|
|
79
|
+
timestamp: undefined
|
|
77
80
|
};
|
|
78
81
|
var postInteractionLog = exports.postInteractionLog = new _postInteractionLog.default();
|
|
79
82
|
var interactionExtraMetrics = exports.interactionExtraMetrics = new _interactionExtraMetrics.default();
|
|
@@ -751,6 +754,11 @@ function finishInteraction(id, data) {
|
|
|
751
754
|
remove(id);
|
|
752
755
|
}
|
|
753
756
|
}
|
|
757
|
+
if ((0, _platformFeatureFlags.fg)('platform_ufo_enable_terminal_errors')) {
|
|
758
|
+
PreviousInteractionLog.id = data.id;
|
|
759
|
+
PreviousInteractionLog.type = data.type;
|
|
760
|
+
PreviousInteractionLog.timestamp = data.end;
|
|
761
|
+
}
|
|
754
762
|
PreviousInteractionLog.name = data.ufoName || 'unknown';
|
|
755
763
|
PreviousInteractionLog.isAborted = data.abortReason != null;
|
|
756
764
|
if (data.ufoName) {
|
|
@@ -187,6 +187,9 @@ function init(analyticsWebClientAsync, config) {
|
|
|
187
187
|
}
|
|
188
188
|
(0, _hiddenTiming.setupHiddenTimingCapture)();
|
|
189
189
|
(0, _additionalPayload.startLighthouseObserver)();
|
|
190
|
+
if ((0, _platformFeatureFlags.fg)('platform_ufo_is_tab_throttled')) {
|
|
191
|
+
(0, _hiddenTiming.setupThrottleDetection)();
|
|
192
|
+
}
|
|
190
193
|
initialized = true;
|
|
191
194
|
if (typeof PerformanceObserver !== 'undefined') {
|
|
192
195
|
var observer = (0, _interactionsPerformanceObserver.getPerformanceObserver)();
|
|
@@ -18,18 +18,26 @@ function sinkTerminalErrorHandler(fn) {
|
|
|
18
18
|
sinkHandlerFn = fn;
|
|
19
19
|
}
|
|
20
20
|
function setTerminalError(error, additionalAttributes, labelStack) {
|
|
21
|
-
var _activeInteraction$uf, _activeInteraction$id, _activeInteraction$ty;
|
|
21
|
+
var _activeInteraction$uf, _activeInteraction$id, _activeInteraction$ty, _PreviousInteractionL, _PreviousInteractionL2, _PreviousInteractionL3;
|
|
22
22
|
var activeInteraction = (0, _interactionMetrics.getActiveInteraction)();
|
|
23
|
+
var currentTime = performance.now();
|
|
23
24
|
var errorData = _objectSpread({
|
|
24
25
|
errorType: error.name || 'Error',
|
|
25
26
|
errorMessage: error.message.slice(0, 100),
|
|
26
|
-
timestamp:
|
|
27
|
+
timestamp: currentTime
|
|
27
28
|
}, additionalAttributes);
|
|
29
|
+
|
|
30
|
+
// Calculate time since previous interaction
|
|
31
|
+
var timeSincePreviousInteraction = _interactionMetrics.PreviousInteractionLog.timestamp != null ? currentTime - _interactionMetrics.PreviousInteractionLog.timestamp : null;
|
|
28
32
|
var context = {
|
|
29
33
|
labelStack: labelStack !== null && labelStack !== void 0 ? labelStack : null,
|
|
30
34
|
activeInteractionName: (_activeInteraction$uf = activeInteraction === null || activeInteraction === void 0 ? void 0 : activeInteraction.ufoName) !== null && _activeInteraction$uf !== void 0 ? _activeInteraction$uf : null,
|
|
31
35
|
activeInteractionId: (_activeInteraction$id = activeInteraction === null || activeInteraction === void 0 ? void 0 : activeInteraction.id) !== null && _activeInteraction$id !== void 0 ? _activeInteraction$id : null,
|
|
32
|
-
activeInteractionType: (_activeInteraction$ty = activeInteraction === null || activeInteraction === void 0 ? void 0 : activeInteraction.type) !== null && _activeInteraction$ty !== void 0 ? _activeInteraction$ty : null
|
|
36
|
+
activeInteractionType: (_activeInteraction$ty = activeInteraction === null || activeInteraction === void 0 ? void 0 : activeInteraction.type) !== null && _activeInteraction$ty !== void 0 ? _activeInteraction$ty : null,
|
|
37
|
+
previousInteractionId: (_PreviousInteractionL = _interactionMetrics.PreviousInteractionLog.id) !== null && _PreviousInteractionL !== void 0 ? _PreviousInteractionL : null,
|
|
38
|
+
previousInteractionName: (_PreviousInteractionL2 = _interactionMetrics.PreviousInteractionLog.name) !== null && _PreviousInteractionL2 !== void 0 ? _PreviousInteractionL2 : null,
|
|
39
|
+
previousInteractionType: (_PreviousInteractionL3 = _interactionMetrics.PreviousInteractionLog.type) !== null && _PreviousInteractionL3 !== void 0 ? _PreviousInteractionL3 : null,
|
|
40
|
+
timeSincePreviousInteraction: timeSincePreviousInteraction
|
|
33
41
|
};
|
|
34
42
|
sinkHandlerFn(errorData, context);
|
|
35
43
|
}
|
|
@@ -120,13 +120,14 @@ var AbstractVCCalculatorBase = exports.default = /*#__PURE__*/function () {
|
|
|
120
120
|
value: function () {
|
|
121
121
|
var _calculateWithDebugInfo = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(filteredEntries, startTime, stopTime, isPostInteraction, isVCClean, interactionType, isPageVisible, interactionId, dirtyReason, allEntries, include3p, excludeSmartAnswersInSearch, interactionAbortReason, includeSSRRatio) {
|
|
122
122
|
var _window, _window2, _window3, _window4, _window6;
|
|
123
|
-
var percentiles, viewportEntries, vcLogs, vcDetails, percentileIndex, entryDataBuffer, ssrRatio, _iterator4, _step4, _entry3, time, viewportPercentage, entries, elementNames, previousResult, i, percentile, enhancedVcLogs, shouldCalculate3p, shouldCalculateDebugDetails, sortedVcLogs, maxViewportPercentageAtTime, maxSoFar, _iterator5, _step5, log, getBiggestPreviousViewportPercentage, ignoredEntriesByTime, _iterator6, _step6, _entry4, _ignoredEntriesByTime, _viewportData$rect, _viewportData$previou, viewportData, timestamp, additionalVcLogs, _iterator7, _step7, _step7$value, _timestamp, ignoredEntries, _viewportPercentage, v3RevisionDebugDetails, _window5, _window5$__ufo_devtoo, _window7, _window7$__on_ufo_vc_;
|
|
123
|
+
var percentiles, viewportEntries, shouldCalculateSpeedIndex, _yield$calculateTTVCP, vcLogs, speedIndex, vcDetails, percentileIndex, entryDataBuffer, ssrRatio, _iterator4, _step4, _entry3, time, viewportPercentage, entries, elementNames, previousResult, i, percentile, enhancedVcLogs, shouldCalculate3p, shouldCalculateDebugDetails, sortedVcLogs, maxViewportPercentageAtTime, maxSoFar, _iterator5, _step5, log, getBiggestPreviousViewportPercentage, ignoredEntriesByTime, _iterator6, _step6, _entry4, _ignoredEntriesByTime, _viewportData$rect, _viewportData$previou, viewportData, timestamp, additionalVcLogs, _iterator7, _step7, _step7$value, _timestamp, ignoredEntries, _viewportPercentage, v3RevisionDebugDetails, _window5, _window5$__ufo_devtoo, _window7, _window7$__on_ufo_vc_;
|
|
124
124
|
return _regenerator.default.wrap(function _callee$(_context) {
|
|
125
125
|
while (1) switch (_context.prev = _context.next) {
|
|
126
126
|
case 0:
|
|
127
127
|
percentiles = [25, 50, 75, 80, 85, 90, 95, 98, 99, 100];
|
|
128
128
|
viewportEntries = this.filterViewportEntries(filteredEntries);
|
|
129
|
-
|
|
129
|
+
shouldCalculateSpeedIndex = (0, _platformFeatureFlags.fg)('platform_ufo_ttvc_v4_speed_index');
|
|
130
|
+
_context.next = 5;
|
|
130
131
|
return (0, _percentileCalc.calculateTTVCPercentilesWithDebugInfo)({
|
|
131
132
|
viewport: {
|
|
132
133
|
width: (0, _getViewportWidth.default)(),
|
|
@@ -134,24 +135,27 @@ var AbstractVCCalculatorBase = exports.default = /*#__PURE__*/function () {
|
|
|
134
135
|
},
|
|
135
136
|
startTime: startTime,
|
|
136
137
|
stopTime: stopTime,
|
|
137
|
-
orderedEntries: viewportEntries
|
|
138
|
+
orderedEntries: viewportEntries,
|
|
139
|
+
calculateSpeedIndex: shouldCalculateSpeedIndex
|
|
138
140
|
});
|
|
139
|
-
case
|
|
140
|
-
|
|
141
|
+
case 5:
|
|
142
|
+
_yield$calculateTTVCP = _context.sent;
|
|
143
|
+
vcLogs = _yield$calculateTTVCP.entries;
|
|
144
|
+
speedIndex = _yield$calculateTTVCP.speedIndex;
|
|
141
145
|
vcDetails = {};
|
|
142
146
|
percentileIndex = 0;
|
|
143
147
|
entryDataBuffer = new Set();
|
|
144
148
|
ssrRatio = -1;
|
|
145
149
|
if (!vcLogs) {
|
|
146
|
-
_context.next =
|
|
150
|
+
_context.next = 33;
|
|
147
151
|
break;
|
|
148
152
|
}
|
|
149
153
|
_iterator4 = _createForOfIteratorHelper(vcLogs);
|
|
150
|
-
_context.prev =
|
|
154
|
+
_context.prev = 14;
|
|
151
155
|
_iterator4.s();
|
|
152
|
-
case
|
|
156
|
+
case 16:
|
|
153
157
|
if ((_step4 = _iterator4.n()).done) {
|
|
154
|
-
_context.next =
|
|
158
|
+
_context.next = 25;
|
|
155
159
|
break;
|
|
156
160
|
}
|
|
157
161
|
_entry3 = _step4.value;
|
|
@@ -164,11 +168,11 @@ var AbstractVCCalculatorBase = exports.default = /*#__PURE__*/function () {
|
|
|
164
168
|
|
|
165
169
|
// Only process entries if we haven't reached all percentiles
|
|
166
170
|
if (!(percentileIndex >= percentiles.length)) {
|
|
167
|
-
_context.next =
|
|
171
|
+
_context.next = 22;
|
|
168
172
|
break;
|
|
169
173
|
}
|
|
170
|
-
return _context.abrupt("break",
|
|
171
|
-
case
|
|
174
|
+
return _context.abrupt("break", 25);
|
|
175
|
+
case 22:
|
|
172
176
|
// Check if this entry matches any checkpoint percentiles
|
|
173
177
|
if (viewportPercentage >= percentiles[percentileIndex]) {
|
|
174
178
|
elementNames = [];
|
|
@@ -199,21 +203,21 @@ var AbstractVCCalculatorBase = exports.default = /*#__PURE__*/function () {
|
|
|
199
203
|
return entryDataBuffer.add(e);
|
|
200
204
|
});
|
|
201
205
|
}
|
|
202
|
-
case
|
|
203
|
-
_context.next =
|
|
206
|
+
case 23:
|
|
207
|
+
_context.next = 16;
|
|
204
208
|
break;
|
|
205
|
-
case
|
|
206
|
-
_context.next =
|
|
209
|
+
case 25:
|
|
210
|
+
_context.next = 30;
|
|
207
211
|
break;
|
|
208
|
-
case 24:
|
|
209
|
-
_context.prev = 24;
|
|
210
|
-
_context.t0 = _context["catch"](11);
|
|
211
|
-
_iterator4.e(_context.t0);
|
|
212
212
|
case 27:
|
|
213
213
|
_context.prev = 27;
|
|
214
|
-
|
|
215
|
-
|
|
214
|
+
_context.t0 = _context["catch"](14);
|
|
215
|
+
_iterator4.e(_context.t0);
|
|
216
216
|
case 30:
|
|
217
|
+
_context.prev = 30;
|
|
218
|
+
_iterator4.f();
|
|
219
|
+
return _context.finish(30);
|
|
220
|
+
case 33:
|
|
217
221
|
// Fill in any missing percentiles with the last known values
|
|
218
222
|
previousResult = {
|
|
219
223
|
t: 0,
|
|
@@ -373,13 +377,14 @@ var AbstractVCCalculatorBase = exports.default = /*#__PURE__*/function () {
|
|
|
373
377
|
}
|
|
374
378
|
return _context.abrupt("return", {
|
|
375
379
|
vcDetails: vcDetails,
|
|
376
|
-
ssrRatio: ssrRatio
|
|
380
|
+
ssrRatio: ssrRatio,
|
|
381
|
+
speedIndex: speedIndex
|
|
377
382
|
});
|
|
378
|
-
case
|
|
383
|
+
case 45:
|
|
379
384
|
case "end":
|
|
380
385
|
return _context.stop();
|
|
381
386
|
}
|
|
382
|
-
}, _callee, this, [[
|
|
387
|
+
}, _callee, this, [[14, 27, 30, 33]]);
|
|
383
388
|
}));
|
|
384
389
|
function calculateWithDebugInfo(_x, _x2, _x3, _x4, _x5, _x6, _x7, _x8, _x9, _x0, _x1, _x10, _x11, _x12) {
|
|
385
390
|
return _calculateWithDebugInfo.apply(this, arguments);
|
|
@@ -393,7 +398,7 @@ var AbstractVCCalculatorBase = exports.default = /*#__PURE__*/function () {
|
|
|
393
398
|
var _this = this,
|
|
394
399
|
_vcDetails$90$t,
|
|
395
400
|
_vcDetails$;
|
|
396
|
-
var startTime, stopTime, orderedEntries, interactionId, isPostInteraction, include3p, excludeSmartAnswersInSearch, includeSSRRatio, interactionType, isPageVisible, interactionAbortReason, filteredEntries, isVCClean, dirtyReason, getVCCleanStatusResult, _yield$this$calculate, vcDetails, ssrRatio, result;
|
|
401
|
+
var startTime, stopTime, orderedEntries, interactionId, isPostInteraction, include3p, excludeSmartAnswersInSearch, includeSSRRatio, interactionType, isPageVisible, interactionAbortReason, filteredEntries, isVCClean, dirtyReason, getVCCleanStatusResult, _yield$this$calculate, vcDetails, ssrRatio, speedIndex, result;
|
|
397
402
|
return _regenerator.default.wrap(function _callee2$(_context2) {
|
|
398
403
|
while (1) switch (_context2.prev = _context2.next) {
|
|
399
404
|
case 0:
|
|
@@ -422,6 +427,7 @@ var AbstractVCCalculatorBase = exports.default = /*#__PURE__*/function () {
|
|
|
422
427
|
_yield$this$calculate = _context2.sent;
|
|
423
428
|
vcDetails = _yield$this$calculate.vcDetails;
|
|
424
429
|
ssrRatio = _yield$this$calculate.ssrRatio;
|
|
430
|
+
speedIndex = _yield$this$calculate.speedIndex;
|
|
425
431
|
result = {
|
|
426
432
|
revision: this.revisionNo,
|
|
427
433
|
clean: true,
|
|
@@ -432,9 +438,15 @@ var AbstractVCCalculatorBase = exports.default = /*#__PURE__*/function () {
|
|
|
432
438
|
if (ssrRatio !== -1) {
|
|
433
439
|
result.ssrRatio = ssrRatio;
|
|
434
440
|
}
|
|
441
|
+
|
|
442
|
+
// speedIndex is only calculated when platform_ufo_ttvc_v4_speed_index is enabled,
|
|
443
|
+
// so we only include it in the result when it has a meaningful value (> 0)
|
|
444
|
+
if (speedIndex > 0) {
|
|
445
|
+
result.speedIndex = speedIndex;
|
|
446
|
+
}
|
|
435
447
|
result.labelStacks = this.getLabelStacks(filteredEntries, isPostInteraction);
|
|
436
448
|
return _context2.abrupt("return", result);
|
|
437
|
-
case
|
|
449
|
+
case 19:
|
|
438
450
|
case "end":
|
|
439
451
|
return _context2.stop();
|
|
440
452
|
}
|
package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js
CHANGED
|
@@ -85,11 +85,11 @@ function calculateTTVCPercentilesWithDebugInfo(_x2) {
|
|
|
85
85
|
}
|
|
86
86
|
function _calculateTTVCPercentilesWithDebugInfo() {
|
|
87
87
|
_calculateTTVCPercentilesWithDebugInfo = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(_ref2) {
|
|
88
|
-
var viewport, orderedEntries, startTime, canvas, elementMap, _iterator3, _step3, entry, rect, timePixelCounts, canvasDimensions, totalPixels;
|
|
88
|
+
var viewport, orderedEntries, startTime, _ref2$calculateSpeedI, calculateSpeedIndex, canvas, elementMap, _iterator3, _step3, entry, rect, timePixelCounts, canvasDimensions, totalPixels;
|
|
89
89
|
return _regenerator.default.wrap(function _callee2$(_context2) {
|
|
90
90
|
while (1) switch (_context2.prev = _context2.next) {
|
|
91
91
|
case 0:
|
|
92
|
-
viewport = _ref2.viewport, orderedEntries = _ref2.orderedEntries, startTime = _ref2.startTime;
|
|
92
|
+
viewport = _ref2.viewport, orderedEntries = _ref2.orderedEntries, startTime = _ref2.startTime, _ref2$calculateSpeedI = _ref2.calculateSpeedIndex, calculateSpeedIndex = _ref2$calculateSpeedI === void 0 ? false : _ref2$calculateSpeedI;
|
|
93
93
|
canvas = new _canvasPixel.ViewportCanvas(viewport, (0, _platformFeatureFlags.fg)('platform_ufo_canvas_heatmap_full_precision') ? 1 : 0.25);
|
|
94
94
|
elementMap = new Map();
|
|
95
95
|
_iterator3 = _createForOfIteratorHelper(orderedEntries);
|
|
@@ -134,7 +134,7 @@ function _calculateTTVCPercentilesWithDebugInfo() {
|
|
|
134
134
|
timePixelCounts = _context2.sent;
|
|
135
135
|
canvasDimensions = canvas.getScaledDimensions();
|
|
136
136
|
totalPixels = canvasDimensions.width * canvasDimensions.height;
|
|
137
|
-
return _context2.abrupt("return", calculatePercentilesWithDebugInfo(timePixelCounts, elementMap, totalPixels, startTime));
|
|
137
|
+
return _context2.abrupt("return", calculatePercentilesWithDebugInfo(timePixelCounts, elementMap, totalPixels, startTime, calculateSpeedIndex));
|
|
138
138
|
case 30:
|
|
139
139
|
case "end":
|
|
140
140
|
return _context2.stop();
|
|
@@ -209,8 +209,11 @@ function calculatePercentiles(timePixelCounts, elementMap, unorderedPercentiles,
|
|
|
209
209
|
return results;
|
|
210
210
|
}
|
|
211
211
|
function calculatePercentilesWithDebugInfo(timePixelCounts, elementMap, totalPixels, startTime) {
|
|
212
|
-
var
|
|
212
|
+
var calculateSpeedIndex = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
|
|
213
|
+
var entries = new Array(elementMap.size);
|
|
213
214
|
var cumulativePixels = 0;
|
|
215
|
+
var speedIndex = 0;
|
|
216
|
+
var previousPercentCovered = 0;
|
|
214
217
|
var sortedEntries = Array.from(timePixelCounts.entries()).sort(function (_ref7, _ref8) {
|
|
215
218
|
var _ref9 = (0, _slicedToArray2.default)(_ref7, 1),
|
|
216
219
|
timeA = _ref9[0];
|
|
@@ -225,11 +228,23 @@ function calculatePercentilesWithDebugInfo(timePixelCounts, elementMap, totalPix
|
|
|
225
228
|
cumulativePixels += pixelCount;
|
|
226
229
|
var percentCovered = cumulativePixels / totalPixels * 100;
|
|
227
230
|
var entryDatas = elementMap.get(time) || [];
|
|
228
|
-
|
|
229
|
-
|
|
231
|
+
var relativeTime = Math.round(Number(time - startTime));
|
|
232
|
+
entries[i] = {
|
|
233
|
+
time: relativeTime,
|
|
230
234
|
viewportPercentage: percentCovered,
|
|
231
235
|
entries: Array.from(entryDatas)
|
|
232
236
|
};
|
|
237
|
+
|
|
238
|
+
// Speed index calculation: sum of (time × incremental viewport percentage)
|
|
239
|
+
// Only calculate when feature flag is enabled
|
|
240
|
+
if (calculateSpeedIndex) {
|
|
241
|
+
var ratioDelta = (percentCovered - previousPercentCovered) / 100;
|
|
242
|
+
speedIndex += relativeTime * ratioDelta;
|
|
243
|
+
previousPercentCovered = percentCovered;
|
|
244
|
+
}
|
|
233
245
|
}
|
|
234
|
-
return
|
|
246
|
+
return {
|
|
247
|
+
entries: entries,
|
|
248
|
+
speedIndex: Math.round(speedIndex)
|
|
249
|
+
};
|
|
235
250
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import{getDocument}from'@atlaskit/browser-apis';import{fg}from'@atlaskit/platform-feature-flags';// Import common utilities
|
|
2
|
-
import{getLighthouseMetrics}from'../additional-payload';import{CHRReporter}from'../assets';import*as bundleEvalTiming from'../bundle-eval-timing';import coinflip from'../coinflip';import{getConfig,getExperimentalInteractionRate,getUfoNameOverrides,shouldUseRawDataThirdPartyBehavior}from'../config';import{getExperimentalVCMetrics}from'../create-experimental-interaction-metrics-payload';import{getBm3Timings}from'../custom-timings';import{getGlobalErrorCount}from'../global-error-handler';import{getEarliestHiddenTiming,getHasHiddenTimingBeforeSetup,getPageVisibilityState,isOpenedInBackground}from'../hidden-timing';import*as initialPageLoadExtraTiming from'../initial-page-load-extra-timing';import{interactionSpans as atlaskitInteractionSpans}from'../interaction-metrics';import{createMemoryStateReport,createPressureStateReport}from'../machine-utilisation';import*as resourceTiming from'../resource-timing';import{filterResourceTimings}from'../resource-timing/common/utils/resource-timing-buffer';import{roundEpsilon}from'../round-number';import*as ssr from'../ssr';import{buildSegmentTree,getOldSegmentsLabelStack,labelStackStartWith,optimizeLabelStack,sanitizeUfoName,stringifyLabelStackFully}from'./common/utils';import{createCriticalMetricsPayloads}from'./critical-metrics-payload';import{addPerformanceMeasures}from'./utils/add-performance-measures';import{getBatteryInfoToLegacyFormat}from'./utils/get-battery-info';import{getBrowserMetadataToLegacyFormat}from'./utils/get-browser-metadata';import getInteractionStatus from'./utils/get-interaction-status';import{getMoreAccuratePageVisibilityUpToTTAI}from'./utils/get-more-accurate-page-visibility-up-to-ttai';import{getNavigationMetricsToLegacyFormat}from'./utils/get-navigation-metrics';import getPageVisibilityUpToTTAI from'./utils/get-page-visibility-up-to-ttai';import{getPaintMetricsToLegacyFormat}from'./utils/get-paint-metrics';import getPayloadSize from'./utils/get-payload-size';import{getReactUFOPayloadVersion}from'./utils/get-react-ufo-payload-version';import getSSRDoneTimeValue from'./utils/get-ssr-done-time-value';import getSSRSuccessUtil from'./utils/get-ssr-success';import getTTAI from'./utils/get-ttai';import getVCMetrics from'./utils/get-vc-metrics';import{getVisibilityStateFromPerformance}from'./utils/get-visibility-state-from-performance';import{optimizeApdex}from'./utils/optimize-apdex';import{optimizeCustomTimings}from'./utils/optimize-custom-timings';import{optimizeHoldInfo}from'./utils/optimize-hold-info';import{optimizeMarks}from'./utils/optimize-marks';import{optimizeReactProfilerTimings}from'./utils/optimize-react-profiler-timings';import{optimizeRequestInfo}from'./utils/optimize-request-info';import{optimizeSpans}from'./utils/optimize-spans';const MAX_PAYLOAD_SIZE=230;function getUfoNameOverride(interaction){const{ufoName,apdex}=interaction;try{const ufoNameOverrides=getUfoNameOverrides();if(ufoNameOverrides!=null){const metricKey=apdex.length>0?apdex[0].key:'';if(ufoNameOverrides[ufoName][metricKey]){return ufoNameOverrides[ufoName][metricKey];}}return ufoName;}catch{return ufoName;}}function getEarliestLegacyStopTime(interaction,labelStack){let earliestLegacyStopTime=null;interaction.apdex.forEach(a=>{var _a$labelStack,_earliestLegacyStopTi;if(!(a!==null&&a!==void 0&&a.stopTime)){return;}if(!labelStackStartWith((_a$labelStack=a.labelStack)!==null&&_a$labelStack!==void 0?_a$labelStack:[],labelStack)){return;}if(a.stopTime>interaction.start&&((_earliestLegacyStopTi=earliestLegacyStopTime)!==null&&_earliestLegacyStopTi!==void 0?_earliestLegacyStopTi:a.stopTime)>=a.stopTime){earliestLegacyStopTime=a.stopTime;}});return earliestLegacyStopTime;}function getBm3EndTimeOrFallbackValue(interaction,labelStack=[],fallbackValue=interaction.end){var _getEarliestLegacySto;if(interaction.type==='press'){return fallbackValue;}return(_getEarliestLegacySto=getEarliestLegacyStopTime(interaction,labelStack))!==null&&_getEarliestLegacySto!==void 0?_getEarliestLegacySto:fallbackValue;}function getPageVisibilityUpToTTI(interaction){const{start}=interaction;const bm3EndTimeOrInteractionEndTime=getBm3EndTimeOrFallbackValue(interaction);return getPageVisibilityState(start,bm3EndTimeOrInteractionEndTime);}function getMoreAccuratePageVisibilityUpToTTI(interaction){const old=getPageVisibilityUpToTTI(interaction);const tti=getEarliestLegacyStopTime(interaction,[]);if(!tti){return old;}const buffered=getVisibilityStateFromPerformance(tti);if(!buffered){return old;}if(buffered!==old){return'mixed';}return old;}function getResourceTimings(start,end){var _resourceTiming$getRe;return(_resourceTiming$getRe=resourceTiming.getResourceTimings(start,end))!==null&&_resourceTiming$getRe!==void 0?_resourceTiming$getRe:undefined;}function getBundleEvalTimings(start){return bundleEvalTiming.getBundleEvalTimings(start);}function getSSRPhaseSuccess(type){return type==='page_load'?ssr.getSSRPhaseSuccess():undefined;}function getSSRFeatureFlags(type){return type==='page_load'?ssr.getSSRFeatureFlags():undefined;}function getPPSMetrics(interaction){var _interaction$apdex,_interaction$apdex$;const{start,end}=interaction;const config=getConfig();const interactionStatus=getInteractionStatus(interaction);const pageVisibilityUpToTTAI=getPageVisibilityUpToTTAI(interaction);const tti=(_interaction$apdex=interaction.apdex)===null||_interaction$apdex===void 0?void 0:(_interaction$apdex$=_interaction$apdex[0])===null||_interaction$apdex$===void 0?void 0:_interaction$apdex$.stopTime;const ttai=interactionStatus.originalInteractionStatus==='SUCCEEDED'&&pageVisibilityUpToTTAI==='visible'?Math.round(end-start):undefined;const PPSMetricsAtTTI=tti!==undefined?getLighthouseMetrics({start,stop:tti}):null;const PPSMetricsAtTTAI=ttai!==undefined?getLighthouseMetrics({start,stop:interaction.end}):null;if(fg('platform_ufo_remove_deprecated_config_fields')){if(PPSMetricsAtTTAI!==null){return PPSMetricsAtTTAI;}}else{if(config!==null&&config!==void 0&&config.shouldCalculateLighthouseMetricsFromTTAI&&PPSMetricsAtTTAI!==null){return PPSMetricsAtTTAI;}if(PPSMetricsAtTTI!==null){return{...PPSMetricsAtTTI,'metrics@ttai':PPSMetricsAtTTAI};}}return{};}function getSSRProperties(type){const ssrPhases=getSSRPhaseSuccess(type);return{'ssr:success':(ssrPhases===null||ssrPhases===void 0?void 0:ssrPhases.done)!=null?ssrPhases.done:getSSRSuccessUtil(type),'ssr:featureFlags':getSSRFeatureFlags(type),...((ssrPhases===null||ssrPhases===void 0?void 0:ssrPhases.earlyFlush)!=null?{'ssr:earlyflush:success':ssrPhases.earlyFlush}:null),...((ssrPhases===null||ssrPhases===void 0?void 0:ssrPhases.prefetch)!=null?{'ssr:prefetch:success':ssrPhases.prefetch}:null)};}function getAssetsMetrics(interaction,SSRDoneTime){try{const config=getConfig();const{type}=interaction;const allowedTypes=['page_load'];const assetsConfig=config===null||config===void 0?void 0:config.assetsConfig;if(!allowedTypes.includes(type)||!assetsConfig){// Skip if: type not allowed or assetsClassification isn't configured
|
|
2
|
+
import{getLighthouseMetrics}from'../additional-payload';import{CHRReporter}from'../assets';import*as bundleEvalTiming from'../bundle-eval-timing';import coinflip from'../coinflip';import{getConfig,getExperimentalInteractionRate,getUfoNameOverrides,shouldUseRawDataThirdPartyBehavior}from'../config';import{getExperimentalVCMetrics}from'../create-experimental-interaction-metrics-payload';import{getBm3Timings}from'../custom-timings';import{getGlobalErrorCount}from'../global-error-handler';import{getEarliestHiddenTiming,getHasHiddenTimingBeforeSetup,getPageVisibilityState,isOpenedInBackground,isTabThrottled}from'../hidden-timing';import*as initialPageLoadExtraTiming from'../initial-page-load-extra-timing';import{interactionSpans as atlaskitInteractionSpans}from'../interaction-metrics';import{createMemoryStateReport,createPressureStateReport}from'../machine-utilisation';import*as resourceTiming from'../resource-timing';import{filterResourceTimings}from'../resource-timing/common/utils/resource-timing-buffer';import{roundEpsilon}from'../round-number';import*as ssr from'../ssr';import{buildSegmentTree,getOldSegmentsLabelStack,labelStackStartWith,optimizeLabelStack,sanitizeUfoName,stringifyLabelStackFully}from'./common/utils';import{createCriticalMetricsPayloads}from'./critical-metrics-payload';import{addPerformanceMeasures}from'./utils/add-performance-measures';import{getBatteryInfoToLegacyFormat}from'./utils/get-battery-info';import{getBrowserMetadataToLegacyFormat}from'./utils/get-browser-metadata';import getInteractionStatus from'./utils/get-interaction-status';import{getMoreAccuratePageVisibilityUpToTTAI}from'./utils/get-more-accurate-page-visibility-up-to-ttai';import{getNavigationMetricsToLegacyFormat}from'./utils/get-navigation-metrics';import getPageVisibilityUpToTTAI from'./utils/get-page-visibility-up-to-ttai';import{getPaintMetricsToLegacyFormat}from'./utils/get-paint-metrics';import getPayloadSize from'./utils/get-payload-size';import{getReactUFOPayloadVersion}from'./utils/get-react-ufo-payload-version';import getSSRDoneTimeValue from'./utils/get-ssr-done-time-value';import getSSRSuccessUtil from'./utils/get-ssr-success';import getTTAI from'./utils/get-ttai';import getVCMetrics from'./utils/get-vc-metrics';import{getVisibilityStateFromPerformance}from'./utils/get-visibility-state-from-performance';import{optimizeApdex}from'./utils/optimize-apdex';import{optimizeCustomTimings}from'./utils/optimize-custom-timings';import{optimizeHoldInfo}from'./utils/optimize-hold-info';import{optimizeMarks}from'./utils/optimize-marks';import{optimizeReactProfilerTimings}from'./utils/optimize-react-profiler-timings';import{optimizeRequestInfo}from'./utils/optimize-request-info';import{optimizeSpans}from'./utils/optimize-spans';const MAX_PAYLOAD_SIZE=230;function getUfoNameOverride(interaction){const{ufoName,apdex}=interaction;try{const ufoNameOverrides=getUfoNameOverrides();if(ufoNameOverrides!=null){const metricKey=apdex.length>0?apdex[0].key:'';if(ufoNameOverrides[ufoName][metricKey]){return ufoNameOverrides[ufoName][metricKey];}}return ufoName;}catch{return ufoName;}}function getEarliestLegacyStopTime(interaction,labelStack){let earliestLegacyStopTime=null;interaction.apdex.forEach(a=>{var _a$labelStack,_earliestLegacyStopTi;if(!(a!==null&&a!==void 0&&a.stopTime)){return;}if(!labelStackStartWith((_a$labelStack=a.labelStack)!==null&&_a$labelStack!==void 0?_a$labelStack:[],labelStack)){return;}if(a.stopTime>interaction.start&&((_earliestLegacyStopTi=earliestLegacyStopTime)!==null&&_earliestLegacyStopTi!==void 0?_earliestLegacyStopTi:a.stopTime)>=a.stopTime){earliestLegacyStopTime=a.stopTime;}});return earliestLegacyStopTime;}function getBm3EndTimeOrFallbackValue(interaction,labelStack=[],fallbackValue=interaction.end){var _getEarliestLegacySto;if(interaction.type==='press'){return fallbackValue;}return(_getEarliestLegacySto=getEarliestLegacyStopTime(interaction,labelStack))!==null&&_getEarliestLegacySto!==void 0?_getEarliestLegacySto:fallbackValue;}function getPageVisibilityUpToTTI(interaction){const{start}=interaction;const bm3EndTimeOrInteractionEndTime=getBm3EndTimeOrFallbackValue(interaction);return getPageVisibilityState(start,bm3EndTimeOrInteractionEndTime);}function getMoreAccuratePageVisibilityUpToTTI(interaction){const old=getPageVisibilityUpToTTI(interaction);const tti=getEarliestLegacyStopTime(interaction,[]);if(!tti){return old;}const buffered=getVisibilityStateFromPerformance(tti);if(!buffered){return old;}if(buffered!==old){return'mixed';}return old;}function getResourceTimings(start,end){var _resourceTiming$getRe;return(_resourceTiming$getRe=resourceTiming.getResourceTimings(start,end))!==null&&_resourceTiming$getRe!==void 0?_resourceTiming$getRe:undefined;}function getBundleEvalTimings(start){return bundleEvalTiming.getBundleEvalTimings(start);}function getSSRPhaseSuccess(type){return type==='page_load'?ssr.getSSRPhaseSuccess():undefined;}function getSSRFeatureFlags(type){return type==='page_load'?ssr.getSSRFeatureFlags():undefined;}function getPPSMetrics(interaction){var _interaction$apdex,_interaction$apdex$;const{start,end}=interaction;const config=getConfig();const interactionStatus=getInteractionStatus(interaction);const pageVisibilityUpToTTAI=getPageVisibilityUpToTTAI(interaction);const tti=(_interaction$apdex=interaction.apdex)===null||_interaction$apdex===void 0?void 0:(_interaction$apdex$=_interaction$apdex[0])===null||_interaction$apdex$===void 0?void 0:_interaction$apdex$.stopTime;const ttai=interactionStatus.originalInteractionStatus==='SUCCEEDED'&&pageVisibilityUpToTTAI==='visible'?Math.round(end-start):undefined;const PPSMetricsAtTTI=tti!==undefined?getLighthouseMetrics({start,stop:tti}):null;const PPSMetricsAtTTAI=ttai!==undefined?getLighthouseMetrics({start,stop:interaction.end}):null;if(fg('platform_ufo_remove_deprecated_config_fields')){if(PPSMetricsAtTTAI!==null){return PPSMetricsAtTTAI;}}else{if(config!==null&&config!==void 0&&config.shouldCalculateLighthouseMetricsFromTTAI&&PPSMetricsAtTTAI!==null){return PPSMetricsAtTTAI;}if(PPSMetricsAtTTI!==null){return{...PPSMetricsAtTTI,'metrics@ttai':PPSMetricsAtTTAI};}}return{};}function getSSRProperties(type){const ssrPhases=getSSRPhaseSuccess(type);return{'ssr:success':(ssrPhases===null||ssrPhases===void 0?void 0:ssrPhases.done)!=null?ssrPhases.done:getSSRSuccessUtil(type),'ssr:featureFlags':getSSRFeatureFlags(type),...((ssrPhases===null||ssrPhases===void 0?void 0:ssrPhases.earlyFlush)!=null?{'ssr:earlyflush:success':ssrPhases.earlyFlush}:null),...((ssrPhases===null||ssrPhases===void 0?void 0:ssrPhases.prefetch)!=null?{'ssr:prefetch:success':ssrPhases.prefetch}:null)};}function getAssetsMetrics(interaction,SSRDoneTime){try{const config=getConfig();const{type}=interaction;const allowedTypes=['page_load'];const assetsConfig=config===null||config===void 0?void 0:config.assetsConfig;if(!allowedTypes.includes(type)||!assetsConfig){// Skip if: type not allowed or assetsClassification isn't configured
|
|
3
3
|
return{};}const reporter=new CHRReporter();const resourceTimings=filterResourceTimings(interaction.start,interaction.end);const assets=reporter.get(resourceTimings,assetsConfig,SSRDoneTime);if(assets){// Only add assets in case it exists
|
|
4
4
|
return{'event:assets':assets};}return{};}catch{// Skip CHR in case of error
|
|
5
5
|
return{};}}function getTracingContextData(interaction){const{trace,start}=interaction;let tracingContextData={};if(trace){tracingContextData={'ufo:tracingContext':{'X-B3-TraceId':trace.traceId,'X-B3-SpanId':trace.spanId,// eslint-disable-next-line compat/compat
|
|
@@ -11,12 +11,12 @@ const getDetailedInteractionMetrics=resourceTimings=>{if(experimental||window.__
|
|
|
11
11
|
if(shouldInclude3pHolds){var _interaction$hold3pIn;return{...basePayload,hold3pActive:interaction.hold3pActive?[...interaction.hold3pActive.values()]:[],hold3pInfo:optimizeHoldInfo((_interaction$hold3pIn=interaction.hold3pInfo)!==null&&_interaction$hold3pIn!==void 0?_interaction$hold3pIn:[],start,getReactUFOPayloadVersion(interaction.type))};}return basePayload;};// Page load & detailed payload
|
|
12
12
|
const getPageLoadDetailedInteractionMetrics=()=>{var _config$ssr2,_config$ssr2$getSSRTi;if(!isPageLoad||!isDetailedPayload){return{};}const initialPageLoadExtraTimings=objectToArray(initialPageLoadExtraTiming.getTimings());const config=getConfig();const defaultSSRTimings=objectToArray(ssr.getSSRTimings());const ssrTimingsFromConfig=config===null||config===void 0?void 0:(_config$ssr2=config.ssr)===null||_config$ssr2===void 0?void 0:(_config$ssr2$getSSRTi=_config$ssr2.getSSRTimings)===null||_config$ssr2$getSSRTi===void 0?void 0:_config$ssr2$getSSRTi.call(_config$ssr2);return{initialPageLoadExtraTimings,SSRTimings:ssrTimingsFromConfig?[...ssrTimingsFromConfig,...defaultSSRTimings]:defaultSSRTimings};};if(experimental){expTTAI=getTTAI(interaction);}else{regularTTAI=getTTAI(interaction);}const newUFOName=sanitizeUfoName(ufoName);const resourceTimings=getResourceTimings(start,end);const[finalVCMetrics,experimentalMetrics,paintMetrics,batteryInfo]=await Promise.all([vcMetrics||(await getVCMetrics(interaction)),experimental?getExperimentalVCMetrics(interaction):Promise.resolve(undefined),getPaintMetricsToLegacyFormat(type,end),getBatteryInfoToLegacyFormat()]);if(!experimental){addPerformanceMeasures(interaction.start,[...((finalVCMetrics===null||finalVCMetrics===void 0?void 0:finalVCMetrics['ufo:vc:rev'])||[])]);}const getReactHydrationStats=()=>{if(!hydration){return{};}return{hydration};};const payload={actionSubject:'experience',action:'measured',eventType:'operational',source:'measured',tags:['observability'],attributes:{properties:{// basic
|
|
13
13
|
'event:hostname':((_window$location=window.location)===null||_window$location===void 0?void 0:_window$location.hostname)||'unknown','event:product':config.product,'event:population':config.population,'event:schema':'1.0.0','event:sizeInKb':0,'event:source':{name:'react-ufo/web',version:getReactUFOPayloadVersion(interaction.type)},'event:region':config.region||'unknown','experience:key':experimental?'custom.experimental-interaction-metrics':'custom.interaction-metrics','experience:name':newUFOName,// Include CPU usage monitoring data
|
|
14
|
-
'event:cpu:usage':createPressureStateReport(interaction.start,interaction.end),'event:memory:usage':createMemoryStateReport(interaction.start,interaction.end),...(criticalPayloadCount!==undefined?{'ufo:multipayload':true,'ufo:criticalPayloadCount':criticalPayloadCount}:{}),...(fg('platform_ufo_browser_backgrounded_abort_timestamp')?{'ufo:pageVisibilityHiddenTimestamp':getEarliestHiddenTiming(interaction.start,interaction.end)}:{}),'ufo:wasPageHiddenBeforeInit':getHasHiddenTimingBeforeSetup(),'ufo:isOpenedInBackground':isOpenedInBackground(interaction.type),// root
|
|
14
|
+
'event:cpu:usage':createPressureStateReport(interaction.start,interaction.end),'event:memory:usage':createMemoryStateReport(interaction.start,interaction.end),...(criticalPayloadCount!==undefined?{'ufo:multipayload':true,'ufo:criticalPayloadCount':criticalPayloadCount}:{}),...(fg('platform_ufo_browser_backgrounded_abort_timestamp')?{'ufo:pageVisibilityHiddenTimestamp':getEarliestHiddenTiming(interaction.start,interaction.end)}:{}),'ufo:wasPageHiddenBeforeInit':getHasHiddenTimingBeforeSetup(),'ufo:isOpenedInBackground':isOpenedInBackground(interaction.type),...(fg('platform_ufo_is_tab_throttled')?{'ufo:isTabThrottled':isTabThrottled(start,end)}:{}),// root
|
|
15
15
|
...getBrowserMetadataToLegacyFormat(),...batteryInfo,...getSSRProperties(type),...getAssetsMetrics(interaction,pageLoadInteractionMetrics===null||pageLoadInteractionMetrics===void 0?void 0:pageLoadInteractionMetrics.SSRDoneTime),...getPPSMetrics(interaction),...paintMetrics,...getNavigationMetricsToLegacyFormat(type),...finalVCMetrics,...experimentalMetrics,...((_config$additionalPay=config.additionalPayloadData)===null||_config$additionalPay===void 0?void 0:_config$additionalPay.call(config,interaction)),...getTracingContextData(interaction),...getStylesheetMetrics(),...getErrorCounts(interaction),...getReactHydrationStats(),interactionMetrics:{namePrefix:config.namePrefix||'',segmentPrefix:config.segmentPrefix||'',interactionId,pageVisibilityAtTTI,pageVisibilityAtTTAI,experimental__pageVisibilityAtTTI:moreAccuratePageVisibilityAtTTI,experimental__pageVisibilityAtTTAI:moreAccuratePageVisibilityAtTTAI,// raw interaction metrics
|
|
16
16
|
rate,routeName,type,abortReason,featureFlags,previousInteractionName,isPreviousInteractionAborted,abortedByInteractionName,// performance
|
|
17
17
|
apdex:optimizeApdex(interaction.apdex,getReactUFOPayloadVersion(interaction.type)),end:Math.round(end),...(interaction.end3p?{end3p:Math.round(interaction.end3p)}:{}),start:Math.round(start),segments:getReactUFOPayloadVersion(interaction.type)==='2.0.0'?segmentTree:getOldSegmentsLabelStack(segments,interaction.type),marks:optimizeMarks(interaction.marks,getReactUFOPayloadVersion(interaction.type)),customData:optimizeCustomData(interaction),reactProfilerTimings:optimizeReactProfilerTimings(interaction.reactProfilerTimings,start,getReactUFOPayloadVersion(interaction.type)),minorInteractions:interaction.minorInteractions,...(responsiveness?{responsiveness}:{}),...labelStack,...pageLoadInteractionMetrics,...getDetailedInteractionMetrics(resourceTimings),...getPageLoadDetailedInteractionMetrics(),...getBm3TrackerTimings(interaction),'metric:ttai':experimental?regularTTAI||expTTAI:undefined,'metric:experimental:ttai':expTTAI,...(unknownElementName?{unknownElementName}:{}),...(unknownElementHierarchy?{unknownElementHierarchy}:{})},'ufo:payloadTime':roundEpsilon(performance.now()-interactionPayloadStart)}}};if(experimental){regularTTAI=undefined;expTTAI=undefined;}if(fg('platform_ufo_enable_vc_raw_data')){const size=getPayloadSize(payload.attributes.properties);const vcRev=payload.attributes.properties['ufo:vc:rev'];const rawData=vcRev.find(item=>item.revision==='raw-handler');if(rawData){const rawDataSize=getPayloadSize(rawData);payload.attributes.properties['ufo:vc:raw:size']=rawDataSize;if(size>MAX_PAYLOAD_SIZE&&Array.isArray(vcRev)&&vcRev.length>0){payload.attributes.properties['ufo:vc:rev']=vcRev.filter(item=>item.revision!=='raw-handler');payload.attributes.properties['ufo:vc:raw:removed']=true;}}payload.attributes.properties['event:sizeInKb']=getPayloadSize(payload.attributes.properties);}else{payload.attributes.properties['event:sizeInKb']=getPayloadSize(payload.attributes.properties);}// in order of importance, first one being least important
|
|
18
18
|
// we can add more fields as necessary
|
|
19
|
-
const interactionMetricsFieldsToTrim=['requestInfo','featureFlags','resourceTimings'];const properties=payload.attributes.properties;const interactionMetrics=properties.interactionMetrics;if(interactionMetrics){for(const field of interactionMetricsFieldsToTrim){if(getPayloadSize(properties)<=MAX_PAYLOAD_SIZE){continue;}interactionMetrics[field]=undefined;properties['event:isTrimmed']=true;let trimmedFields=properties['event:trimmedFields'];if(!Array.isArray(trimmedFields)){trimmedFields=[];}trimmedFields.push(`interactionMetrics.${field}`);properties['event:trimmedFields']=trimmedFields;}}return payload;}export async function createPayloads(interactionId,interaction){const ufoNameOverride=getUfoNameOverride(interaction);const modifiedInteraction={...interaction,ufoName:ufoNameOverride};const payloads=[];const isCriticalMetricsEnabled=fg('platform_ufo_critical_metrics_payload');// Calculate VC metrics once to avoid duplicate expensive calculations
|
|
19
|
+
const interactionMetricsFieldsToTrim=fg('ufo_remove_featureflags_from_trimmed_fields')?['requestInfo','resourceTimings']:['requestInfo','featureFlags','resourceTimings'];const properties=payload.attributes.properties;const interactionMetrics=properties.interactionMetrics;if(interactionMetrics){for(const field of interactionMetricsFieldsToTrim){if(getPayloadSize(properties)<=MAX_PAYLOAD_SIZE){continue;}interactionMetrics[field]=undefined;properties['event:isTrimmed']=true;let trimmedFields=properties['event:trimmedFields'];if(!Array.isArray(trimmedFields)){trimmedFields=[];}trimmedFields.push(`interactionMetrics.${field}`);properties['event:trimmedFields']=trimmedFields;}}return payload;}export async function createPayloads(interactionId,interaction){const ufoNameOverride=getUfoNameOverride(interaction);const modifiedInteraction={...interaction,ufoName:ufoNameOverride};const payloads=[];const isCriticalMetricsEnabled=fg('platform_ufo_critical_metrics_payload');// Calculate VC metrics once to avoid duplicate expensive calculations
|
|
20
20
|
const vcMetrics=await getVCMetrics(interaction);// typeof Promise<CriticalMetricsPayload[]>
|
|
21
21
|
const criticalMetricsPayloads=isCriticalMetricsEnabled?await createCriticalMetricsPayloads(interactionId,interaction,vcMetrics):[];payloads.push(...criticalMetricsPayloads);const criticalPayloadCount=isCriticalMetricsEnabled?criticalMetricsPayloads.length:undefined;const interactionMetricsPayload=await createInteractionMetricsPayload(modifiedInteraction,interactionId,undefined,criticalPayloadCount,vcMetrics);payloads.push(interactionMetricsPayload);return payloads.filter(Boolean);}export async function createExperimentalMetricsPayload(interactionId,interaction){const config=getConfig();if(!config){throw Error('UFO Configuration not provided');}const ufoName=sanitizeUfoName(interaction.ufoName);const rate=getExperimentalInteractionRate(ufoName,interaction.type);if(!coinflip(rate)){return null;}const pageVisibilityState=getPageVisibilityState(interaction.start,interaction.end);if(pageVisibilityState!=='visible'){return null;}const result=await createInteractionMetricsPayload(interaction,interactionId,true);return result;}export async function createExtraSearchPageInteractionPayload(interactionId,interaction){var _newEnd;const SAIN_HOLD_NAMES=['search-ai-dialog-visible-text-loading','search-ai-dialog-all-text-loading'];const NAME_OVERRIDE='search-page-ignoring-smart-answers';const SEARCH_PAGE_SMART_ANSWERS_SEGMENT_LABEL='search-page-smart-answers';const newInteractionId=`${interactionId}-ignoring-smart-answers`;// Calculate a new end time which excludes SAIN holds
|
|
22
22
|
let newEnd;const{holdInfo,reactProfilerTimings}=interaction;const lastHold=holdInfo.at(-1);const isLastHoldSAIN=Boolean(lastHold&&SAIN_HOLD_NAMES.includes(lastHold.name));// A new end time is only calculated if the last hold is a SAIN hold
|
|
@@ -148,4 +148,132 @@ export function getPageVisibilityState(start, end) {
|
|
|
148
148
|
hiddenState = timings[startIdx].hidden ? 'hidden' : 'visible';
|
|
149
149
|
}
|
|
150
150
|
return hiddenState;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Throttle detection configuration
|
|
154
|
+
// Expected interval for timer checks (in milliseconds)
|
|
155
|
+
const THROTTLE_CHECK_INTERVAL_MS = 1000;
|
|
156
|
+
// Threshold for considering a timer as throttled (50% drift tolerance)
|
|
157
|
+
const THROTTLE_DRIFT_THRESHOLD = 1.5;
|
|
158
|
+
// Maximum number of throttle measurements to store (circular buffer)
|
|
159
|
+
const THROTTLE_BUFFER_SIZE = 120;
|
|
160
|
+
// Circular buffer to store throttle measurements
|
|
161
|
+
const throttleMeasurements = [];
|
|
162
|
+
let throttleInsertIndex = 0;
|
|
163
|
+
let throttleIntervalId = null;
|
|
164
|
+
let lastThrottleCheckTime = null;
|
|
165
|
+
let throttleSetupDone = false;
|
|
166
|
+
function recordThrottleMeasurement(expectedElapsed, actualElapsed) {
|
|
167
|
+
const isThrottled = actualElapsed > expectedElapsed * THROTTLE_DRIFT_THRESHOLD;
|
|
168
|
+
throttleMeasurements[throttleInsertIndex] = {
|
|
169
|
+
time: performance.now(),
|
|
170
|
+
expectedElapsed,
|
|
171
|
+
actualElapsed,
|
|
172
|
+
isThrottled
|
|
173
|
+
};
|
|
174
|
+
throttleInsertIndex = (throttleInsertIndex + 1) % THROTTLE_BUFFER_SIZE;
|
|
175
|
+
}
|
|
176
|
+
function throttleCheckCallback() {
|
|
177
|
+
const currentTime = performance.now();
|
|
178
|
+
if (lastThrottleCheckTime !== null) {
|
|
179
|
+
const actualElapsed = currentTime - lastThrottleCheckTime;
|
|
180
|
+
recordThrottleMeasurement(THROTTLE_CHECK_INTERVAL_MS, actualElapsed);
|
|
181
|
+
}
|
|
182
|
+
lastThrottleCheckTime = currentTime;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Sets up the throttle detection mechanism.
|
|
187
|
+
* This should be called early in the page lifecycle.
|
|
188
|
+
* Uses a periodic timer to detect browser throttling by measuring timer drift.
|
|
189
|
+
*/
|
|
190
|
+
export function setupThrottleDetection() {
|
|
191
|
+
if (throttleSetupDone) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
throttleSetupDone = true;
|
|
195
|
+
|
|
196
|
+
// Record the initial timestamp
|
|
197
|
+
lastThrottleCheckTime = performance.now();
|
|
198
|
+
|
|
199
|
+
// Start the periodic timer for throttle detection
|
|
200
|
+
throttleIntervalId = setInterval(throttleCheckCallback, THROTTLE_CHECK_INTERVAL_MS);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Stops the throttle detection mechanism.
|
|
205
|
+
* Useful for cleanup in tests or when the feature is no longer needed.
|
|
206
|
+
*/
|
|
207
|
+
export function stopThrottleDetection() {
|
|
208
|
+
if (throttleIntervalId !== null) {
|
|
209
|
+
clearInterval(throttleIntervalId);
|
|
210
|
+
throttleIntervalId = null;
|
|
211
|
+
}
|
|
212
|
+
lastThrottleCheckTime = null;
|
|
213
|
+
throttleSetupDone = false;
|
|
214
|
+
throttleMeasurements.length = 0;
|
|
215
|
+
throttleInsertIndex = 0;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Checks if the tab was throttled at any point during the specified time window.
|
|
220
|
+
* Returns true if any timer measurement showed significant drift (throttling).
|
|
221
|
+
*
|
|
222
|
+
* @param startTime - The start timestamp of the window to check (DOMHighResTimeStamp)
|
|
223
|
+
* @param endTime - The end timestamp of the window to check (DOMHighResTimeStamp)
|
|
224
|
+
* @returns boolean - true if throttling was detected during the time window, false otherwise
|
|
225
|
+
*/
|
|
226
|
+
export function isTabThrottled(startTime, endTime) {
|
|
227
|
+
// Input validation
|
|
228
|
+
if (!Number.isFinite(startTime) || !Number.isFinite(endTime) || startTime >= endTime) {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// No measurements available
|
|
233
|
+
if (throttleMeasurements.length === 0) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Check if any measurement within the time window indicates throttling
|
|
238
|
+
for (let i = 0; i < throttleMeasurements.length; i++) {
|
|
239
|
+
const measurement = throttleMeasurements[i];
|
|
240
|
+
if (measurement && measurement.time >= startTime && measurement.time <= endTime && measurement.isThrottled) {
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Gets detailed throttle information for debugging purposes.
|
|
249
|
+
* Returns all throttle measurements within the specified time window.
|
|
250
|
+
*
|
|
251
|
+
* @param startTime - The start timestamp of the window to check
|
|
252
|
+
* @param endTime - The end timestamp of the window to check
|
|
253
|
+
* @returns Array of throttle measurements within the time window
|
|
254
|
+
*/
|
|
255
|
+
export function getThrottleMeasurements(startTime, endTime) {
|
|
256
|
+
// Input validation
|
|
257
|
+
if (!Number.isFinite(startTime) || !Number.isFinite(endTime) || startTime >= endTime) {
|
|
258
|
+
return [];
|
|
259
|
+
}
|
|
260
|
+
return throttleMeasurements.filter(measurement => measurement && measurement.time >= startTime && measurement.time <= endTime);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Injects a fake throttle measurement for testing purposes.
|
|
265
|
+
* This allows integration tests to simulate throttling scenarios.
|
|
266
|
+
*
|
|
267
|
+
* @param measurement - The throttle measurement to inject
|
|
268
|
+
*/
|
|
269
|
+
export function __injectThrottleMeasurementForTesting(measurement) {
|
|
270
|
+
throttleMeasurements[throttleInsertIndex] = measurement;
|
|
271
|
+
throttleInsertIndex = (throttleInsertIndex + 1) % THROTTLE_BUFFER_SIZE;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Expose testing API on window for integration tests
|
|
275
|
+
if (typeof window !== 'undefined') {
|
|
276
|
+
window.__reactUfoHiddenTiming = {
|
|
277
|
+
__injectThrottleMeasurementForTesting
|
|
278
|
+
};
|
|
151
279
|
}
|
|
@@ -14,9 +14,12 @@ import { newVCObserver } from '../vc';
|
|
|
14
14
|
import { interactions } from './common/constants';
|
|
15
15
|
import InteractionExtraMetrics from './interaction-extra-metrics';
|
|
16
16
|
import PostInteractionLog from './post-interaction-log';
|
|
17
|
-
const PreviousInteractionLog = {
|
|
17
|
+
export const PreviousInteractionLog = {
|
|
18
|
+
id: undefined,
|
|
18
19
|
name: undefined,
|
|
19
|
-
|
|
20
|
+
type: undefined,
|
|
21
|
+
isAborted: undefined,
|
|
22
|
+
timestamp: undefined
|
|
20
23
|
};
|
|
21
24
|
export const postInteractionLog = new PostInteractionLog();
|
|
22
25
|
export const interactionExtraMetrics = new InteractionExtraMetrics();
|
|
@@ -667,6 +670,11 @@ function finishInteraction(id, data, endTime = performance.now()) {
|
|
|
667
670
|
remove(id);
|
|
668
671
|
}
|
|
669
672
|
}
|
|
673
|
+
if (fg('platform_ufo_enable_terminal_errors')) {
|
|
674
|
+
PreviousInteractionLog.id = data.id;
|
|
675
|
+
PreviousInteractionLog.type = data.type;
|
|
676
|
+
PreviousInteractionLog.timestamp = data.end;
|
|
677
|
+
}
|
|
670
678
|
PreviousInteractionLog.name = data.ufoName || 'unknown';
|
|
671
679
|
PreviousInteractionLog.isAborted = data.abortReason != null;
|
|
672
680
|
if (data.ufoName) {
|
|
@@ -5,7 +5,7 @@ import { setUFOConfig } from '../config';
|
|
|
5
5
|
import { experimentalVC, sinkExperimentalHandler } from '../create-experimental-interaction-metrics-payload';
|
|
6
6
|
import { sinkExtraSearchPageInteractionHandler } from '../create-extra-search-page-interaction-payload';
|
|
7
7
|
import { setContextManager, UFOContextManager } from '../experience-trace-id-context/context-manager';
|
|
8
|
-
import { setupHiddenTimingCapture } from '../hidden-timing';
|
|
8
|
+
import { setupHiddenTimingCapture, setupThrottleDetection } from '../hidden-timing';
|
|
9
9
|
import { interactionExtraMetrics, postInteractionLog, sinkInteractionHandler, sinkPostInteractionLogHandler } from '../interaction-metrics';
|
|
10
10
|
import { getPerformanceObserver } from '../interactions-performance-observer';
|
|
11
11
|
import { initialiseMemoryObserver, initialisePressureObserver } from '../machine-utilisation';
|
|
@@ -163,6 +163,9 @@ export function init(analyticsWebClientAsync, config) {
|
|
|
163
163
|
}
|
|
164
164
|
setupHiddenTimingCapture();
|
|
165
165
|
startLighthouseObserver();
|
|
166
|
+
if (fg('platform_ufo_is_tab_throttled')) {
|
|
167
|
+
setupThrottleDetection();
|
|
168
|
+
}
|
|
166
169
|
initialized = true;
|
|
167
170
|
if (typeof PerformanceObserver !== 'undefined') {
|
|
168
171
|
const observer = getPerformanceObserver();
|
|
@@ -1,24 +1,32 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'react';
|
|
2
2
|
import { useInteractionContext } from '../interaction-context';
|
|
3
|
-
import { getActiveInteraction } from '../interaction-metrics';
|
|
3
|
+
import { getActiveInteraction, PreviousInteractionLog } from '../interaction-metrics';
|
|
4
4
|
let sinkHandlerFn = () => {};
|
|
5
5
|
export function sinkTerminalErrorHandler(fn) {
|
|
6
6
|
sinkHandlerFn = fn;
|
|
7
7
|
}
|
|
8
8
|
export function setTerminalError(error, additionalAttributes, labelStack) {
|
|
9
|
-
var _activeInteraction$uf, _activeInteraction$id, _activeInteraction$ty;
|
|
9
|
+
var _activeInteraction$uf, _activeInteraction$id, _activeInteraction$ty, _PreviousInteractionL, _PreviousInteractionL2, _PreviousInteractionL3;
|
|
10
10
|
const activeInteraction = getActiveInteraction();
|
|
11
|
+
const currentTime = performance.now();
|
|
11
12
|
const errorData = {
|
|
12
13
|
errorType: error.name || 'Error',
|
|
13
14
|
errorMessage: error.message.slice(0, 100),
|
|
14
|
-
timestamp:
|
|
15
|
+
timestamp: currentTime,
|
|
15
16
|
...additionalAttributes
|
|
16
17
|
};
|
|
18
|
+
|
|
19
|
+
// Calculate time since previous interaction
|
|
20
|
+
const timeSincePreviousInteraction = PreviousInteractionLog.timestamp != null ? currentTime - PreviousInteractionLog.timestamp : null;
|
|
17
21
|
const context = {
|
|
18
22
|
labelStack: labelStack !== null && labelStack !== void 0 ? labelStack : null,
|
|
19
23
|
activeInteractionName: (_activeInteraction$uf = activeInteraction === null || activeInteraction === void 0 ? void 0 : activeInteraction.ufoName) !== null && _activeInteraction$uf !== void 0 ? _activeInteraction$uf : null,
|
|
20
24
|
activeInteractionId: (_activeInteraction$id = activeInteraction === null || activeInteraction === void 0 ? void 0 : activeInteraction.id) !== null && _activeInteraction$id !== void 0 ? _activeInteraction$id : null,
|
|
21
|
-
activeInteractionType: (_activeInteraction$ty = activeInteraction === null || activeInteraction === void 0 ? void 0 : activeInteraction.type) !== null && _activeInteraction$ty !== void 0 ? _activeInteraction$ty : null
|
|
25
|
+
activeInteractionType: (_activeInteraction$ty = activeInteraction === null || activeInteraction === void 0 ? void 0 : activeInteraction.type) !== null && _activeInteraction$ty !== void 0 ? _activeInteraction$ty : null,
|
|
26
|
+
previousInteractionId: (_PreviousInteractionL = PreviousInteractionLog.id) !== null && _PreviousInteractionL !== void 0 ? _PreviousInteractionL : null,
|
|
27
|
+
previousInteractionName: (_PreviousInteractionL2 = PreviousInteractionLog.name) !== null && _PreviousInteractionL2 !== void 0 ? _PreviousInteractionL2 : null,
|
|
28
|
+
previousInteractionType: (_PreviousInteractionL3 = PreviousInteractionLog.type) !== null && _PreviousInteractionL3 !== void 0 ? _PreviousInteractionL3 : null,
|
|
29
|
+
timeSincePreviousInteraction
|
|
22
30
|
};
|
|
23
31
|
sinkHandlerFn(errorData, context);
|
|
24
32
|
}
|
|
@@ -64,14 +64,19 @@ export default class AbstractVCCalculatorBase {
|
|
|
64
64
|
var _window, _window2, _window3, _window4, _window6;
|
|
65
65
|
const percentiles = [25, 50, 75, 80, 85, 90, 95, 98, 99, 100];
|
|
66
66
|
const viewportEntries = this.filterViewportEntries(filteredEntries);
|
|
67
|
-
const
|
|
67
|
+
const shouldCalculateSpeedIndex = fg('platform_ufo_ttvc_v4_speed_index');
|
|
68
|
+
const {
|
|
69
|
+
entries: vcLogs,
|
|
70
|
+
speedIndex
|
|
71
|
+
} = await calculateTTVCPercentilesWithDebugInfo({
|
|
68
72
|
viewport: {
|
|
69
73
|
width: getViewportWidth(),
|
|
70
74
|
height: getViewportHeight()
|
|
71
75
|
},
|
|
72
76
|
startTime,
|
|
73
77
|
stopTime,
|
|
74
|
-
orderedEntries: viewportEntries
|
|
78
|
+
orderedEntries: viewportEntries,
|
|
79
|
+
calculateSpeedIndex: shouldCalculateSpeedIndex
|
|
75
80
|
});
|
|
76
81
|
const vcDetails = {};
|
|
77
82
|
let percentileIndex = 0;
|
|
@@ -263,7 +268,8 @@ export default class AbstractVCCalculatorBase {
|
|
|
263
268
|
}
|
|
264
269
|
return {
|
|
265
270
|
vcDetails,
|
|
266
|
-
ssrRatio
|
|
271
|
+
ssrRatio,
|
|
272
|
+
speedIndex
|
|
267
273
|
};
|
|
268
274
|
}
|
|
269
275
|
async calculate({
|
|
@@ -299,7 +305,8 @@ export default class AbstractVCCalculatorBase {
|
|
|
299
305
|
}
|
|
300
306
|
const {
|
|
301
307
|
vcDetails,
|
|
302
|
-
ssrRatio
|
|
308
|
+
ssrRatio,
|
|
309
|
+
speedIndex
|
|
303
310
|
} = await this.calculateWithDebugInfo(filteredEntries, startTime, stopTime, isPostInteraction, isVCClean, interactionType, isPageVisible, interactionId, dirtyReason, orderedEntries, include3p, excludeSmartAnswersInSearch, interactionAbortReason, includeSSRRatio);
|
|
304
311
|
const result = {
|
|
305
312
|
revision: this.revisionNo,
|
|
@@ -311,6 +318,12 @@ export default class AbstractVCCalculatorBase {
|
|
|
311
318
|
if (ssrRatio !== -1) {
|
|
312
319
|
result.ssrRatio = ssrRatio;
|
|
313
320
|
}
|
|
321
|
+
|
|
322
|
+
// speedIndex is only calculated when platform_ufo_ttvc_v4_speed_index is enabled,
|
|
323
|
+
// so we only include it in the result when it has a meaningful value (> 0)
|
|
324
|
+
if (speedIndex > 0) {
|
|
325
|
+
result.speedIndex = speedIndex;
|
|
326
|
+
}
|
|
314
327
|
result.labelStacks = this.getLabelStacks(filteredEntries, isPostInteraction);
|
|
315
328
|
return result;
|
|
316
329
|
}
|