@atlaskit/react-ufo 3.10.3 → 3.11.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/config/index.js +51 -5
  3. package/dist/cjs/create-experimental-interaction-metrics-payload/index.js +2 -1
  4. package/dist/cjs/create-payload/utils/get-vc-metrics.js +41 -20
  5. package/dist/cjs/interaction-metrics/index.js +2 -1
  6. package/dist/cjs/interaction-metrics/post-interaction-log.js +4 -2
  7. package/dist/cjs/vc/index.js +123 -35
  8. package/dist/cjs/vc/vc-observer/getVCRevisionsData.js +48 -23
  9. package/dist/cjs/vc/vc-observer/index.js +17 -6
  10. package/dist/cjs/vc/vc-observer-new/entries-timeline/index.js +1 -3
  11. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.js +25 -7
  12. package/dist/cjs/vc/vc-observer-new/viewport-observer/index.js +136 -115
  13. package/dist/es2019/config/index.js +49 -6
  14. package/dist/es2019/create-experimental-interaction-metrics-payload/index.js +2 -1
  15. package/dist/es2019/create-payload/utils/get-vc-metrics.js +36 -19
  16. package/dist/es2019/interaction-metrics/index.js +2 -1
  17. package/dist/es2019/interaction-metrics/post-interaction-log.js +2 -1
  18. package/dist/es2019/vc/index.js +98 -35
  19. package/dist/es2019/vc/vc-observer/getVCRevisionsData.js +52 -25
  20. package/dist/es2019/vc/vc-observer/index.js +18 -6
  21. package/dist/es2019/vc/vc-observer-new/entries-timeline/index.js +1 -3
  22. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.js +23 -7
  23. package/dist/es2019/vc/vc-observer-new/viewport-observer/index.js +122 -103
  24. package/dist/esm/config/index.js +49 -5
  25. package/dist/esm/create-experimental-interaction-metrics-payload/index.js +2 -1
  26. package/dist/esm/create-payload/utils/get-vc-metrics.js +42 -21
  27. package/dist/esm/interaction-metrics/index.js +2 -1
  28. package/dist/esm/interaction-metrics/post-interaction-log.js +4 -2
  29. package/dist/esm/vc/index.js +123 -35
  30. package/dist/esm/vc/vc-observer/getVCRevisionsData.js +48 -23
  31. package/dist/esm/vc/vc-observer/index.js +17 -6
  32. package/dist/esm/vc/vc-observer-new/entries-timeline/index.js +1 -3
  33. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.js +25 -7
  34. package/dist/esm/vc/vc-observer-new/viewport-observer/index.js +136 -115
  35. package/dist/types/config/index.d.ts +14 -3
  36. package/dist/types/create-post-interaction-log-payload/index.d.ts +2 -2
  37. package/dist/types/vc/types.d.ts +3 -1
  38. package/dist/types/vc/vc-observer/getVCRevisionsData.d.ts +5 -3
  39. package/dist/types/vc/vc-observer/index.d.ts +1 -1
  40. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.d.ts +8 -3
  41. package/dist/types/vc/vc-observer-new/viewport-observer/index.d.ts +8 -1
  42. package/dist/types-ts4.5/config/index.d.ts +14 -3
  43. package/dist/types-ts4.5/create-post-interaction-log-payload/index.d.ts +2 -2
  44. package/dist/types-ts4.5/vc/types.d.ts +3 -1
  45. package/dist/types-ts4.5/vc/vc-observer/getVCRevisionsData.d.ts +5 -3
  46. package/dist/types-ts4.5/vc/vc-observer/index.d.ts +1 -1
  47. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/canvas-pixel.d.ts +8 -3
  48. package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/index.d.ts +8 -1
  49. package/package.json +7 -1
@@ -15,6 +15,7 @@ var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/h
15
15
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
16
16
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
17
17
  var _config = require("../../config");
18
+ var _interactionMetrics = require("../../interaction-metrics");
18
19
  var _attachAbortListeners = require("./attachAbortListeners");
19
20
  var _getVCRevisionsData = require("./getVCRevisionsData");
20
21
  var _getViewport = require("./getViewport");
@@ -105,11 +106,11 @@ var VCObserver = exports.VCObserver = /*#__PURE__*/function () {
105
106
  });
106
107
  (0, _defineProperty2.default)(this, "getVCResult", /*#__PURE__*/function () {
107
108
  var _ref4 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(_ref3) {
108
- var start, stop, tti, prefix, ssr, vc, isEventAborted, startTime, fullPrefix, rawData, abortReason, abortReasonInfo, heatmap, heatmapNext, outOfBoundaryInfo, totalTime, componentsLog, viewport, devToolsEnabled, ratios, multiHeatmap, isTTVCv1Disabled, vcAbortedResultWithRevisions, ttvcV1Result, VC, VCBox, VCEntries, totalPainted, _componentsLog, vcNext, outOfBoundary, stopTime, ttvcV1DevToolInfo, ttvcV2DevToolInfo, isVCClean, revisionsData, speedIndex, isTTVCv3Enabled;
109
+ var start, stop, tti, prefix, ssr, vc, isEventAborted, experienceKey, startTime, fullPrefix, rawData, abortReason, abortReasonInfo, heatmap, heatmapNext, outOfBoundaryInfo, totalTime, componentsLog, viewport, devToolsEnabled, ratios, multiHeatmap, isTTVCv1Disabled, vcAbortedResultWithRevisions, ttvcV1Result, VC, VCBox, VCEntries, totalPainted, _componentsLog, vcNext, outOfBoundary, stopTime, ttvcV1DevToolInfo, ttvcV2DevToolInfo, isVCClean, revisionsData, speedIndex, isTTVCv3Enabled;
109
110
  return _regenerator.default.wrap(function _callee$(_context) {
110
111
  while (1) switch (_context.prev = _context.next) {
111
112
  case 0:
112
- start = _ref3.start, stop = _ref3.stop, tti = _ref3.tti, prefix = _ref3.prefix, ssr = _ref3.ssr, vc = _ref3.vc, isEventAborted = _ref3.isEventAborted;
113
+ start = _ref3.start, stop = _ref3.stop, tti = _ref3.tti, prefix = _ref3.prefix, ssr = _ref3.ssr, vc = _ref3.vc, isEventAborted = _ref3.isEventAborted, experienceKey = _ref3.experienceKey;
113
114
  startTime = performance.now(); // add local measurement
114
115
  fullPrefix = prefix !== undefined && prefix !== '' ? "".concat(prefix, ":") : '';
115
116
  rawData = vc !== undefined ? vc : _this.getVCRawData();
@@ -120,7 +121,7 @@ var VCObserver = exports.VCObserver = /*#__PURE__*/function () {
120
121
  return _context.abrupt("return", {});
121
122
  case 6:
122
123
  abortReason = rawData.abortReason, abortReasonInfo = rawData.abortReasonInfo, heatmap = rawData.heatmap, heatmapNext = rawData.heatmapNext, outOfBoundaryInfo = rawData.outOfBoundaryInfo, totalTime = rawData.totalTime, componentsLog = rawData.componentsLog, viewport = rawData.viewport, devToolsEnabled = rawData.devToolsEnabled, ratios = rawData.ratios, multiHeatmap = rawData.multiHeatmap;
123
- isTTVCv1Disabled = !(0, _config.isVCRevisionEnabled)('fy25.01'); // NOTE: as part of platform_ufo_add_vc_abort_reason_by_revisions feature,
124
+ isTTVCv1Disabled = (0, _platformFeatureFlags.fg)('platform_ufo_vc_enable_revisions_by_experience') ? !(0, _config.isVCRevisionEnabled)('fy25.01', experienceKey) : !(0, _config.isVCRevisionEnabled)('fy25.01'); // NOTE: as part of platform_ufo_add_vc_abort_reason_by_revisions feature,
124
125
  // we want to report abort by scroll events the same way as other abort reasons
125
126
  // i.e. not have the concept of `abortReason.blocking` anymore
126
127
  if (!(abortReasonInfo !== null && (0, _platformFeatureFlags.fg)('platform_ufo_add_vc_abort_reason_by_revisions'))) {
@@ -308,7 +309,8 @@ var VCObserver = exports.VCObserver = /*#__PURE__*/function () {
308
309
  VC: vcNext.VC,
309
310
  VCBox: vcNext.VCBox
310
311
  },
311
- isEventAborted: isEventAborted
312
+ isEventAborted: isEventAborted,
313
+ experienceKey: experienceKey
312
314
  });
313
315
  speedIndex = (0, _defineProperty2.default)((0, _defineProperty2.default)({}, "ufo:speedIndex", isTTVCv1Disabled ? vcNext.VCEntries.speedIndex : VCEntries.speedIndex), "ufo:next:speedIndex", vcNext.VCEntries.speedIndex);
314
316
  if (!isTTVCv1Disabled) {
@@ -317,7 +319,7 @@ var VCObserver = exports.VCObserver = /*#__PURE__*/function () {
317
319
  }
318
320
  return _context.abrupt("return", _objectSpread(_objectSpread(_objectSpread((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)({}, "".concat(fullPrefix, "vc:size"), viewport), "".concat(fullPrefix, "vc:time"), Math.round(totalTime + (stopTime - startTime))), "".concat(fullPrefix, "vc:ratios"), ratios), outOfBoundary), {}, (0, _defineProperty2.default)({}, "".concat(fullPrefix, "vc:ignored"), _this.getIgnoredElements(componentsLog)), revisionsData), speedIndex));
319
321
  case 32:
320
- isTTVCv3Enabled = (0, _config.isVCRevisionEnabled)('fy25.03');
322
+ isTTVCv3Enabled = (0, _platformFeatureFlags.fg)('platform_ufo_vc_enable_revisions_by_experience') ? (0, _config.isVCRevisionEnabled)('fy25.03', experienceKey) : (0, _config.isVCRevisionEnabled)('fy25.03');
321
323
  return _context.abrupt("return", _objectSpread(_objectSpread(_objectSpread((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)({
322
324
  'metrics:vc': VC
323
325
  }, "".concat(fullPrefix, "vc:state"), true), "".concat(fullPrefix, "vc:clean"), isVCClean), "".concat(fullPrefix, "vc:dom"), VCBox), "".concat(fullPrefix, "vc:updates"), isTTVCv3Enabled ? undefined : VCEntries.rel.slice(0, 50)), "".concat(fullPrefix, "vc:size"), viewport), "".concat(fullPrefix, "vc:time"), Math.round(totalTime + (stopTime - startTime))), "".concat(fullPrefix, "vc:total"), totalPainted), "".concat(fullPrefix, "vc:ratios"), ratios), outOfBoundary), {}, (0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)({}, "".concat(fullPrefix, "vc:next"), vcNext.VC), "".concat(fullPrefix, "vc:next:updates"), isTTVCv3Enabled ? undefined : vcNext.VCEntries.rel.slice(0, 50)), "".concat(fullPrefix, "vc:next:dom"), vcNext.VCBox), "".concat(fullPrefix, "vc:ignored"), _this.getIgnoredElements(componentsLog)), revisionsData), speedIndex));
@@ -334,7 +336,12 @@ var VCObserver = exports.VCObserver = /*#__PURE__*/function () {
334
336
  (0, _defineProperty2.default)(this, "handleUpdate", function (rawTime, intersectionRect, targetName, element, type, ignoreReason, attributeName, oldValue, newValue) {
335
337
  _this.measureStart();
336
338
  _this.legacyHandleUpdate(rawTime, intersectionRect, targetName, element, type, ignoreReason, attributeName, oldValue, newValue);
337
- if (!(0, _config.isVCRevisionEnabled)('fy25.03')) {
339
+ var isTTVCv3Disabled = !(0, _config.isVCRevisionEnabled)('fy25.03');
340
+ if ((0, _platformFeatureFlags.fg)('platform_ufo_vc_enable_revisions_by_experience')) {
341
+ var interaction = (0, _interactionMetrics.getActiveInteraction)();
342
+ isTTVCv3Disabled = !(0, _config.isVCRevisionEnabled)('fy25.03', interaction === null || interaction === void 0 ? void 0 : interaction.ufoName);
343
+ }
344
+ if (isTTVCv3Disabled) {
338
345
  _this.onViewportChangeDetected({
339
346
  timestamp: rawTime,
340
347
  intersectionRect: intersectionRect,
@@ -358,6 +365,10 @@ var VCObserver = exports.VCObserver = /*#__PURE__*/function () {
358
365
  _this.applyChangesToHeatMap(mappedValues, time, _this.heatmapNext);
359
366
  }
360
367
  var isTTVCv1Disabled = !(0, _config.isVCRevisionEnabled)('fy25.01');
368
+ if ((0, _platformFeatureFlags.fg)('platform_ufo_vc_enable_revisions_by_experience')) {
369
+ var interaction = (0, _interactionMetrics.getActiveInteraction)();
370
+ isTTVCv1Disabled = !(0, _config.isVCRevisionEnabled)('fy25.01', interaction === null || interaction === void 0 ? void 0 : interaction.ufoName);
371
+ }
361
372
  if (!isTTVCv1Disabled && (!ignoreReason || ignoreReason === 'not-visible') && type !== 'attr') {
362
373
  _this.applyChangesToHeatMap(mappedValues, time, _this.heatmap);
363
374
  }
@@ -7,13 +7,11 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.default = void 0;
8
8
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
9
9
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
10
- var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11
10
  var EntriesTimeline = exports.default = /*#__PURE__*/function () {
12
11
  function EntriesTimeline() {
13
12
  (0, _classCallCheck2.default)(this, EntriesTimeline);
14
- (0, _defineProperty2.default)(this, "unorderedEntries", []);
15
- (0, _defineProperty2.default)(this, "sortedEntriesCache", new Map());
16
13
  this.unorderedEntries = [];
14
+ this.sortedEntriesCache = new Map();
17
15
  }
18
16
  return (0, _createClass2.default)(EntriesTimeline, [{
19
17
  key: "push",
@@ -11,15 +11,16 @@ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"))
11
11
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
12
12
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
13
13
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
14
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
14
15
  var _taskYield = _interopRequireDefault(require("../../utils/task-yield"));
15
16
  // 24-bit color value
16
17
  /**
17
18
  * Class responsible for managing a scaled canvas and tracking pixel drawing operations.
18
- * It uses an OffscreenCanvas for better performance and maintains a mapping between
19
- * colors and timestamps for pixel counting purposes.
19
+ * It uses either an OffscreenCanvas (if available) or a regular HTML Canvas for better performance
20
+ * and maintains a mapping between colors and timestamps for pixel counting purposes.
20
21
  */
21
22
  var ViewportCanvas = exports.ViewportCanvas = /*#__PURE__*/function () {
22
- /** The underlying OffscreenCanvas instance */
23
+ /** The underlying Canvas instance (either OffscreenCanvas or HTMLCanvasElement) */
23
24
 
24
25
  /** The 2D rendering context of the canvas */
25
26
 
@@ -50,10 +51,8 @@ var ViewportCanvas = exports.ViewportCanvas = /*#__PURE__*/function () {
50
51
  this.scaleX = this.scaledWidth / safeViewportWidth;
51
52
  this.scaleY = this.scaledHeight / safeViewportHeight;
52
53
 
53
- // Initialize OffscreenCanvas with scaled dimensions
54
- this.canvas = document.createElement('canvas');
55
- this.canvas.width = this.scaledWidth;
56
- this.canvas.height = this.scaledHeight;
54
+ // Initialize canvas with scaled dimensions
55
+ this.canvas = this.createCanvas(this.scaledWidth, this.scaledHeight);
57
56
  var ctx = this.canvas.getContext('2d', {
58
57
  alpha: false,
59
58
  // Disable alpha channel for better performance
@@ -66,9 +65,28 @@ var ViewportCanvas = exports.ViewportCanvas = /*#__PURE__*/function () {
66
65
  }
67
66
  this.ctx = ctx;
68
67
  this.ctx.globalCompositeOperation = 'source-over';
68
+ if ((0, _platformFeatureFlags.fg)('platform_ufo_use_offscreen_canvas')) {
69
+ this.ctx.imageSmoothingEnabled = false; // Disable image smoothing for better performance
70
+ }
69
71
  this.clear();
70
72
  }
73
+
74
+ /**
75
+ * Creates a canvas instance, falling back to HTMLCanvasElement if OffscreenCanvas is not available
76
+ * or if the feature flag is disabled
77
+ */
71
78
  return (0, _createClass2.default)(ViewportCanvas, [{
79
+ key: "createCanvas",
80
+ value: function createCanvas(width, height) {
81
+ if (typeof OffscreenCanvas !== 'undefined' && (0, _platformFeatureFlags.fg)('platform_ufo_use_offscreen_canvas')) {
82
+ return new OffscreenCanvas(width, height);
83
+ }
84
+ var canvas = document.createElement('canvas');
85
+ canvas.width = width;
86
+ canvas.height = height;
87
+ return canvas;
88
+ }
89
+ }, {
72
90
  key: "getScaledDimensions",
73
91
  value: function getScaledDimensions() {
74
92
  return {
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.default = void 0;
8
8
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
9
9
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
10
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
11
  var _vcUtils = require("../../vc-observer/media-wrapper/vc-utils");
11
12
  var _isNonVisualStyleMutation = _interopRequireDefault(require("../../vc-observer/observers/non-visual-styles/is-non-visual-style-mutation"));
12
13
  var _intersectionObserver = require("./intersection-observer");
@@ -61,139 +62,154 @@ var ViewportObserver = exports.default = /*#__PURE__*/function () {
61
62
  var _this = this;
62
63
  var onChange = _ref2.onChange;
63
64
  (0, _classCallCheck2.default)(this, ViewportObserver);
64
- this.mapVisibleNodeRects = new WeakMap();
65
- this.intersectionObserver = (0, _intersectionObserver.createIntersectionObserver)({
66
- onEntry: function onEntry(_ref3) {
67
- var target = _ref3.target,
68
- rect = _ref3.rect,
69
- time = _ref3.time,
70
- type = _ref3.type,
71
- mutationData = _ref3.mutationData;
72
- if (!target) {
73
- return;
74
- }
75
- var visible = isElementVisible(target);
76
- var lastElementRect = _this.mapVisibleNodeRects.get(target);
77
- _this.mapVisibleNodeRects.set(target, rect);
78
- onChange({
79
- time: time,
80
- type: type,
81
- elementRef: new WeakRef(target),
82
- visible: visible,
83
- rect: rect,
84
- previousRect: lastElementRect,
85
- mutationData: mutationData
86
- });
65
+ (0, _defineProperty2.default)(this, "handleIntersectionEntry", function (_ref3) {
66
+ var target = _ref3.target,
67
+ rect = _ref3.rect,
68
+ time = _ref3.time,
69
+ type = _ref3.type,
70
+ mutationData = _ref3.mutationData;
71
+ if (!target) {
72
+ return;
87
73
  }
74
+ var visible = isElementVisible(target);
75
+ var lastElementRect = _this.mapVisibleNodeRects.get(target);
76
+ _this.mapVisibleNodeRects.set(target, rect);
77
+ _this.onChange({
78
+ time: time,
79
+ type: type,
80
+ elementRef: new WeakRef(target),
81
+ visible: visible,
82
+ rect: rect,
83
+ previousRect: lastElementRect,
84
+ mutationData: mutationData
85
+ });
88
86
  });
89
- this.mutationObserver = (0, _mutationObserver.default)({
90
- onChildListMutation: function onChildListMutation(_ref4) {
91
- var _removedNodes$map;
92
- var addedNodes = _ref4.addedNodes,
93
- removedNodes = _ref4.removedNodes;
94
- var removedNodeRects = (_removedNodes$map = removedNodes === null || removedNodes === void 0 ? void 0 : removedNodes.map(function (n) {
95
- return _this.mapVisibleNodeRects.get(n);
96
- })) !== null && _removedNodes$map !== void 0 ? _removedNodes$map : [];
97
- addedNodes.forEach(function (addedNode) {
98
- var _this$intersectionObs3;
99
- // for (const elem of addedNode.querySelectorAll('*')) {
100
- // this.intersectionObserver?.watchAndTag(elem, 'mutation:child-element');
101
- // }
102
-
103
- var sameDeletedNode = removedNodes.find(function (n) {
104
- return n.isEqualNode(addedNode);
105
- });
106
- if (sameDeletedNode) {
107
- var _this$intersectionObs;
108
- (_this$intersectionObs = _this.intersectionObserver) === null || _this$intersectionObs === void 0 || _this$intersectionObs.watchAndTag(addedNode, 'mutation:remount');
109
- return;
110
- }
111
- if ((0, _vcUtils.isContainedWithinMediaWrapper)(addedNode)) {
112
- var _this$intersectionObs2;
113
- (_this$intersectionObs2 = _this.intersectionObserver) === null || _this$intersectionObs2 === void 0 || _this$intersectionObs2.watchAndTag(addedNode, 'mutation:media');
114
- return;
115
- }
116
- (_this$intersectionObs3 = _this.intersectionObserver) === null || _this$intersectionObs3 === void 0 || _this$intersectionObs3.watchAndTag(addedNode, createElementMutationsWatcher(removedNodeRects));
87
+ (0, _defineProperty2.default)(this, "handleChildListMutation", function (_ref4) {
88
+ var addedNodes = _ref4.addedNodes,
89
+ removedNodes = _ref4.removedNodes;
90
+ var removedNodeRects = removedNodes.map(function (n) {
91
+ return _this.mapVisibleNodeRects.get(n);
92
+ });
93
+ addedNodes.forEach(function (addedNode) {
94
+ var _this$intersectionObs3;
95
+ var sameDeletedNode = removedNodes.find(function (n) {
96
+ return n.isEqualNode(addedNode);
117
97
  });
118
- },
119
- onAttributeMutation: function onAttributeMutation(_ref5) {
120
- var _this$intersectionObs4;
121
- var target = _ref5.target,
122
- attributeName = _ref5.attributeName;
123
- (_this$intersectionObs4 = _this.intersectionObserver) === null || _this$intersectionObs4 === void 0 || _this$intersectionObs4.watchAndTag(target, function (_ref6) {
124
- var target = _ref6.target,
125
- rect = _ref6.rect;
126
- if ((0, _vcUtils.isContainedWithinMediaWrapper)(target)) {
127
- return {
128
- type: 'mutation:media',
129
- mutationData: {
130
- attributeName: attributeName
131
- }
132
- };
133
- }
134
- if ((0, _isNonVisualStyleMutation.default)({
135
- target: target,
136
- attributeName: attributeName,
137
- type: 'attributes'
138
- })) {
139
- return {
140
- type: 'mutation:attribute:non-visual-style',
141
- mutationData: {
142
- attributeName: attributeName
143
- }
144
- };
145
- }
146
- var lastElementRect = _this.mapVisibleNodeRects.get(target);
147
- if (lastElementRect && sameRectSize(rect, lastElementRect)) {
148
- return {
149
- type: 'mutation:attribute:no-layout-shift',
150
- mutationData: {
151
- attributeName: attributeName
152
- }
153
- };
154
- }
98
+ if (sameDeletedNode) {
99
+ var _this$intersectionObs;
100
+ (_this$intersectionObs = _this.intersectionObserver) === null || _this$intersectionObs === void 0 || _this$intersectionObs.watchAndTag(addedNode, 'mutation:remount');
101
+ return;
102
+ }
103
+ if ((0, _vcUtils.isContainedWithinMediaWrapper)(addedNode)) {
104
+ var _this$intersectionObs2;
105
+ (_this$intersectionObs2 = _this.intersectionObserver) === null || _this$intersectionObs2 === void 0 || _this$intersectionObs2.watchAndTag(addedNode, 'mutation:media');
106
+ return;
107
+ }
108
+ (_this$intersectionObs3 = _this.intersectionObserver) === null || _this$intersectionObs3 === void 0 || _this$intersectionObs3.watchAndTag(addedNode, createElementMutationsWatcher(removedNodeRects));
109
+ });
110
+ });
111
+ (0, _defineProperty2.default)(this, "handleAttributeMutation", function (_ref5) {
112
+ var _this$intersectionObs4;
113
+ var target = _ref5.target,
114
+ attributeName = _ref5.attributeName;
115
+ (_this$intersectionObs4 = _this.intersectionObserver) === null || _this$intersectionObs4 === void 0 || _this$intersectionObs4.watchAndTag(target, function (_ref6) {
116
+ var target = _ref6.target,
117
+ rect = _ref6.rect;
118
+ if ((0, _vcUtils.isContainedWithinMediaWrapper)(target)) {
155
119
  return {
156
- type: 'mutation:attribute',
120
+ type: 'mutation:media',
157
121
  mutationData: {
158
122
  attributeName: attributeName
159
123
  }
160
124
  };
161
- });
162
- }
163
- });
164
- this.performanceObserver = (0, _performanceObserver.default)({
165
- onLayoutShift: function onLayoutShift(_ref7) {
166
- var time = _ref7.time,
167
- changedRects = _ref7.changedRects;
168
- var _iterator = _createForOfIteratorHelper(changedRects),
169
- _step;
170
- try {
171
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
172
- var changedRect = _step.value;
173
- var target = changedRect.node;
174
- if (target) {
175
- onChange({
176
- time: time,
177
- elementRef: new WeakRef(target),
178
- visible: true,
179
- rect: changedRect.rect,
180
- previousRect: changedRect.previousRect,
181
- type: 'layout-shift'
182
- });
125
+ }
126
+ if ((0, _isNonVisualStyleMutation.default)({
127
+ target: target,
128
+ attributeName: attributeName,
129
+ type: 'attributes'
130
+ })) {
131
+ return {
132
+ type: 'mutation:attribute:non-visual-style',
133
+ mutationData: {
134
+ attributeName: attributeName
135
+ }
136
+ };
137
+ }
138
+ var lastElementRect = _this.mapVisibleNodeRects.get(target);
139
+ if (lastElementRect && sameRectSize(rect, lastElementRect)) {
140
+ return {
141
+ type: 'mutation:attribute:no-layout-shift',
142
+ mutationData: {
143
+ attributeName: attributeName
183
144
  }
145
+ };
146
+ }
147
+ return {
148
+ type: 'mutation:attribute',
149
+ mutationData: {
150
+ attributeName: attributeName
151
+ }
152
+ };
153
+ });
154
+ });
155
+ (0, _defineProperty2.default)(this, "handleLayoutShift", function (_ref7) {
156
+ var time = _ref7.time,
157
+ changedRects = _ref7.changedRects;
158
+ var _iterator = _createForOfIteratorHelper(changedRects),
159
+ _step;
160
+ try {
161
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
162
+ var changedRect = _step.value;
163
+ var target = changedRect.node;
164
+ if (target) {
165
+ _this.onChange({
166
+ time: time,
167
+ elementRef: new WeakRef(target),
168
+ visible: true,
169
+ rect: changedRect.rect,
170
+ previousRect: changedRect.previousRect,
171
+ type: 'layout-shift'
172
+ });
184
173
  }
185
- } catch (err) {
186
- _iterator.e(err);
187
- } finally {
188
- _iterator.f();
189
174
  }
175
+ } catch (err) {
176
+ _iterator.e(err);
177
+ } finally {
178
+ _iterator.f();
190
179
  }
191
180
  });
181
+ this.mapVisibleNodeRects = new WeakMap();
182
+ this.onChange = onChange;
183
+ this.isStarted = false;
184
+ this.intersectionObserver = null;
185
+ this.mutationObserver = null;
186
+ this.performanceObserver = null;
192
187
  }
193
188
  return (0, _createClass2.default)(ViewportObserver, [{
189
+ key: "initializeObservers",
190
+ value: function initializeObservers() {
191
+ if (this.isStarted) {
192
+ return;
193
+ }
194
+ this.intersectionObserver = (0, _intersectionObserver.createIntersectionObserver)({
195
+ onEntry: this.handleIntersectionEntry
196
+ });
197
+ this.mutationObserver = (0, _mutationObserver.default)({
198
+ onChildListMutation: this.handleChildListMutation,
199
+ onAttributeMutation: this.handleAttributeMutation
200
+ });
201
+ this.performanceObserver = (0, _performanceObserver.default)({
202
+ onLayoutShift: this.handleLayoutShift
203
+ });
204
+ }
205
+ }, {
194
206
  key: "start",
195
207
  value: function start() {
196
208
  var _this$mutationObserve, _this$performanceObse;
209
+ if (this.isStarted) {
210
+ return;
211
+ }
212
+ this.initializeObservers();
197
213
  (_this$mutationObserve = this.mutationObserver) === null || _this$mutationObserve === void 0 || _this$mutationObserve.observe(document.body, {
198
214
  attributeOldValue: true,
199
215
  attributes: true,
@@ -206,14 +222,19 @@ var ViewportObserver = exports.default = /*#__PURE__*/function () {
206
222
  // @ts-ignore-error
207
223
  durationThreshold: 30
208
224
  });
225
+ this.isStarted = true;
209
226
  }
210
227
  }, {
211
228
  key: "stop",
212
229
  value: function stop() {
213
230
  var _this$mutationObserve2, _this$intersectionObs5, _this$performanceObse2;
231
+ if (!this.isStarted) {
232
+ return;
233
+ }
214
234
  (_this$mutationObserve2 = this.mutationObserver) === null || _this$mutationObserve2 === void 0 || _this$mutationObserve2.disconnect();
215
235
  (_this$intersectionObs5 = this.intersectionObserver) === null || _this$intersectionObs5 === void 0 || _this$intersectionObs5.disconnect();
216
236
  (_this$performanceObse2 = this.performanceObserver) === null || _this$performanceObse2 === void 0 || _this$performanceObse2.disconnect();
237
+ this.isStarted = false;
217
238
  }
218
239
  }]);
219
240
  }();
@@ -1,3 +1,4 @@
1
+ import { fg } from '@atlaskit/platform-feature-flags';
1
2
  let config;
2
3
  const DEFAULT_TTVC_REVISION = 'fy25.02';
3
4
 
@@ -5,28 +6,70 @@ const DEFAULT_TTVC_REVISION = 'fy25.02';
5
6
  // and they could delete empty members
6
7
 
7
8
  export function setUFOConfig(newConfig) {
8
- config = newConfig;
9
+ if (fg('platform_ufo_vc_enable_revisions_by_experience')) {
10
+ var _newConfig$vc;
11
+ // Handle edge cases with `enabledVCRevisions`
12
+ const {
13
+ enabledVCRevisions
14
+ } = (_newConfig$vc = newConfig === null || newConfig === void 0 ? void 0 : newConfig.vc) !== null && _newConfig$vc !== void 0 ? _newConfig$vc : {};
15
+ if (typeof (enabledVCRevisions === null || enabledVCRevisions === void 0 ? void 0 : enabledVCRevisions.byExperience) === 'object') {
16
+ config = {
17
+ ...newConfig,
18
+ vc: {
19
+ ...newConfig.vc,
20
+ enabledVCRevisions: {
21
+ // enforce axiom about `enabledVCRevisions.all` config
22
+ all: Array.from(new Set([DEFAULT_TTVC_REVISION, ...(enabledVCRevisions === null || enabledVCRevisions === void 0 ? void 0 : enabledVCRevisions.all), ...Object.values(enabledVCRevisions === null || enabledVCRevisions === void 0 ? void 0 : enabledVCRevisions.byExperience).flat()])),
23
+ byExperience: {
24
+ ...(enabledVCRevisions === null || enabledVCRevisions === void 0 ? void 0 : enabledVCRevisions.byExperience)
25
+ }
26
+ }
27
+ }
28
+ };
29
+ } else {
30
+ config = newConfig;
31
+ }
32
+ } else {
33
+ config = newConfig;
34
+ }
9
35
  }
10
36
  export function getConfig() {
11
37
  return config;
12
38
  }
13
- export function getEnabledVCRevisions() {
39
+ const isValidConfigArray = array => {
40
+ return Array.isArray(array) && array.length > 0;
41
+ };
42
+ export function getEnabledVCRevisions(experienceKey = '') {
14
43
  try {
15
44
  var _config$vc;
16
45
  if (!config) {
17
46
  return [];
18
47
  }
19
48
  if ((_config$vc = config.vc) !== null && _config$vc !== void 0 && _config$vc.enabled) {
20
- var _config$vc2, _config$vc3;
21
- return Array.isArray((_config$vc2 = config.vc) === null || _config$vc2 === void 0 ? void 0 : _config$vc2.enabledVCRevisions) && ((_config$vc3 = config.vc) === null || _config$vc3 === void 0 ? void 0 : _config$vc3.enabledVCRevisions.length) > 0 ? config.vc.enabledVCRevisions : [DEFAULT_TTVC_REVISION];
49
+ var _config$vc2, _enabledVCRevisions$b;
50
+ const {
51
+ enabledVCRevisions
52
+ } = (_config$vc2 = config.vc) !== null && _config$vc2 !== void 0 ? _config$vc2 : {};
53
+ if (isValidConfigArray(enabledVCRevisions === null || enabledVCRevisions === void 0 ? void 0 : (_enabledVCRevisions$b = enabledVCRevisions.byExperience) === null || _enabledVCRevisions$b === void 0 ? void 0 : _enabledVCRevisions$b[experienceKey]) && fg('platform_ufo_vc_enable_revisions_by_experience')) {
54
+ var _enabledVCRevisions$b2;
55
+ return (_enabledVCRevisions$b2 = enabledVCRevisions.byExperience) === null || _enabledVCRevisions$b2 === void 0 ? void 0 : _enabledVCRevisions$b2[experienceKey];
56
+ }
57
+ if (isValidConfigArray(enabledVCRevisions === null || enabledVCRevisions === void 0 ? void 0 : enabledVCRevisions.all)) {
58
+ return enabledVCRevisions.all;
59
+ }
60
+ return [DEFAULT_TTVC_REVISION];
22
61
  }
23
62
  return [];
24
63
  } catch {
25
64
  return [];
26
65
  }
27
66
  }
28
- export function isVCRevisionEnabled(revision) {
29
- return getEnabledVCRevisions().includes(revision);
67
+ export function isVCRevisionEnabled(revision, experienceKey) {
68
+ return getEnabledVCRevisions(experienceKey).includes(revision);
69
+ }
70
+ export function getMostRecentVCRevision(experienceKey = '') {
71
+ const enabledVCRevisions = getEnabledVCRevisions(experienceKey);
72
+ return enabledVCRevisions[enabledVCRevisions.length - 1];
30
73
  }
31
74
  export function getInteractionRate(name, interactionKind) {
32
75
  try {
@@ -67,7 +67,8 @@ export async function getExperimentalVCMetrics(interaction) {
67
67
  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,
68
68
  isEventAborted: !!interaction.abortReason,
69
69
  prefix,
70
- vc: interaction.vc
70
+ vc: interaction.vc,
71
+ experienceKey: interaction.ufoName
71
72
  });
72
73
  const VC = result === null || result === void 0 ? void 0 : result['metrics:vc'];
73
74
  if (!VC || !(result !== null && result !== void 0 && result[`${prefix}:vc:clean`])) {
@@ -1,5 +1,5 @@
1
1
  import { fg } from '@atlaskit/platform-feature-flags';
2
- import { getConfig, isVCRevisionEnabled } from '../../config';
2
+ import { getConfig, getMostRecentVCRevision, isVCRevisionEnabled } from '../../config';
3
3
  import { postInteractionLog } from '../../interaction-metrics';
4
4
  import { getVCObserver } from '../../vc';
5
5
  import getInteractionStatus from './get-interaction-status';
@@ -16,8 +16,9 @@ async function getVCMetrics(interaction) {
16
16
  }
17
17
  const interactionStatus = getInteractionStatus(interaction);
18
18
  const pageVisibilityUpToTTAI = getPageVisibilityUpToTTAI(interaction);
19
- if ((interactionStatus.originalInteractionStatus !== 'SUCCEEDED' || pageVisibilityUpToTTAI !== 'visible') && fg('platform_ufo_no_vc_on_aborted')) {
20
- getVCObserver().stop();
19
+ const shouldReportVCMetrics = interactionStatus.originalInteractionStatus === 'SUCCEEDED' && pageVisibilityUpToTTAI === 'visible';
20
+ if (!shouldReportVCMetrics && fg('platform_ufo_no_vc_on_aborted')) {
21
+ getVCObserver().stop(interaction.ufoName);
21
22
  return {};
22
23
  }
23
24
  const isSSREnabled = (config === null || config === void 0 ? void 0 : config.ssr) || (config === null || config === void 0 ? void 0 : (_config$vc$ssrWhiteli = config.vc.ssrWhitelist) === null || _config$vc$ssrWhiteli === void 0 ? void 0 : _config$vc$ssrWhiteli.includes(interaction.ufoName));
@@ -34,36 +35,52 @@ async function getVCMetrics(interaction) {
34
35
  prefix,
35
36
  vc: interaction.vc,
36
37
  isEventAborted: interactionStatus.originalInteractionStatus !== 'SUCCEEDED',
38
+ experienceKey: interaction.ufoName,
37
39
  ...ssr
38
40
  });
39
41
  if ((_config$experimentalI = config.experimentalInteractionMetrics) !== null && _config$experimentalI !== void 0 && _config$experimentalI.enabled) {
40
- getVCObserver().stop();
42
+ getVCObserver().stop(interaction.ufoName);
41
43
  }
42
44
  postInteractionLog.setLastInteractionFinishVCResult(result);
43
- if (!isVCRevisionEnabled('fy25.01')) {
45
+ if (fg('platform_ufo_vc_enable_revisions_by_experience')) {
44
46
  var _result$ufoVcRev;
45
- const ttvcV2Revision = result === null || result === void 0 ? void 0 : (_result$ufoVcRev = result['ufo:vc:rev']) === null || _result$ufoVcRev === void 0 ? void 0 : _result$ufoVcRev.find(({
47
+ const mostRecentVCRevision = getMostRecentVCRevision(interaction.ufoName);
48
+ const mostRecentVCRevisionPayload = result === null || result === void 0 ? void 0 : (_result$ufoVcRev = result['ufo:vc:rev']) === null || _result$ufoVcRev === void 0 ? void 0 : _result$ufoVcRev.find(({
46
49
  revision
47
- }) => revision === 'fy25.02');
48
- if (!(ttvcV2Revision !== null && ttvcV2Revision !== void 0 && ttvcV2Revision.clean)) {
50
+ }) => revision === mostRecentVCRevision);
51
+ if (!shouldReportVCMetrics || !(mostRecentVCRevisionPayload !== null && mostRecentVCRevisionPayload !== void 0 && mostRecentVCRevisionPayload.clean)) {
49
52
  return result;
50
53
  }
51
54
  return {
52
55
  ...result,
53
- 'metric:vc90': ttvcV2Revision['metric:vc90']
56
+ 'metric:vc90': mostRecentVCRevisionPayload['metric:vc90']
54
57
  };
55
58
  } else {
56
- const VC = result === null || result === void 0 ? void 0 : result['metrics:vc'];
57
- if (!VC || !(result !== null && result !== void 0 && result[`${prefix}:vc:clean`])) {
58
- return result;
59
- }
60
- if (interactionStatus.originalInteractionStatus !== 'SUCCEEDED' || pageVisibilityUpToTTAI !== 'visible') {
61
- return result;
59
+ if (!isVCRevisionEnabled('fy25.01')) {
60
+ var _result$ufoVcRev2;
61
+ const ttvcV2Revision = result === null || result === void 0 ? void 0 : (_result$ufoVcRev2 = result['ufo:vc:rev']) === null || _result$ufoVcRev2 === void 0 ? void 0 : _result$ufoVcRev2.find(({
62
+ revision
63
+ }) => revision === 'fy25.02');
64
+ if (!(ttvcV2Revision !== null && ttvcV2Revision !== void 0 && ttvcV2Revision.clean)) {
65
+ return result;
66
+ }
67
+ return {
68
+ ...result,
69
+ 'metric:vc90': ttvcV2Revision['metric:vc90']
70
+ };
71
+ } else {
72
+ const VC = result === null || result === void 0 ? void 0 : result['metrics:vc'];
73
+ if (!VC || !(result !== null && result !== void 0 && result[`${prefix}:vc:clean`])) {
74
+ return result;
75
+ }
76
+ if (interactionStatus.originalInteractionStatus !== 'SUCCEEDED' || pageVisibilityUpToTTAI !== 'visible') {
77
+ return result;
78
+ }
79
+ return {
80
+ ...result,
81
+ 'metric:vc90': VC['90']
82
+ };
62
83
  }
63
- return {
64
- ...result,
65
- 'metric:vc90': VC['90']
66
- };
67
84
  }
68
85
  }
69
86
  export default getVCMetrics;
@@ -719,7 +719,8 @@ export function addNewInteraction(interactionId, ufoName, type, startTime, rate,
719
719
  if (type === 'transition') {
720
720
  var _getConfig14, _getConfig14$experime;
721
721
  getVCObserver().start({
722
- startTime
722
+ startTime,
723
+ experienceKey: ufoName
723
724
  });
724
725
  postInteractionLog.startVCObserver({
725
726
  startTime