@atlaskit/react-ufo 3.14.6 → 3.14.8
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.
- package/CHANGELOG.md +16 -0
- package/dist/cjs/create-experimental-interaction-metrics-payload/index.js +14 -12
- package/dist/cjs/create-payload/utils/get-vc-metrics.js +17 -13
- package/dist/cjs/interaction-metrics/index.js +35 -15
- package/dist/cjs/interaction-metrics-init/index.js +5 -3
- package/dist/cjs/vc/index.js +46 -6
- package/dist/cjs/vc/vc-observer/index.js +10 -2
- package/dist/cjs/vc/vc-observer/observers/index.js +12 -7
- package/dist/cjs/vc/vc-observer/observers/ssr-placeholders/index.js +76 -40
- package/dist/cjs/vc/vc-observer-new/index.js +84 -0
- package/dist/cjs/vc/vc-observer-new/viewport-observer/index.js +214 -71
- package/dist/cjs/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +97 -59
- package/dist/es2019/create-experimental-interaction-metrics-payload/index.js +4 -2
- package/dist/es2019/create-payload/utils/get-vc-metrics.js +10 -4
- package/dist/es2019/interaction-metrics/index.js +36 -16
- package/dist/es2019/interaction-metrics-init/index.js +5 -3
- package/dist/es2019/vc/index.js +42 -5
- package/dist/es2019/vc/vc-observer/index.js +8 -2
- package/dist/es2019/vc/vc-observer/observers/index.js +11 -5
- package/dist/es2019/vc/vc-observer/observers/ssr-placeholders/index.js +57 -26
- package/dist/es2019/vc/vc-observer-new/index.js +67 -1
- package/dist/es2019/vc/vc-observer-new/viewport-observer/index.js +87 -22
- package/dist/es2019/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +50 -34
- package/dist/esm/create-experimental-interaction-metrics-payload/index.js +14 -12
- package/dist/esm/create-payload/utils/get-vc-metrics.js +17 -13
- package/dist/esm/interaction-metrics/index.js +36 -16
- package/dist/esm/interaction-metrics-init/index.js +5 -3
- package/dist/esm/vc/index.js +45 -6
- package/dist/esm/vc/vc-observer/index.js +10 -2
- package/dist/esm/vc/vc-observer/observers/index.js +12 -7
- package/dist/esm/vc/vc-observer/observers/ssr-placeholders/index.js +76 -40
- package/dist/esm/vc/vc-observer-new/index.js +84 -0
- package/dist/esm/vc/vc-observer-new/viewport-observer/index.js +214 -71
- package/dist/esm/vc/vc-observer-new/viewport-observer/mutation-observer/index.js +97 -59
- package/dist/types/common/common/types.d.ts +4 -1
- package/dist/types/vc/index.d.ts +3 -0
- package/dist/types/vc/types.d.ts +2 -0
- package/dist/types/vc/vc-observer/index.d.ts +1 -0
- package/dist/types/vc/vc-observer/observers/index.d.ts +2 -0
- package/dist/types/vc/vc-observer/observers/ssr-placeholders/index.d.ts +6 -0
- package/dist/types/vc/vc-observer-new/index.d.ts +30 -0
- package/dist/types/vc/vc-observer-new/types.d.ts +1 -1
- package/dist/types/vc/vc-observer-new/viewport-observer/index.d.ts +5 -1
- package/dist/types/vc/vc-observer-new/viewport-observer/mutation-observer/index.d.ts +2 -0
- package/dist/types-ts4.5/common/common/types.d.ts +4 -1
- package/dist/types-ts4.5/vc/index.d.ts +3 -0
- package/dist/types-ts4.5/vc/types.d.ts +2 -0
- package/dist/types-ts4.5/vc/vc-observer/index.d.ts +1 -0
- package/dist/types-ts4.5/vc/vc-observer/observers/index.d.ts +2 -0
- package/dist/types-ts4.5/vc/vc-observer/observers/ssr-placeholders/index.d.ts +6 -0
- package/dist/types-ts4.5/vc/vc-observer-new/index.d.ts +30 -0
- package/dist/types-ts4.5/vc/vc-observer-new/types.d.ts +1 -1
- package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/index.d.ts +5 -1
- package/dist/types-ts4.5/vc/vc-observer-new/viewport-observer/mutation-observer/index.d.ts +2 -0
- package/package.json +11 -6
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
2
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
3
|
+
import { SSRPlaceholderHandlers } from '../vc-observer/observers/ssr-placeholders';
|
|
3
4
|
import EntriesTimeline from './entries-timeline';
|
|
4
5
|
import getElementName from './get-element-name';
|
|
5
6
|
import VCCalculator_FY25_03 from './metric-calculator/fy25_03';
|
|
@@ -7,6 +8,11 @@ import getViewportHeight from './metric-calculator/utils/get-viewport-height';
|
|
|
7
8
|
import getViewportWidth from './metric-calculator/utils/get-viewport-width';
|
|
8
9
|
import ViewportObserver from './viewport-observer';
|
|
9
10
|
import WindowEventObserver from './window-event-observer';
|
|
11
|
+
const SSRState = {
|
|
12
|
+
normal: 1,
|
|
13
|
+
waitingForFirstRender: 2,
|
|
14
|
+
ignoring: 3
|
|
15
|
+
};
|
|
10
16
|
const DEFAULT_SELECTOR_CONFIG = {
|
|
11
17
|
id: false,
|
|
12
18
|
testId: true,
|
|
@@ -19,9 +25,31 @@ export default class VCObserverNew {
|
|
|
19
25
|
var _config$isPostInterac, _config$selectorConfi;
|
|
20
26
|
_defineProperty(this, "viewportObserver", null);
|
|
21
27
|
_defineProperty(this, "windowEventObserver", null);
|
|
28
|
+
// SSR related properties
|
|
29
|
+
_defineProperty(this, "ssrPlaceholderHandler", null);
|
|
30
|
+
_defineProperty(this, "ssr", {
|
|
31
|
+
state: SSRState.normal,
|
|
32
|
+
reactRootElement: null,
|
|
33
|
+
renderStart: -1,
|
|
34
|
+
renderStop: -1
|
|
35
|
+
});
|
|
22
36
|
this.entriesTimeline = new EntriesTimeline();
|
|
23
37
|
this.isPostInteraction = (_config$isPostInterac = config.isPostInteraction) !== null && _config$isPostInterac !== void 0 ? _config$isPostInterac : false;
|
|
24
38
|
this.selectorConfig = (_config$selectorConfi = config.selectorConfig) !== null && _config$selectorConfi !== void 0 ? _config$selectorConfi : DEFAULT_SELECTOR_CONFIG;
|
|
39
|
+
|
|
40
|
+
// Use shared SSR placeholder handler if provided, otherwise create new one if feature flag is enabled
|
|
41
|
+
if (config.ssrPlaceholderHandler) {
|
|
42
|
+
this.ssrPlaceholderHandler = config.ssrPlaceholderHandler;
|
|
43
|
+
} else {
|
|
44
|
+
var _config$SSRConfig$ena, _config$SSRConfig, _config$SSRConfig$dis, _config$SSRConfig2;
|
|
45
|
+
this.ssrPlaceholderHandler = new SSRPlaceholderHandlers({
|
|
46
|
+
enablePageLayoutPlaceholder: (_config$SSRConfig$ena = (_config$SSRConfig = config.SSRConfig) === null || _config$SSRConfig === void 0 ? void 0 : _config$SSRConfig.enablePageLayoutPlaceholder) !== null && _config$SSRConfig$ena !== void 0 ? _config$SSRConfig$ena : false,
|
|
47
|
+
disableSizeAndPositionCheck: (_config$SSRConfig$dis = (_config$SSRConfig2 = config.SSRConfig) === null || _config$SSRConfig2 === void 0 ? void 0 : _config$SSRConfig2.disableSizeAndPositionCheck) !== null && _config$SSRConfig$dis !== void 0 ? _config$SSRConfig$dis : {
|
|
48
|
+
v: false,
|
|
49
|
+
h: false
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
25
53
|
this.viewportObserver = new ViewportObserver({
|
|
26
54
|
onChange: onChangeArg => {
|
|
27
55
|
const {
|
|
@@ -51,7 +79,10 @@ export default class VCObserverNew {
|
|
|
51
79
|
newValue: mutationData === null || mutationData === void 0 ? void 0 : mutationData.newValue
|
|
52
80
|
}
|
|
53
81
|
});
|
|
54
|
-
}
|
|
82
|
+
},
|
|
83
|
+
// Pass SSR context to ViewportObserver
|
|
84
|
+
getSSRState: () => this.getSSRState(),
|
|
85
|
+
getSSRPlaceholderHandler: () => this.getSSRPlaceholderHandler()
|
|
55
86
|
});
|
|
56
87
|
this.windowEventObserver = new WindowEventObserver({
|
|
57
88
|
onEvent: ({
|
|
@@ -72,6 +103,14 @@ export default class VCObserverNew {
|
|
|
72
103
|
startTime
|
|
73
104
|
}) {
|
|
74
105
|
var _this$viewportObserve, _window, _this$windowEventObse;
|
|
106
|
+
// Reset SSR state on start (matches old VCObserver behavior)
|
|
107
|
+
this.ssr = {
|
|
108
|
+
state: SSRState.normal,
|
|
109
|
+
reactRootElement: null,
|
|
110
|
+
// Reset to null (matches old VCObserver)
|
|
111
|
+
renderStart: -1,
|
|
112
|
+
renderStop: -1
|
|
113
|
+
};
|
|
75
114
|
(_this$viewportObserve = this.viewportObserver) === null || _this$viewportObserve === void 0 ? void 0 : _this$viewportObserve.start();
|
|
76
115
|
if ((_window = window) !== null && _window !== void 0 && _window.__SSR_ABORT_LISTENERS__ && fg('platform_ufo_vc_observer_new_ssr_abort_listener')) {
|
|
77
116
|
const abortListeners = window.__SSR_ABORT_LISTENERS__;
|
|
@@ -97,6 +136,33 @@ export default class VCObserverNew {
|
|
|
97
136
|
var _this$viewportObserve2, _this$windowEventObse2;
|
|
98
137
|
(_this$viewportObserve2 = this.viewportObserver) === null || _this$viewportObserve2 === void 0 ? void 0 : _this$viewportObserve2.stop();
|
|
99
138
|
(_this$windowEventObse2 = this.windowEventObserver) === null || _this$windowEventObse2 === void 0 ? void 0 : _this$windowEventObse2.stop();
|
|
139
|
+
|
|
140
|
+
// Clear SSR state on stop (matches old VCObserver behavior)
|
|
141
|
+
this.ssr.reactRootElement = null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// SSR related methods
|
|
145
|
+
setReactRootElement(element) {
|
|
146
|
+
this.ssr.reactRootElement = element;
|
|
147
|
+
}
|
|
148
|
+
setReactRootRenderStart(startTime = performance.now()) {
|
|
149
|
+
this.ssr.renderStart = startTime;
|
|
150
|
+
this.ssr.state = SSRState.waitingForFirstRender;
|
|
151
|
+
}
|
|
152
|
+
setReactRootRenderStop(stopTime = performance.now()) {
|
|
153
|
+
this.ssr.renderStop = stopTime;
|
|
154
|
+
}
|
|
155
|
+
collectSSRPlaceholders() {
|
|
156
|
+
// This is handled by the shared SSRPlaceholderHandlers in VCObserverWrapper
|
|
157
|
+
// Individual observers don't need to implement this
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Internal methods for ViewportObserver to access SSR state
|
|
161
|
+
getSSRState() {
|
|
162
|
+
return this.ssr;
|
|
163
|
+
}
|
|
164
|
+
getSSRPlaceholderHandler() {
|
|
165
|
+
return this.ssrPlaceholderHandler;
|
|
100
166
|
}
|
|
101
167
|
addSSR(ssr) {
|
|
102
168
|
this.entriesTimeline.push({
|
|
@@ -14,7 +14,7 @@ function isElementVisible(element) {
|
|
|
14
14
|
}
|
|
15
15
|
try {
|
|
16
16
|
const visible = element.checkVisibility({
|
|
17
|
-
// @ts-
|
|
17
|
+
// @ts-ignore - visibilityProperty may not exist in all TS environments
|
|
18
18
|
visibilityProperty: true,
|
|
19
19
|
contentVisibilityAuto: true,
|
|
20
20
|
opacityProperty: true
|
|
@@ -59,8 +59,12 @@ const createElementMutationsWatcher = removedNodeRects => ({
|
|
|
59
59
|
return 'mutation:element';
|
|
60
60
|
};
|
|
61
61
|
export default class ViewportObserver {
|
|
62
|
+
// SSR context functions
|
|
63
|
+
|
|
62
64
|
constructor({
|
|
63
|
-
onChange
|
|
65
|
+
onChange,
|
|
66
|
+
getSSRState,
|
|
67
|
+
getSSRPlaceholderHandler
|
|
64
68
|
}) {
|
|
65
69
|
_defineProperty(this, "handleIntersectionEntry", ({
|
|
66
70
|
target,
|
|
@@ -85,9 +89,11 @@ export default class ViewportObserver {
|
|
|
85
89
|
mutationData
|
|
86
90
|
});
|
|
87
91
|
});
|
|
88
|
-
_defineProperty(this, "handleChildListMutation", ({
|
|
92
|
+
_defineProperty(this, "handleChildListMutation", async ({
|
|
93
|
+
target,
|
|
89
94
|
addedNodes,
|
|
90
|
-
removedNodes
|
|
95
|
+
removedNodes,
|
|
96
|
+
timestamp
|
|
91
97
|
}) => {
|
|
92
98
|
const removedNodeRects = removedNodes.map(ref => {
|
|
93
99
|
const n = ref.deref();
|
|
@@ -96,11 +102,66 @@ export default class ViewportObserver {
|
|
|
96
102
|
}
|
|
97
103
|
return this.mapVisibleNodeRects.get(n);
|
|
98
104
|
});
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
const targetNode = target.deref();
|
|
106
|
+
for (const addedNodeRef of addedNodes) {
|
|
107
|
+
var _this$intersectionObs8;
|
|
101
108
|
const addedNode = addedNodeRef.deref();
|
|
102
109
|
if (!addedNode) {
|
|
103
|
-
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// SSR hydration logic
|
|
114
|
+
if (this.getSSRState && fg('platform_ufo_vc_v3_ssr_placeholder')) {
|
|
115
|
+
const ssrState = this.getSSRState();
|
|
116
|
+
const SSRStateEnum = {
|
|
117
|
+
normal: 1,
|
|
118
|
+
waitingForFirstRender: 2,
|
|
119
|
+
ignoring: 3
|
|
120
|
+
};
|
|
121
|
+
if (ssrState.state === SSRStateEnum.waitingForFirstRender && timestamp > ssrState.renderStart && targetNode === ssrState.reactRootElement) {
|
|
122
|
+
var _this$intersectionObs;
|
|
123
|
+
ssrState.state = SSRStateEnum.ignoring;
|
|
124
|
+
if (ssrState.renderStop === -1) {
|
|
125
|
+
// arbitrary 500ms DOM update window
|
|
126
|
+
ssrState.renderStop = timestamp + 500;
|
|
127
|
+
}
|
|
128
|
+
(_this$intersectionObs = this.intersectionObserver) === null || _this$intersectionObs === void 0 ? void 0 : _this$intersectionObs.watchAndTag(addedNode, 'ssr-hydration');
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (ssrState.state === SSRStateEnum.ignoring && timestamp > ssrState.renderStart && targetNode === ssrState.reactRootElement) {
|
|
132
|
+
if (timestamp <= ssrState.renderStop) {
|
|
133
|
+
var _this$intersectionObs2;
|
|
134
|
+
(_this$intersectionObs2 = this.intersectionObserver) === null || _this$intersectionObs2 === void 0 ? void 0 : _this$intersectionObs2.watchAndTag(addedNode, 'ssr-hydration');
|
|
135
|
+
continue;
|
|
136
|
+
} else {
|
|
137
|
+
ssrState.state = SSRStateEnum.normal;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// SSR placeholder logic - check and handle with await
|
|
143
|
+
if (this.getSSRPlaceholderHandler && fg('platform_ufo_vc_v3_ssr_placeholder')) {
|
|
144
|
+
const ssrPlaceholderHandler = this.getSSRPlaceholderHandler();
|
|
145
|
+
if (ssrPlaceholderHandler) {
|
|
146
|
+
if (ssrPlaceholderHandler.isPlaceholder(addedNode) || ssrPlaceholderHandler.isPlaceholderIgnored(addedNode)) {
|
|
147
|
+
const result = await ssrPlaceholderHandler.checkIfExistedAndSizeMatching(addedNode);
|
|
148
|
+
if (result !== false) {
|
|
149
|
+
var _this$intersectionObs3;
|
|
150
|
+
(_this$intersectionObs3 = this.intersectionObserver) === null || _this$intersectionObs3 === void 0 ? void 0 : _this$intersectionObs3.watchAndTag(addedNode, 'mutation:ssr-placeholder');
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
// If result is false, continue to normal mutation logic below
|
|
154
|
+
}
|
|
155
|
+
if (ssrPlaceholderHandler.isPlaceholderReplacement(addedNode) || ssrPlaceholderHandler.isPlaceholderIgnored(addedNode)) {
|
|
156
|
+
const result = await ssrPlaceholderHandler.validateReactComponentMatchToPlaceholder(addedNode);
|
|
157
|
+
if (result !== false) {
|
|
158
|
+
var _this$intersectionObs4;
|
|
159
|
+
(_this$intersectionObs4 = this.intersectionObserver) === null || _this$intersectionObs4 === void 0 ? void 0 : _this$intersectionObs4.watchAndTag(addedNode, 'mutation:ssr-placeholder');
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
// If result is false, continue to normal mutation logic below
|
|
163
|
+
}
|
|
164
|
+
}
|
|
104
165
|
}
|
|
105
166
|
const sameDeletedNode = removedNodes.find(ref => {
|
|
106
167
|
const n = ref.deref();
|
|
@@ -115,27 +176,27 @@ export default class ViewportObserver {
|
|
|
115
176
|
// When fg('platform_vc_ignore_no_ls_mutation_marker') is not enabled,
|
|
116
177
|
// no layout shift mutation is excluded as per existing fy25.03 logic
|
|
117
178
|
if (sameDeletedNode && (!isNoLsMarkerEnabled || isInIgnoreLsMarker)) {
|
|
118
|
-
var _this$
|
|
119
|
-
(_this$
|
|
120
|
-
|
|
179
|
+
var _this$intersectionObs5;
|
|
180
|
+
(_this$intersectionObs5 = this.intersectionObserver) === null || _this$intersectionObs5 === void 0 ? void 0 : _this$intersectionObs5.watchAndTag(addedNode, 'mutation:remount');
|
|
181
|
+
continue;
|
|
121
182
|
}
|
|
122
183
|
if (isContainedWithinMediaWrapper(addedNode)) {
|
|
123
|
-
var _this$
|
|
124
|
-
(_this$
|
|
125
|
-
|
|
184
|
+
var _this$intersectionObs6;
|
|
185
|
+
(_this$intersectionObs6 = this.intersectionObserver) === null || _this$intersectionObs6 === void 0 ? void 0 : _this$intersectionObs6.watchAndTag(addedNode, 'mutation:media');
|
|
186
|
+
continue;
|
|
126
187
|
}
|
|
127
188
|
const {
|
|
128
189
|
isWithinThirdPartySegment,
|
|
129
190
|
ignoredReason
|
|
130
191
|
} = checkThirdPartySegmentWithIgnoreReason(addedNode);
|
|
131
192
|
if (isWithinThirdPartySegment) {
|
|
132
|
-
var _this$
|
|
193
|
+
var _this$intersectionObs7;
|
|
133
194
|
const assignedReason = createMutationTypeWithIgnoredReason(ignoredReason || 'third-party-element');
|
|
134
|
-
(_this$
|
|
135
|
-
|
|
195
|
+
(_this$intersectionObs7 = this.intersectionObserver) === null || _this$intersectionObs7 === void 0 ? void 0 : _this$intersectionObs7.watchAndTag(addedNode, assignedReason);
|
|
196
|
+
continue;
|
|
136
197
|
}
|
|
137
|
-
(_this$
|
|
138
|
-
}
|
|
198
|
+
(_this$intersectionObs8 = this.intersectionObserver) === null || _this$intersectionObs8 === void 0 ? void 0 : _this$intersectionObs8.watchAndTag(addedNode, createElementMutationsWatcher(removedNodeRects));
|
|
199
|
+
}
|
|
139
200
|
});
|
|
140
201
|
_defineProperty(this, "handleAttributeMutation", ({
|
|
141
202
|
target,
|
|
@@ -143,8 +204,8 @@ export default class ViewportObserver {
|
|
|
143
204
|
oldValue,
|
|
144
205
|
newValue
|
|
145
206
|
}) => {
|
|
146
|
-
var _this$
|
|
147
|
-
(_this$
|
|
207
|
+
var _this$intersectionObs9;
|
|
208
|
+
(_this$intersectionObs9 = this.intersectionObserver) === null || _this$intersectionObs9 === void 0 ? void 0 : _this$intersectionObs9.watchAndTag(target, ({
|
|
148
209
|
target,
|
|
149
210
|
rect
|
|
150
211
|
}) => {
|
|
@@ -243,6 +304,10 @@ export default class ViewportObserver {
|
|
|
243
304
|
this.intersectionObserver = null;
|
|
244
305
|
this.mutationObserver = null;
|
|
245
306
|
this.performanceObserver = null;
|
|
307
|
+
|
|
308
|
+
// Initialize SSR context functions
|
|
309
|
+
this.getSSRState = getSSRState;
|
|
310
|
+
this.getSSRPlaceholderHandler = getSSRPlaceholderHandler;
|
|
246
311
|
}
|
|
247
312
|
initializeObservers() {
|
|
248
313
|
if (this.isStarted) {
|
|
@@ -280,12 +345,12 @@ export default class ViewportObserver {
|
|
|
280
345
|
this.isStarted = true;
|
|
281
346
|
}
|
|
282
347
|
stop() {
|
|
283
|
-
var _this$mutationObserve2, _this$
|
|
348
|
+
var _this$mutationObserve2, _this$intersectionObs0, _this$performanceObse2;
|
|
284
349
|
if (!this.isStarted) {
|
|
285
350
|
return;
|
|
286
351
|
}
|
|
287
352
|
(_this$mutationObserve2 = this.mutationObserver) === null || _this$mutationObserve2 === void 0 ? void 0 : _this$mutationObserve2.disconnect();
|
|
288
|
-
(_this$
|
|
353
|
+
(_this$intersectionObs0 = this.intersectionObserver) === null || _this$intersectionObs0 === void 0 ? void 0 : _this$intersectionObs0.disconnect();
|
|
289
354
|
(_this$performanceObse2 = this.performanceObserver) === null || _this$performanceObse2 === void 0 ? void 0 : _this$performanceObse2.disconnect();
|
|
290
355
|
this.isStarted = false;
|
|
291
356
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
// Batched mutation data for performance optimization
|
|
2
|
+
|
|
2
3
|
function createMutationObserver({
|
|
3
4
|
onAttributeMutation,
|
|
4
5
|
onChildListMutation,
|
|
@@ -8,10 +9,10 @@ function createMutationObserver({
|
|
|
8
9
|
return null;
|
|
9
10
|
}
|
|
10
11
|
const mutationObserverCallback = mutations => {
|
|
11
|
-
const addedNodes = [];
|
|
12
|
-
const removedNodes = [];
|
|
13
|
-
const attributeMutations = [];
|
|
14
12
|
const targets = [];
|
|
13
|
+
// Use nested Maps for O(1) batching performance
|
|
14
|
+
// Short-lived Maps are safe since they're discarded after each callback
|
|
15
|
+
const batchedMutations = new Map();
|
|
15
16
|
for (const mut of mutations) {
|
|
16
17
|
if (!(mut.target instanceof HTMLElement)) {
|
|
17
18
|
continue;
|
|
@@ -29,51 +30,66 @@ function createMutationObserver({
|
|
|
29
30
|
const oldValue = (_mut$oldValue = mut.oldValue) !== null && _mut$oldValue !== void 0 ? _mut$oldValue : undefined;
|
|
30
31
|
const newValue = mut.attributeName ? mut.target.getAttribute(mut.attributeName) : undefined;
|
|
31
32
|
if (oldValue !== newValue) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
});
|
|
40
|
-
} else {
|
|
41
|
-
var _mut$attributeName2;
|
|
42
|
-
onAttributeMutation({
|
|
43
|
-
target: mut.target,
|
|
44
|
-
attributeName: (_mut$attributeName2 = mut.attributeName) !== null && _mut$attributeName2 !== void 0 ? _mut$attributeName2 : 'unknown',
|
|
45
|
-
oldValue,
|
|
46
|
-
newValue
|
|
47
|
-
});
|
|
48
|
-
}
|
|
33
|
+
var _mut$attributeName;
|
|
34
|
+
onAttributeMutation({
|
|
35
|
+
target: mut.target,
|
|
36
|
+
attributeName: (_mut$attributeName = mut.attributeName) !== null && _mut$attributeName !== void 0 ? _mut$attributeName : 'unknown',
|
|
37
|
+
oldValue,
|
|
38
|
+
newValue
|
|
39
|
+
});
|
|
49
40
|
}
|
|
50
41
|
continue;
|
|
51
42
|
} else if (mut.type === 'childList') {
|
|
52
43
|
var _mut$addedNodes, _mut$removedNodes;
|
|
44
|
+
// In chromium browser MutationRecord has timestamp field, which we should use.
|
|
45
|
+
const timestamp = Math.round(mut.timestamp || performance.now());
|
|
46
|
+
|
|
47
|
+
// Get or create timestamp bucket
|
|
48
|
+
let timestampBucket = batchedMutations.get(timestamp);
|
|
49
|
+
if (!timestampBucket) {
|
|
50
|
+
timestampBucket = new Map();
|
|
51
|
+
batchedMutations.set(timestamp, timestampBucket);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Get or create target batch within timestamp bucket
|
|
55
|
+
let batch = timestampBucket.get(mut.target);
|
|
56
|
+
if (!batch) {
|
|
57
|
+
batch = {
|
|
58
|
+
target: new WeakRef(mut.target),
|
|
59
|
+
addedNodes: [],
|
|
60
|
+
removedNodes: [],
|
|
61
|
+
timestamp
|
|
62
|
+
};
|
|
63
|
+
timestampBucket.set(mut.target, batch);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Accumulate added nodes
|
|
53
67
|
((_mut$addedNodes = mut.addedNodes) !== null && _mut$addedNodes !== void 0 ? _mut$addedNodes : []).forEach(node => {
|
|
54
68
|
if (node instanceof HTMLElement) {
|
|
55
|
-
addedNodes.push(new WeakRef(node));
|
|
69
|
+
batch.addedNodes.push(new WeakRef(node));
|
|
56
70
|
}
|
|
57
71
|
});
|
|
72
|
+
|
|
73
|
+
// Accumulate removed nodes
|
|
58
74
|
((_mut$removedNodes = mut.removedNodes) !== null && _mut$removedNodes !== void 0 ? _mut$removedNodes : []).forEach(node => {
|
|
59
75
|
if (node instanceof HTMLElement) {
|
|
60
|
-
removedNodes.push(new WeakRef(node));
|
|
76
|
+
batch.removedNodes.push(new WeakRef(node));
|
|
61
77
|
}
|
|
62
78
|
});
|
|
63
79
|
}
|
|
64
80
|
targets.push(mut.target);
|
|
65
81
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
82
|
+
|
|
83
|
+
// Process all batched childList mutations
|
|
84
|
+
for (const timestampBucket of batchedMutations.values()) {
|
|
85
|
+
for (const batch of timestampBucket.values()) {
|
|
86
|
+
onChildListMutation({
|
|
87
|
+
target: batch.target,
|
|
88
|
+
addedNodes: batch.addedNodes,
|
|
89
|
+
removedNodes: batch.removedNodes,
|
|
90
|
+
timestamp: batch.timestamp
|
|
91
|
+
});
|
|
92
|
+
}
|
|
77
93
|
}
|
|
78
94
|
onMutationFinished === null || onMutationFinished === void 0 ? void 0 : onMutationFinished({
|
|
79
95
|
targets
|
|
@@ -84,17 +84,19 @@ export function getExperimentalVCMetrics(_x) {
|
|
|
84
84
|
}
|
|
85
85
|
function _getExperimentalVCMetrics() {
|
|
86
86
|
_getExperimentalVCMetrics = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(interaction) {
|
|
87
|
-
var _interaction$apdex, prefix, result, VC, pageVisibilityUpToTTAI;
|
|
87
|
+
var vcObserver, _interaction$apdex, prefix, result, VC, pageVisibilityUpToTTAI;
|
|
88
88
|
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
89
89
|
while (1) switch (_context.prev = _context.next) {
|
|
90
90
|
case 0:
|
|
91
|
-
if
|
|
92
|
-
|
|
91
|
+
// Use per-interaction VC observer if available, otherwise fall back to global experimentalVC
|
|
92
|
+
vcObserver = interaction.experimentalVCObserver || experimentalVC.vcObserver;
|
|
93
|
+
if (!vcObserver) {
|
|
94
|
+
_context.next = 13;
|
|
93
95
|
break;
|
|
94
96
|
}
|
|
95
97
|
prefix = 'ufo-experimental';
|
|
96
|
-
_context.next =
|
|
97
|
-
return
|
|
98
|
+
_context.next = 5;
|
|
99
|
+
return vcObserver.getVCResult({
|
|
98
100
|
start: interaction.start,
|
|
99
101
|
stop: interaction.end,
|
|
100
102
|
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,
|
|
@@ -104,28 +106,28 @@ function _getExperimentalVCMetrics() {
|
|
|
104
106
|
experienceKey: interaction.ufoName,
|
|
105
107
|
interactionId: interaction.id
|
|
106
108
|
});
|
|
107
|
-
case
|
|
109
|
+
case 5:
|
|
108
110
|
result = _context.sent;
|
|
109
111
|
VC = result === null || result === void 0 ? void 0 : result['metrics:vc'];
|
|
110
112
|
if (!(!VC || !(result !== null && result !== void 0 && result["".concat(prefix, ":vc:clean")]))) {
|
|
111
|
-
_context.next =
|
|
113
|
+
_context.next = 9;
|
|
112
114
|
break;
|
|
113
115
|
}
|
|
114
116
|
return _context.abrupt("return", result);
|
|
115
|
-
case
|
|
117
|
+
case 9:
|
|
116
118
|
pageVisibilityUpToTTAI = getPageVisibilityState(interaction.start, interaction.end);
|
|
117
119
|
if (!(interaction.abortReason || pageVisibilityUpToTTAI !== 'visible')) {
|
|
118
|
-
_context.next =
|
|
120
|
+
_context.next = 12;
|
|
119
121
|
break;
|
|
120
122
|
}
|
|
121
123
|
return _context.abrupt("return", result);
|
|
122
|
-
case
|
|
124
|
+
case 12:
|
|
123
125
|
return _context.abrupt("return", _objectSpread(_objectSpread({}, result), {}, {
|
|
124
126
|
'metric:experimental:vc90': VC['90']
|
|
125
127
|
}));
|
|
126
|
-
case 12:
|
|
127
|
-
return _context.abrupt("return", null);
|
|
128
128
|
case 13:
|
|
129
|
+
return _context.abrupt("return", null);
|
|
130
|
+
case 14:
|
|
129
131
|
case "end":
|
|
130
132
|
return _context.stop();
|
|
131
133
|
}
|
|
@@ -16,7 +16,7 @@ function getVCMetrics(_x) {
|
|
|
16
16
|
function _getVCMetrics() {
|
|
17
17
|
_getVCMetrics = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(interaction) {
|
|
18
18
|
var _config$vc, _config$vc$ssrWhiteli, _interaction$apdex, _config$vc2, _config$vc3, _config$experimentalI, _result$ufoVcRev;
|
|
19
|
-
var config, interactionStatus, pageVisibilityUpToTTAI, shouldReportVCMetrics, isSSREnabled, ssr, tti, prefix, result, mostRecentVCRevision, mostRecentVCRevisionPayload;
|
|
19
|
+
var config, interactionStatus, pageVisibilityUpToTTAI, shouldReportVCMetrics, observer, isSSREnabled, ssr, tti, prefix, result, mostRecentVCRevision, mostRecentVCRevisionPayload;
|
|
20
20
|
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
21
21
|
while (1) switch (_context.prev = _context.next) {
|
|
22
22
|
case 0:
|
|
@@ -27,7 +27,7 @@ function _getVCMetrics() {
|
|
|
27
27
|
}
|
|
28
28
|
return _context.abrupt("return", {});
|
|
29
29
|
case 3:
|
|
30
|
-
if (!
|
|
30
|
+
if (!fg('platform_ufo_enable_vc_press_interactions')) {
|
|
31
31
|
_context.next = 8;
|
|
32
32
|
break;
|
|
33
33
|
}
|
|
@@ -48,14 +48,15 @@ function _getVCMetrics() {
|
|
|
48
48
|
case 10:
|
|
49
49
|
interactionStatus = getInteractionStatus(interaction);
|
|
50
50
|
pageVisibilityUpToTTAI = getPageVisibilityUpToTTAI(interaction);
|
|
51
|
-
shouldReportVCMetrics = interactionStatus.originalInteractionStatus === 'SUCCEEDED' && pageVisibilityUpToTTAI === 'visible';
|
|
51
|
+
shouldReportVCMetrics = interactionStatus.originalInteractionStatus === 'SUCCEEDED' && pageVisibilityUpToTTAI === 'visible'; // Use per-interaction VC observer if available, otherwise fall back to global
|
|
52
|
+
observer = interaction.vcObserver || getVCObserver();
|
|
52
53
|
if (!(!shouldReportVCMetrics && fg('platform_ufo_no_vc_on_aborted'))) {
|
|
53
|
-
_context.next =
|
|
54
|
+
_context.next = 17;
|
|
54
55
|
break;
|
|
55
56
|
}
|
|
56
|
-
|
|
57
|
+
observer.stop(interaction.ufoName);
|
|
57
58
|
return _context.abrupt("return", {});
|
|
58
|
-
case
|
|
59
|
+
case 17:
|
|
59
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)));
|
|
60
61
|
ssr = interaction.type === 'page_load' && isSSREnabled ? {
|
|
61
62
|
ssr: getSSRDoneTimeValue(config)
|
|
@@ -63,8 +64,8 @@ function _getVCMetrics() {
|
|
|
63
64
|
postInteractionLog.setVCObserverSSRConfig(ssr);
|
|
64
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;
|
|
65
66
|
prefix = 'ufo';
|
|
66
|
-
_context.next =
|
|
67
|
-
return
|
|
67
|
+
_context.next = 24;
|
|
68
|
+
return observer.getVCResult(_objectSpread({
|
|
68
69
|
start: interaction.start,
|
|
69
70
|
stop: interaction.end,
|
|
70
71
|
tti: tti,
|
|
@@ -76,10 +77,13 @@ function _getVCMetrics() {
|
|
|
76
77
|
interactionId: interaction.id,
|
|
77
78
|
includeSSRRatio: (_config$vc3 = config.vc) === null || _config$vc3 === void 0 ? void 0 : _config$vc3.includeSSRRatio
|
|
78
79
|
}, ssr));
|
|
79
|
-
case
|
|
80
|
+
case 24:
|
|
80
81
|
result = _context.sent;
|
|
82
|
+
if (fg('platform_ufo_enable_vc_observer_per_interaction')) {
|
|
83
|
+
observer.stop(interaction.ufoName);
|
|
84
|
+
}
|
|
81
85
|
if ((_config$experimentalI = config.experimentalInteractionMetrics) !== null && _config$experimentalI !== void 0 && _config$experimentalI.enabled) {
|
|
82
|
-
|
|
86
|
+
observer.stop(interaction.ufoName);
|
|
83
87
|
}
|
|
84
88
|
postInteractionLog.setLastInteractionFinishVCResult(result);
|
|
85
89
|
mostRecentVCRevision = getMostRecentVCRevision(interaction.ufoName);
|
|
@@ -88,15 +92,15 @@ function _getVCMetrics() {
|
|
|
88
92
|
return revision === mostRecentVCRevision;
|
|
89
93
|
});
|
|
90
94
|
if (!(!shouldReportVCMetrics || !(mostRecentVCRevisionPayload !== null && mostRecentVCRevisionPayload !== void 0 && mostRecentVCRevisionPayload.clean))) {
|
|
91
|
-
_context.next =
|
|
95
|
+
_context.next = 32;
|
|
92
96
|
break;
|
|
93
97
|
}
|
|
94
98
|
return _context.abrupt("return", result);
|
|
95
|
-
case
|
|
99
|
+
case 32:
|
|
96
100
|
return _context.abrupt("return", _objectSpread(_objectSpread({}, result), {}, {
|
|
97
101
|
'metric:vc90': mostRecentVCRevisionPayload['metric:vc90']
|
|
98
102
|
}));
|
|
99
|
-
case
|
|
103
|
+
case 33:
|
|
100
104
|
case "end":
|
|
101
105
|
return _context.stop();
|
|
102
106
|
}
|
|
@@ -16,7 +16,7 @@ import { experimentalVC, getExperimentalVCMetrics, onExperimentalInteractionComp
|
|
|
16
16
|
import { clearActiveTrace } from '../experience-trace-id-context';
|
|
17
17
|
import { allFeatureFlagsAccessed, currentFeatureFlagsAccessed } from '../feature-flags-accessed';
|
|
18
18
|
import { getInteractionId } from '../interaction-id-context';
|
|
19
|
-
import { getVCObserver } from '../vc';
|
|
19
|
+
import { getVCObserver, newVCObserver } from '../vc';
|
|
20
20
|
import { interactions } from './common/constants';
|
|
21
21
|
import PostInteractionLog from './post-interaction-log';
|
|
22
22
|
var PreviousInteractionLog = {
|
|
@@ -523,7 +523,9 @@ function finishInteraction(id, data) {
|
|
|
523
523
|
clearActiveTrace();
|
|
524
524
|
callCleanUpCallbacks(data);
|
|
525
525
|
if ((_getConfig4 = getConfig()) !== null && _getConfig4 !== void 0 && (_getConfig4 = _getConfig4.vc) !== null && _getConfig4 !== void 0 && _getConfig4.stopVCAtInteractionFinish) {
|
|
526
|
-
|
|
526
|
+
// Use per-interaction VC observer if available, otherwise fall back to global
|
|
527
|
+
var observer = data.vcObserver || getVCObserver();
|
|
528
|
+
data.vc = observer.getVCRawData();
|
|
527
529
|
}
|
|
528
530
|
if (!((_getConfig5 = getConfig()) !== null && _getConfig5 !== void 0 && (_getConfig5 = _getConfig5.experimentalInteractionMetrics) !== null && _getConfig5 !== void 0 && _getConfig5.enabled)) {
|
|
529
531
|
remove(id);
|
|
@@ -735,6 +737,7 @@ export function addNewInteraction(interactionId, ufoName, type, startTime, rate,
|
|
|
735
737
|
if ((_getConfig11 = getConfig()) !== null && _getConfig11 !== void 0 && (_getConfig11 = _getConfig11.postInteractionLog) !== null && _getConfig11 !== void 0 && _getConfig11.enabled) {
|
|
736
738
|
postInteractionLog.reset();
|
|
737
739
|
}
|
|
740
|
+
var vcObserver;
|
|
738
741
|
var previousTime = startTime;
|
|
739
742
|
var timeoutTime = fg('platform_ufo_enable_timeout_config') ? getInteractionTimeout(ufoName) : CLEANUP_TIMEOUT;
|
|
740
743
|
var timerID = setTimeout(function () {
|
|
@@ -757,6 +760,21 @@ export function addNewInteraction(interactionId, ufoName, type, startTime, rate,
|
|
|
757
760
|
this.timerID = newTimerID;
|
|
758
761
|
}
|
|
759
762
|
var addFeatureFlagsToInteraction = coinflip(getCapabilityRate('feature_flag_access'));
|
|
763
|
+
var config = getConfig();
|
|
764
|
+
if (config && config.vc) {
|
|
765
|
+
var vcOptions = {
|
|
766
|
+
heatmapSize: config.vc.heatmapSize,
|
|
767
|
+
oldDomUpdates: config.vc.oldDomUpdates,
|
|
768
|
+
devToolsEnabled: config.vc.devToolsEnabled,
|
|
769
|
+
selectorConfig: config.vc.selectorConfig,
|
|
770
|
+
ssrEnablePageLayoutPlaceholder: config.vc.ssrEnablePageLayoutPlaceholder,
|
|
771
|
+
disableSizeAndPositionCheck: config.vc.disableSizeAndPositionCheck
|
|
772
|
+
};
|
|
773
|
+
vcObserver = fg('platform_ufo_enable_vc_observer_per_interaction') ? newVCObserver(vcOptions) : undefined;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// Create per-interaction VC observer when feature flag is enabled
|
|
777
|
+
|
|
760
778
|
var metrics = {
|
|
761
779
|
id: interactionId,
|
|
762
780
|
start: startTime,
|
|
@@ -797,7 +815,8 @@ export function addNewInteraction(interactionId, ufoName, type, startTime, rate,
|
|
|
797
815
|
redirects: [],
|
|
798
816
|
timerID: timerID,
|
|
799
817
|
changeTimeout: changeTimeout,
|
|
800
|
-
trace: trace
|
|
818
|
+
trace: trace,
|
|
819
|
+
vcObserver: vcObserver
|
|
801
820
|
};
|
|
802
821
|
if (addFeatureFlagsToInteraction) {
|
|
803
822
|
currentFeatureFlagsAccessed.clear();
|
|
@@ -816,13 +835,21 @@ export function addNewInteraction(interactionId, ufoName, type, startTime, rate,
|
|
|
816
835
|
metrics.cleanupCallbacks.push(function () {
|
|
817
836
|
clearTimeout(metrics.timerID);
|
|
818
837
|
});
|
|
838
|
+
// Add cleanup for per-interaction VC observer
|
|
839
|
+
if (vcObserver) {
|
|
840
|
+
metrics.cleanupCallbacks.push(function () {
|
|
841
|
+
vcObserver.stop(ufoName);
|
|
842
|
+
});
|
|
843
|
+
}
|
|
819
844
|
var awaitBM3TTIList = getAwaitBM3TTIList();
|
|
820
845
|
if (awaitBM3TTIList.includes(ufoName)) {
|
|
821
846
|
addHoldByID(interactionId, [], ufoName, ufoName, true);
|
|
822
847
|
}
|
|
823
|
-
if (type === 'transition') {
|
|
848
|
+
if (type === 'transition' || type === 'page_load') {
|
|
824
849
|
var _getConfig12;
|
|
825
|
-
|
|
850
|
+
// Use per-interaction VC observer if available, otherwise fall back to global
|
|
851
|
+
var observer = vcObserver || getVCObserver();
|
|
852
|
+
observer.start({
|
|
826
853
|
startTime: startTime,
|
|
827
854
|
experienceKey: ufoName
|
|
828
855
|
});
|
|
@@ -835,20 +862,13 @@ export function addNewInteraction(interactionId, ufoName, type, startTime, rate,
|
|
|
835
862
|
});
|
|
836
863
|
}
|
|
837
864
|
}
|
|
838
|
-
if (type === 'press' &&
|
|
839
|
-
|
|
840
|
-
getVCObserver()
|
|
865
|
+
if (type === 'press' && fg('platform_ufo_enable_vc_press_interactions')) {
|
|
866
|
+
// Use per-interaction VC observer if available, otherwise fall back to global
|
|
867
|
+
var _observer = vcObserver || getVCObserver();
|
|
868
|
+
_observer.start({
|
|
841
869
|
startTime: startTime,
|
|
842
870
|
experienceKey: ufoName
|
|
843
871
|
});
|
|
844
|
-
postInteractionLog.startVCObserver({
|
|
845
|
-
startTime: startTime
|
|
846
|
-
});
|
|
847
|
-
if ((_getConfig13 = getConfig()) !== null && _getConfig13 !== void 0 && (_getConfig13 = _getConfig13.experimentalInteractionMetrics) !== null && _getConfig13 !== void 0 && _getConfig13.enabled) {
|
|
848
|
-
experimentalVC.start({
|
|
849
|
-
startTime: startTime
|
|
850
|
-
});
|
|
851
|
-
}
|
|
852
872
|
}
|
|
853
873
|
}
|
|
854
874
|
export function addBrowserMetricEvent(event) {
|