@atlaskit/react-ufo 4.10.2 → 4.11.1

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 (51) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/create-post-interaction-log-payload/index.js +12 -3
  3. package/dist/cjs/interaction-metrics/index.js +10 -4
  4. package/dist/cjs/interaction-metrics/post-interaction-log.js +13 -1
  5. package/dist/cjs/vc/vc-observer-new/metric-calculator/fy25_03/index.js +5 -0
  6. package/dist/cjs/vc/vc-observer-new/metric-calculator/vcnext/index.js +4 -1
  7. package/dist/cjs/vc/vc-observer-new/viewport-observer/index.js +26 -40
  8. package/dist/cjs/vc/vc-observer-new/viewport-observer/utils/is-element-visible.js +23 -0
  9. package/dist/cjs/vc/vc-observer-new/viewport-observer/utils/is-same-rect-dimensions.js +13 -0
  10. package/dist/cjs/vc/vc-observer-new/viewport-observer/utils/is-same-rect-size.js +13 -0
  11. package/dist/es2019/create-post-interaction-log-payload/index.js +9 -2
  12. package/dist/es2019/interaction-metrics/index.js +10 -4
  13. package/dist/es2019/interaction-metrics/post-interaction-log.js +11 -1
  14. package/dist/es2019/vc/vc-observer-new/metric-calculator/fy25_03/index.js +5 -0
  15. package/dist/es2019/vc/vc-observer-new/metric-calculator/vcnext/index.js +4 -1
  16. package/dist/es2019/vc/vc-observer-new/viewport-observer/index.js +25 -39
  17. package/dist/es2019/vc/vc-observer-new/viewport-observer/utils/is-element-visible.js +17 -0
  18. package/dist/es2019/vc/vc-observer-new/viewport-observer/utils/is-same-rect-dimensions.js +7 -0
  19. package/dist/es2019/vc/vc-observer-new/viewport-observer/utils/is-same-rect-size.js +7 -0
  20. package/dist/esm/create-post-interaction-log-payload/index.js +12 -3
  21. package/dist/esm/interaction-metrics/index.js +10 -4
  22. package/dist/esm/interaction-metrics/post-interaction-log.js +13 -1
  23. package/dist/esm/vc/vc-observer-new/metric-calculator/fy25_03/index.js +5 -0
  24. package/dist/esm/vc/vc-observer-new/metric-calculator/vcnext/index.js +4 -1
  25. package/dist/esm/vc/vc-observer-new/viewport-observer/index.js +25 -39
  26. package/dist/esm/vc/vc-observer-new/viewport-observer/utils/is-element-visible.js +17 -0
  27. package/dist/esm/vc/vc-observer-new/viewport-observer/utils/is-same-rect-dimensions.js +7 -0
  28. package/dist/esm/vc/vc-observer-new/viewport-observer/utils/is-same-rect-size.js +7 -0
  29. package/dist/types/common/common/types.d.ts +2 -0
  30. package/dist/types/common/react-ufo-payload-schema.d.ts +1 -0
  31. package/dist/types/create-interaction-extra-metrics-payload/index.d.ts +1 -0
  32. package/dist/types/create-payload/index.d.ts +384 -0
  33. package/dist/types/create-post-interaction-log-payload/index.d.ts +7 -1
  34. package/dist/types/interaction-metrics/index.d.ts +2 -2
  35. package/dist/types/interaction-metrics/post-interaction-log.d.ts +3 -1
  36. package/dist/types/vc/vc-observer-new/types.d.ts +1 -1
  37. package/dist/types/vc/vc-observer-new/viewport-observer/utils/is-element-visible.d.ts +1 -0
  38. package/dist/types/vc/vc-observer-new/viewport-observer/utils/is-same-rect-dimensions.d.ts +1 -0
  39. package/dist/types/vc/vc-observer-new/viewport-observer/utils/is-same-rect-size.d.ts +1 -0
  40. package/dist/types-ts4.5/common/common/types.d.ts +2 -0
  41. package/dist/types-ts4.5/common/react-ufo-payload-schema.d.ts +1 -0
  42. package/dist/types-ts4.5/create-interaction-extra-metrics-payload/index.d.ts +1 -0
  43. package/dist/types-ts4.5/create-payload/index.d.ts +384 -0
  44. package/dist/types-ts4.5/create-post-interaction-log-payload/index.d.ts +7 -1
  45. package/dist/types-ts4.5/interaction-metrics/index.d.ts +2 -2
  46. package/dist/types-ts4.5/interaction-metrics/post-interaction-log.d.ts +3 -1
  47. package/dist/types-ts4.5/vc/vc-observer-new/types.d.ts +1 -1
  48. package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/utils/is-element-visible.d.ts +1 -0
  49. package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/utils/is-same-rect-dimensions.d.ts +1 -0
  50. package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/utils/is-same-rect-size.d.ts +1 -0
  51. package/package.json +7 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @atlaskit/ufo-interaction-ignore
2
2
 
3
+ ## 4.11.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`89aa5e2bb9363`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/89aa5e2bb9363) -
8
+ Mark layout-shift entries with same-rect in VC observer
9
+
10
+ ## 4.11.0
11
+
12
+ ### Minor Changes
13
+
14
+ - [`53cdd8bde9bff`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/53cdd8bde9bff) -
15
+ add optional errorStatusCode param to ufo's addError() and addErrorToAll() api
16
+
3
17
  ## 4.10.2
4
18
 
5
19
  ### Patch Changes
@@ -125,7 +125,8 @@ function createPostInteractionLogPayload(_ref2) {
125
125
  var lastInteractionFinish = _ref2.lastInteractionFinish,
126
126
  reactProfilerTimings = _ref2.reactProfilerTimings,
127
127
  lastInteractionFinishVCResult = _ref2.lastInteractionFinishVCResult,
128
- postInteractionFinishVCResult = _ref2.postInteractionFinishVCResult;
128
+ postInteractionFinishVCResult = _ref2.postInteractionFinishVCResult,
129
+ postInteractionHoldInfo = _ref2.postInteractionHoldInfo;
129
130
  var config = (0, _config.getConfig)();
130
131
  if (!config) {
131
132
  throw Error('UFO Configuration not provided');
@@ -213,7 +214,7 @@ function createPostInteractionLogPayload(_ref2) {
213
214
  },
214
215
  'event:region': config.region || 'unknown',
215
216
  'experience:key': 'custom.post-interaction-logs',
216
- postInteractionLog: {
217
+ postInteractionLog: _objectSpread({
217
218
  lastInteractionFinish: _objectSpread(_objectSpread({}, lastInteractionFinish), {}, {
218
219
  ufoName: ufoName,
219
220
  start: lastInteractionFinishStart,
@@ -228,7 +229,15 @@ function createPostInteractionLogPayload(_ref2) {
228
229
  vcClean: postInteractionFinishVCClean,
229
230
  lateMutations: lateMutations,
230
231
  reactProfilerTimings: transformReactProfilerTimings(reactProfilerTimings)
231
- }
232
+ }, (0, _platformFeatureFlags.fg)('platform_ufo_enable_late_holds_post_interaction') ? {
233
+ postInteractionHoldInfo: postInteractionHoldInfo === null || postInteractionHoldInfo === void 0 ? void 0 : postInteractionHoldInfo.map(function (hold) {
234
+ return _objectSpread(_objectSpread({}, hold), {}, {
235
+ labelStack: hold.labelStack.map(function (label) {
236
+ return label.name;
237
+ }).join('/')
238
+ });
239
+ })
240
+ } : {})
232
241
  }
233
242
  }
234
243
  };
@@ -385,6 +385,10 @@ function removeHoldCriterion(id) {
385
385
  function addHold(interactionId, labelStack, name, experimental) {
386
386
  var interaction = _constants.interactions.get(interactionId);
387
387
  var id = (0, _uuid.v4)();
388
+ if (!interaction && (0, _platformFeatureFlags.fg)('platform_ufo_enable_late_holds_post_interaction')) {
389
+ // add hold timestamp to post interaction log if interaction is complete
390
+ postInteractionLog.addHoldInfo(labelStack, name, performance.now());
391
+ }
388
392
  if (interaction != null) {
389
393
  var start = performance.now();
390
394
  var holdActive = {
@@ -588,7 +592,7 @@ var ModuleLoadingProfiler = exports.ModuleLoadingProfiler = {
588
592
  removeHoldByID(currentInteractionId, id);
589
593
  }
590
594
  };
591
- function addError(interactionId, name, labelStack, errorType, errorMessage, errorStack, forcedError, errorHash) {
595
+ function addError(interactionId, name, labelStack, errorType, errorMessage, errorStack, forcedError, errorHash, errorStatusCode) {
592
596
  var interaction = _constants.interactions.get(interactionId);
593
597
  if (interaction != null) {
594
598
  interaction.errors.push({
@@ -598,11 +602,12 @@ function addError(interactionId, name, labelStack, errorType, errorMessage, erro
598
602
  errorMessage: errorMessage,
599
603
  errorStack: errorStack,
600
604
  forcedError: forcedError,
601
- errorHash: errorHash
605
+ errorHash: errorHash,
606
+ errorStatusCode: errorStatusCode
602
607
  });
603
608
  }
604
609
  }
605
- function addErrorToAll(name, labelStack, errorType, errorMessage, errorStack, errorHash) {
610
+ function addErrorToAll(name, labelStack, errorType, errorMessage, errorStack, errorHash, errorStatusCode) {
606
611
  _constants.interactions.forEach(function (interaction) {
607
612
  interaction.errors.push({
608
613
  name: name,
@@ -610,7 +615,8 @@ function addErrorToAll(name, labelStack, errorType, errorMessage, errorStack, er
610
615
  errorType: errorType,
611
616
  errorMessage: errorMessage,
612
617
  errorStack: errorStack,
613
- errorHash: errorHash
618
+ errorHash: errorHash,
619
+ errorStatusCode: errorStatusCode
614
620
  });
615
621
  });
616
622
  }
@@ -32,6 +32,7 @@ var PostInteractionLog = exports.default = /*#__PURE__*/function () {
32
32
  * Store the scheduled sink timeout Id so that it can be cancelled when needed
33
33
  */
34
34
  (0, _defineProperty2.default)(this, "sinkTimeoutId", null);
35
+ (0, _defineProperty2.default)(this, "holdInfo", []);
35
36
  /**
36
37
  * independent VC observer, that observes until `custom.post-interaction-logs` event is sent
37
38
  */
@@ -98,6 +99,7 @@ var PostInteractionLog = exports.default = /*#__PURE__*/function () {
98
99
  value: function reset() {
99
100
  this.lastInteractionFinish = null;
100
101
  this.reactProfilerTimings = [];
102
+ this.holdInfo = [];
101
103
  if (this.sinkTimeoutId != null) {
102
104
  clearTimeout(this.sinkTimeoutId);
103
105
  this.sinkTimeoutId = null;
@@ -162,7 +164,8 @@ var PostInteractionLog = exports.default = /*#__PURE__*/function () {
162
164
  lastInteractionFinish: this.lastInteractionFinish,
163
165
  reactProfilerTimings: this.reactProfilerTimings,
164
166
  postInteractionFinishVCResult: postInteractionFinishVCResult,
165
- lastInteractionFinishVCResult: this.lastInteractionFinishVCResult
167
+ lastInteractionFinishVCResult: this.lastInteractionFinishVCResult,
168
+ postInteractionHoldInfo: this.holdInfo
166
169
  });
167
170
  this.reset();
168
171
  case 13:
@@ -243,5 +246,14 @@ var PostInteractionLog = exports.default = /*#__PURE__*/function () {
243
246
  });
244
247
  }
245
248
  }
249
+ }, {
250
+ key: "addHoldInfo",
251
+ value: function addHoldInfo(labelStack, name, start) {
252
+ this.holdInfo.push({
253
+ name: name,
254
+ labelStack: labelStack,
255
+ start: start
256
+ });
257
+ }
246
258
  }]);
247
259
  }();
@@ -21,6 +21,11 @@ var REVISION_NO = 'fy25.03';
21
21
  var getConsideredEntryTypes = function getConsideredEntryTypes(include3p) {
22
22
  var entryTypes = ['mutation:child-element', 'mutation:element', 'mutation:attribute', 'layout-shift', 'window:event'];
23
23
 
24
+ // in rolling this out, still include it in TTVC v3
25
+ if ((0, _platformFeatureFlags.fg)('platform_ufo_vc_next_filter_ls_entries_same_rect')) {
26
+ entryTypes.push('layout-shift:same-rect');
27
+ }
28
+
24
29
  // If not exclude 3p elements from ttvc,
25
30
  // including the tags into the ConsideredEntryTypes so that it won't be ignored for TTVC calculation
26
31
  if (!(0, _platformFeatureFlags.fg)('platform_ufo_exclude_3p_elements_from_ttvc') || include3p) {
@@ -20,6 +20,9 @@ var REVISION_NO = 'next';
20
20
  var getConsideredEntryTypes = function getConsideredEntryTypes() {
21
21
  return ['mutation:display-contents-children-element'];
22
22
  };
23
+ var getExcludedEntryTypes = function getExcludedEntryTypes() {
24
+ return ['layout-shift:same-rect'];
25
+ };
23
26
 
24
27
  // NOTE: `VCNext` to be renamed `FY26_04` once stable
25
28
  var VCNextCalculator = exports.default = /*#__PURE__*/function (_VCCalculator_FY25_) {
@@ -32,7 +35,7 @@ var VCNextCalculator = exports.default = /*#__PURE__*/function (_VCCalculator_FY
32
35
  key: "isEntryIncluded",
33
36
  value: function isEntryIncluded(entry, include3p) {
34
37
  var isEntryIncludedInV3 = _superPropGet(VCNextCalculator, "isEntryIncluded", this, 3)([entry, include3p]);
35
- if (isEntryIncludedInV3) {
38
+ if (isEntryIncludedInV3 && !getExcludedEntryTypes().includes(entry.data.type)) {
36
39
  return true;
37
40
  }
38
41
  return getConsideredEntryTypes().includes(entry.data.type);
@@ -22,41 +22,15 @@ var _performanceObserver = _interopRequireDefault(require("./performance-observe
22
22
  var _checkDisplayContent = _interopRequireDefault(require("./utils/check-display-content"));
23
23
  var _checkWithinComponent2 = _interopRequireWildcard(require("./utils/check-within-component"));
24
24
  var _getMutatedElements = require("./utils/get-mutated-elements");
25
+ var _isElementVisible = require("./utils/is-element-visible");
25
26
  var _isInVcIgnoreIfNoLayoutShiftMarker = _interopRequireDefault(require("./utils/is-in-vc-ignore-if-no-layout-shift-marker"));
27
+ var _isSameRectDimensions = require("./utils/is-same-rect-dimensions");
28
+ var _isSameRectSize = require("./utils/is-same-rect-size");
26
29
  var _trackDisplayContentOccurrence = _interopRequireDefault(require("./utils/track-display-content-occurrence"));
27
30
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
28
31
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
29
32
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
30
33
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
31
- function isElementVisible(element) {
32
- if (!(element instanceof HTMLElement)) {
33
- return true;
34
- }
35
- try {
36
- var visible = element.checkVisibility({
37
- // @ts-ignore - visibilityProperty may not exist in all TS environments
38
- visibilityProperty: true,
39
- contentVisibilityAuto: true,
40
- opacityProperty: true
41
- });
42
- return visible;
43
- } catch (e) {
44
- // there is no support for checkVisibility
45
- return true;
46
- }
47
- }
48
- function sameRectSize(a, b) {
49
- if (!a || !b) {
50
- return false;
51
- }
52
- return a.width === b.width && a.height === b.height;
53
- }
54
- function sameRectDimensions(a, b) {
55
- if (!a || !b) {
56
- return false;
57
- }
58
- return a.width === b.width && a.height === b.height && a.x === b.x && a.y === b.y;
59
- }
60
34
  var createElementMutationsWatcher = function createElementMutationsWatcher(removedNodeRects) {
61
35
  return function (_ref) {
62
36
  var target = _ref.target,
@@ -70,7 +44,7 @@ var createElementMutationsWatcher = function createElementMutationsWatcher(remov
70
44
  return 'mutation:rll-placeholder';
71
45
  }
72
46
  var wasDeleted = removedNodeRects.some(function (nr) {
73
- return sameRectDimensions(nr, rect);
47
+ return (0, _isSameRectDimensions.isSameRectDimensions)(nr, rect);
74
48
  });
75
49
  if (wasDeleted && isInIgnoreLsMarker) {
76
50
  return 'mutation:element-replacement';
@@ -96,7 +70,7 @@ var ViewportObserver = exports.default = /*#__PURE__*/function () {
96
70
  if (!target) {
97
71
  return;
98
72
  }
99
- var visible = isElementVisible(target);
73
+ var visible = (0, _isElementVisible.isElementVisible)(target);
100
74
  var lastElementRect = _this.mapVisibleNodeRects.get(target);
101
75
  _this.mapVisibleNodeRects.set(target, rect);
102
76
  _this.onChange({
@@ -381,7 +355,7 @@ var ViewportObserver = exports.default = /*#__PURE__*/function () {
381
355
  };
382
356
  }
383
357
  var lastElementRect = _this.mapVisibleNodeRects.get(target);
384
- if (lastElementRect && sameRectSize(rect, lastElementRect)) {
358
+ if (lastElementRect && (0, _isSameRectSize.isSameRectSize)(rect, lastElementRect)) {
385
359
  return {
386
360
  type: 'mutation:attribute:no-layout-shift',
387
361
  mutationData: {
@@ -411,14 +385,26 @@ var ViewportObserver = exports.default = /*#__PURE__*/function () {
411
385
  var changedRect = _step4.value;
412
386
  var target = changedRect.node;
413
387
  if (target) {
414
- _this.onChange({
415
- time: time,
416
- elementRef: new WeakRef(target),
417
- visible: true,
418
- rect: changedRect.rect,
419
- previousRect: changedRect.previousRect,
420
- type: 'layout-shift'
421
- });
388
+ if ((0, _platformFeatureFlags.fg)('platform_ufo_vc_next_filter_ls_entries_same_rect')) {
389
+ var isSameCurrentAndPreviousRects = (0, _isSameRectDimensions.isSameRectDimensions)(changedRect.rect, changedRect.previousRect);
390
+ _this.onChange({
391
+ time: time,
392
+ elementRef: new WeakRef(target),
393
+ visible: true,
394
+ rect: changedRect.rect,
395
+ previousRect: changedRect.previousRect,
396
+ type: isSameCurrentAndPreviousRects ? 'layout-shift:same-rect' : 'layout-shift'
397
+ });
398
+ } else {
399
+ _this.onChange({
400
+ time: time,
401
+ elementRef: new WeakRef(target),
402
+ visible: true,
403
+ rect: changedRect.rect,
404
+ previousRect: changedRect.previousRect,
405
+ type: 'layout-shift'
406
+ });
407
+ }
422
408
  }
423
409
  }
424
410
  } catch (err) {
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isElementVisible = isElementVisible;
7
+ function isElementVisible(element) {
8
+ if (!(element instanceof HTMLElement)) {
9
+ return true;
10
+ }
11
+ try {
12
+ var visible = element.checkVisibility({
13
+ // @ts-ignore - visibilityProperty may not exist in all TS environments
14
+ visibilityProperty: true,
15
+ contentVisibilityAuto: true,
16
+ opacityProperty: true
17
+ });
18
+ return visible;
19
+ } catch (e) {
20
+ // there is no support for checkVisibility
21
+ return true;
22
+ }
23
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isSameRectDimensions = isSameRectDimensions;
7
+ var DIMENSIONS_CHECK_TOLERANCE_MARGIN_IN_PX = 1;
8
+ function isSameRectDimensions(a, b) {
9
+ if (!a || !b) {
10
+ return false;
11
+ }
12
+ return Math.abs(a.width - b.width) <= DIMENSIONS_CHECK_TOLERANCE_MARGIN_IN_PX && Math.abs(a.height - b.height) <= DIMENSIONS_CHECK_TOLERANCE_MARGIN_IN_PX && Math.abs(a.x - b.x) <= DIMENSIONS_CHECK_TOLERANCE_MARGIN_IN_PX && Math.abs(a.y - b.y) <= DIMENSIONS_CHECK_TOLERANCE_MARGIN_IN_PX;
13
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isSameRectSize = isSameRectSize;
7
+ var SIZE_CHECK_TOLERANCE_MARGIN_IN_PX = 1;
8
+ function isSameRectSize(a, b) {
9
+ if (!a || !b) {
10
+ return false;
11
+ }
12
+ return Math.abs(a.width - b.width) <= SIZE_CHECK_TOLERANCE_MARGIN_IN_PX && Math.abs(a.height - b.height) <= SIZE_CHECK_TOLERANCE_MARGIN_IN_PX;
13
+ }
@@ -110,7 +110,8 @@ function createPostInteractionLogPayload({
110
110
  lastInteractionFinish,
111
111
  reactProfilerTimings,
112
112
  lastInteractionFinishVCResult,
113
- postInteractionFinishVCResult
113
+ postInteractionFinishVCResult,
114
+ postInteractionHoldInfo
114
115
  }) {
115
116
  var _getMostRecentVCRevis, _window$location;
116
117
  const config = getConfig();
@@ -211,7 +212,13 @@ function createPostInteractionLogPayload({
211
212
  revisedVC90,
212
213
  vcClean: postInteractionFinishVCClean,
213
214
  lateMutations,
214
- reactProfilerTimings: transformReactProfilerTimings(reactProfilerTimings)
215
+ reactProfilerTimings: transformReactProfilerTimings(reactProfilerTimings),
216
+ ...(fg('platform_ufo_enable_late_holds_post_interaction') ? {
217
+ postInteractionHoldInfo: postInteractionHoldInfo === null || postInteractionHoldInfo === void 0 ? void 0 : postInteractionHoldInfo.map(hold => ({
218
+ ...hold,
219
+ labelStack: hold.labelStack.map(label => label.name).join('/')
220
+ }))
221
+ } : {})
215
222
  }
216
223
  }
217
224
  }
@@ -299,6 +299,10 @@ function removeHoldCriterion(id) {
299
299
  export function addHold(interactionId, labelStack, name, experimental) {
300
300
  const interaction = interactions.get(interactionId);
301
301
  const id = createUUID();
302
+ if (!interaction && fg('platform_ufo_enable_late_holds_post_interaction')) {
303
+ // add hold timestamp to post interaction log if interaction is complete
304
+ postInteractionLog.addHoldInfo(labelStack, name, performance.now());
305
+ }
302
306
  if (interaction != null) {
303
307
  const start = performance.now();
304
308
  const holdActive = {
@@ -507,7 +511,7 @@ export const ModuleLoadingProfiler = {
507
511
  removeHoldByID(currentInteractionId, id);
508
512
  }
509
513
  };
510
- export function addError(interactionId, name, labelStack, errorType, errorMessage, errorStack, forcedError, errorHash) {
514
+ export function addError(interactionId, name, labelStack, errorType, errorMessage, errorStack, forcedError, errorHash, errorStatusCode) {
511
515
  const interaction = interactions.get(interactionId);
512
516
  if (interaction != null) {
513
517
  interaction.errors.push({
@@ -517,11 +521,12 @@ export function addError(interactionId, name, labelStack, errorType, errorMessag
517
521
  errorMessage,
518
522
  errorStack,
519
523
  forcedError,
520
- errorHash
524
+ errorHash,
525
+ errorStatusCode
521
526
  });
522
527
  }
523
528
  }
524
- export function addErrorToAll(name, labelStack, errorType, errorMessage, errorStack, errorHash) {
529
+ export function addErrorToAll(name, labelStack, errorType, errorMessage, errorStack, errorHash, errorStatusCode) {
525
530
  interactions.forEach(interaction => {
526
531
  interaction.errors.push({
527
532
  name,
@@ -529,7 +534,8 @@ export function addErrorToAll(name, labelStack, errorType, errorMessage, errorSt
529
534
  errorType,
530
535
  errorMessage,
531
536
  errorStack,
532
- errorHash
537
+ errorHash,
538
+ errorStatusCode
533
539
  });
534
540
  });
535
541
  }
@@ -18,6 +18,7 @@ export default class PostInteractionLog {
18
18
  * Store the scheduled sink timeout Id so that it can be cancelled when needed
19
19
  */
20
20
  _defineProperty(this, "sinkTimeoutId", null);
21
+ _defineProperty(this, "holdInfo", []);
21
22
  /**
22
23
  * independent VC observer, that observes until `custom.post-interaction-logs` event is sent
23
24
  */
@@ -71,6 +72,7 @@ export default class PostInteractionLog {
71
72
  reset() {
72
73
  this.lastInteractionFinish = null;
73
74
  this.reactProfilerTimings = [];
75
+ this.holdInfo = [];
74
76
  if (this.sinkTimeoutId != null) {
75
77
  clearTimeout(this.sinkTimeoutId);
76
78
  this.sinkTimeoutId = null;
@@ -121,7 +123,8 @@ export default class PostInteractionLog {
121
123
  lastInteractionFinish: this.lastInteractionFinish,
122
124
  reactProfilerTimings: this.reactProfilerTimings,
123
125
  postInteractionFinishVCResult,
124
- lastInteractionFinishVCResult: this.lastInteractionFinishVCResult
126
+ lastInteractionFinishVCResult: this.lastInteractionFinishVCResult,
127
+ postInteractionHoldInfo: this.holdInfo
125
128
  });
126
129
  this.reset();
127
130
  }
@@ -179,4 +182,11 @@ export default class PostInteractionLog {
179
182
  });
180
183
  }
181
184
  }
185
+ addHoldInfo(labelStack, name, start) {
186
+ this.holdInfo.push({
187
+ name,
188
+ labelStack,
189
+ start
190
+ });
191
+ }
182
192
  }
@@ -7,6 +7,11 @@ const REVISION_NO = 'fy25.03';
7
7
  const getConsideredEntryTypes = include3p => {
8
8
  const entryTypes = ['mutation:child-element', 'mutation:element', 'mutation:attribute', 'layout-shift', 'window:event'];
9
9
 
10
+ // in rolling this out, still include it in TTVC v3
11
+ if (fg('platform_ufo_vc_next_filter_ls_entries_same_rect')) {
12
+ entryTypes.push('layout-shift:same-rect');
13
+ }
14
+
10
15
  // If not exclude 3p elements from ttvc,
11
16
  // including the tags into the ConsideredEntryTypes so that it won't be ignored for TTVC calculation
12
17
  if (!fg('platform_ufo_exclude_3p_elements_from_ttvc') || include3p) {
@@ -5,6 +5,9 @@ const REVISION_NO = 'next';
5
5
  const getConsideredEntryTypes = () => {
6
6
  return ['mutation:display-contents-children-element'];
7
7
  };
8
+ const getExcludedEntryTypes = () => {
9
+ return ['layout-shift:same-rect'];
10
+ };
8
11
 
9
12
  // NOTE: `VCNext` to be renamed `FY26_04` once stable
10
13
  export default class VCNextCalculator extends VCCalculator_FY25_03 {
@@ -13,7 +16,7 @@ export default class VCNextCalculator extends VCCalculator_FY25_03 {
13
16
  }
14
17
  isEntryIncluded(entry, include3p) {
15
18
  const isEntryIncludedInV3 = super.isEntryIncluded(entry, include3p);
16
- if (isEntryIncludedInV3) {
19
+ if (isEntryIncludedInV3 && !getExcludedEntryTypes().includes(entry.data.type)) {
17
20
  return true;
18
21
  }
19
22
  return getConsideredEntryTypes().includes(entry.data.type);
@@ -10,37 +10,11 @@ import createPerformanceObserver from './performance-observer';
10
10
  import checkCssProperty from './utils/check-display-content';
11
11
  import checkWithinComponent, { cleanupCaches } from './utils/check-within-component';
12
12
  import { getMutatedElements } from './utils/get-mutated-elements';
13
+ import { isElementVisible } from './utils/is-element-visible';
13
14
  import isInVCIgnoreIfNoLayoutShiftMarker from './utils/is-in-vc-ignore-if-no-layout-shift-marker';
15
+ import { isSameRectDimensions } from './utils/is-same-rect-dimensions';
16
+ import { isSameRectSize } from './utils/is-same-rect-size';
14
17
  import trackDisplayContentsOccurrence from './utils/track-display-content-occurrence';
15
- function isElementVisible(element) {
16
- if (!(element instanceof HTMLElement)) {
17
- return true;
18
- }
19
- try {
20
- const visible = element.checkVisibility({
21
- // @ts-ignore - visibilityProperty may not exist in all TS environments
22
- visibilityProperty: true,
23
- contentVisibilityAuto: true,
24
- opacityProperty: true
25
- });
26
- return visible;
27
- } catch (e) {
28
- // there is no support for checkVisibility
29
- return true;
30
- }
31
- }
32
- function sameRectSize(a, b) {
33
- if (!a || !b) {
34
- return false;
35
- }
36
- return a.width === b.width && a.height === b.height;
37
- }
38
- function sameRectDimensions(a, b) {
39
- if (!a || !b) {
40
- return false;
41
- }
42
- return a.width === b.width && a.height === b.height && a.x === b.x && a.y === b.y;
43
- }
44
18
  const createElementMutationsWatcher = removedNodeRects => ({
45
19
  target,
46
20
  rect
@@ -53,7 +27,7 @@ const createElementMutationsWatcher = removedNodeRects => ({
53
27
  if (isRLLPlaceholder && isInIgnoreLsMarker) {
54
28
  return 'mutation:rll-placeholder';
55
29
  }
56
- const wasDeleted = removedNodeRects.some(nr => sameRectDimensions(nr, rect));
30
+ const wasDeleted = removedNodeRects.some(nr => isSameRectDimensions(nr, rect));
57
31
  if (wasDeleted && isInIgnoreLsMarker) {
58
32
  return 'mutation:element-replacement';
59
33
  }
@@ -281,7 +255,7 @@ export default class ViewportObserver {
281
255
  };
282
256
  }
283
257
  const lastElementRect = this.mapVisibleNodeRects.get(target);
284
- if (lastElementRect && sameRectSize(rect, lastElementRect)) {
258
+ if (lastElementRect && isSameRectSize(rect, lastElementRect)) {
285
259
  return {
286
260
  type: 'mutation:attribute:no-layout-shift',
287
261
  mutationData: {
@@ -308,14 +282,26 @@ export default class ViewportObserver {
308
282
  for (const changedRect of changedRects) {
309
283
  const target = changedRect.node;
310
284
  if (target) {
311
- this.onChange({
312
- time,
313
- elementRef: new WeakRef(target),
314
- visible: true,
315
- rect: changedRect.rect,
316
- previousRect: changedRect.previousRect,
317
- type: 'layout-shift'
318
- });
285
+ if (fg('platform_ufo_vc_next_filter_ls_entries_same_rect')) {
286
+ const isSameCurrentAndPreviousRects = isSameRectDimensions(changedRect.rect, changedRect.previousRect);
287
+ this.onChange({
288
+ time,
289
+ elementRef: new WeakRef(target),
290
+ visible: true,
291
+ rect: changedRect.rect,
292
+ previousRect: changedRect.previousRect,
293
+ type: isSameCurrentAndPreviousRects ? 'layout-shift:same-rect' : 'layout-shift'
294
+ });
295
+ } else {
296
+ this.onChange({
297
+ time,
298
+ elementRef: new WeakRef(target),
299
+ visible: true,
300
+ rect: changedRect.rect,
301
+ previousRect: changedRect.previousRect,
302
+ type: 'layout-shift'
303
+ });
304
+ }
319
305
  }
320
306
  }
321
307
  });
@@ -0,0 +1,17 @@
1
+ export function isElementVisible(element) {
2
+ if (!(element instanceof HTMLElement)) {
3
+ return true;
4
+ }
5
+ try {
6
+ const visible = element.checkVisibility({
7
+ // @ts-ignore - visibilityProperty may not exist in all TS environments
8
+ visibilityProperty: true,
9
+ contentVisibilityAuto: true,
10
+ opacityProperty: true
11
+ });
12
+ return visible;
13
+ } catch (e) {
14
+ // there is no support for checkVisibility
15
+ return true;
16
+ }
17
+ }
@@ -0,0 +1,7 @@
1
+ const DIMENSIONS_CHECK_TOLERANCE_MARGIN_IN_PX = 1;
2
+ export function isSameRectDimensions(a, b) {
3
+ if (!a || !b) {
4
+ return false;
5
+ }
6
+ return Math.abs(a.width - b.width) <= DIMENSIONS_CHECK_TOLERANCE_MARGIN_IN_PX && Math.abs(a.height - b.height) <= DIMENSIONS_CHECK_TOLERANCE_MARGIN_IN_PX && Math.abs(a.x - b.x) <= DIMENSIONS_CHECK_TOLERANCE_MARGIN_IN_PX && Math.abs(a.y - b.y) <= DIMENSIONS_CHECK_TOLERANCE_MARGIN_IN_PX;
7
+ }
@@ -0,0 +1,7 @@
1
+ const SIZE_CHECK_TOLERANCE_MARGIN_IN_PX = 1;
2
+ export function isSameRectSize(a, b) {
3
+ if (!a || !b) {
4
+ return false;
5
+ }
6
+ return Math.abs(a.width - b.width) <= SIZE_CHECK_TOLERANCE_MARGIN_IN_PX && Math.abs(a.height - b.height) <= SIZE_CHECK_TOLERANCE_MARGIN_IN_PX;
7
+ }
@@ -118,7 +118,8 @@ function createPostInteractionLogPayload(_ref2) {
118
118
  var lastInteractionFinish = _ref2.lastInteractionFinish,
119
119
  reactProfilerTimings = _ref2.reactProfilerTimings,
120
120
  lastInteractionFinishVCResult = _ref2.lastInteractionFinishVCResult,
121
- postInteractionFinishVCResult = _ref2.postInteractionFinishVCResult;
121
+ postInteractionFinishVCResult = _ref2.postInteractionFinishVCResult,
122
+ postInteractionHoldInfo = _ref2.postInteractionHoldInfo;
122
123
  var config = getConfig();
123
124
  if (!config) {
124
125
  throw Error('UFO Configuration not provided');
@@ -206,7 +207,7 @@ function createPostInteractionLogPayload(_ref2) {
206
207
  },
207
208
  'event:region': config.region || 'unknown',
208
209
  'experience:key': 'custom.post-interaction-logs',
209
- postInteractionLog: {
210
+ postInteractionLog: _objectSpread({
210
211
  lastInteractionFinish: _objectSpread(_objectSpread({}, lastInteractionFinish), {}, {
211
212
  ufoName: ufoName,
212
213
  start: lastInteractionFinishStart,
@@ -221,7 +222,15 @@ function createPostInteractionLogPayload(_ref2) {
221
222
  vcClean: postInteractionFinishVCClean,
222
223
  lateMutations: lateMutations,
223
224
  reactProfilerTimings: transformReactProfilerTimings(reactProfilerTimings)
224
- }
225
+ }, fg('platform_ufo_enable_late_holds_post_interaction') ? {
226
+ postInteractionHoldInfo: postInteractionHoldInfo === null || postInteractionHoldInfo === void 0 ? void 0 : postInteractionHoldInfo.map(function (hold) {
227
+ return _objectSpread(_objectSpread({}, hold), {}, {
228
+ labelStack: hold.labelStack.map(function (label) {
229
+ return label.name;
230
+ }).join('/')
231
+ });
232
+ })
233
+ } : {})
225
234
  }
226
235
  }
227
236
  };