@atlaskit/react-ufo 3.12.3 → 3.12.5

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 (98) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/create-experimental-interaction-metrics-payload/index.js +2 -1
  3. package/dist/cjs/create-payload/utils/get-vc-metrics.js +2 -1
  4. package/dist/cjs/create-post-interaction-log-payload/index.js +6 -7
  5. package/dist/cjs/interaction-metrics/post-interaction-log.js +3 -11
  6. package/dist/cjs/vc/index.js +4 -2
  7. package/dist/cjs/vc/vc-observer/getVCRevisionDebugDetails.js +41 -0
  8. package/dist/cjs/vc/vc-observer/index.js +59 -33
  9. package/dist/cjs/vc/vc-observer-new/index.js +13 -7
  10. package/dist/cjs/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +211 -36
  11. package/dist/cjs/vc/vc-observer-new/metric-calculator/fy25_03/index.js +4 -4
  12. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +94 -4
  13. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/test-with-debug-info.js +108 -0
  14. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/index.js +16 -57
  15. package/dist/cjs/vc/vc-observer-new/viewport-observer/index.js +15 -5
  16. package/dist/cjs/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +3 -1
  17. package/dist/es2019/create-experimental-interaction-metrics-payload/index.js +2 -1
  18. package/dist/es2019/create-payload/utils/get-vc-metrics.js +1 -0
  19. package/dist/es2019/create-post-interaction-log-payload/index.js +6 -7
  20. package/dist/es2019/interaction-metrics/post-interaction-log.js +4 -13
  21. package/dist/es2019/vc/index.js +4 -2
  22. package/dist/es2019/vc/vc-observer/getVCRevisionDebugDetails.js +32 -0
  23. package/dist/es2019/vc/vc-observer/index.js +30 -1
  24. package/dist/es2019/vc/vc-observer-new/index.js +12 -6
  25. package/dist/es2019/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +115 -17
  26. package/dist/es2019/vc/vc-observer-new/metric-calculator/fy25_03/index.js +4 -4
  27. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +44 -1
  28. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/test-with-debug-info.js +75 -0
  29. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/index.js +2 -20
  30. package/dist/es2019/vc/vc-observer-new/viewport-observer/index.js +15 -5
  31. package/dist/es2019/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +3 -1
  32. package/dist/esm/create-experimental-interaction-metrics-payload/index.js +2 -1
  33. package/dist/esm/create-payload/utils/get-vc-metrics.js +2 -1
  34. package/dist/esm/create-post-interaction-log-payload/index.js +6 -7
  35. package/dist/esm/interaction-metrics/post-interaction-log.js +3 -11
  36. package/dist/esm/vc/index.js +4 -2
  37. package/dist/esm/vc/vc-observer/getVCRevisionDebugDetails.js +35 -0
  38. package/dist/esm/vc/vc-observer/index.js +59 -33
  39. package/dist/esm/vc/vc-observer-new/index.js +13 -7
  40. package/dist/esm/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +211 -36
  41. package/dist/esm/vc/vc-observer-new/metric-calculator/fy25_03/index.js +4 -4
  42. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +94 -5
  43. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/test-with-debug-info.js +106 -0
  44. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/index.js +2 -55
  45. package/dist/esm/vc/vc-observer-new/viewport-observer/index.js +15 -5
  46. package/dist/esm/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +3 -1
  47. package/dist/types/vc/types.d.ts +1 -0
  48. package/dist/types/vc/vc-observer/getVCRevisionDebugDetails.d.ts +30 -0
  49. package/dist/types/vc/vc-observer/index.d.ts +1 -1
  50. package/dist/types/vc/vc-observer-new/index.d.ts +2 -0
  51. package/dist/types/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +4 -1
  52. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.d.ts +5 -1
  53. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/test-with-debug-info.d.ts +1 -0
  54. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/index.d.ts +2 -4
  55. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/types.d.ts +20 -2
  56. package/dist/types/vc/vc-observer-new/metric-calculator/types.d.ts +2 -0
  57. package/dist/types/vc/vc-observer-new/types.d.ts +5 -1
  58. package/dist/types/vc/vc-observer-new/viewport-observer/mutation-observer/index.d.ts +2 -0
  59. package/dist/types/vc/vc-observer-new/viewport-observer/types.d.ts +2 -0
  60. package/dist/types-ts4.5/vc/types.d.ts +1 -0
  61. package/dist/types-ts4.5/vc/vc-observer/getVCRevisionDebugDetails.d.ts +30 -0
  62. package/dist/types-ts4.5/vc/vc-observer/index.d.ts +1 -1
  63. package/dist/types-ts4.5/vc/vc-observer-new/index.d.ts +2 -0
  64. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +4 -1
  65. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.d.ts +5 -1
  66. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/test-with-debug-info.d.ts +1 -0
  67. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/index.d.ts +2 -4
  68. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/types.d.ts +20 -2
  69. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/types.d.ts +2 -0
  70. package/dist/types-ts4.5/vc/vc-observer-new/types.d.ts +5 -1
  71. package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/mutation-observer/index.d.ts +2 -0
  72. package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/types.d.ts +2 -0
  73. package/package.json +4 -4
  74. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.js +0 -367
  75. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.js +0 -398
  76. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.js +0 -5
  77. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.js +0 -152
  78. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.js +0 -108
  79. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.js +0 -248
  80. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.js +0 -263
  81. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.js +0 -1
  82. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.js +0 -99
  83. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.js +0 -60
  84. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.js +0 -361
  85. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.js +0 -391
  86. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.js +0 -1
  87. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.js +0 -145
  88. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.js +0 -101
  89. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.d.ts +0 -39
  90. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.d.ts +0 -10
  91. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.d.ts +0 -43
  92. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.d.ts +0 -12
  93. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.d.ts +0 -25
  94. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/heatmap.d.ts +0 -39
  95. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/index.d.ts +0 -10
  96. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/heatmap/types.d.ts +0 -43
  97. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/calc-union-area.d.ts +0 -12
  98. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/rect-sweeping-line/index.d.ts +0 -25
@@ -123,7 +123,9 @@ var ViewportObserver = exports.default = /*#__PURE__*/function () {
123
123
  (0, _defineProperty2.default)(this, "handleAttributeMutation", function (_ref5) {
124
124
  var _this$intersectionObs4;
125
125
  var target = _ref5.target,
126
- attributeName = _ref5.attributeName;
126
+ attributeName = _ref5.attributeName,
127
+ oldValue = _ref5.oldValue,
128
+ newValue = _ref5.newValue;
127
129
  (_this$intersectionObs4 = _this.intersectionObserver) === null || _this$intersectionObs4 === void 0 || _this$intersectionObs4.watchAndTag(target, function (_ref6) {
128
130
  var target = _ref6.target,
129
131
  rect = _ref6.rect;
@@ -131,7 +133,9 @@ var ViewportObserver = exports.default = /*#__PURE__*/function () {
131
133
  return {
132
134
  type: 'mutation:media',
133
135
  mutationData: {
134
- attributeName: attributeName
136
+ attributeName: attributeName,
137
+ oldValue: oldValue,
138
+ newValue: newValue
135
139
  }
136
140
  };
137
141
  }
@@ -143,7 +147,9 @@ var ViewportObserver = exports.default = /*#__PURE__*/function () {
143
147
  return {
144
148
  type: 'mutation:attribute:non-visual-style',
145
149
  mutationData: {
146
- attributeName: attributeName
150
+ attributeName: attributeName,
151
+ oldValue: oldValue,
152
+ newValue: newValue
147
153
  }
148
154
  };
149
155
  }
@@ -152,14 +158,18 @@ var ViewportObserver = exports.default = /*#__PURE__*/function () {
152
158
  return {
153
159
  type: 'mutation:attribute:no-layout-shift',
154
160
  mutationData: {
155
- attributeName: attributeName
161
+ attributeName: attributeName,
162
+ oldValue: oldValue,
163
+ newValue: newValue
156
164
  }
157
165
  };
158
166
  }
159
167
  return {
160
168
  type: 'mutation:attribute',
161
169
  mutationData: {
162
- attributeName: attributeName
170
+ attributeName: attributeName,
171
+ oldValue: oldValue,
172
+ newValue: newValue
163
173
  }
164
174
  };
165
175
  });
@@ -42,7 +42,9 @@ function createMutationObserver(_ref) {
42
42
  var _mut$attributeName;
43
43
  onAttributeMutation({
44
44
  target: mut.target,
45
- attributeName: (_mut$attributeName = mut.attributeName) !== null && _mut$attributeName !== void 0 ? _mut$attributeName : 'unknown'
45
+ attributeName: (_mut$attributeName = mut.attributeName) !== null && _mut$attributeName !== void 0 ? _mut$attributeName : 'unknown',
46
+ oldValue: oldValue,
47
+ newValue: newValue
46
48
  });
47
49
  }
48
50
  continue;
@@ -68,7 +68,8 @@ export async function getExperimentalVCMetrics(interaction) {
68
68
  isEventAborted: !!interaction.abortReason,
69
69
  prefix,
70
70
  vc: interaction.vc,
71
- experienceKey: interaction.ufoName
71
+ experienceKey: interaction.ufoName,
72
+ interactionId: interaction.id
72
73
  });
73
74
  const VC = result === null || result === void 0 ? void 0 : result['metrics:vc'];
74
75
  if (!VC || !(result !== null && result !== void 0 && result[`${prefix}:vc:clean`])) {
@@ -36,6 +36,7 @@ async function getVCMetrics(interaction) {
36
36
  vc: interaction.vc,
37
37
  isEventAborted: interactionStatus.originalInteractionStatus !== 'SUCCEEDED',
38
38
  experienceKey: interaction.ufoName,
39
+ interactionId: interaction.id,
39
40
  ...ssr
40
41
  });
41
42
  if ((_config$experimentalI = config.experimentalInteractionMetrics) !== null && _config$experimentalI !== void 0 && _config$experimentalI.enabled) {
@@ -123,13 +123,12 @@ function createPostInteractionLogPayload({
123
123
 
124
124
  // Align post-interaction-logs closer to UFO event behaviour,
125
125
  // e.g. also check for aborted or failed events
126
- if (fg('platform_ufo_vc_align_revisions_on_watchdog_event')) {
127
- if (lastInteractionFinish.abortReason) {
128
- return null;
129
- }
130
- if (lastInteractionFinish.errors.length > 0) {
131
- return null;
132
- }
126
+
127
+ if (lastInteractionFinish.abortReason) {
128
+ return null;
129
+ }
130
+ if (lastInteractionFinish.errors.length > 0) {
131
+ return null;
133
132
  }
134
133
  const maxEndTimeFromProfiler = reactProfilerTimings ? Math.max(...reactProfilerTimings.map(t => t.commitTime)) : lastInteractionFinish.end;
135
134
  const revisedEndTime = Math.round(maxEndTimeFromProfiler);
@@ -1,8 +1,6 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
- import { fg } from '@atlaskit/platform-feature-flags';
3
2
  import { getConfig } from '../config';
4
3
  import { VCObserverWrapper } from '../vc';
5
- import { VCObserver } from '../vc/vc-observer';
6
4
  const POST_INTERACTION_LOG_SEND_DEFAULT_TIMEOUT = 3000;
7
5
  export default class PostInteractionLog {
8
6
  constructor() {
@@ -29,17 +27,10 @@ export default class PostInteractionLog {
29
27
  _defineProperty(this, "sinkHandlerFn", () => {});
30
28
  }
31
29
  initializeVCObserver(options) {
32
- if (fg('platform_ufo_vc_align_revisions_on_watchdog_event')) {
33
- this.vcObserver = new VCObserverWrapper({
34
- ...options,
35
- isPostInteraction: true
36
- });
37
- } else if (this.vcObserver === null) {
38
- this.vcObserver = new VCObserver({
39
- ...options,
40
- isPostInteraction: true
41
- });
42
- }
30
+ this.vcObserver = new VCObserverWrapper({
31
+ ...options,
32
+ isPostInteraction: true
33
+ });
43
34
  }
44
35
  startVCObserver({
45
36
  startTime
@@ -82,7 +82,8 @@ export class VCObserverWrapper {
82
82
  const v1v2Result = isVCRevisionEnabled('fy25.01', experienceKey) || isVCRevisionEnabled('fy25.02', experienceKey) ? await ((_this$oldVCObserver6 = this.oldVCObserver) === null || _this$oldVCObserver6 === void 0 ? void 0 : _this$oldVCObserver6.getVCResult(param)) : {};
83
83
  const v3Result = isVCRevisionEnabled('fy25.03', experienceKey) ? await ((_this$newVCObserver5 = this.newVCObserver) === null || _this$newVCObserver5 === void 0 ? void 0 : _this$newVCObserver5.getVCResult({
84
84
  start: param.start,
85
- stop: param.stop
85
+ stop: param.stop,
86
+ interactionId: param.interactionId
86
87
  })) : [];
87
88
  if (!v3Result) {
88
89
  return v1v2Result !== null && v1v2Result !== void 0 ? v1v2Result : {};
@@ -96,7 +97,8 @@ export class VCObserverWrapper {
96
97
  const oldResult = await ((_this$oldVCObserver7 = this.oldVCObserver) === null || _this$oldVCObserver7 === void 0 ? void 0 : _this$oldVCObserver7.getVCResult(param));
97
98
  const newResult = await ((_this$newVCObserver6 = this.newVCObserver) === null || _this$newVCObserver6 === void 0 ? void 0 : _this$newVCObserver6.getVCResult({
98
99
  start: param.start,
99
- stop: param.stop
100
+ stop: param.stop,
101
+ interactionId: param.interactionId
100
102
  }));
101
103
  if (oldResult && !newResult) {
102
104
  return oldResult;
@@ -0,0 +1,32 @@
1
+ export function getVCRevisionDebugDetails({
2
+ revision,
3
+ isClean,
4
+ abortReason,
5
+ VCEntries,
6
+ componentsLog,
7
+ interactionId
8
+ }) {
9
+ return {
10
+ revision,
11
+ isClean,
12
+ abortReason,
13
+ vcLogs: VCEntries.map(entry => ({
14
+ time: entry.time,
15
+ viewportPercentage: entry.vc,
16
+ entries: entry.elements.map(element => {
17
+ var _componentsLog$entry$;
18
+ const logEntry = (_componentsLog$entry$ = componentsLog[entry.time]) === null || _componentsLog$entry$ === void 0 ? void 0 : _componentsLog$entry$.find(log => log.targetName === element);
19
+ return {
20
+ elementName: element,
21
+ type: logEntry === null || logEntry === void 0 ? void 0 : logEntry.type,
22
+ rect: logEntry === null || logEntry === void 0 ? void 0 : logEntry.intersectionRect,
23
+ visible: true,
24
+ attributeName: logEntry === null || logEntry === void 0 ? void 0 : logEntry.attributeName,
25
+ oldValue: logEntry === null || logEntry === void 0 ? void 0 : logEntry.oldValue,
26
+ newValue: logEntry === null || logEntry === void 0 ? void 0 : logEntry.newValue
27
+ };
28
+ })
29
+ })),
30
+ interactionId
31
+ };
32
+ }
@@ -3,6 +3,7 @@ import { fg } from '@atlaskit/platform-feature-flags';
3
3
  import { isVCRevisionEnabled } from '../../config';
4
4
  import { getActiveInteraction } from '../../interaction-metrics';
5
5
  import { attachAbortListeners } from './attachAbortListeners';
6
+ import { getVCRevisionDebugDetails } from './getVCRevisionDebugDetails';
6
7
  import { getVCRevisionsData } from './getVCRevisionsData';
7
8
  import { getViewportHeight, getViewportWidth } from './getViewport';
8
9
  import { MultiRevisionHeatmap } from './heatmap/heatmap';
@@ -96,7 +97,8 @@ export class VCObserver {
96
97
  ssr,
97
98
  vc,
98
99
  isEventAborted,
99
- experienceKey
100
+ experienceKey,
101
+ interactionId
100
102
  }) => {
101
103
  const startTime = performance.now();
102
104
  // add local measurement
@@ -298,6 +300,33 @@ export class VCObserver {
298
300
  entries: isTTVCv1Disabled ? vcNext.VCEntries.rel : VCEntries.rel
299
301
  }
300
302
  }));
303
+
304
+ // Add devtool callback for both v1 and v2
305
+ if (typeof window.__ufo_devtool_onVCRevisionReady__ === 'function' && fg('platform_ufo_ttvc_v3_devtool')) {
306
+ var _ufo_devtool_onVCRev2, _ref2;
307
+ // Handle v1 if not disabled
308
+ if (!isTTVCv1Disabled) {
309
+ var _ufo_devtool_onVCRev, _ref;
310
+ (_ufo_devtool_onVCRev = (_ref = window).__ufo_devtool_onVCRevisionReady__) === null || _ufo_devtool_onVCRev === void 0 ? void 0 : _ufo_devtool_onVCRev.call(_ref, getVCRevisionDebugDetails({
311
+ revision: 'fy25.01',
312
+ isClean: !abortReasonInfo,
313
+ abortReason: abortReason.reason,
314
+ VCEntries: VCEntries.rel,
315
+ componentsLog,
316
+ interactionId
317
+ }));
318
+ }
319
+
320
+ // Handle v2
321
+ (_ufo_devtool_onVCRev2 = (_ref2 = window).__ufo_devtool_onVCRevisionReady__) === null || _ufo_devtool_onVCRev2 === void 0 ? void 0 : _ufo_devtool_onVCRev2.call(_ref2, getVCRevisionDebugDetails({
322
+ revision: 'fy25.02',
323
+ isClean: !abortReasonInfo,
324
+ abortReason: abortReason.reason,
325
+ VCEntries: vcNext.VCEntries.rel,
326
+ componentsLog,
327
+ interactionId
328
+ }));
329
+ }
301
330
  }
302
331
  } catch (e) {
303
332
  /* do nothing */
@@ -13,10 +13,11 @@ const DEFAULT_SELECTOR_CONFIG = {
13
13
  };
14
14
  export default class VCObserverNew {
15
15
  constructor(config) {
16
- var _config$selectorConfi;
16
+ var _config$isPostInterac, _config$selectorConfi;
17
17
  _defineProperty(this, "viewportObserver", null);
18
18
  _defineProperty(this, "windowEventObserver", null);
19
19
  this.entriesTimeline = new EntriesTimeline();
20
+ this.isPostInteraction = (_config$isPostInterac = config.isPostInteraction) !== null && _config$isPostInterac !== void 0 ? _config$isPostInterac : false;
20
21
  this.selectorConfig = (_config$selectorConfi = config.selectorConfig) !== null && _config$selectorConfi !== void 0 ? _config$selectorConfi : DEFAULT_SELECTOR_CONFIG;
21
22
  this.viewportObserver = new ViewportObserver({
22
23
  onChange: onChangeArg => {
@@ -36,13 +37,15 @@ export default class VCObserverNew {
36
37
  }
37
38
  this.entriesTimeline.push({
38
39
  time,
39
- type,
40
40
  data: {
41
+ type,
41
42
  elementName,
42
43
  rect,
43
44
  previousRect,
44
45
  visible,
45
- attributeName: mutationData === null || mutationData === void 0 ? void 0 : mutationData.attributeName
46
+ attributeName: mutationData === null || mutationData === void 0 ? void 0 : mutationData.attributeName,
47
+ oldValue: mutationData === null || mutationData === void 0 ? void 0 : mutationData.oldValue,
48
+ newValue: mutationData === null || mutationData === void 0 ? void 0 : mutationData.newValue
46
49
  }
47
50
  });
48
51
  }
@@ -54,8 +57,8 @@ export default class VCObserverNew {
54
57
  }) => {
55
58
  this.entriesTimeline.push({
56
59
  time,
57
- type: 'window:event',
58
60
  data: {
61
+ type: 'window:event',
59
62
  eventType: type
60
63
  }
61
64
  });
@@ -78,7 +81,8 @@ export default class VCObserverNew {
78
81
  async getVCResult(param) {
79
82
  const {
80
83
  start,
81
- stop
84
+ stop,
85
+ interactionId
82
86
  } = param;
83
87
  const results = [];
84
88
  const calculator_fy25_03 = new VCCalculator_FY25_03();
@@ -89,7 +93,9 @@ export default class VCObserverNew {
89
93
  const fy25_03 = await calculator_fy25_03.calculate({
90
94
  orderedEntries,
91
95
  startTime: start,
92
- stopTime: stop
96
+ stopTime: stop,
97
+ interactionId,
98
+ isPostInteraction: this.isPostInteraction
93
99
  });
94
100
  if (fy25_03) {
95
101
  results.push(fy25_03);
@@ -1,25 +1,131 @@
1
1
  import { fg } from '@atlaskit/platform-feature-flags';
2
- import calculateTTVCPercentiles from './percentile-calc';
2
+ import { calculateTTVCPercentiles, calculateTTVCPercentilesWithDebugInfo } from './percentile-calc';
3
3
  import getViewportHeight from './utils/get-viewport-height';
4
4
  import getViewportWidth from './utils/get-viewport-width';
5
5
  export default class AbstractVCCalculatorBase {
6
6
  constructor(revisionNo) {
7
7
  this.revisionNo = revisionNo;
8
8
  }
9
+ filterViewportEntries(entries) {
10
+ return entries.filter(entry => {
11
+ return 'rect' in entry.data;
12
+ });
13
+ }
14
+ async calculateBasic(filteredEntries, startTime, stopTime) {
15
+ const percentiles = [25, 50, 75, 80, 85, 90, 95, 98, 99];
16
+ const viewportEntries = this.filterViewportEntries(filteredEntries);
17
+ const vcLogs = await calculateTTVCPercentiles({
18
+ viewport: {
19
+ width: getViewportWidth(),
20
+ height: getViewportHeight()
21
+ },
22
+ startTime,
23
+ stopTime,
24
+ orderedEntries: viewportEntries,
25
+ percentiles
26
+ });
27
+ return vcLogs;
28
+ }
29
+ async calculateWithDebugInfo(filteredEntries, startTime, stopTime, isPostInteraction, isVCClean, interactionId, dirtyReason) {
30
+ const percentiles = [25, 50, 75, 80, 85, 90, 95, 98, 99];
31
+ const viewportEntries = this.filterViewportEntries(filteredEntries);
32
+ const vcLogs = await calculateTTVCPercentilesWithDebugInfo({
33
+ viewport: {
34
+ width: getViewportWidth(),
35
+ height: getViewportHeight()
36
+ },
37
+ startTime,
38
+ stopTime,
39
+ orderedEntries: viewportEntries
40
+ });
41
+ const vcDetails = {};
42
+ let percentileIndex = 0;
43
+ const entryDataBuffer = new Set();
44
+ if (vcLogs) {
45
+ for (const entry of vcLogs) {
46
+ const {
47
+ time,
48
+ viewportPercentage,
49
+ entries
50
+ } = entry;
51
+
52
+ // Only process entries if we haven't reached all percentiles
53
+ if (percentileIndex >= percentiles.length) {
54
+ break;
55
+ }
56
+
57
+ // Check if this entry matches any checkpoint percentiles
58
+ if (viewportPercentage >= percentiles[percentileIndex]) {
59
+ const elementNames = entries.map(e => e.elementName);
60
+
61
+ // Process all matching percentiles in one go
62
+ while (percentileIndex < percentiles.length && viewportPercentage >= percentiles[percentileIndex]) {
63
+ vcDetails[`${percentiles[percentileIndex]}`] = {
64
+ t: Math.round(time),
65
+ e: elementNames
66
+ };
67
+ percentileIndex++;
68
+ }
69
+
70
+ // Clear buffer after processing all matching percentiles
71
+ entryDataBuffer.clear();
72
+ } else {
73
+ // Only add to buffer if we haven't reached all percentiles
74
+ entries.forEach(e => entryDataBuffer.add(e));
75
+ }
76
+ }
77
+ }
78
+
79
+ // Fill in any missing percentiles with the last known values
80
+ let previousResult = {
81
+ t: 0,
82
+ e: []
83
+ };
84
+ for (let i = 0; i < percentiles.length; i++) {
85
+ const percentile = percentiles[i];
86
+ if (!(percentile in vcDetails)) {
87
+ vcDetails[`${percentile}`] = previousResult;
88
+ } else {
89
+ previousResult = vcDetails[`${percentile}`];
90
+ }
91
+ }
92
+
93
+ // Handle devtool callback
94
+ if (!isPostInteraction && typeof window !== 'undefined' && typeof window.__ufo_devtool_onVCRevisionReady__ === 'function' && fg('platform_ufo_ttvc_v3_devtool')) {
95
+ try {
96
+ var _ufo_devtool_onVCRev, _ref;
97
+ (_ufo_devtool_onVCRev = (_ref = window).__ufo_devtool_onVCRevisionReady__) === null || _ufo_devtool_onVCRev === void 0 ? void 0 : _ufo_devtool_onVCRev.call(_ref, {
98
+ revision: this.revisionNo,
99
+ isClean: isVCClean,
100
+ abortReason: dirtyReason,
101
+ vcLogs,
102
+ interactionId
103
+ });
104
+ } catch (e) {
105
+ // if any error communicating with devtool, we don't want to break the app
106
+ // eslint-disable-next-line no-console
107
+ console.error('Error in onVCRevisionReady', e);
108
+ }
109
+ }
110
+ return vcDetails;
111
+ }
9
112
  async calculate({
10
113
  startTime,
11
114
  stopTime,
12
- orderedEntries
115
+ orderedEntries,
116
+ interactionId,
117
+ isPostInteraction
13
118
  }) {
14
119
  var _vcDetails$90$t, _vcDetails$;
15
120
  const filteredEntries = orderedEntries.filter(entry => {
16
121
  return this.isEntryIncluded(entry);
17
122
  });
123
+ let isVCClean;
124
+ let dirtyReason;
18
125
  if (fg('platform_ufo_add_vc_abort_reason_by_revisions')) {
19
- const {
20
- isVCClean,
21
- dirtyReason
22
- } = this.getVCCleanStatus(filteredEntries);
126
+ const getVCCleanStatusResult = this.getVCCleanStatus(filteredEntries);
127
+ isVCClean = getVCCleanStatusResult.isVCClean;
128
+ dirtyReason = getVCCleanStatusResult.dirtyReason;
23
129
  if (!isVCClean) {
24
130
  return {
25
131
  revision: this.revisionNo,
@@ -29,7 +135,7 @@ export default class AbstractVCCalculatorBase {
29
135
  };
30
136
  }
31
137
  } else {
32
- const isVCClean = this.isVCClean(filteredEntries);
138
+ isVCClean = this.isVCClean(filteredEntries);
33
139
  if (!isVCClean) {
34
140
  return {
35
141
  revision: this.revisionNo,
@@ -38,16 +144,8 @@ export default class AbstractVCCalculatorBase {
38
144
  };
39
145
  }
40
146
  }
41
- const vcDetails = await calculateTTVCPercentiles({
42
- viewport: {
43
- width: getViewportWidth(),
44
- height: getViewportHeight()
45
- },
46
- startTime,
47
- stopTime,
48
- orderedEntries: filteredEntries,
49
- percentiles: [25, 50, 75, 80, 85, 90, 95, 98, 99]
50
- });
147
+ const useDebugInfo = fg('platform_ufo_ttvc_v3_devtool');
148
+ const vcDetails = useDebugInfo ? await this.calculateWithDebugInfo(filteredEntries, startTime, stopTime, isPostInteraction, isVCClean, interactionId, dirtyReason) : await this.calculateBasic(filteredEntries, startTime, stopTime);
51
149
  return {
52
150
  revision: this.revisionNo,
53
151
  clean: true,
@@ -13,10 +13,10 @@ export default class VCCalculator_FY25_03 extends AbstractVCCalculatorBase {
13
13
  super(REVISION_NO);
14
14
  }
15
15
  isEntryIncluded(entry) {
16
- if (!CONSIDERED_ENTRY_TYPE.includes(entry.type)) {
16
+ if (!CONSIDERED_ENTRY_TYPE.includes(entry.data.type)) {
17
17
  return false;
18
18
  }
19
- if (entry.type === 'mutation:attribute') {
19
+ if (entry.data.type === 'mutation:attribute') {
20
20
  const entryData = entry.data;
21
21
  const attributeName = entryData.attributeName;
22
22
  if (!attributeName || KNOWN_ATTRIBUTES_THAT_DOES_NOT_CAUSE_LAYOUT_SHIFTS.includes(attributeName)) {
@@ -31,7 +31,7 @@ export default class VCCalculator_FY25_03 extends AbstractVCCalculatorBase {
31
31
  }
32
32
  isVCClean(filteredEntries) {
33
33
  const hasAbortEvent = filteredEntries.some(entry => {
34
- if (entry.type === 'window:event') {
34
+ if (entry.data.type === 'window:event') {
35
35
  const data = entry.data;
36
36
  if (ABORTING_WINDOW_EVENT.includes(data.eventType)) {
37
37
  return true;
@@ -44,7 +44,7 @@ export default class VCCalculator_FY25_03 extends AbstractVCCalculatorBase {
44
44
  getVCCleanStatus(filteredEntries) {
45
45
  let dirtyReason = '';
46
46
  const hasAbortEvent = filteredEntries.some(entry => {
47
- if (entry.type === 'window:event') {
47
+ if (entry.data.type === 'window:event') {
48
48
  const data = entry.data;
49
49
  if (ABORTING_WINDOW_EVENT.includes(data.eventType)) {
50
50
  dirtyReason = data.eventType === 'keydown' ? 'keypress' : data.eventType;
@@ -27,6 +27,31 @@ async function calculateTTVCPercentiles({
27
27
  const totalPixels = canvasDimenstions.width * canvasDimenstions.height;
28
28
  return calculatePercentiles(timePixelCounts, elementMap, percentiles, totalPixels, startTime);
29
29
  }
30
+ async function calculateTTVCPercentilesWithDebugInfo({
31
+ viewport,
32
+ orderedEntries,
33
+ startTime
34
+ }) {
35
+ const canvas = new ViewportCanvas(viewport, fg('platform_ufo_canvas_heatmap_full_precision') ? 1 : 0.25);
36
+ const elementMap = new Map();
37
+ for (const entry of orderedEntries) {
38
+ if (!('rect' in entry.data)) {
39
+ continue;
40
+ }
41
+ const rect = entry.data.rect;
42
+ canvas.drawRect(rect, entry.time);
43
+ if (!elementMap.has(entry.time)) {
44
+ elementMap.set(entry.time, []);
45
+ }
46
+ elementMap.get(entry.time).push(entry.data);
47
+ }
48
+
49
+ // Get pixel counts
50
+ const timePixelCounts = await canvas.getPixelCounts();
51
+ const canvasDimensions = canvas.getScaledDimensions();
52
+ const totalPixels = canvasDimensions.width * canvasDimensions.height;
53
+ return calculatePercentilesWithDebugInfo(timePixelCounts, elementMap, totalPixels, startTime);
54
+ }
30
55
  export default calculateTTVCPercentiles;
31
56
  export function calculatePercentiles(timePixelCounts, elementMap, unorderedPercentiles, totalPixels, startTime) {
32
57
  const results = {};
@@ -70,4 +95,22 @@ export function calculatePercentiles(timePixelCounts, elementMap, unorderedPerce
70
95
  previousResult = results[`${percentile}`];
71
96
  }
72
97
  return results;
73
- }
98
+ }
99
+ export function calculatePercentilesWithDebugInfo(timePixelCounts, elementMap, totalPixels, startTime) {
100
+ const results = new Array(elementMap.size);
101
+ let cumulativePixels = 0;
102
+ const sortedEntries = Array.from(timePixelCounts.entries()).sort(([timeA], [timeB]) => Number(timeA) - Number(timeB));
103
+ for (let i = 0; i < sortedEntries.length; i++) {
104
+ const [time, pixelCount] = sortedEntries[i];
105
+ cumulativePixels += pixelCount;
106
+ const percentCovered = cumulativePixels / totalPixels * 100;
107
+ const entryDatas = elementMap.get(time) || [];
108
+ results[i] = {
109
+ time: Math.round(Number(time - startTime)),
110
+ viewportPercentage: percentCovered,
111
+ entries: Array.from(entryDatas)
112
+ };
113
+ }
114
+ return results;
115
+ }
116
+ export { calculateTTVCPercentilesWithDebugInfo };
@@ -0,0 +1,75 @@
1
+ import { calculatePercentilesWithDebugInfo } from './index';
2
+
3
+ // Test utilities
4
+ const createMockRect = (x = 0, y = 0, width = 100, height = 100) => new MockDOMRect(x, y, width, height);
5
+ const createViewportEntry = (elementName, rect = createMockRect(), visible = true, type = 'mutation:element') => ({
6
+ elementName,
7
+ rect,
8
+ visible,
9
+ type
10
+ });
11
+ const createTimePixelCounts = counts => new Map(counts);
12
+ const createElementMap = entries => new Map(entries);
13
+ const createExpectedResult = (time, viewportPercentage, entries) => ({
14
+ time,
15
+ viewportPercentage,
16
+ entries
17
+ });
18
+ class MockDOMRect {
19
+ constructor(x, y, width, height) {
20
+ this.x = x;
21
+ this.y = y;
22
+ this.width = width;
23
+ this.height = height;
24
+ }
25
+ get bottom() {
26
+ return this.y + this.height;
27
+ }
28
+ get left() {
29
+ return this.x;
30
+ }
31
+ get right() {
32
+ return this.x + this.width;
33
+ }
34
+ get top() {
35
+ return this.y;
36
+ }
37
+ toJSON() {
38
+ return {
39
+ x: this.x,
40
+ y: this.y,
41
+ width: this.width,
42
+ height: this.height
43
+ };
44
+ }
45
+ }
46
+ describe('calculatePercentilesWithDebugInfo', () => {
47
+ it('should correctly calculate percentiles with accumulated elements from timestamps', () => {
48
+ const timePixelCounts = createTimePixelCounts([[100, 10], [200, 20], [300, 10], [400, 10]]);
49
+ const elementMap = createElementMap([[100, [createViewportEntry('div'), createViewportEntry('span')]], [200, [createViewportEntry('img')]], [300, [createViewportEntry('p'), createViewportEntry('a')]], [400, [createViewportEntry('img')]]]);
50
+ const expected = [createExpectedResult(100, 20, [createViewportEntry('div'), createViewportEntry('span')]), createExpectedResult(200, 60, [createViewportEntry('img')]), createExpectedResult(300, 80, [createViewportEntry('p'), createViewportEntry('a')]), createExpectedResult(400, 100, [createViewportEntry('img')])];
51
+ const result = calculatePercentilesWithDebugInfo(timePixelCounts, elementMap, 50, 0);
52
+ expect(result).toEqual(expected);
53
+ });
54
+ it('should handle empty entries gracefully', () => {
55
+ const timePixelCounts = new Map();
56
+ const elementMap = new Map();
57
+ const expected = [];
58
+ const result = calculatePercentilesWithDebugInfo(timePixelCounts, elementMap, 100, 0);
59
+ expect(result).toEqual(expected);
60
+ });
61
+ it('should handle non-sequential timestamps', () => {
62
+ const timePixelCounts = createTimePixelCounts([[300, 70], [100, 30]]);
63
+ const elementMap = createElementMap([[300, [createViewportEntry('p'), createViewportEntry('a')]], [100, [createViewportEntry('div')]]]);
64
+ const expected = [createExpectedResult(100, 30, [createViewportEntry('div')]), createExpectedResult(300, 100, [createViewportEntry('p'), createViewportEntry('a')])];
65
+ const result = calculatePercentilesWithDebugInfo(timePixelCounts, elementMap, 100, 0);
66
+ expect(result).toEqual(expected);
67
+ });
68
+ it('should correctly calculate percentiles with startTime offset', () => {
69
+ const timePixelCounts = createTimePixelCounts([[100, 10], [200, 20], [300, 10], [400, 10]]);
70
+ const elementMap = createElementMap([[100, [createViewportEntry('div'), createViewportEntry('span')]], [200, [createViewportEntry('img')]], [300, [createViewportEntry('p'), createViewportEntry('a')]], [400, [createViewportEntry('img')]]]);
71
+ const expected = [createExpectedResult(50, 20, [createViewportEntry('div'), createViewportEntry('span')]), createExpectedResult(150, 60, [createViewportEntry('img')]), createExpectedResult(250, 80, [createViewportEntry('p'), createViewportEntry('a')]), createExpectedResult(350, 100, [createViewportEntry('img')])];
72
+ const result = calculatePercentilesWithDebugInfo(timePixelCounts, elementMap, 50, 50);
73
+ expect(result).toEqual(expected);
74
+ });
75
+ });
@@ -1,20 +1,2 @@
1
- import calcUsingCanvas from './canvas-heatmap';
2
- import calcUsingOldHeatmap from './heatmap';
3
- import calcUsingRectSweepingLine from './rect-sweeping-line';
4
- async function calculateTTVCPercentiles(arg) {
5
- const algo = 'canvas_heatmap';
6
- if (algo === 'canvas_heatmap') {
7
- const vcDetails = await calcUsingCanvas(arg);
8
- return vcDetails;
9
- }
10
- if (algo === 'rect_sweeping') {
11
- const vcDetails = await calcUsingRectSweepingLine(arg);
12
- return vcDetails;
13
- }
14
- if (algo === 'old_heatmap') {
15
- const vcDetails = await calcUsingOldHeatmap(arg);
16
- return vcDetails;
17
- }
18
- throw new Error('unexpected Error algo not chosen correctly');
19
- }
20
- export default calculateTTVCPercentiles;
1
+ export { default as calculateTTVCPercentiles } from './canvas-heatmap';
2
+ export { calculateTTVCPercentilesWithDebugInfo } from './canvas-heatmap';