@atlaskit/react-ufo 5.1.2 → 5.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/AGENTS.md +426 -0
  2. package/CHANGELOG.md +16 -0
  3. package/dist/cjs/interaction-metrics/index.js +11 -3
  4. package/dist/cjs/set-terminal-error/index.js +11 -3
  5. package/dist/cjs/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +39 -27
  6. package/dist/cjs/vc/vc-observer-new/metric-calculator/fy25_03/index.js +1 -3
  7. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +22 -7
  8. package/dist/es2019/interaction-metrics/index.js +10 -2
  9. package/dist/es2019/set-terminal-error/index.js +12 -4
  10. package/dist/es2019/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +17 -4
  11. package/dist/es2019/vc/vc-observer-new/metric-calculator/fy25_03/index.js +1 -3
  12. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +22 -7
  13. package/dist/esm/interaction-metrics/index.js +10 -2
  14. package/dist/esm/set-terminal-error/index.js +12 -4
  15. package/dist/esm/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +39 -27
  16. package/dist/esm/vc/vc-observer-new/metric-calculator/fy25_03/index.js +1 -3
  17. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +22 -7
  18. package/dist/types/common/vc/types.d.ts +2 -0
  19. package/dist/types/create-terminal-error-payload/index.d.ts +4 -0
  20. package/dist/types/interaction-metrics/index.d.ts +8 -0
  21. package/dist/types/set-terminal-error/index.d.ts +4 -0
  22. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.d.ts +3 -3
  23. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/types.d.ts +12 -0
  24. package/dist/types-ts4.5/common/vc/types.d.ts +2 -0
  25. package/dist/types-ts4.5/create-terminal-error-payload/index.d.ts +4 -0
  26. package/dist/types-ts4.5/interaction-metrics/index.d.ts +8 -0
  27. package/dist/types-ts4.5/set-terminal-error/index.d.ts +4 -0
  28. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.d.ts +3 -3
  29. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/types.d.ts +12 -0
  30. package/package.json +4 -4
@@ -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 results = new Array(elementMap.size);
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
- results[i] = {
229
- time: Math.round(Number(time - startTime)),
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 results;
246
+ return {
247
+ entries: entries,
248
+ speedIndex: Math.round(speedIndex)
249
+ };
235
250
  }
@@ -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
- isAborted: undefined
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) {
@@ -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: performance.now(),
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 vcLogs = await calculateTTVCPercentilesWithDebugInfo({
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
  }
@@ -20,9 +20,7 @@ const getConsideredEntryTypes = (include3p, excludeSmartAnswersInSearch) => {
20
20
  entryTypes.push('mutation:smart-answers-element');
21
21
  entryTypes.push('mutation:smart-answers-attribute');
22
22
  }
23
- if (fg('platform_ufo_enable_media_for_ttvc_v3')) {
24
- entryTypes.push('mutation:media');
25
- }
23
+ entryTypes.push('mutation:media');
26
24
 
27
25
  // Still included as part of TTVC v3
28
26
  entryTypes.push('mutation:attribute:non-visual-input-name');
@@ -30,7 +30,8 @@ async function calculateTTVCPercentiles({
30
30
  async function calculateTTVCPercentilesWithDebugInfo({
31
31
  viewport,
32
32
  orderedEntries,
33
- startTime
33
+ startTime,
34
+ calculateSpeedIndex = false
34
35
  }) {
35
36
  const canvas = new ViewportCanvas(viewport, fg('platform_ufo_canvas_heatmap_full_precision') ? 1 : 0.25);
36
37
  const elementMap = new Map();
@@ -50,7 +51,7 @@ async function calculateTTVCPercentilesWithDebugInfo({
50
51
  const timePixelCounts = await canvas.getPixelCounts();
51
52
  const canvasDimensions = canvas.getScaledDimensions();
52
53
  const totalPixels = canvasDimensions.width * canvasDimensions.height;
53
- return calculatePercentilesWithDebugInfo(timePixelCounts, elementMap, totalPixels, startTime);
54
+ return calculatePercentilesWithDebugInfo(timePixelCounts, elementMap, totalPixels, startTime, calculateSpeedIndex);
54
55
  }
55
56
  export default calculateTTVCPercentiles;
56
57
  export function calculatePercentiles(timePixelCounts, elementMap, unorderedPercentiles, totalPixels, startTime) {
@@ -96,21 +97,35 @@ export function calculatePercentiles(timePixelCounts, elementMap, unorderedPerce
96
97
  }
97
98
  return results;
98
99
  }
99
- export function calculatePercentilesWithDebugInfo(timePixelCounts, elementMap, totalPixels, startTime) {
100
- const results = new Array(elementMap.size);
100
+ export function calculatePercentilesWithDebugInfo(timePixelCounts, elementMap, totalPixels, startTime, calculateSpeedIndex = false) {
101
+ const entries = new Array(elementMap.size);
101
102
  let cumulativePixels = 0;
103
+ let speedIndex = 0;
104
+ let previousPercentCovered = 0;
102
105
  const sortedEntries = Array.from(timePixelCounts.entries()).sort(([timeA], [timeB]) => Number(timeA) - Number(timeB));
103
106
  for (let i = 0; i < sortedEntries.length; i++) {
104
107
  const [time, pixelCount] = sortedEntries[i];
105
108
  cumulativePixels += pixelCount;
106
109
  const percentCovered = cumulativePixels / totalPixels * 100;
107
110
  const entryDatas = elementMap.get(time) || [];
108
- results[i] = {
109
- time: Math.round(Number(time - startTime)),
111
+ const relativeTime = Math.round(Number(time - startTime));
112
+ entries[i] = {
113
+ time: relativeTime,
110
114
  viewportPercentage: percentCovered,
111
115
  entries: Array.from(entryDatas)
112
116
  };
117
+
118
+ // Speed index calculation: sum of (time × incremental viewport percentage)
119
+ // Only calculate when feature flag is enabled
120
+ if (calculateSpeedIndex) {
121
+ const ratioDelta = (percentCovered - previousPercentCovered) / 100;
122
+ speedIndex += relativeTime * ratioDelta;
123
+ previousPercentCovered = percentCovered;
124
+ }
113
125
  }
114
- return results;
126
+ return {
127
+ entries,
128
+ speedIndex: Math.round(speedIndex)
129
+ };
115
130
  }
116
131
  export { calculateTTVCPercentilesWithDebugInfo };
@@ -24,9 +24,12 @@ import { newVCObserver } from '../vc';
24
24
  import { interactions } from './common/constants';
25
25
  import InteractionExtraMetrics from './interaction-extra-metrics';
26
26
  import PostInteractionLog from './post-interaction-log';
27
- var PreviousInteractionLog = {
27
+ export var PreviousInteractionLog = {
28
+ id: undefined,
28
29
  name: undefined,
29
- isAborted: undefined
30
+ type: undefined,
31
+ isAborted: undefined,
32
+ timestamp: undefined
30
33
  };
31
34
  export var postInteractionLog = new PostInteractionLog();
32
35
  export var interactionExtraMetrics = new InteractionExtraMetrics();
@@ -704,6 +707,11 @@ function finishInteraction(id, data) {
704
707
  remove(id);
705
708
  }
706
709
  }
710
+ if (fg('platform_ufo_enable_terminal_errors')) {
711
+ PreviousInteractionLog.id = data.id;
712
+ PreviousInteractionLog.type = data.type;
713
+ PreviousInteractionLog.timestamp = data.end;
714
+ }
707
715
  PreviousInteractionLog.name = data.ufoName || 'unknown';
708
716
  PreviousInteractionLog.isAborted = data.abortReason != null;
709
717
  if (data.ufoName) {
@@ -3,24 +3,32 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbol
3
3
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
4
  import { useEffect, useRef } from 'react';
5
5
  import { useInteractionContext } from '../interaction-context';
6
- import { getActiveInteraction } from '../interaction-metrics';
6
+ import { getActiveInteraction, PreviousInteractionLog } from '../interaction-metrics';
7
7
  var sinkHandlerFn = function sinkHandlerFn() {};
8
8
  export function sinkTerminalErrorHandler(fn) {
9
9
  sinkHandlerFn = fn;
10
10
  }
11
11
  export function setTerminalError(error, additionalAttributes, labelStack) {
12
- var _activeInteraction$uf, _activeInteraction$id, _activeInteraction$ty;
12
+ var _activeInteraction$uf, _activeInteraction$id, _activeInteraction$ty, _PreviousInteractionL, _PreviousInteractionL2, _PreviousInteractionL3;
13
13
  var activeInteraction = getActiveInteraction();
14
+ var currentTime = performance.now();
14
15
  var errorData = _objectSpread({
15
16
  errorType: error.name || 'Error',
16
17
  errorMessage: error.message.slice(0, 100),
17
- timestamp: performance.now()
18
+ timestamp: currentTime
18
19
  }, additionalAttributes);
20
+
21
+ // Calculate time since previous interaction
22
+ var timeSincePreviousInteraction = PreviousInteractionLog.timestamp != null ? currentTime - PreviousInteractionLog.timestamp : null;
19
23
  var context = {
20
24
  labelStack: labelStack !== null && labelStack !== void 0 ? labelStack : null,
21
25
  activeInteractionName: (_activeInteraction$uf = activeInteraction === null || activeInteraction === void 0 ? void 0 : activeInteraction.ufoName) !== null && _activeInteraction$uf !== void 0 ? _activeInteraction$uf : null,
22
26
  activeInteractionId: (_activeInteraction$id = activeInteraction === null || activeInteraction === void 0 ? void 0 : activeInteraction.id) !== null && _activeInteraction$id !== void 0 ? _activeInteraction$id : null,
23
- activeInteractionType: (_activeInteraction$ty = activeInteraction === null || activeInteraction === void 0 ? void 0 : activeInteraction.type) !== null && _activeInteraction$ty !== void 0 ? _activeInteraction$ty : null
27
+ activeInteractionType: (_activeInteraction$ty = activeInteraction === null || activeInteraction === void 0 ? void 0 : activeInteraction.type) !== null && _activeInteraction$ty !== void 0 ? _activeInteraction$ty : null,
28
+ previousInteractionId: (_PreviousInteractionL = PreviousInteractionLog.id) !== null && _PreviousInteractionL !== void 0 ? _PreviousInteractionL : null,
29
+ previousInteractionName: (_PreviousInteractionL2 = PreviousInteractionLog.name) !== null && _PreviousInteractionL2 !== void 0 ? _PreviousInteractionL2 : null,
30
+ previousInteractionType: (_PreviousInteractionL3 = PreviousInteractionLog.type) !== null && _PreviousInteractionL3 !== void 0 ? _PreviousInteractionL3 : null,
31
+ timeSincePreviousInteraction: timeSincePreviousInteraction
24
32
  };
25
33
  sinkHandlerFn(errorData, context);
26
34
  }
@@ -114,13 +114,14 @@ var AbstractVCCalculatorBase = /*#__PURE__*/function () {
114
114
  value: function () {
115
115
  var _calculateWithDebugInfo = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(filteredEntries, startTime, stopTime, isPostInteraction, isVCClean, interactionType, isPageVisible, interactionId, dirtyReason, allEntries, include3p, excludeSmartAnswersInSearch, interactionAbortReason, includeSSRRatio) {
116
116
  var _window, _window2, _window3, _window4, _window6;
117
- 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_;
117
+ 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_;
118
118
  return _regeneratorRuntime.wrap(function _callee$(_context) {
119
119
  while (1) switch (_context.prev = _context.next) {
120
120
  case 0:
121
121
  percentiles = [25, 50, 75, 80, 85, 90, 95, 98, 99, 100];
122
122
  viewportEntries = this.filterViewportEntries(filteredEntries);
123
- _context.next = 4;
123
+ shouldCalculateSpeedIndex = fg('platform_ufo_ttvc_v4_speed_index');
124
+ _context.next = 5;
124
125
  return calculateTTVCPercentilesWithDebugInfo({
125
126
  viewport: {
126
127
  width: getViewportWidth(),
@@ -128,24 +129,27 @@ var AbstractVCCalculatorBase = /*#__PURE__*/function () {
128
129
  },
129
130
  startTime: startTime,
130
131
  stopTime: stopTime,
131
- orderedEntries: viewportEntries
132
+ orderedEntries: viewportEntries,
133
+ calculateSpeedIndex: shouldCalculateSpeedIndex
132
134
  });
133
- case 4:
134
- vcLogs = _context.sent;
135
+ case 5:
136
+ _yield$calculateTTVCP = _context.sent;
137
+ vcLogs = _yield$calculateTTVCP.entries;
138
+ speedIndex = _yield$calculateTTVCP.speedIndex;
135
139
  vcDetails = {};
136
140
  percentileIndex = 0;
137
141
  entryDataBuffer = new Set();
138
142
  ssrRatio = -1;
139
143
  if (!vcLogs) {
140
- _context.next = 30;
144
+ _context.next = 33;
141
145
  break;
142
146
  }
143
147
  _iterator4 = _createForOfIteratorHelper(vcLogs);
144
- _context.prev = 11;
148
+ _context.prev = 14;
145
149
  _iterator4.s();
146
- case 13:
150
+ case 16:
147
151
  if ((_step4 = _iterator4.n()).done) {
148
- _context.next = 22;
152
+ _context.next = 25;
149
153
  break;
150
154
  }
151
155
  _entry3 = _step4.value;
@@ -158,11 +162,11 @@ var AbstractVCCalculatorBase = /*#__PURE__*/function () {
158
162
 
159
163
  // Only process entries if we haven't reached all percentiles
160
164
  if (!(percentileIndex >= percentiles.length)) {
161
- _context.next = 19;
165
+ _context.next = 22;
162
166
  break;
163
167
  }
164
- return _context.abrupt("break", 22);
165
- case 19:
168
+ return _context.abrupt("break", 25);
169
+ case 22:
166
170
  // Check if this entry matches any checkpoint percentiles
167
171
  if (viewportPercentage >= percentiles[percentileIndex]) {
168
172
  elementNames = [];
@@ -193,21 +197,21 @@ var AbstractVCCalculatorBase = /*#__PURE__*/function () {
193
197
  return entryDataBuffer.add(e);
194
198
  });
195
199
  }
196
- case 20:
197
- _context.next = 13;
200
+ case 23:
201
+ _context.next = 16;
198
202
  break;
199
- case 22:
200
- _context.next = 27;
203
+ case 25:
204
+ _context.next = 30;
201
205
  break;
202
- case 24:
203
- _context.prev = 24;
204
- _context.t0 = _context["catch"](11);
205
- _iterator4.e(_context.t0);
206
206
  case 27:
207
207
  _context.prev = 27;
208
- _iterator4.f();
209
- return _context.finish(27);
208
+ _context.t0 = _context["catch"](14);
209
+ _iterator4.e(_context.t0);
210
210
  case 30:
211
+ _context.prev = 30;
212
+ _iterator4.f();
213
+ return _context.finish(30);
214
+ case 33:
211
215
  // Fill in any missing percentiles with the last known values
212
216
  previousResult = {
213
217
  t: 0,
@@ -367,13 +371,14 @@ var AbstractVCCalculatorBase = /*#__PURE__*/function () {
367
371
  }
368
372
  return _context.abrupt("return", {
369
373
  vcDetails: vcDetails,
370
- ssrRatio: ssrRatio
374
+ ssrRatio: ssrRatio,
375
+ speedIndex: speedIndex
371
376
  });
372
- case 42:
377
+ case 45:
373
378
  case "end":
374
379
  return _context.stop();
375
380
  }
376
- }, _callee, this, [[11, 24, 27, 30]]);
381
+ }, _callee, this, [[14, 27, 30, 33]]);
377
382
  }));
378
383
  function calculateWithDebugInfo(_x, _x2, _x3, _x4, _x5, _x6, _x7, _x8, _x9, _x0, _x1, _x10, _x11, _x12) {
379
384
  return _calculateWithDebugInfo.apply(this, arguments);
@@ -387,7 +392,7 @@ var AbstractVCCalculatorBase = /*#__PURE__*/function () {
387
392
  var _this = this,
388
393
  _vcDetails$90$t,
389
394
  _vcDetails$;
390
- var startTime, stopTime, orderedEntries, interactionId, isPostInteraction, include3p, excludeSmartAnswersInSearch, includeSSRRatio, interactionType, isPageVisible, interactionAbortReason, filteredEntries, isVCClean, dirtyReason, getVCCleanStatusResult, _yield$this$calculate, vcDetails, ssrRatio, result;
395
+ var startTime, stopTime, orderedEntries, interactionId, isPostInteraction, include3p, excludeSmartAnswersInSearch, includeSSRRatio, interactionType, isPageVisible, interactionAbortReason, filteredEntries, isVCClean, dirtyReason, getVCCleanStatusResult, _yield$this$calculate, vcDetails, ssrRatio, speedIndex, result;
391
396
  return _regeneratorRuntime.wrap(function _callee2$(_context2) {
392
397
  while (1) switch (_context2.prev = _context2.next) {
393
398
  case 0:
@@ -416,6 +421,7 @@ var AbstractVCCalculatorBase = /*#__PURE__*/function () {
416
421
  _yield$this$calculate = _context2.sent;
417
422
  vcDetails = _yield$this$calculate.vcDetails;
418
423
  ssrRatio = _yield$this$calculate.ssrRatio;
424
+ speedIndex = _yield$this$calculate.speedIndex;
419
425
  result = {
420
426
  revision: this.revisionNo,
421
427
  clean: true,
@@ -426,9 +432,15 @@ var AbstractVCCalculatorBase = /*#__PURE__*/function () {
426
432
  if (ssrRatio !== -1) {
427
433
  result.ssrRatio = ssrRatio;
428
434
  }
435
+
436
+ // speedIndex is only calculated when platform_ufo_ttvc_v4_speed_index is enabled,
437
+ // so we only include it in the result when it has a meaningful value (> 0)
438
+ if (speedIndex > 0) {
439
+ result.speedIndex = speedIndex;
440
+ }
429
441
  result.labelStacks = this.getLabelStacks(filteredEntries, isPostInteraction);
430
442
  return _context2.abrupt("return", result);
431
- case 17:
443
+ case 19:
432
444
  case "end":
433
445
  return _context2.stop();
434
446
  }
@@ -27,9 +27,7 @@ var getConsideredEntryTypes = function getConsideredEntryTypes(include3p, exclud
27
27
  entryTypes.push('mutation:smart-answers-element');
28
28
  entryTypes.push('mutation:smart-answers-attribute');
29
29
  }
30
- if (fg('platform_ufo_enable_media_for_ttvc_v3')) {
31
- entryTypes.push('mutation:media');
32
- }
30
+ entryTypes.push('mutation:media');
33
31
 
34
32
  // Still included as part of TTVC v3
35
33
  entryTypes.push('mutation:attribute:non-visual-input-name');
@@ -75,11 +75,11 @@ function calculateTTVCPercentilesWithDebugInfo(_x2) {
75
75
  }
76
76
  function _calculateTTVCPercentilesWithDebugInfo() {
77
77
  _calculateTTVCPercentilesWithDebugInfo = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(_ref2) {
78
- var viewport, orderedEntries, startTime, canvas, elementMap, _iterator3, _step3, entry, rect, timePixelCounts, canvasDimensions, totalPixels;
78
+ var viewport, orderedEntries, startTime, _ref2$calculateSpeedI, calculateSpeedIndex, canvas, elementMap, _iterator3, _step3, entry, rect, timePixelCounts, canvasDimensions, totalPixels;
79
79
  return _regeneratorRuntime.wrap(function _callee2$(_context2) {
80
80
  while (1) switch (_context2.prev = _context2.next) {
81
81
  case 0:
82
- viewport = _ref2.viewport, orderedEntries = _ref2.orderedEntries, startTime = _ref2.startTime;
82
+ viewport = _ref2.viewport, orderedEntries = _ref2.orderedEntries, startTime = _ref2.startTime, _ref2$calculateSpeedI = _ref2.calculateSpeedIndex, calculateSpeedIndex = _ref2$calculateSpeedI === void 0 ? false : _ref2$calculateSpeedI;
83
83
  canvas = new ViewportCanvas(viewport, fg('platform_ufo_canvas_heatmap_full_precision') ? 1 : 0.25);
84
84
  elementMap = new Map();
85
85
  _iterator3 = _createForOfIteratorHelper(orderedEntries);
@@ -124,7 +124,7 @@ function _calculateTTVCPercentilesWithDebugInfo() {
124
124
  timePixelCounts = _context2.sent;
125
125
  canvasDimensions = canvas.getScaledDimensions();
126
126
  totalPixels = canvasDimensions.width * canvasDimensions.height;
127
- return _context2.abrupt("return", calculatePercentilesWithDebugInfo(timePixelCounts, elementMap, totalPixels, startTime));
127
+ return _context2.abrupt("return", calculatePercentilesWithDebugInfo(timePixelCounts, elementMap, totalPixels, startTime, calculateSpeedIndex));
128
128
  case 30:
129
129
  case "end":
130
130
  return _context2.stop();
@@ -199,8 +199,11 @@ export function calculatePercentiles(timePixelCounts, elementMap, unorderedPerce
199
199
  return results;
200
200
  }
201
201
  export function calculatePercentilesWithDebugInfo(timePixelCounts, elementMap, totalPixels, startTime) {
202
- var results = new Array(elementMap.size);
202
+ var calculateSpeedIndex = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
203
+ var entries = new Array(elementMap.size);
203
204
  var cumulativePixels = 0;
205
+ var speedIndex = 0;
206
+ var previousPercentCovered = 0;
204
207
  var sortedEntries = Array.from(timePixelCounts.entries()).sort(function (_ref7, _ref8) {
205
208
  var _ref9 = _slicedToArray(_ref7, 1),
206
209
  timeA = _ref9[0];
@@ -215,12 +218,24 @@ export function calculatePercentilesWithDebugInfo(timePixelCounts, elementMap, t
215
218
  cumulativePixels += pixelCount;
216
219
  var percentCovered = cumulativePixels / totalPixels * 100;
217
220
  var entryDatas = elementMap.get(time) || [];
218
- results[i] = {
219
- time: Math.round(Number(time - startTime)),
221
+ var relativeTime = Math.round(Number(time - startTime));
222
+ entries[i] = {
223
+ time: relativeTime,
220
224
  viewportPercentage: percentCovered,
221
225
  entries: Array.from(entryDatas)
222
226
  };
227
+
228
+ // Speed index calculation: sum of (time × incremental viewport percentage)
229
+ // Only calculate when feature flag is enabled
230
+ if (calculateSpeedIndex) {
231
+ var ratioDelta = (percentCovered - previousPercentCovered) / 100;
232
+ speedIndex += relativeTime * ratioDelta;
233
+ previousPercentCovered = percentCovered;
234
+ }
223
235
  }
224
- return results;
236
+ return {
237
+ entries: entries,
238
+ speedIndex: Math.round(speedIndex)
239
+ };
225
240
  }
226
241
  export { calculateTTVCPercentilesWithDebugInfo };
@@ -117,6 +117,7 @@ export type RevisionPayloadVCDetails = Record<string, {
117
117
  export type CalculateTTVCResult = {
118
118
  vcDetails: RevisionPayloadVCDetails;
119
119
  ssrRatio: number;
120
+ speedIndex: number;
120
121
  };
121
122
  export type RawObservation = {
122
123
  t: number;
@@ -136,6 +137,7 @@ export type RevisionPayloadEntry = {
136
137
  vcDetails?: RevisionPayloadVCDetails;
137
138
  ratios?: VCRatioType;
138
139
  ssrRatio?: number;
140
+ speedIndex?: number;
139
141
  labelStacks?: VCLabelStacks;
140
142
  abortReason?: VCAbortReason | null;
141
143
  abortTimestamp?: number;
@@ -21,6 +21,10 @@ export interface TerminalErrorPayload {
21
21
  activeInteractionName: string | null;
22
22
  activeInteractionId: string | null;
23
23
  activeInteractionType: string | null;
24
+ previousInteractionId: string | null;
25
+ previousInteractionName: string | null;
26
+ previousInteractionType: string | null;
27
+ timeSincePreviousInteraction: number | null;
24
28
  labelStack: LabelStack | null;
25
29
  };
26
30
  };
@@ -5,6 +5,14 @@ import type { LabelStack } from '../interaction-context';
5
5
  import InteractionExtraMetrics from './interaction-extra-metrics';
6
6
  import PostInteractionLog from './post-interaction-log';
7
7
  export type { InteractionMetrics, LifecycleMarkType, Span, Mark, MarkType, InteractionType, AbortReasonType, ReactProfilerTiming, RequestInfo, ApdexType, CustomData, CustomTiming, InteractionError, };
8
+ export type PreviousInteractionLogType = {
9
+ id: string | undefined;
10
+ name: string | undefined;
11
+ type: string | undefined;
12
+ isAborted: boolean | undefined;
13
+ timestamp: number | undefined;
14
+ };
15
+ export declare const PreviousInteractionLog: PreviousInteractionLogType;
8
16
  export declare const postInteractionLog: PostInteractionLog;
9
17
  export declare const interactionExtraMetrics: InteractionExtraMetrics;
10
18
  export declare const segmentUnmountCache: Map<string, number>;
@@ -18,6 +18,10 @@ export interface TerminalErrorContext {
18
18
  activeInteractionName: string | null;
19
19
  activeInteractionId: string | null;
20
20
  activeInteractionType: string | null;
21
+ previousInteractionId: string | null;
22
+ previousInteractionName: string | null;
23
+ previousInteractionType: string | null;
24
+ timeSincePreviousInteraction: number | null;
21
25
  }
22
26
  export declare function sinkTerminalErrorHandler(fn: (errorData: TerminalErrorData, context: TerminalErrorContext) => void | Promise<void>): void;
23
27
  export declare function setTerminalError(error: Error, additionalAttributes?: TerminalErrorAdditionalAttributes, labelStack?: LabelStack): void;
@@ -1,9 +1,9 @@
1
1
  import type { RevisionPayloadVCDetails } from '../../../../../common/vc/types';
2
2
  import type { ViewportEntryData } from '../../../types';
3
- import type { CalcTTVCPercentilesArg, CalcTTVCPercentilesArgWithDebugInfo, PercentileCalcResult } from '../types';
3
+ import type { CalcTTVCPercentilesArg, CalcTTVCPercentilesArgWithDebugInfo, PercentileCalcResultWithSpeedIndex } from '../types';
4
4
  declare function calculateTTVCPercentiles({ viewport, orderedEntries, percentiles, startTime, }: CalcTTVCPercentilesArg): Promise<RevisionPayloadVCDetails>;
5
- declare function calculateTTVCPercentilesWithDebugInfo({ viewport, orderedEntries, startTime, }: CalcTTVCPercentilesArgWithDebugInfo): Promise<PercentileCalcResult>;
5
+ declare function calculateTTVCPercentilesWithDebugInfo({ viewport, orderedEntries, startTime, calculateSpeedIndex, }: CalcTTVCPercentilesArgWithDebugInfo): Promise<PercentileCalcResultWithSpeedIndex>;
6
6
  export default calculateTTVCPercentiles;
7
7
  export declare function calculatePercentiles(timePixelCounts: Map<DOMHighResTimeStamp, number>, elementMap: ReadonlyMap<DOMHighResTimeStamp, Set<string>>, unorderedPercentiles: number[], totalPixels: number, startTime: DOMHighResTimeStamp): RevisionPayloadVCDetails;
8
- export declare function calculatePercentilesWithDebugInfo(timePixelCounts: Map<DOMHighResTimeStamp, number>, elementMap: ReadonlyMap<DOMHighResTimeStamp, ViewportEntryData[]>, totalPixels: number, startTime: DOMHighResTimeStamp): PercentileCalcResult;
8
+ export declare function calculatePercentilesWithDebugInfo(timePixelCounts: Map<DOMHighResTimeStamp, number>, elementMap: ReadonlyMap<DOMHighResTimeStamp, ViewportEntryData[]>, totalPixels: number, startTime: DOMHighResTimeStamp, calculateSpeedIndex?: boolean): PercentileCalcResultWithSpeedIndex;
9
9
  export { calculateTTVCPercentilesWithDebugInfo };
@@ -17,6 +17,11 @@ export type CalcTTVCPercentilesArgWithDebugInfo = {
17
17
  orderedEntries: ReadonlyArray<VCObserverEntry>;
18
18
  startTime: DOMHighResTimeStamp;
19
19
  stopTime: DOMHighResTimeStamp;
20
+ /**
21
+ * Whether to calculate speed index metric.
22
+ * Controlled by platform_ufo_ttvc_v4_speed_index feature flag.
23
+ */
24
+ calculateSpeedIndex?: boolean;
20
25
  };
21
26
  export interface PercentileCalcResultItem {
22
27
  time: number;
@@ -27,3 +32,10 @@ export interface PercentileCalcResultItem {
27
32
  * Ordered by time
28
33
  */
29
34
  export type PercentileCalcResult = PercentileCalcResultItem[];
35
+ /**
36
+ * Result of percentile calculation with debug info, including speed index
37
+ */
38
+ export type PercentileCalcResultWithSpeedIndex = {
39
+ entries: PercentileCalcResult;
40
+ speedIndex: number;
41
+ };