@atlaskit/react-ufo 4.8.0 → 4.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cjs/additional-payload/utils/lighthouse-metrics/cls/index.js +17 -2
  3. package/dist/cjs/create-extra-search-page-interaction-payload/index.js +50 -0
  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/create-post-interaction-log-payload/index.js +1 -1
  7. package/dist/cjs/interaction-metrics/index.js +15 -7
  8. package/dist/cjs/interaction-metrics/post-interaction-log.js +2 -1
  9. package/dist/cjs/interaction-metrics-init/index.js +29 -2
  10. package/dist/cjs/vc/index.js +4 -3
  11. package/dist/cjs/vc/vc-observer-new/index.js +4 -3
  12. package/dist/cjs/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +8 -8
  13. package/dist/cjs/vc/vc-observer-new/metric-calculator/fy25_03/index.js +5 -1
  14. package/dist/cjs/vc/vc-observer-new/metric-calculator/utils/is-entry-smart-answers-in-search.js +27 -0
  15. package/dist/es2019/additional-payload/utils/lighthouse-metrics/cls/index.js +17 -1
  16. package/dist/es2019/create-extra-search-page-interaction-payload/index.js +32 -0
  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/create-post-interaction-log-payload/index.js +1 -1
  20. package/dist/es2019/interaction-metrics/index.js +15 -7
  21. package/dist/es2019/interaction-metrics/post-interaction-log.js +2 -1
  22. package/dist/es2019/interaction-metrics-init/index.js +29 -2
  23. package/dist/es2019/vc/index.js +4 -2
  24. package/dist/es2019/vc/vc-observer-new/index.js +4 -2
  25. package/dist/es2019/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +6 -5
  26. package/dist/es2019/vc/vc-observer-new/metric-calculator/fy25_03/index.js +5 -1
  27. package/dist/es2019/vc/vc-observer-new/metric-calculator/utils/is-entry-smart-answers-in-search.js +22 -0
  28. package/dist/esm/additional-payload/utils/lighthouse-metrics/cls/index.js +17 -2
  29. package/dist/esm/create-extra-search-page-interaction-payload/index.js +43 -0
  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/create-post-interaction-log-payload/index.js +1 -1
  33. package/dist/esm/interaction-metrics/index.js +15 -7
  34. package/dist/esm/interaction-metrics/post-interaction-log.js +2 -1
  35. package/dist/esm/interaction-metrics-init/index.js +29 -2
  36. package/dist/esm/vc/index.js +4 -3
  37. package/dist/esm/vc/vc-observer-new/index.js +4 -3
  38. package/dist/esm/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +8 -8
  39. package/dist/esm/vc/vc-observer-new/metric-calculator/fy25_03/index.js +5 -1
  40. package/dist/esm/vc/vc-observer-new/metric-calculator/utils/is-entry-smart-answers-in-search.js +21 -0
  41. package/dist/types/additional-payload/utils/lighthouse-metrics/cls/types.d.ts +7 -0
  42. package/dist/types/config/index.d.ts +7 -0
  43. package/dist/types/create-extra-search-page-interaction-payload/index.d.ts +3 -0
  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 +1 -1
  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/types.d.ts +1 -0
  52. package/dist/types-ts4.5/additional-payload/utils/lighthouse-metrics/cls/types.d.ts +7 -0
  53. package/dist/types-ts4.5/config/index.d.ts +7 -0
  54. package/dist/types-ts4.5/create-extra-search-page-interaction-payload/index.d.ts +3 -0
  55. package/dist/types-ts4.5/create-payload/index.d.ts +25434 -0
  56. package/dist/types-ts4.5/create-payload/utils/get-vc-metrics.d.ts +1 -1
  57. package/dist/types-ts4.5/vc/types.d.ts +1 -0
  58. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +2 -2
  59. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/fy25_03/index.d.ts +1 -1
  60. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/types.d.ts +1 -0
  61. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/utils/is-entry-smart-answers-in-search.d.ts +2 -0
  62. package/dist/types-ts4.5/vc/vc-observer-new/types.d.ts +1 -0
  63. package/package.json +10 -1
@@ -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
+ }
@@ -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) {
@@ -139,7 +139,7 @@ function createPostInteractionLogPayload({
139
139
  if (lastInteractionFinish.errors.length > 0) {
140
140
  return null;
141
141
  }
142
- const maxEndTimeFromProfiler = reactProfilerTimings ? Math.max(...reactProfilerTimings.map(t => t.commitTime)) : lastInteractionFinish.end;
142
+ const maxEndTimeFromProfiler = reactProfilerTimings && reactProfilerTimings.length > 0 ? Math.max(...reactProfilerTimings.map(t => t.commitTime)) : lastInteractionFinish.end;
143
143
  const revisedEndTime = Math.round(maxEndTimeFromProfiler);
144
144
  const revisedTtai = Math.round(maxEndTimeFromProfiler - lastInteractionFinish.start);
145
145
  const lastInteractionFinishStart = Math.round(lastInteractionFinish.start);
@@ -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
  });
@@ -1,4 +1,5 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import { fg } from '@atlaskit/platform-feature-flags';
2
3
  import { getConfig } from '../config';
3
4
  import { VCObserverWrapper } from '../vc';
4
5
  const POST_INTERACTION_LOG_SEND_DEFAULT_TIMEOUT = 3000;
@@ -89,7 +90,7 @@ export default class PostInteractionLog {
89
90
  */
90
91
  async sendPostInteractionLog() {
91
92
  var _this$vcObserver4, _config$vc, _config$vc2, _this$vcObserver5;
92
- if (!this.hasData() || !this.lastInteractionFinish || !this.sinkHandlerFn) {
93
+ if (!this.lastInteractionFinish || !this.sinkHandlerFn || !this.hasData() && !fg('platform_ufo_always_send_post_interaction_log')) {
93
94
  var _this$vcObserver3;
94
95
  this.reset();
95
96
  (_this$vcObserver3 = this.vcObserver) === null || _this$vcObserver3 === void 0 ? void 0 : _this$vcObserver3.stop();
@@ -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
  }
@@ -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 : {};
@@ -196,7 +196,8 @@ export default class VCObserverNew {
196
196
  start,
197
197
  stop,
198
198
  interactionId,
199
- include3p
199
+ include3p,
200
+ excludeSmartAnswersInSearch
200
201
  } = param;
201
202
  const results = [];
202
203
  this.addStartEntry(start);
@@ -214,7 +215,8 @@ export default class VCObserverNew {
214
215
  stopTime: stop,
215
216
  interactionId,
216
217
  isPostInteraction: this.isPostInteraction,
217
- include3p
218
+ include3p,
219
+ excludeSmartAnswersInSearch
218
220
  });
219
221
  if (fy25_03) {
220
222
  results.push(fy25_03);
@@ -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';
@@ -33,10 +34,13 @@ export default class VCCalculator_FY25_03 extends AbstractVCCalculatorBase {
33
34
  constructor(revisionNo) {
34
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
+ }
@@ -1,9 +1,24 @@
1
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
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
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
+ import { fg } from '@atlaskit/platform-feature-flags';
5
+ function isSameRects(rect1, rect2) {
6
+ return rect1.x === rect2.x && rect1.y === rect2.y && rect1.width === rect2.width && rect1.height === rect2.height && rect1.top === rect2.top && rect1.right === rect2.right && rect1.bottom === rect2.bottom && rect1.left === rect2.left;
7
+ }
4
8
  export function getCLS(start, stop, buffer) {
5
- var layoutShifts = buffer.getAll().filter(function (entry) {
6
- return entry.startTime >= start && entry.startTime <= stop;
9
+ var layoutShifts = buffer.getAll().filter(function (_entry) {
10
+ var entry = _entry;
11
+ var isWithinObservationWindow = entry.startTime >= start && entry.startTime <= stop;
12
+ if (fg('platform_ufo_filter_cls_logs_same_rects_positions')) {
13
+ var _entry$sources;
14
+ var allSourcesHaveSameRects = entry === null || entry === void 0 || (_entry$sources = entry.sources) === null || _entry$sources === void 0 ? void 0 : _entry$sources.every(function (_ref) {
15
+ var previousRect = _ref.previousRect,
16
+ currentRect = _ref.currentRect;
17
+ return isSameRects(previousRect, currentRect);
18
+ });
19
+ return isWithinObservationWindow && !allSourcesHaveSameRects;
20
+ }
21
+ return isWithinObservationWindow;
7
22
  });
8
23
  var sessionWindows = [];
9
24
  var currentWindow = null;
@@ -0,0 +1,43 @@
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 interactionBuffer = [];
5
+ var bufferInteractionData = function bufferInteractionData(interactionId, data) {
6
+ interactionBuffer.push({
7
+ interactionId: interactionId,
8
+ data: data
9
+ });
10
+ };
11
+ function clearInteractionBuffer() {
12
+ interactionBuffer.length = 0;
13
+ }
14
+ function appendInteractionData(interactionId, data) {
15
+ bufferInteractionData(interactionId, data);
16
+ }
17
+ function installInteractionSink(handler) {
18
+ var _iterator = _createForOfIteratorHelper(interactionBuffer),
19
+ _step;
20
+ try {
21
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
22
+ var _step$value = _step.value,
23
+ _interactionId = _step$value.interactionId,
24
+ data = _step$value.data;
25
+ handler(_interactionId, data);
26
+ }
27
+ } catch (err) {
28
+ _iterator.e(err);
29
+ } finally {
30
+ _iterator.f();
31
+ }
32
+ clearInteractionBuffer();
33
+ bufferInteractionData = handler;
34
+ }
35
+ export function sinkExtraSearchPageInteractionHandler(sinkFn) {
36
+ installInteractionSink(sinkFn);
37
+ }
38
+ export function onSearchPageInteractionComplete(interactionId, data) {
39
+ if (data.ufoName) {
40
+ appendInteractionData(interactionId, data);
41
+ clearInteractionBuffer();
42
+ }
43
+ }
@@ -696,4 +696,66 @@ function _createExperimentalMetricsPayload() {
696
696
  }, _callee3);
697
697
  }));
698
698
  return _createExperimentalMetricsPayload.apply(this, arguments);
699
+ }
700
+ export function createExtraSearchPageInteractionPayload(_x0, _x1) {
701
+ return _createExtraSearchPageInteractionPayload.apply(this, arguments);
702
+ }
703
+ function _createExtraSearchPageInteractionPayload() {
704
+ _createExtraSearchPageInteractionPayload = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(interactionId, interaction) {
705
+ var SAIN_HOLD_NAMES, NAME_OVERRIDE, SEARCH_PAGE_SMART_ANSWERS_SEGMENT_LABEL, newInteractionId, newEnd, holdInfo, reactProfilerTimings, lastHold, isLastHoldSAIN, lastFilteredTime, filteredReactProfilerTimings, lastTiming, modifiedInteraction, payloads, vcMetrics, interactionMetricsPayload;
706
+ return _regeneratorRuntime.wrap(function _callee4$(_context4) {
707
+ while (1) switch (_context4.prev = _context4.next) {
708
+ case 0:
709
+ SAIN_HOLD_NAMES = ['search-ai-dialog-visible-text-loading', 'search-ai-dialog-all-text-loading'];
710
+ NAME_OVERRIDE = 'search-page-ignoring-smart-answers';
711
+ SEARCH_PAGE_SMART_ANSWERS_SEGMENT_LABEL = 'search-page-smart-answers';
712
+ newInteractionId = "".concat(interactionId, "-ignoring-smart-answers"); // Calculate a new end time which excludes SAIN holds
713
+ holdInfo = interaction.holdInfo, reactProfilerTimings = interaction.reactProfilerTimings;
714
+ lastHold = holdInfo.at(-1);
715
+ isLastHoldSAIN = Boolean(lastHold && SAIN_HOLD_NAMES.includes(lastHold.name)); // A new end time is only calculated if the last hold is a SAIN hold
716
+ if (isLastHoldSAIN) {
717
+ lastFilteredTime = null;
718
+ filteredReactProfilerTimings = reactProfilerTimings.filter(function (timing) {
719
+ if (timing.commitTime === lastFilteredTime) {
720
+ return false;
721
+ }
722
+ var isTimingSmartAnswersInSearch = timing.labelStack.some(function (label) {
723
+ return label.name === SEARCH_PAGE_SMART_ANSWERS_SEGMENT_LABEL;
724
+ });
725
+ if (isTimingSmartAnswersInSearch) {
726
+ lastFilteredTime = timing.commitTime;
727
+ return false;
728
+ }
729
+ return true;
730
+ });
731
+ lastTiming = filteredReactProfilerTimings.at(-1);
732
+ if (lastTiming) {
733
+ newEnd = lastTiming.commitTime;
734
+ }
735
+ }
736
+ modifiedInteraction = _objectSpread(_objectSpread({}, interaction), {}, {
737
+ end: newEnd !== null && newEnd !== void 0 ? newEnd : interaction.end,
738
+ holdInfo: [],
739
+ knownSegments: [],
740
+ reactProfilerTimings: [],
741
+ ufoName: NAME_OVERRIDE
742
+ });
743
+ payloads = [];
744
+ _context4.next = 12;
745
+ return getVCMetrics(interaction, false, true);
746
+ case 12:
747
+ vcMetrics = _context4.sent;
748
+ _context4.next = 15;
749
+ return createInteractionMetricsPayload(modifiedInteraction, newInteractionId, undefined, undefined, vcMetrics);
750
+ case 15:
751
+ interactionMetricsPayload = _context4.sent;
752
+ payloads.push(interactionMetricsPayload);
753
+ return _context4.abrupt("return", payloads.filter(Boolean));
754
+ case 18:
755
+ case "end":
756
+ return _context4.stop();
757
+ }
758
+ }, _callee4);
759
+ }));
760
+ return _createExtraSearchPageInteractionPayload.apply(this, arguments);
699
761
  }
@@ -15,6 +15,7 @@ function _getVCMetrics() {
15
15
  _getVCMetrics = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(interaction) {
16
16
  var _config$vc, _config$vc$ssrWhiteli, _interaction$apdex, _config$vc2, _config$vc3, _result$ufoVcRev;
17
17
  var include3p,
18
+ excludeSmartAnswersInSearch,
18
19
  config,
19
20
  interactionStatus,
20
21
  pageVisibilityUpToTTAI,
@@ -32,29 +33,30 @@ function _getVCMetrics() {
32
33
  while (1) switch (_context.prev = _context.next) {
33
34
  case 0:
34
35
  include3p = _args.length > 1 && _args[1] !== undefined ? _args[1] : false;
36
+ excludeSmartAnswersInSearch = _args.length > 2 && _args[2] !== undefined ? _args[2] : false;
35
37
  config = getConfig();
36
38
  if (config !== null && config !== void 0 && (_config$vc = config.vc) !== null && _config$vc !== void 0 && _config$vc.enabled) {
37
- _context.next = 4;
39
+ _context.next = 5;
38
40
  break;
39
41
  }
40
42
  return _context.abrupt("return", {});
41
- case 4:
43
+ case 5:
42
44
  if (!(interaction.type !== 'page_load' && interaction.type !== 'transition' && interaction.type !== 'press')) {
43
- _context.next = 6;
45
+ _context.next = 7;
44
46
  break;
45
47
  }
46
48
  return _context.abrupt("return", {});
47
- case 6:
49
+ case 7:
48
50
  interactionStatus = getInteractionStatus(interaction);
49
51
  pageVisibilityUpToTTAI = getPageVisibilityUpToTTAI(interaction);
50
52
  shouldReportVCMetrics = interactionStatus.originalInteractionStatus === 'SUCCEEDED' && pageVisibilityUpToTTAI === 'visible'; // Use per-interaction VC observer if available, otherwise fall back to global
51
53
  observer = interaction.vcObserver;
52
54
  if (observer) {
53
- _context.next = 12;
55
+ _context.next = 13;
54
56
  break;
55
57
  }
56
58
  return _context.abrupt("return", {});
57
- case 12:
59
+ case 13:
58
60
  isSSREnabled = interaction.type === 'page_load' && ((config === null || config === void 0 ? void 0 : config.ssr) || (config === null || config === void 0 || (_config$vc$ssrWhiteli = config.vc.ssrWhitelist) === null || _config$vc$ssrWhiteli === void 0 ? void 0 : _config$vc$ssrWhiteli.includes(interaction.ufoName)));
59
61
  ssr = interaction.type === 'page_load' && isSSREnabled ? {
60
62
  ssr: getSSRDoneTimeValue(config)
@@ -62,7 +64,7 @@ function _getVCMetrics() {
62
64
  postInteractionLog.setVCObserverSSRConfig(ssr);
63
65
  tti = (_interaction$apdex = interaction.apdex) === null || _interaction$apdex === void 0 || (_interaction$apdex = _interaction$apdex[0]) === null || _interaction$apdex === void 0 ? void 0 : _interaction$apdex.stopTime;
64
66
  prefix = 'ufo';
65
- _context.next = 19;
67
+ _context.next = 20;
66
68
  return observer.getVCResult(_objectSpread(_objectSpread({
67
69
  start: interaction.start,
68
70
  stop: interaction.end,
@@ -75,9 +77,10 @@ function _getVCMetrics() {
75
77
  interactionId: interaction.id,
76
78
  includeSSRRatio: (_config$vc3 = config.vc) === null || _config$vc3 === void 0 ? void 0 : _config$vc3.includeSSRRatio
77
79
  }, ssr), {}, {
78
- include3p: include3p
80
+ include3p: include3p,
81
+ excludeSmartAnswersInSearch: excludeSmartAnswersInSearch
79
82
  }));
80
- case 19:
83
+ case 20:
81
84
  result = _context.sent;
82
85
  observer.stop(interaction.ufoName);
83
86
  if (!include3p) {
@@ -90,15 +93,15 @@ function _getVCMetrics() {
90
93
  return revision === mostRecentVCRevision;
91
94
  });
92
95
  if (!(!shouldReportVCMetrics || !(mostRecentVCRevisionPayload !== null && mostRecentVCRevisionPayload !== void 0 && mostRecentVCRevisionPayload.clean))) {
93
- _context.next = 26;
96
+ _context.next = 27;
94
97
  break;
95
98
  }
96
99
  return _context.abrupt("return", result);
97
- case 26:
100
+ case 27:
98
101
  return _context.abrupt("return", _objectSpread(_objectSpread({}, result), {}, {
99
102
  'metric:vc90': mostRecentVCRevisionPayload['metric:vc90']
100
103
  }));
101
- case 27:
104
+ case 28:
102
105
  case "end":
103
106
  return _context.stop();
104
107
  }
@@ -145,7 +145,7 @@ function createPostInteractionLogPayload(_ref2) {
145
145
  if (lastInteractionFinish.errors.length > 0) {
146
146
  return null;
147
147
  }
148
- var maxEndTimeFromProfiler = reactProfilerTimings ? Math.max.apply(Math, _toConsumableArray(reactProfilerTimings.map(function (t) {
148
+ var maxEndTimeFromProfiler = reactProfilerTimings && reactProfilerTimings.length > 0 ? Math.max.apply(Math, _toConsumableArray(reactProfilerTimings.map(function (t) {
149
149
  return t.commitTime;
150
150
  }))) : lastInteractionFinish.end;
151
151
  var revisedEndTime = Math.round(maxEndTimeFromProfiler);