@atlaskit/react-ufo 5.2.9 → 5.3.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 (61) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/create-payload/index.js +6 -5
  3. package/dist/cjs/hidden-timing/index.js +52 -0
  4. package/dist/cjs/interaction-metrics/index.js +1 -0
  5. package/dist/cjs/interaction-metrics-init/index.js +2 -1
  6. package/dist/cjs/vc/index.js +4 -2
  7. package/dist/cjs/vc/vc-observer-new/index.js +10 -4
  8. package/dist/cjs/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +84 -29
  9. package/dist/cjs/vc/vc-observer-new/metric-calculator/utils/detect-layout-shift-cause.js +164 -0
  10. package/dist/cjs/vc/vc-observer-new/viewport-observer/index.js +173 -34
  11. package/dist/cjs/vc/vc-observer-new/viewport-observer/intersection-observer/index.js +1 -1
  12. package/dist/cjs/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +2 -1
  13. package/dist/es2019/create-payload/index.js +5 -3
  14. package/dist/es2019/hidden-timing/index.js +51 -0
  15. package/dist/es2019/interaction-metrics/index.js +1 -0
  16. package/dist/es2019/interaction-metrics-init/index.js +2 -1
  17. package/dist/es2019/vc/index.js +4 -2
  18. package/dist/es2019/vc/vc-observer-new/index.js +10 -5
  19. package/dist/es2019/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +61 -7
  20. package/dist/es2019/vc/vc-observer-new/metric-calculator/utils/detect-layout-shift-cause.js +138 -0
  21. package/dist/es2019/vc/vc-observer-new/viewport-observer/index.js +145 -10
  22. package/dist/es2019/vc/vc-observer-new/viewport-observer/intersection-observer/index.js +1 -1
  23. package/dist/es2019/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +2 -1
  24. package/dist/esm/create-payload/index.js +7 -6
  25. package/dist/esm/hidden-timing/index.js +51 -0
  26. package/dist/esm/interaction-metrics/index.js +1 -0
  27. package/dist/esm/interaction-metrics-init/index.js +2 -1
  28. package/dist/esm/vc/index.js +4 -2
  29. package/dist/esm/vc/vc-observer-new/index.js +10 -4
  30. package/dist/esm/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +84 -29
  31. package/dist/esm/vc/vc-observer-new/metric-calculator/utils/detect-layout-shift-cause.js +158 -0
  32. package/dist/esm/vc/vc-observer-new/viewport-observer/index.js +173 -34
  33. package/dist/esm/vc/vc-observer-new/viewport-observer/intersection-observer/index.js +1 -1
  34. package/dist/esm/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +2 -1
  35. package/dist/types/common/react-ufo-payload-schema.d.ts +2 -0
  36. package/dist/types/common/vc/types.d.ts +55 -0
  37. package/dist/types/config/index.d.ts +1 -0
  38. package/dist/types/hidden-timing/index.d.ts +9 -0
  39. package/dist/types/vc/types.d.ts +2 -0
  40. package/dist/types/vc/vc-observer-new/index.d.ts +2 -0
  41. package/dist/types/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +1 -1
  42. package/dist/types/vc/vc-observer-new/metric-calculator/types.d.ts +1 -0
  43. package/dist/types/vc/vc-observer-new/metric-calculator/utils/detect-layout-shift-cause.d.ts +31 -0
  44. package/dist/types/vc/vc-observer-new/types.d.ts +2 -0
  45. package/dist/types/vc/vc-observer-new/viewport-observer/index.d.ts +3 -1
  46. package/dist/types/vc/vc-observer-new/viewport-observer/mutation-observer/index.d.ts +1 -0
  47. package/dist/types/vc/vc-observer-new/viewport-observer/types.d.ts +5 -2
  48. package/dist/types-ts4.5/common/react-ufo-payload-schema.d.ts +2 -0
  49. package/dist/types-ts4.5/common/vc/types.d.ts +55 -0
  50. package/dist/types-ts4.5/config/index.d.ts +1 -0
  51. package/dist/types-ts4.5/hidden-timing/index.d.ts +9 -0
  52. package/dist/types-ts4.5/vc/types.d.ts +2 -0
  53. package/dist/types-ts4.5/vc/vc-observer-new/index.d.ts +2 -0
  54. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +1 -1
  55. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/types.d.ts +1 -0
  56. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/utils/detect-layout-shift-cause.d.ts +31 -0
  57. package/dist/types-ts4.5/vc/vc-observer-new/types.d.ts +2 -0
  58. package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/index.d.ts +3 -1
  59. package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/mutation-observer/index.d.ts +1 -0
  60. package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/types.d.ts +5 -2
  61. package/package.json +4 -1
@@ -0,0 +1,158 @@
1
+ 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; } } }; }
2
+ 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; } }
3
+ 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; }
4
+ var calculateOffenderState = function calculateOffenderState(current, matchesCurrentEntry) {
5
+ // Once we know it's mixed across entries, we can short-circuit.
6
+ if (current === 'some') {
7
+ return 'some';
8
+ }
9
+ var entryState = matchesCurrentEntry ? 'all' : 'none';
10
+ if (current == null) {
11
+ return entryState;
12
+ }
13
+
14
+ // If the state flips between entries (all <-> none), it's "some".
15
+ if (current !== entryState) {
16
+ return 'some';
17
+ }
18
+ return current;
19
+ };
20
+ var calculateLayoutShiftMovement = function calculateLayoutShiftMovement(layoutShiftEntries) {
21
+ var movements = layoutShiftEntries.map(function (lsEntry) {
22
+ var rect = lsEntry.rect;
23
+ var previousRect = lsEntry.previousRect;
24
+ if (!rect || !previousRect) {
25
+ return null;
26
+ }
27
+ var dx = rect.x - previousRect.x;
28
+ var dy = rect.y - previousRect.y;
29
+ return {
30
+ dx: dx,
31
+ dy: dy,
32
+ movedX: dx !== 0,
33
+ movedY: dy !== 0,
34
+ dirX: Math.sign(dx),
35
+ dirY: Math.sign(dy),
36
+ absDx: Math.abs(dx),
37
+ absDy: Math.abs(dy)
38
+ };
39
+ });
40
+ var hasMovement = function hasMovement(m) {
41
+ return m !== null;
42
+ };
43
+ var validMovements = movements.filter(hasMovement);
44
+ var allHaveRects = layoutShiftEntries.length > 0 && validMovements.length === layoutShiftEntries.length;
45
+ if (!allHaveRects) {
46
+ return {
47
+ allHaveRects: allHaveRects,
48
+ allMovedSameWay: false,
49
+ allMovedSameAmount: false
50
+ };
51
+ }
52
+ var first = validMovements[0];
53
+ var allMovedSameWay = validMovements.every(function (m) {
54
+ return m.movedX === first.movedX && m.movedY === first.movedY && m.dirX === first.dirX && m.dirY === first.dirY;
55
+ });
56
+ var allMovedSameAmount = allMovedSameWay && validMovements.every(function (m) {
57
+ return m.absDx === first.absDx && m.absDy === first.absDy;
58
+ });
59
+ return {
60
+ allHaveRects: allHaveRects,
61
+ allMovedSameWay: allMovedSameWay,
62
+ allMovedSameAmount: allMovedSameAmount,
63
+ deltaX: first.dx,
64
+ deltaY: first.dy
65
+ };
66
+ };
67
+ export var detectLayoutShiftCause = function detectLayoutShiftCause(_ref) {
68
+ var viewportEntries = _ref.viewportEntries,
69
+ layoutShiftEntries = _ref.layoutShiftEntries,
70
+ time = _ref.time,
71
+ startTime = _ref.startTime,
72
+ _ref$offenderWindowMs = _ref.offenderWindowMs,
73
+ offenderWindowMs = _ref$offenderWindowMs === void 0 ? 250 : _ref$offenderWindowMs;
74
+ var checkpointTimestamp = startTime + time;
75
+ var filteredLSPotentialOffenders = viewportEntries.filter(function (viewportEntry) {
76
+ return (viewportEntry.time < checkpointTimestamp && viewportEntry.time > checkpointTimestamp - offenderWindowMs || viewportEntry.time > checkpointTimestamp && viewportEntry.time < checkpointTimestamp + offenderWindowMs) && viewportEntry.data.type !== 'layout-shift';
77
+ });
78
+
79
+ // Summarize whether all layout-shift entries moved in a consistent direction and by the same amount.
80
+ var layoutShiftVariables = calculateLayoutShiftMovement(layoutShiftEntries);
81
+
82
+ // Classify each offender against *all* layout-shift entries when the layout shift is consistently moving in a
83
+ // single axis (pure X or pure Y) so we can reason about whether an offender could have caused the movement.
84
+ var layoutShiftOffenders = filteredLSPotentialOffenders.reduce(function (acc, offender) {
85
+ var _offenderData$origina;
86
+ var offenderData = offender.data;
87
+ var offenderRect = offenderData.rect;
88
+ if (!offenderRect) {
89
+ return acc;
90
+ }
91
+ var offenderTimestamp = (_offenderData$origina = offenderData.originalMutationTimestamp) !== null && _offenderData$origina !== void 0 ? _offenderData$origina : offender.time;
92
+ var happenedBefore = offenderTimestamp <= checkpointTimestamp;
93
+ var offenderLeft = offenderRect.x;
94
+ var offenderRight = offenderRect.x + offenderRect.width;
95
+ var offenderTop = offenderRect.y;
96
+ var offenderBottom = offenderRect.y + offenderRect.height;
97
+ var shouldClassifyAgainstAllEntries = layoutShiftVariables.allMovedSameWay && 'deltaX' in layoutShiftVariables && 'deltaY' in layoutShiftVariables && layoutShiftVariables.deltaX === 0 !== (layoutShiftVariables.deltaY === 0);
98
+ var isAbove = null;
99
+ var isLeft = null;
100
+ var isRight = null;
101
+ var hasHorizontalOverlap = null;
102
+ var hasVerticalOverlap = null;
103
+ if (shouldClassifyAgainstAllEntries) {
104
+ var _iterator = _createForOfIteratorHelper(layoutShiftEntries),
105
+ _step;
106
+ try {
107
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
108
+ var lsEntry = _step.value;
109
+ var lsRect = lsEntry.rect;
110
+ if (!lsRect) {
111
+ continue;
112
+ }
113
+ var lsLeft = lsRect.x;
114
+ var lsRight = lsRect.x + lsRect.width;
115
+ var lsTop = lsRect.y;
116
+ var lsBottom = lsRect.y + lsRect.height;
117
+ isAbove = calculateOffenderState(isAbove, offenderBottom <= lsTop);
118
+ isLeft = calculateOffenderState(isLeft, offenderRight <= lsLeft);
119
+ isRight = calculateOffenderState(isRight, offenderLeft >= lsRight);
120
+ hasHorizontalOverlap = calculateOffenderState(hasHorizontalOverlap, offenderLeft < lsRight && offenderRight > lsLeft);
121
+ hasVerticalOverlap = calculateOffenderState(hasVerticalOverlap, offenderTop < lsBottom && offenderBottom > lsTop);
122
+ }
123
+ } catch (err) {
124
+ _iterator.e(err);
125
+ } finally {
126
+ _iterator.f();
127
+ }
128
+ }
129
+ var matchesLayoutShiftDelta = false;
130
+ if (shouldClassifyAgainstAllEntries && layoutShiftVariables.allMovedSameAmount && offenderData.previousRect) {
131
+ var dx = offenderRect.x - offenderData.previousRect.x;
132
+ var dy = offenderRect.y - offenderData.previousRect.y;
133
+ var isPureX = layoutShiftVariables.deltaX !== 0 && layoutShiftVariables.deltaY === 0;
134
+ var isPureY = layoutShiftVariables.deltaY !== 0 && layoutShiftVariables.deltaX === 0;
135
+ if (isPureX) {
136
+ matchesLayoutShiftDelta = dx === layoutShiftVariables.deltaX && dy === 0;
137
+ } else if (isPureY) {
138
+ matchesLayoutShiftDelta = dy === layoutShiftVariables.deltaY && dx === 0;
139
+ }
140
+ }
141
+ acc.push({
142
+ offender: offenderData.elementName,
143
+ happenedBefore: happenedBefore,
144
+ distanceToLS: Math.round(offenderTimestamp - checkpointTimestamp),
145
+ isAbove: isAbove !== null && isAbove !== void 0 ? isAbove : 'none',
146
+ isLeft: isLeft !== null && isLeft !== void 0 ? isLeft : 'none',
147
+ isRight: isRight !== null && isRight !== void 0 ? isRight : 'none',
148
+ hasHorizontalOverlap: hasHorizontalOverlap !== null && hasHorizontalOverlap !== void 0 ? hasHorizontalOverlap : 'none',
149
+ hasVerticalOverlap: hasVerticalOverlap !== null && hasVerticalOverlap !== void 0 ? hasVerticalOverlap : 'none',
150
+ matchesLayoutShiftDelta: matchesLayoutShiftDelta
151
+ });
152
+ return acc;
153
+ }, []);
154
+ return {
155
+ layoutShiftVariables: layoutShiftVariables,
156
+ layoutShiftOffenders: layoutShiftOffenders
157
+ };
158
+ };
@@ -88,22 +88,151 @@ var createElementMutationsWatcher = function createElementMutationsWatcher(remov
88
88
  return 'mutation:element';
89
89
  };
90
90
  };
91
+ var createElementMutationsWatcherNew = function createElementMutationsWatcherNew(removedNodeRects, isWithinThirdPartySegment, isWithinSmartAnswersSegment, hasSameDeletedNode, timestamp, isTargetReactRoot, getSSRState, getSSRPlaceholderHandler) {
92
+ return function (_ref2) {
93
+ var target = _ref2.target,
94
+ rect = _ref2.rect;
95
+ if (getSSRState) {
96
+ var ssrState = getSSRState();
97
+ var SSRStateEnum = {
98
+ normal: 1,
99
+ waitingForFirstRender: 2,
100
+ ignoring: 3
101
+ };
102
+ if (ssrState.state === SSRStateEnum.waitingForFirstRender && timestamp > ssrState.renderStart && isTargetReactRoot) {
103
+ ssrState.state = SSRStateEnum.ignoring;
104
+ if (ssrState.renderStop === -1) {
105
+ // arbitrary 500ms DOM update window
106
+ ssrState.renderStop = timestamp + 500;
107
+ }
108
+ return {
109
+ type: 'ssr-hydration',
110
+ mutationData: {
111
+ timestamp: timestamp
112
+ }
113
+ };
114
+ }
115
+ if (ssrState.state === SSRStateEnum.ignoring && timestamp > ssrState.renderStart && isTargetReactRoot) {
116
+ if (timestamp <= ssrState.renderStop) {
117
+ return {
118
+ type: 'ssr-hydration',
119
+ mutationData: {
120
+ timestamp: timestamp
121
+ }
122
+ };
123
+ } else {
124
+ ssrState.state = SSRStateEnum.normal;
125
+ }
126
+ }
127
+ }
128
+ if (getSSRPlaceholderHandler) {
129
+ var ssrPlaceholderHandler = getSSRPlaceholderHandler();
130
+ if (ssrPlaceholderHandler) {
131
+ if ((ssrPlaceholderHandler.isPlaceholderV4(target) || ssrPlaceholderHandler.isPlaceholderIgnored(target)) && ssrPlaceholderHandler.checkIfExistedAndSizeMatchingV3(target)) {
132
+ return {
133
+ type: 'mutation:ssr-placeholder',
134
+ mutationData: {
135
+ timestamp: timestamp
136
+ }
137
+ };
138
+ }
139
+ if ((ssrPlaceholderHandler.isPlaceholderReplacementV4(target) || ssrPlaceholderHandler.isPlaceholderIgnored(target)) && ssrPlaceholderHandler.validateReactComponentMatchToPlaceholderV4(target)) {
140
+ return {
141
+ type: 'mutation:ssr-placeholder',
142
+ mutationData: {
143
+ timestamp: timestamp
144
+ }
145
+ };
146
+ }
147
+ }
148
+ }
149
+ if (hasSameDeletedNode && isInVCIgnoreIfNoLayoutShiftMarker(target)) {
150
+ return {
151
+ type: 'mutation:remount',
152
+ mutationData: {
153
+ timestamp: timestamp
154
+ }
155
+ };
156
+ }
157
+ if (isContainedWithinMediaWrapper(target)) {
158
+ return {
159
+ type: 'mutation:media',
160
+ mutationData: {
161
+ timestamp: timestamp
162
+ }
163
+ };
164
+ }
165
+ if (isWithinThirdPartySegment) {
166
+ return {
167
+ type: 'mutation:third-party-element',
168
+ mutationData: {
169
+ timestamp: timestamp
170
+ }
171
+ };
172
+ }
173
+ if (isWithinSmartAnswersSegment) {
174
+ return {
175
+ type: 'mutation:smart-answers-element',
176
+ mutationData: {
177
+ timestamp: timestamp
178
+ }
179
+ };
180
+ }
181
+ var isInIgnoreLsMarker = isInVCIgnoreIfNoLayoutShiftMarker(target);
182
+ if (!isInIgnoreLsMarker) {
183
+ return {
184
+ type: 'mutation:element',
185
+ mutationData: {
186
+ timestamp: timestamp
187
+ }
188
+ };
189
+ }
190
+ var isRLLPlaceholder = RLLPlaceholderHandlers.getInstance().isRLLPlaceholderHydration(rect);
191
+ if (isRLLPlaceholder && isInIgnoreLsMarker) {
192
+ return {
193
+ type: 'mutation:rll-placeholder',
194
+ mutationData: {
195
+ timestamp: timestamp
196
+ }
197
+ };
198
+ }
199
+ var wasDeleted = removedNodeRects.some(function (nr) {
200
+ return isSameRectDimensions(nr, rect);
201
+ });
202
+ if (wasDeleted && isInIgnoreLsMarker) {
203
+ return {
204
+ type: 'mutation:element-replacement',
205
+ mutationData: {
206
+ timestamp: timestamp
207
+ }
208
+ };
209
+ }
210
+ return {
211
+ type: 'mutation:element',
212
+ mutationData: {
213
+ timestamp: timestamp
214
+ }
215
+ };
216
+ };
217
+ };
91
218
  var ViewportObserver = /*#__PURE__*/function () {
92
219
  // SSR context functions
93
220
 
94
- function ViewportObserver(_ref2) {
221
+ function ViewportObserver(_ref3) {
95
222
  var _this = this;
96
- var onChange = _ref2.onChange,
97
- getSSRState = _ref2.getSSRState,
98
- getSSRPlaceholderHandler = _ref2.getSSRPlaceholderHandler,
99
- searchPageConfig = _ref2.searchPageConfig;
223
+ var onChange = _ref3.onChange,
224
+ getSSRState = _ref3.getSSRState,
225
+ getSSRPlaceholderHandler = _ref3.getSSRPlaceholderHandler,
226
+ _ref3$trackLayoutShif = _ref3.trackLayoutShiftOffenders,
227
+ trackLayoutShiftOffenders = _ref3$trackLayoutShif === void 0 ? false : _ref3$trackLayoutShif,
228
+ searchPageConfig = _ref3.searchPageConfig;
100
229
  _classCallCheck(this, ViewportObserver);
101
- _defineProperty(this, "handleIntersectionEntry", function (_ref3) {
102
- var target = _ref3.target,
103
- rect = _ref3.rect,
104
- time = _ref3.time,
105
- type = _ref3.type,
106
- mutationData = _ref3.mutationData;
230
+ _defineProperty(this, "handleIntersectionEntry", function (_ref4) {
231
+ var target = _ref4.target,
232
+ rect = _ref4.rect,
233
+ time = _ref4.time,
234
+ type = _ref4.type,
235
+ mutationData = _ref4.mutationData;
107
236
  if (!target) {
108
237
  return;
109
238
  }
@@ -121,12 +250,12 @@ var ViewportObserver = /*#__PURE__*/function () {
121
250
  });
122
251
  });
123
252
  _defineProperty(this, "handleChildListMutation", /*#__PURE__*/function () {
124
- var _ref5 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(_ref4) {
253
+ var _ref6 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(_ref5) {
125
254
  var target, addedNodes, removedNodes, timestamp, removedNodeRects, targetNode, _iterator, _step, _loop;
126
255
  return _regeneratorRuntime.wrap(function _callee$(_context2) {
127
256
  while (1) switch (_context2.prev = _context2.next) {
128
257
  case 0:
129
- target = _ref4.target, addedNodes = _ref4.addedNodes, removedNodes = _ref4.removedNodes, timestamp = _ref4.timestamp;
258
+ target = _ref5.target, addedNodes = _ref5.addedNodes, removedNodes = _ref5.removedNodes, timestamp = _ref5.timestamp;
130
259
  removedNodeRects = removedNodes.map(function (ref) {
131
260
  var n = ref.deref();
132
261
  if (!n) {
@@ -161,7 +290,7 @@ var ViewportObserver = /*#__PURE__*/function () {
161
290
  _checkWithinComponent = checkWithinComponent(addedNode, 'UFOThirdPartySegment', _this.mapIs3pResult), isWithinThirdPartySegment = _checkWithinComponent.isWithin;
162
291
  isWithinSmartAnswersSegment = Boolean(_this.shouldCheckSmartAnswersMutations() && isContainedWithinSmartAnswers(addedNode));
163
292
  isTargetReactRoot = targetNode === ((_this$getSSRState = _this.getSSRState) === null || _this$getSSRState === void 0 || (_this$getSSRState = _this$getSSRState.call(_this)) === null || _this$getSSRState === void 0 ? void 0 : _this$getSSRState.reactRootElement);
164
- (_this$intersectionObs = _this.intersectionObserver) === null || _this$intersectionObs === void 0 || _this$intersectionObs.watchAndTag(addedNode, createElementMutationsWatcher(removedNodeRects, isWithinThirdPartySegment, isWithinSmartAnswersSegment, !!hasSameDeletedNode, timestamp, isTargetReactRoot, _this.getSSRState, _this.getSSRPlaceholderHandler));
293
+ (_this$intersectionObs = _this.intersectionObserver) === null || _this$intersectionObs === void 0 || _this$intersectionObs.watchAndTag(addedNode, (_this.trackLayoutShiftOffenders ? createElementMutationsWatcherNew : createElementMutationsWatcher)(removedNodeRects, isWithinThirdPartySegment, isWithinSmartAnswersSegment, !!hasSameDeletedNode, timestamp, isTargetReactRoot, _this.getSSRState, _this.getSSRPlaceholderHandler));
165
294
  case 9:
166
295
  case "end":
167
296
  return _context.stop();
@@ -202,25 +331,27 @@ var ViewportObserver = /*#__PURE__*/function () {
202
331
  }, _callee, null, [[4, 15, 18, 21]]);
203
332
  }));
204
333
  return function (_x) {
205
- return _ref5.apply(this, arguments);
334
+ return _ref6.apply(this, arguments);
206
335
  };
207
336
  }());
208
- _defineProperty(this, "handleAttributeMutation", function (_ref6) {
337
+ _defineProperty(this, "handleAttributeMutation", function (_ref7) {
209
338
  var _this$intersectionObs2;
210
- var target = _ref6.target,
211
- attributeName = _ref6.attributeName,
212
- oldValue = _ref6.oldValue,
213
- newValue = _ref6.newValue;
214
- (_this$intersectionObs2 = _this.intersectionObserver) === null || _this$intersectionObs2 === void 0 || _this$intersectionObs2.watchAndTag(target, function (_ref7) {
215
- var target = _ref7.target,
216
- rect = _ref7.rect;
339
+ var target = _ref7.target,
340
+ attributeName = _ref7.attributeName,
341
+ oldValue = _ref7.oldValue,
342
+ newValue = _ref7.newValue,
343
+ timestamp = _ref7.timestamp;
344
+ (_this$intersectionObs2 = _this.intersectionObserver) === null || _this$intersectionObs2 === void 0 || _this$intersectionObs2.watchAndTag(target, function (_ref8) {
345
+ var target = _ref8.target,
346
+ rect = _ref8.rect;
217
347
  if (isContainedWithinMediaWrapper(target)) {
218
348
  return {
219
349
  type: 'mutation:media',
220
350
  mutationData: {
221
351
  attributeName: attributeName,
222
352
  oldValue: oldValue,
223
- newValue: newValue
353
+ newValue: newValue,
354
+ timestamp: timestamp
224
355
  }
225
356
  };
226
357
  }
@@ -233,7 +364,8 @@ var ViewportObserver = /*#__PURE__*/function () {
233
364
  mutationData: {
234
365
  attributeName: attributeName,
235
366
  oldValue: oldValue,
236
- newValue: newValue
367
+ newValue: newValue,
368
+ timestamp: timestamp
237
369
  }
238
370
  };
239
371
  }
@@ -259,7 +391,8 @@ var ViewportObserver = /*#__PURE__*/function () {
259
391
  mutationData: {
260
392
  attributeName: attributeName,
261
393
  oldValue: oldValue,
262
- newValue: newValue
394
+ newValue: newValue,
395
+ timestamp: timestamp
263
396
  }
264
397
  };
265
398
  }
@@ -273,7 +406,8 @@ var ViewportObserver = /*#__PURE__*/function () {
273
406
  mutationData: {
274
407
  attributeName: attributeName,
275
408
  oldValue: oldValue,
276
- newValue: newValue
409
+ newValue: newValue,
410
+ timestamp: timestamp
277
411
  }
278
412
  };
279
413
  }
@@ -288,7 +422,8 @@ var ViewportObserver = /*#__PURE__*/function () {
288
422
  mutationData: {
289
423
  attributeName: attributeName,
290
424
  oldValue: oldValue,
291
- newValue: newValue
425
+ newValue: newValue,
426
+ timestamp: timestamp
292
427
  }
293
428
  };
294
429
  }
@@ -299,7 +434,8 @@ var ViewportObserver = /*#__PURE__*/function () {
299
434
  mutationData: {
300
435
  attributeName: attributeName,
301
436
  oldValue: oldValue,
302
- newValue: newValue
437
+ newValue: newValue,
438
+ timestamp: timestamp
303
439
  }
304
440
  };
305
441
  }
@@ -310,7 +446,8 @@ var ViewportObserver = /*#__PURE__*/function () {
310
446
  mutationData: {
311
447
  attributeName: attributeName,
312
448
  oldValue: oldValue,
313
- newValue: newValue
449
+ newValue: newValue,
450
+ timestamp: timestamp
314
451
  }
315
452
  };
316
453
  }
@@ -319,14 +456,15 @@ var ViewportObserver = /*#__PURE__*/function () {
319
456
  mutationData: {
320
457
  attributeName: attributeName,
321
458
  oldValue: oldValue,
322
- newValue: newValue
459
+ newValue: newValue,
460
+ timestamp: timestamp
323
461
  }
324
462
  };
325
463
  });
326
464
  });
327
- _defineProperty(this, "handleLayoutShift", function (_ref8) {
328
- var time = _ref8.time,
329
- changedRects = _ref8.changedRects;
465
+ _defineProperty(this, "handleLayoutShift", function (_ref9) {
466
+ var time = _ref9.time,
467
+ changedRects = _ref9.changedRects;
330
468
  var _iterator2 = _createForOfIteratorHelper(changedRects),
331
469
  _step2;
332
470
  try {
@@ -362,6 +500,7 @@ var ViewportObserver = /*#__PURE__*/function () {
362
500
  this.intersectionObserver = null;
363
501
  this.mutationObserver = null;
364
502
  this.performanceObserver = null;
503
+ this.trackLayoutShiftOffenders = trackLayoutShiftOffenders;
365
504
 
366
505
  // Initialize SSR context functions
367
506
  this.getSSRState = getSSRState;
@@ -30,7 +30,7 @@ export function createIntersectionObserver(_ref) {
30
30
  }
31
31
 
32
32
  // override as display-contents mutation
33
- if (tagOrCallbackResult && typeof tagOrCallbackResult !== 'string' && tagOrCallbackResult.type === 'mutation:attribute') {
33
+ if (tagOrCallbackResult && typeof tagOrCallbackResult !== 'string' && tagOrCallbackResult.type === 'mutation:attribute' && 'oldValue' in tagOrCallbackResult.mutationData) {
34
34
  var _tagOrCallbackResult$ = tagOrCallbackResult.mutationData,
35
35
  attributeName = _tagOrCallbackResult$.attributeName,
36
36
  oldValue = _tagOrCallbackResult$.oldValue,
@@ -53,7 +53,8 @@ function createMutationObserver(_ref) {
53
53
  target: mut.target,
54
54
  attributeName: (_mut$attributeName = mut.attributeName) !== null && _mut$attributeName !== void 0 ? _mut$attributeName : 'unknown',
55
55
  oldValue: oldValue,
56
- newValue: newValue
56
+ newValue: newValue,
57
+ timestamp: mut.timestamp || performance.now()
57
58
  });
58
59
  }
59
60
  return 0; // continue
@@ -1,4 +1,5 @@
1
1
  import { type createPayloads } from '../create-payload';
2
+ import type { HiddenTimingItem } from '../hidden-timing';
2
3
  import { type LabelStack } from '../interaction-context';
3
4
  import { type VCObserver } from '../vc/vc-observer';
4
5
  import type { AbortReasonType, ApdexType, HoldActive, InteractionError, InteractionType, MinorInteraction, SegmentInfo } from './common/types';
@@ -120,6 +121,7 @@ export type ReactUFOPayload = {
120
121
  'ufo:wasPageHiddenBeforeInit'?: boolean;
121
122
  'ufo:isOpenedInBackground'?: boolean;
122
123
  'ufo:isTabThrottled'?: boolean;
124
+ 'ufo:pageVisibilityTimeline'?: HiddenTimingItem[];
123
125
  interactionMetrics: {
124
126
  namePrefix: string;
125
127
  segmentPrefix: string;
@@ -118,6 +118,7 @@ export type CalculateTTVCResult = {
118
118
  vcDetails: RevisionPayloadVCDetails;
119
119
  ssrRatio: number;
120
120
  speedIndex: number;
121
+ VC90layoutShiftInsights: LayoutShiftInsights;
121
122
  };
122
123
  export type RawObservation = {
123
124
  t: number;
@@ -130,8 +131,31 @@ export type RawEventObservation = {
130
131
  t: number;
131
132
  evt: number;
132
133
  };
134
+ export type LayoutShiftInsightsPayload = {
135
+ impact: number;
136
+ sources: number;
137
+ same: {
138
+ dir: boolean;
139
+ dist: boolean;
140
+ };
141
+ total_mut: number;
142
+ mut: Array<{
143
+ e: string;
144
+ size: number;
145
+ attr: {
146
+ t_before: boolean;
147
+ p_above: 'all' | 'some' | 'none';
148
+ p_left: 'all' | 'some' | 'none';
149
+ p_right: 'all' | 'some' | 'none';
150
+ p_h_overlap: 'all' | 'some' | 'none';
151
+ p_v_overlap: 'all' | 'some' | 'none';
152
+ p_same_offset: 'all' | 'some' | 'none';
153
+ };
154
+ }>;
155
+ };
133
156
  export type RevisionPayloadEntry = {
134
157
  'metric:vc90': number | null;
158
+ 'vc90:ls'?: LayoutShiftInsightsPayload;
135
159
  revision: string;
136
160
  clean: boolean;
137
161
  vcDetails?: RevisionPayloadVCDetails;
@@ -157,4 +181,35 @@ export type RevisionPayloadEntry = {
157
181
  rawVCTime?: number;
158
182
  };
159
183
  export type RevisionPayload = RevisionPayloadEntry[];
184
+ export type LayoutShiftVariables = {
185
+ allHaveRects: false;
186
+ allMovedSameWay: false;
187
+ allMovedSameAmount: false;
188
+ } | {
189
+ allHaveRects: true;
190
+ allMovedSameWay: boolean;
191
+ allMovedSameAmount: boolean;
192
+ deltaX: number;
193
+ deltaY: number;
194
+ };
195
+ export type LayoutShiftOffenderMatchState = 'all' | 'some' | 'none';
196
+ export type LayoutShiftOffender = {
197
+ offender: string;
198
+ happenedBefore: boolean;
199
+ distanceToLS: number;
200
+ isAbove: LayoutShiftOffenderMatchState;
201
+ isLeft: LayoutShiftOffenderMatchState;
202
+ isRight: LayoutShiftOffenderMatchState;
203
+ hasHorizontalOverlap: LayoutShiftOffenderMatchState;
204
+ hasVerticalOverlap: LayoutShiftOffenderMatchState;
205
+ matchesLayoutShiftDelta: boolean;
206
+ };
207
+ export type LayoutShiftInsights = {
208
+ layoutShiftOffendersResult: {
209
+ layoutShiftVariables: LayoutShiftVariables;
210
+ layoutShiftOffenders: LayoutShiftOffender[];
211
+ };
212
+ layoutShiftEntriesCount: number;
213
+ layoutShiftImpact: number;
214
+ } | null;
160
215
  export {};
@@ -135,6 +135,7 @@ export type Config = {
135
135
  all: readonly TTVCRevision[];
136
136
  byExperience?: Record<string, readonly TTVCRevision[]>;
137
137
  };
138
+ readonly trackLayoutShiftOffenders?: boolean;
138
139
  };
139
140
  readonly postInteractionLog?: {
140
141
  readonly enabled?: boolean;
@@ -51,3 +51,12 @@ export declare function getThrottleMeasurements(startTime: number, endTime: numb
51
51
  * @param measurement - The throttle measurement to inject
52
52
  */
53
53
  export declare function __injectThrottleMeasurementForTesting(measurement: ThrottleMeasurement): void;
54
+ /**
55
+ * Returns the page visibility timeline entries within the specified time window.
56
+ * Each entry contains the time (relative to startTime) and whether the page was hidden.
57
+ *
58
+ * @param startTime - The start timestamp of the window (DOMHighResTimeStamp)
59
+ * @param endTime - The end timestamp of the window (DOMHighResTimeStamp)
60
+ * @returns Array of HiddenTimingItem entries within the time window, with times relative to startTime
61
+ */
62
+ export declare function getPageVisibilityTimeline(startTime: number, endTime: number): HiddenTimingItem[];
@@ -19,6 +19,7 @@ export type GetVCResultType = {
19
19
  interactionAbortReason?: AbortReasonType;
20
20
  includeRawData?: boolean;
21
21
  rawDataStopTime?: number;
22
+ reportLayoutShiftOffenders?: boolean;
22
23
  };
23
24
  export type SelectorConfig = {
24
25
  id: boolean;
@@ -48,6 +49,7 @@ export type VCObserverOptions = {
48
49
  isPostInteraction?: boolean;
49
50
  ssrEnablePageLayoutPlaceholder?: boolean;
50
51
  ssrPlaceholderHandler?: any;
52
+ trackLayoutShiftOffenders?: boolean;
51
53
  searchPageConfig?: SearchPageConfig;
52
54
  };
53
55
  export interface VCObserverInterface {
@@ -10,6 +10,7 @@ export type VCObserverNewConfig = {
10
10
  enablePageLayoutPlaceholder?: boolean;
11
11
  };
12
12
  ssrPlaceholderHandler?: SSRPlaceholderHandlers | null;
13
+ trackLayoutShiftOffenders?: boolean;
13
14
  searchPageConfig?: SearchPageConfig;
14
15
  };
15
16
  declare const SSRState: {
@@ -31,6 +32,7 @@ export default class VCObserverNew {
31
32
  private windowEventObserver;
32
33
  private entriesTimeline;
33
34
  private isPostInteraction;
35
+ private trackLayoutShiftOffenders;
34
36
  private ssrPlaceholderHandler;
35
37
  private ssr;
36
38
  constructor(config: VCObserverNewConfig);
@@ -25,5 +25,5 @@ export default abstract class AbstractVCCalculatorBase implements VCCalculator {
25
25
  private calculateRatios;
26
26
  private getLabelStacks;
27
27
  private calculateWithDebugInfo;
28
- calculate({ startTime, stopTime, orderedEntries, interactionId, isPostInteraction, include3p, excludeSmartAnswersInSearch, includeSSRRatio, interactionType, isPageVisible, interactionAbortReason, }: VCCalculatorParam): Promise<RevisionPayloadEntry | undefined>;
28
+ calculate({ startTime, stopTime, orderedEntries, interactionId, isPostInteraction, include3p, excludeSmartAnswersInSearch, includeSSRRatio, interactionType, isPageVisible, interactionAbortReason, reportLayoutShiftOffenders, }: VCCalculatorParam): Promise<RevisionPayloadEntry | undefined>;
29
29
  }
@@ -13,6 +13,7 @@ export type VCCalculatorParam = {
13
13
  interactionType: InteractionType;
14
14
  isPageVisible: boolean;
15
15
  interactionAbortReason?: AbortReasonType;
16
+ reportLayoutShiftOffenders?: boolean;
16
17
  };
17
18
  export interface VCCalculator {
18
19
  calculate(param: VCCalculatorParam): Promise<RevisionPayloadEntry | undefined>;