@atlaskit/react-ufo 4.7.6 → 4.9.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 (65) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cjs/create-extra-search-page-interaction-payload/index.js +50 -0
  3. package/dist/cjs/create-interaction-extra-metrics-payload/index.js +1 -1
  4. package/dist/cjs/create-payload/index.js +63 -0
  5. package/dist/cjs/create-payload/utils/get-vc-metrics.js +15 -12
  6. package/dist/cjs/interaction-metrics/index.js +15 -7
  7. package/dist/cjs/interaction-metrics-init/index.js +29 -2
  8. package/dist/cjs/vc/index.js +5 -4
  9. package/dist/cjs/vc/vc-observer-new/index.js +28 -4
  10. package/dist/cjs/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +8 -8
  11. package/dist/cjs/vc/vc-observer-new/metric-calculator/fy25_03/index.js +7 -3
  12. package/dist/cjs/vc/vc-observer-new/metric-calculator/utils/is-entry-smart-answers-in-search.js +27 -0
  13. package/dist/cjs/vc/vc-observer-new/metric-calculator/vcnext/index.js +41 -0
  14. package/dist/cjs/vc/vc-observer-new/viewport-observer/index.js +46 -26
  15. package/dist/es2019/create-extra-search-page-interaction-payload/index.js +32 -0
  16. package/dist/es2019/create-interaction-extra-metrics-payload/index.js +1 -1
  17. package/dist/es2019/create-payload/index.js +49 -0
  18. package/dist/es2019/create-payload/utils/get-vc-metrics.js +3 -2
  19. package/dist/es2019/interaction-metrics/index.js +15 -7
  20. package/dist/es2019/interaction-metrics-init/index.js +29 -2
  21. package/dist/es2019/vc/index.js +5 -3
  22. package/dist/es2019/vc/vc-observer-new/index.js +21 -2
  23. package/dist/es2019/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +6 -5
  24. package/dist/es2019/vc/vc-observer-new/metric-calculator/fy25_03/index.js +7 -3
  25. package/dist/es2019/vc/vc-observer-new/metric-calculator/utils/is-entry-smart-answers-in-search.js +22 -0
  26. package/dist/es2019/vc/vc-observer-new/metric-calculator/vcnext/index.js +21 -0
  27. package/dist/es2019/vc/vc-observer-new/viewport-observer/index.js +29 -14
  28. package/dist/esm/create-extra-search-page-interaction-payload/index.js +43 -0
  29. package/dist/esm/create-interaction-extra-metrics-payload/index.js +1 -1
  30. package/dist/esm/create-payload/index.js +62 -0
  31. package/dist/esm/create-payload/utils/get-vc-metrics.js +15 -12
  32. package/dist/esm/interaction-metrics/index.js +15 -7
  33. package/dist/esm/interaction-metrics-init/index.js +29 -2
  34. package/dist/esm/vc/index.js +5 -4
  35. package/dist/esm/vc/vc-observer-new/index.js +28 -4
  36. package/dist/esm/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +8 -8
  37. package/dist/esm/vc/vc-observer-new/metric-calculator/fy25_03/index.js +7 -3
  38. package/dist/esm/vc/vc-observer-new/metric-calculator/utils/is-entry-smart-answers-in-search.js +21 -0
  39. package/dist/esm/vc/vc-observer-new/metric-calculator/vcnext/index.js +36 -0
  40. package/dist/esm/vc/vc-observer-new/viewport-observer/index.js +46 -26
  41. package/dist/types/config/index.d.ts +8 -1
  42. package/dist/types/create-extra-search-page-interaction-payload/index.d.ts +3 -0
  43. package/dist/types/create-interaction-extra-metrics-payload/index.d.ts +1 -1
  44. package/dist/types/create-payload/index.d.ts +25434 -0
  45. package/dist/types/create-payload/utils/get-vc-metrics.d.ts +1 -1
  46. package/dist/types/vc/types.d.ts +1 -0
  47. package/dist/types/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +2 -2
  48. package/dist/types/vc/vc-observer-new/metric-calculator/fy25_03/index.d.ts +2 -2
  49. package/dist/types/vc/vc-observer-new/metric-calculator/types.d.ts +1 -0
  50. package/dist/types/vc/vc-observer-new/metric-calculator/utils/is-entry-smart-answers-in-search.d.ts +2 -0
  51. package/dist/types/vc/vc-observer-new/metric-calculator/vcnext/index.d.ts +6 -0
  52. package/dist/types/vc/vc-observer-new/types.d.ts +2 -1
  53. package/dist/types-ts4.5/config/index.d.ts +8 -1
  54. package/dist/types-ts4.5/create-extra-search-page-interaction-payload/index.d.ts +3 -0
  55. package/dist/types-ts4.5/create-interaction-extra-metrics-payload/index.d.ts +1 -1
  56. package/dist/types-ts4.5/create-payload/index.d.ts +25434 -0
  57. package/dist/types-ts4.5/create-payload/utils/get-vc-metrics.d.ts +1 -1
  58. package/dist/types-ts4.5/vc/types.d.ts +1 -0
  59. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +2 -2
  60. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/fy25_03/index.d.ts +2 -2
  61. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/types.d.ts +1 -0
  62. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/utils/is-entry-smart-answers-in-search.d.ts +2 -0
  63. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/vcnext/index.d.ts +6 -0
  64. package/dist/types-ts4.5/vc/vc-observer-new/types.d.ts +2 -1
  65. package/package.json +7 -1
@@ -126,7 +126,7 @@ var ViewportObserver = exports.default = /*#__PURE__*/function () {
126
126
  _iterator = _createForOfIteratorHelper(addedNodes);
127
127
  _context2.prev = 4;
128
128
  _loop = /*#__PURE__*/_regenerator.default.mark(function _loop() {
129
- var addedNodeRef, addedNode, ssrState, SSRStateEnum, _this$intersectionObs, _this$intersectionObs2, ssrPlaceholderHandler, _this$intersectionObs3, result, _this$intersectionObs4, sameDeletedNode, isInIgnoreLsMarker, _this$intersectionObs5, _this$intersectionObs6, _checkWithinComponent, isWithinThirdPartySegment, _this$intersectionObs7, validTargets, _iterator2, _step2, _this$intersectionObs8, validTarget, _this$intersectionObs9;
129
+ var addedNodeRef, addedNode, ssrState, SSRStateEnum, _this$intersectionObs, _this$intersectionObs2, ssrPlaceholderHandler, _this$intersectionObs3, result, _this$intersectionObs4, sameDeletedNode, isInIgnoreLsMarker, _this$intersectionObs5, _this$intersectionObs6, _checkWithinComponent, isWithinThirdPartySegment, _this$intersectionObs7, _window, _iterator2, _step2, child, _this$intersectionObs8, _this$intersectionObs9, validTargets, _iterator3, _step3, _this$intersectionObs0, validTarget, _this$intersectionObs1;
130
130
  return _regenerator.default.wrap(function _loop$(_context) {
131
131
  while (1) switch (_context.prev = _context.next) {
132
132
  case 0:
@@ -238,25 +238,45 @@ var ViewportObserver = exports.default = /*#__PURE__*/function () {
238
238
  (_this$intersectionObs7 = _this.intersectionObserver) === null || _this$intersectionObs7 === void 0 || _this$intersectionObs7.watchAndTag(addedNode, 'mutation:third-party-element');
239
239
  return _context.abrupt("return", 0);
240
240
  case 45:
241
- if ((0, _platformFeatureFlags.fg)('platform_ufo_display_content_resolution_ttvc_v3')) {
242
- // Check if the target has display:content css property, return array of valid targets
243
- validTargets = (0, _checkDisplayContent.default)(addedNode);
244
- _iterator2 = _createForOfIteratorHelper(validTargets);
245
- try {
246
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
247
- validTarget = _step2.value;
248
- (_this$intersectionObs8 = _this.intersectionObserver) === null || _this$intersectionObs8 === void 0 || _this$intersectionObs8.watchAndTag(validTarget, createElementMutationsWatcher(removedNodeRects));
241
+ if ((0, _platformFeatureFlags.fg)('platform_ufo_vcnext_v4_enabled')) {
242
+ if (((_window = window) === null || _window === void 0 || (_window = _window.getComputedStyle(addedNode)) === null || _window === void 0 ? void 0 : _window.display) === 'contents') {
243
+ _iterator2 = _createForOfIteratorHelper(addedNode.children);
244
+ try {
245
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
246
+ child = _step2.value;
247
+ if (child instanceof HTMLElement) {
248
+ (_this$intersectionObs8 = _this.intersectionObserver) === null || _this$intersectionObs8 === void 0 || _this$intersectionObs8.watchAndTag(child, 'mutation:display-contents-children-element');
249
+ }
250
+ }
251
+ } catch (err) {
252
+ _iterator2.e(err);
253
+ } finally {
254
+ _iterator2.f();
249
255
  }
250
- } catch (err) {
251
- _iterator2.e(err);
252
- } finally {
253
- _iterator2.f();
256
+ } else {
257
+ (_this$intersectionObs9 = _this.intersectionObserver) === null || _this$intersectionObs9 === void 0 || _this$intersectionObs9.watchAndTag(addedNode, createElementMutationsWatcher(removedNodeRects));
254
258
  }
255
259
  } else {
256
- if ((0, _platformFeatureFlags.fg)('platform_ufo_display_content_track_occurrence')) {
257
- (0, _trackDisplayContentOccurrence.default)(addedNode);
260
+ if ((0, _platformFeatureFlags.fg)('platform_ufo_display_content_resolution_ttvc_v3')) {
261
+ // Check if the target has display:content css property, return array of valid targets
262
+ validTargets = (0, _checkDisplayContent.default)(addedNode);
263
+ _iterator3 = _createForOfIteratorHelper(validTargets);
264
+ try {
265
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
266
+ validTarget = _step3.value;
267
+ (_this$intersectionObs0 = _this.intersectionObserver) === null || _this$intersectionObs0 === void 0 || _this$intersectionObs0.watchAndTag(validTarget, createElementMutationsWatcher(removedNodeRects));
268
+ }
269
+ } catch (err) {
270
+ _iterator3.e(err);
271
+ } finally {
272
+ _iterator3.f();
273
+ }
274
+ } else {
275
+ if ((0, _platformFeatureFlags.fg)('platform_ufo_display_content_track_occurrence')) {
276
+ (0, _trackDisplayContentOccurrence.default)(addedNode);
277
+ }
278
+ (_this$intersectionObs1 = _this.intersectionObserver) === null || _this$intersectionObs1 === void 0 || _this$intersectionObs1.watchAndTag(addedNode, createElementMutationsWatcher(removedNodeRects));
258
279
  }
259
- (_this$intersectionObs9 = _this.intersectionObserver) === null || _this$intersectionObs9 === void 0 || _this$intersectionObs9.watchAndTag(addedNode, createElementMutationsWatcher(removedNodeRects));
260
280
  }
261
281
  case 46:
262
282
  case "end":
@@ -303,12 +323,12 @@ var ViewportObserver = exports.default = /*#__PURE__*/function () {
303
323
  };
304
324
  }());
305
325
  (0, _defineProperty2.default)(this, "handleAttributeMutation", function (_ref6) {
306
- var _this$intersectionObs0;
326
+ var _this$intersectionObs10;
307
327
  var target = _ref6.target,
308
328
  attributeName = _ref6.attributeName,
309
329
  oldValue = _ref6.oldValue,
310
330
  newValue = _ref6.newValue;
311
- (_this$intersectionObs0 = _this.intersectionObserver) === null || _this$intersectionObs0 === void 0 || _this$intersectionObs0.watchAndTag(target, function (_ref7) {
331
+ (_this$intersectionObs10 = _this.intersectionObserver) === null || _this$intersectionObs10 === void 0 || _this$intersectionObs10.watchAndTag(target, function (_ref7) {
312
332
  var target = _ref7.target,
313
333
  rect = _ref7.rect;
314
334
  if ((0, _vcUtils.isContainedWithinMediaWrapper)(target)) {
@@ -386,11 +406,11 @@ var ViewportObserver = exports.default = /*#__PURE__*/function () {
386
406
  (0, _defineProperty2.default)(this, "handleLayoutShift", function (_ref8) {
387
407
  var time = _ref8.time,
388
408
  changedRects = _ref8.changedRects;
389
- var _iterator3 = _createForOfIteratorHelper(changedRects),
390
- _step3;
409
+ var _iterator4 = _createForOfIteratorHelper(changedRects),
410
+ _step4;
391
411
  try {
392
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
393
- var changedRect = _step3.value;
412
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
413
+ var changedRect = _step4.value;
394
414
  var target = changedRect.node;
395
415
  if (target) {
396
416
  _this.onChange({
@@ -404,9 +424,9 @@ var ViewportObserver = exports.default = /*#__PURE__*/function () {
404
424
  }
405
425
  }
406
426
  } catch (err) {
407
- _iterator3.e(err);
427
+ _iterator4.e(err);
408
428
  } finally {
409
- _iterator3.f();
429
+ _iterator4.f();
410
430
  }
411
431
  });
412
432
  this.mapVisibleNodeRects = new WeakMap();
@@ -463,12 +483,12 @@ var ViewportObserver = exports.default = /*#__PURE__*/function () {
463
483
  }, {
464
484
  key: "stop",
465
485
  value: function stop() {
466
- var _this$mutationObserve2, _this$intersectionObs1, _this$performanceObse2;
486
+ var _this$mutationObserve2, _this$intersectionObs11, _this$performanceObse2;
467
487
  if (!this.isStarted) {
468
488
  return;
469
489
  }
470
490
  (_this$mutationObserve2 = this.mutationObserver) === null || _this$mutationObserve2 === void 0 || _this$mutationObserve2.disconnect();
471
- (_this$intersectionObs1 = this.intersectionObserver) === null || _this$intersectionObs1 === void 0 || _this$intersectionObs1.disconnect();
491
+ (_this$intersectionObs11 = this.intersectionObserver) === null || _this$intersectionObs11 === void 0 || _this$intersectionObs11.disconnect();
472
492
  (_this$performanceObse2 = this.performanceObserver) === null || _this$performanceObse2 === void 0 || _this$performanceObse2.disconnect();
473
493
  this.isStarted = false;
474
494
  // Clean up caches when stopping
@@ -0,0 +1,32 @@
1
+ const interactionBuffer = [];
2
+ let bufferInteractionData = (interactionId, data) => {
3
+ interactionBuffer.push({
4
+ interactionId,
5
+ data
6
+ });
7
+ };
8
+ function clearInteractionBuffer() {
9
+ interactionBuffer.length = 0;
10
+ }
11
+ function appendInteractionData(interactionId, data) {
12
+ bufferInteractionData(interactionId, data);
13
+ }
14
+ function installInteractionSink(handler) {
15
+ for (const {
16
+ interactionId,
17
+ data
18
+ } of interactionBuffer) {
19
+ handler(interactionId, data);
20
+ }
21
+ clearInteractionBuffer();
22
+ bufferInteractionData = handler;
23
+ }
24
+ export function sinkExtraSearchPageInteractionHandler(sinkFn) {
25
+ installInteractionSink(sinkFn);
26
+ }
27
+ export function onSearchPageInteractionComplete(interactionId, data) {
28
+ if (data.ufoName) {
29
+ appendInteractionData(interactionId, data);
30
+ clearInteractionBuffer();
31
+ }
32
+ }
@@ -57,7 +57,7 @@ async function createInteractionExtraLogPayload(interactionId, interaction) {
57
57
  const effectiveVCRevisionPayload = vcRevisionPayload === null || vcRevisionPayload === void 0 ? void 0 : vcRevisionPayload.find(({
58
58
  revision
59
59
  }) => revision === DEFAULT_TTVC_REVISION);
60
- if (!(effectiveVCRevisionPayload !== null && effectiveVCRevisionPayload !== void 0 && effectiveVCRevisionPayload.clean) || (effectiveVCRevisionPayload === null || effectiveVCRevisionPayload === void 0 ? void 0 : effectiveVCRevisionPayload['metric:vc90']) === undefined) {
60
+ if (!(effectiveVCRevisionPayload !== null && effectiveVCRevisionPayload !== void 0 && effectiveVCRevisionPayload.clean) || (effectiveVCRevisionPayload === null || effectiveVCRevisionPayload === void 0 ? void 0 : effectiveVCRevisionPayload['metric:vc90']) === undefined || typeof (effectiveVCRevisionPayload === null || effectiveVCRevisionPayload === void 0 ? void 0 : effectiveVCRevisionPayload['metric:vc90']) !== 'number' || extraTTAI === undefined || typeof extraTTAI !== 'number' || interaction.errors.length > 0) {
61
61
  return null;
62
62
  }
63
63
 
@@ -644,4 +644,53 @@ export async function createExperimentalMetricsPayload(interactionId, interactio
644
644
  }
645
645
  const result = await createInteractionMetricsPayload(interaction, interactionId, true);
646
646
  return result;
647
+ }
648
+ export async function createExtraSearchPageInteractionPayload(interactionId, interaction) {
649
+ var _newEnd;
650
+ const SAIN_HOLD_NAMES = ['search-ai-dialog-visible-text-loading', 'search-ai-dialog-all-text-loading'];
651
+ const NAME_OVERRIDE = 'search-page-ignoring-smart-answers';
652
+ const SEARCH_PAGE_SMART_ANSWERS_SEGMENT_LABEL = 'search-page-smart-answers';
653
+ const newInteractionId = `${interactionId}-ignoring-smart-answers`;
654
+
655
+ // Calculate a new end time which excludes SAIN holds
656
+ let newEnd;
657
+ const {
658
+ holdInfo,
659
+ reactProfilerTimings
660
+ } = interaction;
661
+ const lastHold = holdInfo.at(-1);
662
+ const isLastHoldSAIN = Boolean(lastHold && SAIN_HOLD_NAMES.includes(lastHold.name));
663
+
664
+ // A new end time is only calculated if the last hold is a SAIN hold
665
+ if (isLastHoldSAIN) {
666
+ let lastFilteredTime = null;
667
+ const filteredReactProfilerTimings = reactProfilerTimings.filter(timing => {
668
+ if (timing.commitTime === lastFilteredTime) {
669
+ return false;
670
+ }
671
+ const isTimingSmartAnswersInSearch = timing.labelStack.some(label => label.name === SEARCH_PAGE_SMART_ANSWERS_SEGMENT_LABEL);
672
+ if (isTimingSmartAnswersInSearch) {
673
+ lastFilteredTime = timing.commitTime;
674
+ return false;
675
+ }
676
+ return true;
677
+ });
678
+ const lastTiming = filteredReactProfilerTimings.at(-1);
679
+ if (lastTiming) {
680
+ newEnd = lastTiming.commitTime;
681
+ }
682
+ }
683
+ const modifiedInteraction = {
684
+ ...interaction,
685
+ end: (_newEnd = newEnd) !== null && _newEnd !== void 0 ? _newEnd : interaction.end,
686
+ holdInfo: [],
687
+ knownSegments: [],
688
+ reactProfilerTimings: [],
689
+ ufoName: NAME_OVERRIDE
690
+ };
691
+ const payloads = [];
692
+ const vcMetrics = await getVCMetrics(interaction, false, true);
693
+ const interactionMetricsPayload = await createInteractionMetricsPayload(modifiedInteraction, newInteractionId, undefined, undefined, vcMetrics);
694
+ payloads.push(interactionMetricsPayload);
695
+ return payloads.filter(Boolean);
647
696
  }
@@ -3,7 +3,7 @@ import { postInteractionLog } from '../../interaction-metrics';
3
3
  import getInteractionStatus from './get-interaction-status';
4
4
  import getPageVisibilityUpToTTAI from './get-page-visibility-up-to-ttai';
5
5
  import getSSRDoneTimeValue from './get-ssr-done-time-value';
6
- async function getVCMetrics(interaction, include3p = false) {
6
+ async function getVCMetrics(interaction, include3p = false, excludeSmartAnswersInSearch = false) {
7
7
  var _config$vc, _config$vc$ssrWhiteli, _interaction$apdex, _interaction$apdex$, _config$vc2, _config$vc3, _result$ufoVcRev;
8
8
  const config = getConfig();
9
9
  if (!(config !== null && config !== void 0 && (_config$vc = config.vc) !== null && _config$vc !== void 0 && _config$vc.enabled)) {
@@ -40,7 +40,8 @@ async function getVCMetrics(interaction, include3p = false) {
40
40
  interactionId: interaction.id,
41
41
  includeSSRRatio: (_config$vc3 = config.vc) === null || _config$vc3 === void 0 ? void 0 : _config$vc3.includeSSRRatio,
42
42
  ...ssr,
43
- include3p
43
+ include3p,
44
+ excludeSmartAnswersInSearch
44
45
  });
45
46
  observer.stop(interaction.ufoName);
46
47
  if (!include3p) {
@@ -3,6 +3,7 @@ import { fg } from '@atlaskit/platform-feature-flags';
3
3
  import coinflip from '../coinflip';
4
4
  import { getAwaitBM3TTIList, getCapabilityRate, getConfig, getExperimentalInteractionRate, getExtraInteractionRate, getFinishInteractionOnTransition, getInteractionTimeout, getPostInteractionRate, getReactHydrationStats } from '../config';
5
5
  import { experimentalVC, getExperimentalVCMetrics, onExperimentalInteractionComplete } from '../create-experimental-interaction-metrics-payload';
6
+ import { onSearchPageInteractionComplete } from '../create-extra-search-page-interaction-payload';
6
7
  import { sanitizeUfoName, stringifyLabelStackFully } from '../create-payload/common/utils';
7
8
  import { clearActiveTrace } from '../experience-trace-id-context';
8
9
  import { allFeatureFlagsAccessed, currentFeatureFlagsAccessed } from '../feature-flags-accessed';
@@ -759,16 +760,19 @@ export function tryComplete(interactionId, endTime) {
759
760
  if (noMoreActiveHolds && interactionExtraMetrics.finishedInteractionId !== interactionId) {
760
761
  // If it's not waiting for extra metrics to complete, finish the interaction as normal
761
762
  if (!activeSubmitted) {
762
- var _getConfig1, _getConfig1$extraInte;
763
+ var _getConfig1, _getConfig1$extraInte, _getConfig10, _getConfig10$extraSea, _getConfig11, _getConfig11$extraSea;
763
764
  finishInteraction(interactionId, interaction, endTime);
764
765
  if ((_getConfig1 = getConfig()) !== null && _getConfig1 !== void 0 && (_getConfig1$extraInte = _getConfig1.extraInteractionMetrics) !== null && _getConfig1$extraInte !== void 0 && _getConfig1$extraInte.enabled) {
765
766
  interactionExtraMetrics.updateFinishedInteractionId(interactionId);
766
767
  }
768
+ if ((_getConfig10 = getConfig()) !== null && _getConfig10 !== void 0 && (_getConfig10$extraSea = _getConfig10.extraSearchPageInteraction) !== null && _getConfig10$extraSea !== void 0 && _getConfig10$extraSea.enabled && interaction.ufoName === ((_getConfig11 = getConfig()) === null || _getConfig11 === void 0 ? void 0 : (_getConfig11$extraSea = _getConfig11.extraSearchPageInteraction) === null || _getConfig11$extraSea === void 0 ? void 0 : _getConfig11$extraSea.searchPageMetricName) && fg('react_ufo_unified_search_ignoring_sain_metric')) {
769
+ onSearchPageInteractionComplete(interactionId, interaction);
770
+ }
767
771
  activeSubmitted = true;
768
772
  }
769
773
  if (noMoreExpHolds) {
770
- var _getConfig10, _getConfig10$experime;
771
- if ((_getConfig10 = getConfig()) !== null && _getConfig10 !== void 0 && (_getConfig10$experime = _getConfig10.experimentalInteractionMetrics) !== null && _getConfig10$experime !== void 0 && _getConfig10$experime.enabled) {
774
+ var _getConfig12, _getConfig12$experime;
775
+ if ((_getConfig12 = getConfig()) !== null && _getConfig12 !== void 0 && (_getConfig12$experime = _getConfig12.experimentalInteractionMetrics) !== null && _getConfig12$experime !== void 0 && _getConfig12$experime.enabled) {
772
776
  onExperimentalInteractionComplete(interactionId, interaction, endTime);
773
777
  }
774
778
  postInteraction();
@@ -784,12 +788,16 @@ export function tryComplete(interactionId, endTime) {
784
788
  } else {
785
789
  if (noMoreActiveHolds) {
786
790
  if (!activeSubmitted) {
791
+ var _getConfig13, _getConfig13$extraSea, _getConfig14, _getConfig14$extraSea;
787
792
  finishInteraction(interactionId, interaction, endTime);
793
+ if ((_getConfig13 = getConfig()) !== null && _getConfig13 !== void 0 && (_getConfig13$extraSea = _getConfig13.extraSearchPageInteraction) !== null && _getConfig13$extraSea !== void 0 && _getConfig13$extraSea.enabled && interaction.ufoName === ((_getConfig14 = getConfig()) === null || _getConfig14 === void 0 ? void 0 : (_getConfig14$extraSea = _getConfig14.extraSearchPageInteraction) === null || _getConfig14$extraSea === void 0 ? void 0 : _getConfig14$extraSea.searchPageMetricName) && fg('react_ufo_unified_search_ignoring_sain_metric')) {
794
+ onSearchPageInteractionComplete(interactionId, interaction);
795
+ }
788
796
  activeSubmitted = true;
789
797
  }
790
798
  if (noMoreExpHolds) {
791
- var _getConfig11, _getConfig11$experime;
792
- if ((_getConfig11 = getConfig()) !== null && _getConfig11 !== void 0 && (_getConfig11$experime = _getConfig11.experimentalInteractionMetrics) !== null && _getConfig11$experime !== void 0 && _getConfig11$experime.enabled) {
799
+ var _getConfig15, _getConfig15$experime;
800
+ if ((_getConfig15 = getConfig()) !== null && _getConfig15 !== void 0 && (_getConfig15$experime = _getConfig15.experimentalInteractionMetrics) !== null && _getConfig15$experime !== void 0 && _getConfig15$experime.enabled) {
793
801
  onExperimentalInteractionComplete(interactionId, interaction, endTime);
794
802
  }
795
803
  postInteraction();
@@ -985,7 +993,7 @@ export function addNewInteraction(interactionId, ufoName, type, startTime, rate,
985
993
  addHoldByID(interactionId, [], ufoName, ufoName, true);
986
994
  }
987
995
  if (type === 'transition' || type === 'page_load') {
988
- var _getConfig12, _getConfig12$postInte, _config$extraInteract;
996
+ var _getConfig16, _getConfig16$postInte, _config$extraInteract;
989
997
  // Use per-interaction VC observer if available, otherwise fall back to global
990
998
  const observer = vcObserver;
991
999
  if (observer) {
@@ -996,7 +1004,7 @@ export function addNewInteraction(interactionId, ufoName, type, startTime, rate,
996
1004
  }
997
1005
  // Start post interaction observer for all if config is enabled
998
1006
  // in case ufoName is updated at later time
999
- if ((_getConfig12 = getConfig()) !== null && _getConfig12 !== void 0 && (_getConfig12$postInte = _getConfig12.postInteractionLog) !== null && _getConfig12$postInte !== void 0 && _getConfig12$postInte.enabled) {
1007
+ if ((_getConfig16 = getConfig()) !== null && _getConfig16 !== void 0 && (_getConfig16$postInte = _getConfig16.postInteractionLog) !== null && _getConfig16$postInte !== void 0 && _getConfig16$postInte.enabled) {
1000
1008
  postInteractionLog.startVCObserver({
1001
1009
  startTime
1002
1010
  });
@@ -2,6 +2,7 @@ import { fg } from '@atlaskit/platform-feature-flags';
2
2
  import { startLighthouseObserver } from '../additional-payload';
3
3
  import { setUFOConfig } from '../config';
4
4
  import { experimentalVC, sinkExperimentalHandler } from '../create-experimental-interaction-metrics-payload';
5
+ import { sinkExtraSearchPageInteractionHandler } from '../create-extra-search-page-interaction-payload';
5
6
  import { setupHiddenTimingCapture } from '../hidden-timing';
6
7
  import { interactionExtraMetrics, postInteractionLog, sinkInteractionHandler, sinkPostInteractionLogHandler } from '../interaction-metrics';
7
8
  import { getPerformanceObserver } from '../interactions-performance-observer';
@@ -86,6 +87,26 @@ function sinkInteractionExtraMetrics(instance, createInteractionExtraLogPayload)
86
87
  });
87
88
  });
88
89
  }
90
+ function sinkExtraSearchPageInteraction(instance, payloadPackage) {
91
+ function sinkFn(interactionId, interaction) {
92
+ function onIdle() {
93
+ payloadPackage.createExtraSearchPageInteractionPayload(interactionId, interaction).then(payloads => {
94
+ // NOTE: This API is used by the UFO DevTool Chrome Extension and Criterion
95
+ const devToolObserver = globalThis.__ufo_devtool_onUfoPayload;
96
+ payloads === null || payloads === void 0 ? void 0 : payloads.forEach(payload => {
97
+ if (typeof devToolObserver === 'function') {
98
+ devToolObserver === null || devToolObserver === void 0 ? void 0 : devToolObserver(payload);
99
+ }
100
+ instance.sendOperationalEvent(payload);
101
+ });
102
+ }).catch(error => {
103
+ throw error;
104
+ });
105
+ }
106
+ scheduleIdleCallback(onIdle);
107
+ }
108
+ sinkExtraSearchPageInteractionHandler(sinkFn);
109
+ }
89
110
  export function init(analyticsWebClientAsync, config) {
90
111
  var _config$vc;
91
112
  if (initialized) {
@@ -129,7 +150,7 @@ export function init(analyticsWebClientAsync, config) {
129
150
  Promise.all([analyticsWebClientAsync, import( /* webpackChunkName: "create-payloads" */'../create-payload'), import( /* webpackChunkName: "create-post-interaction-log-payload" */'../create-post-interaction-log-payload'), import( /* webpackChunkName: "create-interaction-extra-metrics-payload" */'../create-interaction-extra-metrics-payload')]).then(([awc, payloadPackage, createPostInteractionLogPayloadPackage, createInteractionExtraMetricsPayloadPackage]) => {
130
151
  if (awc.getAnalyticsWebClientPromise) {
131
152
  awc.getAnalyticsWebClientPromise().then(client => {
132
- var _config$experimentalI2, _config$postInteracti, _config$extraInteract2;
153
+ var _config$experimentalI2, _config$postInteracti, _config$extraInteract2, _config$extraSearchPa;
133
154
  const instance = client.getInstance();
134
155
  sinkInteraction(instance, payloadPackage);
135
156
  if (config !== null && config !== void 0 && (_config$experimentalI2 = config.experimentalInteractionMetrics) !== null && _config$experimentalI2 !== void 0 && _config$experimentalI2.enabled) {
@@ -141,9 +162,12 @@ export function init(analyticsWebClientAsync, config) {
141
162
  if (config !== null && config !== void 0 && (_config$extraInteract2 = config.extraInteractionMetrics) !== null && _config$extraInteract2 !== void 0 && _config$extraInteract2.enabled && fg('platform_ufo_enable_ttai_with_3p')) {
142
163
  sinkInteractionExtraMetrics(instance, createInteractionExtraMetricsPayloadPackage.default);
143
164
  }
165
+ if (config !== null && config !== void 0 && (_config$extraSearchPa = config.extraSearchPageInteraction) !== null && _config$extraSearchPa !== void 0 && _config$extraSearchPa.enabled && fg('react_ufo_unified_search_ignoring_sain_metric')) {
166
+ sinkExtraSearchPageInteraction(instance, payloadPackage);
167
+ }
144
168
  });
145
169
  } else if (awc.sendOperationalEvent) {
146
- var _config$experimentalI3, _config$postInteracti2, _config$extraInteract3;
170
+ var _config$experimentalI3, _config$postInteracti2, _config$extraInteract3, _config$extraSearchPa2;
147
171
  sinkInteraction(awc, payloadPackage);
148
172
  if (config !== null && config !== void 0 && (_config$experimentalI3 = config.experimentalInteractionMetrics) !== null && _config$experimentalI3 !== void 0 && _config$experimentalI3.enabled) {
149
173
  sinkExperimentalInteractionMetrics(awc, payloadPackage);
@@ -154,6 +178,9 @@ export function init(analyticsWebClientAsync, config) {
154
178
  if (config !== null && config !== void 0 && (_config$extraInteract3 = config.extraInteractionMetrics) !== null && _config$extraInteract3 !== void 0 && _config$extraInteract3.enabled && fg('platform_ufo_enable_ttai_with_3p')) {
155
179
  sinkInteractionExtraMetrics(awc, createInteractionExtraMetricsPayloadPackage.default);
156
180
  }
181
+ if (config !== null && config !== void 0 && (_config$extraSearchPa2 = config.extraSearchPageInteraction) !== null && _config$extraSearchPa2 !== void 0 && _config$extraSearchPa2.enabled) {
182
+ sinkExtraSearchPageInteraction(awc, payloadPackage);
183
+ }
157
184
  }
158
185
  });
159
186
  }
@@ -15,7 +15,7 @@ export class VCObserverWrapper {
15
15
  this.ssrPlaceholderHandler = new SSRPlaceholderHandlers({
16
16
  enablePageLayoutPlaceholder: (_opts$ssrEnablePageLa = opts.ssrEnablePageLayoutPlaceholder) !== null && _opts$ssrEnablePageLa !== void 0 ? _opts$ssrEnablePageLa : false
17
17
  });
18
- if (isVCRevisionEnabled('fy25.03')) {
18
+ if (isVCRevisionEnabled('fy25.03') || isVCRevisionEnabled('next')) {
19
19
  var _opts$ssrEnablePageLa2;
20
20
  this.newVCObserver = new VCObserverNew({
21
21
  selectorConfig: opts.selectorConfig,
@@ -95,7 +95,8 @@ export class VCObserverWrapper {
95
95
  var _this$oldVCObserver4, _this$newVCObserver3, _ref;
96
96
  const {
97
97
  experienceKey,
98
- include3p
98
+ include3p,
99
+ excludeSmartAnswersInSearch
99
100
  } = param;
100
101
  const v1v2Result = isVCRevisionEnabled('fy25.01', experienceKey) || isVCRevisionEnabled('fy25.02', experienceKey) ? await ((_this$oldVCObserver4 = this.oldVCObserver) === null || _this$oldVCObserver4 === void 0 ? void 0 : _this$oldVCObserver4.getVCResult(param)) : {};
101
102
  const v3Result = isVCRevisionEnabled('fy25.03', experienceKey) ? await ((_this$newVCObserver3 = this.newVCObserver) === null || _this$newVCObserver3 === void 0 ? void 0 : _this$newVCObserver3.getVCResult({
@@ -103,7 +104,8 @@ export class VCObserverWrapper {
103
104
  stop: param.stop,
104
105
  interactionId: param.interactionId,
105
106
  ssr: param.includeSSRInV3 ? param.ssr : undefined,
106
- include3p
107
+ include3p,
108
+ excludeSmartAnswersInSearch
107
109
  })) : [];
108
110
  if (!v3Result) {
109
111
  return v1v2Result !== null && v1v2Result !== void 0 ? v1v2Result : {};
@@ -6,6 +6,7 @@ import getElementName from './get-element-name';
6
6
  import VCCalculator_FY25_03 from './metric-calculator/fy25_03';
7
7
  import getViewportHeight from './metric-calculator/utils/get-viewport-height';
8
8
  import getViewportWidth from './metric-calculator/utils/get-viewport-width';
9
+ import VCNextCalculator from './metric-calculator/vcnext';
9
10
  import ViewportObserver from './viewport-observer';
10
11
  import WindowEventObserver from './window-event-observer';
11
12
  const SSRState = {
@@ -195,7 +196,8 @@ export default class VCObserverNew {
195
196
  start,
196
197
  stop,
197
198
  interactionId,
198
- include3p
199
+ include3p,
200
+ excludeSmartAnswersInSearch
199
201
  } = param;
200
202
  const results = [];
201
203
  this.addStartEntry(start);
@@ -213,11 +215,28 @@ export default class VCObserverNew {
213
215
  stopTime: stop,
214
216
  interactionId,
215
217
  isPostInteraction: this.isPostInteraction,
216
- include3p
218
+ include3p,
219
+ excludeSmartAnswersInSearch
217
220
  });
218
221
  if (fy25_03) {
219
222
  results.push(fy25_03);
220
223
  }
224
+
225
+ // TODO on cleanup: put behind `enabledVCRevisions` config
226
+ if (fg('platform_ufo_vcnext_v4_enabled')) {
227
+ const calculator_next = new VCNextCalculator();
228
+ const vcNext = await calculator_next.calculate({
229
+ orderedEntries,
230
+ startTime: start,
231
+ stopTime: stop,
232
+ interactionId,
233
+ isPostInteraction: this.isPostInteraction,
234
+ include3p
235
+ });
236
+ if (vcNext) {
237
+ results.push(vcNext);
238
+ }
239
+ }
221
240
  return results;
222
241
  }
223
242
  addStartEntry(startTime) {
@@ -55,7 +55,7 @@ export default class AbstractVCCalculatorBase {
55
55
  }
56
56
  return labelStacks;
57
57
  }
58
- async calculateWithDebugInfo(filteredEntries, startTime, stopTime, isPostInteraction, isVCClean, interactionId, dirtyReason, allEntries, include3p) {
58
+ async calculateWithDebugInfo(filteredEntries, startTime, stopTime, isPostInteraction, isVCClean, interactionId, dirtyReason, allEntries, include3p, excludeSmartAnswersInSearch) {
59
59
  var _window, _window2, _window6, _window8;
60
60
  const percentiles = [25, 50, 75, 80, 85, 90, 95, 98, 99, 100];
61
61
  const viewportEntries = this.filterViewportEntries(filteredEntries);
@@ -175,7 +175,7 @@ export default class AbstractVCCalculatorBase {
175
175
  // Group ignored entries by timestamp
176
176
  const ignoredEntriesByTime = new Map();
177
177
  for (const entry of allEntries) {
178
- if ('rect' in entry.data && !this.isEntryIncluded(entry, include3p)) {
178
+ if ('rect' in entry.data && !this.isEntryIncluded(entry, include3p, excludeSmartAnswersInSearch)) {
179
179
  var _ignoredEntriesByTime, _viewportData$rect, _viewportData$previou;
180
180
  const viewportData = entry.data;
181
181
  const timestamp = Math.round(entry.time);
@@ -273,11 +273,12 @@ export default class AbstractVCCalculatorBase {
273
273
  orderedEntries,
274
274
  interactionId,
275
275
  isPostInteraction,
276
- include3p
276
+ include3p,
277
+ excludeSmartAnswersInSearch
277
278
  }) {
278
279
  var _vcDetails$90$t, _vcDetails$;
279
280
  const filteredEntries = orderedEntries.filter(entry => {
280
- return this.isEntryIncluded(entry, include3p);
281
+ return this.isEntryIncluded(entry, include3p, excludeSmartAnswersInSearch);
281
282
  });
282
283
  let isVCClean;
283
284
  let dirtyReason;
@@ -293,7 +294,7 @@ export default class AbstractVCCalculatorBase {
293
294
  abortTimestamp: getVCCleanStatusResult.abortTimestamp
294
295
  };
295
296
  }
296
- const vcDetails = await this.calculateWithDebugInfo(filteredEntries, startTime, stopTime, isPostInteraction, isVCClean, interactionId, dirtyReason, orderedEntries, include3p);
297
+ const vcDetails = await this.calculateWithDebugInfo(filteredEntries, startTime, stopTime, isPostInteraction, isVCClean, interactionId, dirtyReason, orderedEntries, include3p, excludeSmartAnswersInSearch);
297
298
  const result = {
298
299
  revision: this.revisionNo,
299
300
  clean: true,
@@ -1,5 +1,6 @@
1
1
  import { fg } from '@atlaskit/platform-feature-flags';
2
2
  import AbstractVCCalculatorBase from '../abstract-base-vc-calculator';
3
+ import { isEntrySmartAnswersInSearch } from '../utils/is-entry-smart-answers-in-search';
3
4
  import isViewportEntryData from '../utils/is-viewport-entry-data';
4
5
  const ABORTING_WINDOW_EVENT = ['wheel', 'scroll', 'keydown', 'resize'];
5
6
  const REVISION_NO = 'fy25.03';
@@ -30,13 +31,16 @@ export const THIRD_PARTY_BROWSER_EXTENSION_ATTRIBUTES = ['bis_skin_checked', 'cz
30
31
  // grammarly extensions
31
32
  'data-new-gr-c-s-loaded', 'data-gr-aaa-notch-connection-id', 'data-gr-aaa-loaded'];
32
33
  export default class VCCalculator_FY25_03 extends AbstractVCCalculatorBase {
33
- constructor() {
34
- super(REVISION_NO);
34
+ constructor(revisionNo) {
35
+ super(revisionNo !== null && revisionNo !== void 0 ? revisionNo : REVISION_NO);
35
36
  }
36
- isEntryIncluded(entry, include3p) {
37
+ isEntryIncluded(entry, include3p, excludeSmartAnswersInSearch) {
37
38
  if (!getConsideredEntryTypes(include3p).includes(entry.data.type)) {
38
39
  return false;
39
40
  }
41
+ if (excludeSmartAnswersInSearch && isEntrySmartAnswersInSearch(entry)) {
42
+ return false;
43
+ }
40
44
  if (entry.data.type === 'mutation:attribute') {
41
45
  const entryData = entry.data;
42
46
  const attributeName = entryData.attributeName;
@@ -0,0 +1,22 @@
1
+ import { getDocument } from '@atlaskit/browser-apis';
2
+ export function isEntrySmartAnswersInSearch(entry) {
3
+ const {
4
+ elementName
5
+ } = entry.data;
6
+ if (!elementName || elementName === 'START') {
7
+ return false;
8
+ }
9
+ const doc = getDocument();
10
+ if (!doc) {
11
+ return false;
12
+ }
13
+ const smartAnswersElement = doc.getElementById('search-page-smart-answers');
14
+ if (!smartAnswersElement) {
15
+ return false;
16
+ }
17
+ const entryDOMElement = doc.querySelector(elementName);
18
+ if (!entryDOMElement) {
19
+ return false;
20
+ }
21
+ return smartAnswersElement.contains(entryDOMElement);
22
+ }
@@ -0,0 +1,21 @@
1
+ import VCCalculator_FY25_03 from '../fy25_03';
2
+
3
+ // NOTE: `next` to be renamed `fy26.04` once stable
4
+ const REVISION_NO = 'next';
5
+ const getConsideredEntryTypes = () => {
6
+ return ['mutation:display-contents-children-element'];
7
+ };
8
+
9
+ // NOTE: `VCNext` to be renamed `FY26_04` once stable
10
+ export default class VCNextCalculator extends VCCalculator_FY25_03 {
11
+ constructor() {
12
+ super(REVISION_NO);
13
+ }
14
+ isEntryIncluded(entry, include3p) {
15
+ const isEntryIncludedInV3 = super.isEntryIncluded(entry, include3p);
16
+ if (isEntryIncludedInV3) {
17
+ return true;
18
+ }
19
+ return getConsideredEntryTypes().includes(entry.data.type);
20
+ }
21
+ }