@atlaskit/react-ufo 5.2.8 → 5.2.10

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/AGENTS.md +118 -73
  2. package/CHANGELOG.md +14 -0
  3. package/dist/cjs/experience-trace-id-context/index.js +3 -3
  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/experience-trace-id-context/index.js +3 -3
  14. package/dist/es2019/interaction-metrics/index.js +1 -0
  15. package/dist/es2019/interaction-metrics-init/index.js +2 -1
  16. package/dist/es2019/vc/index.js +4 -2
  17. package/dist/es2019/vc/vc-observer-new/index.js +10 -5
  18. package/dist/es2019/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +61 -7
  19. package/dist/es2019/vc/vc-observer-new/metric-calculator/utils/detect-layout-shift-cause.js +138 -0
  20. package/dist/es2019/vc/vc-observer-new/metric-calculator/vc-next/index.js +1 -1
  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/experience-trace-id-context/index.js +3 -3
  25. package/dist/esm/interaction-metrics/index.js +1 -0
  26. package/dist/esm/interaction-metrics-init/index.js +2 -1
  27. package/dist/esm/vc/index.js +4 -2
  28. package/dist/esm/vc/vc-observer-new/index.js +10 -4
  29. package/dist/esm/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +84 -29
  30. package/dist/esm/vc/vc-observer-new/metric-calculator/utils/detect-layout-shift-cause.js +158 -0
  31. package/dist/esm/vc/vc-observer-new/metric-calculator/vc-next/index.js +1 -1
  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/vc/types.d.ts +55 -0
  36. package/dist/types/config/index.d.ts +1 -0
  37. package/dist/types/create-payload/utils/get-visibility-state-from-performance.d.ts +1 -1
  38. package/dist/types/vc/types.d.ts +2 -0
  39. package/dist/types/vc/vc-observer-new/index.d.ts +2 -0
  40. package/dist/types/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +1 -1
  41. package/dist/types/vc/vc-observer-new/metric-calculator/types.d.ts +1 -0
  42. package/dist/types/vc/vc-observer-new/metric-calculator/utils/detect-layout-shift-cause.d.ts +31 -0
  43. package/dist/types/vc/vc-observer-new/metric-calculator/vc-next/index.d.ts +2 -2
  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/vc/types.d.ts +55 -0
  49. package/dist/types-ts4.5/config/index.d.ts +1 -0
  50. package/dist/types-ts4.5/create-payload/utils/get-visibility-state-from-performance.d.ts +1 -1
  51. package/dist/types-ts4.5/vc/types.d.ts +2 -0
  52. package/dist/types-ts4.5/vc/vc-observer-new/index.d.ts +2 -0
  53. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +1 -1
  54. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/types.d.ts +1 -0
  55. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/utils/detect-layout-shift-cause.d.ts +31 -0
  56. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/vc-next/index.d.ts +2 -2
  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 +1 -1
@@ -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
@@ -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;
@@ -1,2 +1,2 @@
1
- import type { PageVisibility } from "../../common/react-ufo-payload-schema";
1
+ import type { PageVisibility } from '../../common/react-ufo-payload-schema';
2
2
  export declare function getVisibilityStateFromPerformance(stop: number): PageVisibility | null;
@@ -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>;
@@ -0,0 +1,31 @@
1
+ import type { LayoutShiftOffender, LayoutShiftVariables } from '../../../../common/vc/types';
2
+ import type { VCObserverEntry, ViewportEntryData } from '../../types';
3
+ type DetectLayoutShiftCauseParams = {
4
+ /**
5
+ * All viewport observer entries, used to find potential offenders close to the layout-shift timestamp.
6
+ */
7
+ viewportEntries: ReadonlyArray<VCObserverEntry & {
8
+ data: ViewportEntryData;
9
+ }>;
10
+ /**
11
+ * The layout-shift entries that contributed to a VC checkpoint.
12
+ */
13
+ layoutShiftEntries: ReadonlyArray<ViewportEntryData>;
14
+ /**
15
+ * Timestamp of the VC checkpoint entry.
16
+ */
17
+ time: number;
18
+ /**
19
+ * Start time of the interaction; used to classify whether an offender happened before the VC checkpoint.
20
+ */
21
+ startTime: number;
22
+ /**
23
+ * Window around the checkpoint timestamp used to consider "nearby" offenders.
24
+ */
25
+ offenderWindowMs?: number;
26
+ };
27
+ export declare const detectLayoutShiftCause: ({ viewportEntries, layoutShiftEntries, time, startTime, offenderWindowMs, }: DetectLayoutShiftCauseParams) => {
28
+ layoutShiftVariables: LayoutShiftVariables;
29
+ layoutShiftOffenders: LayoutShiftOffender[];
30
+ };
31
+ export {};
@@ -1,5 +1,5 @@
1
- import type { VCObserverEntry } from "../../types";
2
- import VCCalculator_FY26_04 from "../fy26_04";
1
+ import type { VCObserverEntry } from '../../types';
2
+ import VCCalculator_FY26_04 from '../fy26_04';
3
3
  export default class VCCalculator_Next extends VCCalculator_FY26_04 {
4
4
  constructor();
5
5
  protected isEntryIncluded(entry: VCObserverEntry, include3p?: boolean, excludeSmartAnswersInSearch?: boolean): boolean;
@@ -11,6 +11,7 @@ export type ViewportEntryData = {
11
11
  readonly oldValue?: string | null | undefined;
12
12
  readonly newValue?: string | null | undefined;
13
13
  readonly labelStacks?: VCObserverLabelStacks;
14
+ readonly originalMutationTimestamp?: DOMHighResTimeStamp;
14
15
  };
15
16
  export type VCObserverLabelStacks = {
16
17
  segment: string;
@@ -39,4 +40,5 @@ export type VCObserverGetVCResultParam = {
39
40
  includeRawData?: boolean;
40
41
  includeSSRInV3?: boolean;
41
42
  rawDataStopTime?: number;
43
+ reportLayoutShiftOffenders?: boolean;
42
44
  };
@@ -13,6 +13,7 @@ export type ViewPortObserverConstructorArgs = {
13
13
  }): void;
14
14
  getSSRState?: () => any;
15
15
  getSSRPlaceholderHandler?: () => any;
16
+ trackLayoutShiftOffenders?: boolean;
16
17
  searchPageConfig?: SearchPageConfig;
17
18
  };
18
19
  export default class ViewportObserver {
@@ -23,10 +24,11 @@ export default class ViewportObserver {
23
24
  private mapIs3pResult;
24
25
  private onChange;
25
26
  private isStarted;
27
+ private trackLayoutShiftOffenders;
26
28
  private searchPageConfig;
27
29
  private getSSRState?;
28
30
  private getSSRPlaceholderHandler?;
29
- constructor({ onChange, getSSRState, getSSRPlaceholderHandler, searchPageConfig, }: ViewPortObserverConstructorArgs);
31
+ constructor({ onChange, getSSRState, getSSRPlaceholderHandler, trackLayoutShiftOffenders, searchPageConfig, }: ViewPortObserverConstructorArgs);
30
32
  private handleIntersectionEntry;
31
33
  private handleChildListMutation;
32
34
  private handleAttributeMutation;
@@ -4,6 +4,7 @@ export type CreateMutationObserverProps = {
4
4
  attributeName: string;
5
5
  oldValue?: string | undefined | null;
6
6
  newValue?: string | undefined | null;
7
+ timestamp: DOMHighResTimeStamp;
7
8
  }) => void;
8
9
  onMutationFinished?: (props: {
9
10
  targets: Array<HTMLElement>;
@@ -1,7 +1,10 @@
1
1
  export type MutationRecordWithTimestamp = MutationRecord & {
2
2
  timestamp?: number;
3
3
  };
4
- export type AttributeMutationData = {
4
+ export type MutationDataWithTimestamp = {
5
+ timestamp?: DOMHighResTimeStamp;
6
+ };
7
+ export type AttributeMutationData = MutationDataWithTimestamp & {
5
8
  attributeName: string;
6
9
  oldValue?: string | undefined | null;
7
10
  newValue?: string | undefined | null;
@@ -9,4 +12,4 @@ export type AttributeMutationData = {
9
12
  /**
10
13
  * Add here when there are more type of mutation data
11
14
  */
12
- export type MutationData = AttributeMutationData;
15
+ export type MutationData = AttributeMutationData | MutationDataWithTimestamp;
@@ -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;
@@ -135,8 +136,31 @@ export type RawEventObservation = {
135
136
  t: number;
136
137
  evt: number;
137
138
  };
139
+ export type LayoutShiftInsightsPayload = {
140
+ impact: number;
141
+ sources: number;
142
+ same: {
143
+ dir: boolean;
144
+ dist: boolean;
145
+ };
146
+ total_mut: number;
147
+ mut: Array<{
148
+ e: string;
149
+ size: number;
150
+ attr: {
151
+ t_before: boolean;
152
+ p_above: 'all' | 'some' | 'none';
153
+ p_left: 'all' | 'some' | 'none';
154
+ p_right: 'all' | 'some' | 'none';
155
+ p_h_overlap: 'all' | 'some' | 'none';
156
+ p_v_overlap: 'all' | 'some' | 'none';
157
+ p_same_offset: 'all' | 'some' | 'none';
158
+ };
159
+ }>;
160
+ };
138
161
  export type RevisionPayloadEntry = {
139
162
  'metric:vc90': number | null;
163
+ 'vc90:ls'?: LayoutShiftInsightsPayload;
140
164
  revision: string;
141
165
  clean: boolean;
142
166
  vcDetails?: RevisionPayloadVCDetails;
@@ -162,4 +186,35 @@ export type RevisionPayloadEntry = {
162
186
  rawVCTime?: number;
163
187
  };
164
188
  export type RevisionPayload = RevisionPayloadEntry[];
189
+ export type LayoutShiftVariables = {
190
+ allHaveRects: false;
191
+ allMovedSameWay: false;
192
+ allMovedSameAmount: false;
193
+ } | {
194
+ allHaveRects: true;
195
+ allMovedSameWay: boolean;
196
+ allMovedSameAmount: boolean;
197
+ deltaX: number;
198
+ deltaY: number;
199
+ };
200
+ export type LayoutShiftOffenderMatchState = 'all' | 'some' | 'none';
201
+ export type LayoutShiftOffender = {
202
+ offender: string;
203
+ happenedBefore: boolean;
204
+ distanceToLS: number;
205
+ isAbove: LayoutShiftOffenderMatchState;
206
+ isLeft: LayoutShiftOffenderMatchState;
207
+ isRight: LayoutShiftOffenderMatchState;
208
+ hasHorizontalOverlap: LayoutShiftOffenderMatchState;
209
+ hasVerticalOverlap: LayoutShiftOffenderMatchState;
210
+ matchesLayoutShiftDelta: boolean;
211
+ };
212
+ export type LayoutShiftInsights = {
213
+ layoutShiftOffendersResult: {
214
+ layoutShiftVariables: LayoutShiftVariables;
215
+ layoutShiftOffenders: LayoutShiftOffender[];
216
+ };
217
+ layoutShiftEntriesCount: number;
218
+ layoutShiftImpact: number;
219
+ } | null;
165
220
  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;
@@ -1,2 +1,2 @@
1
- import type { PageVisibility } from "../../common/react-ufo-payload-schema";
1
+ import type { PageVisibility } from '../../common/react-ufo-payload-schema';
2
2
  export declare function getVisibilityStateFromPerformance(stop: number): PageVisibility | null;