@atlaskit/react-ufo 3.13.17 → 3.13.19
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/ssr/index.js +8 -2
- package/dist/cjs/vc/index.js +2 -0
- package/dist/cjs/vc/vc-observer/getVCRevisionDebugDetails.js +109 -11
- package/dist/cjs/vc/vc-observer/index.js +56 -43
- package/dist/cjs/vc/vc-observer/observers/index.js +4 -0
- package/dist/cjs/vc/vc-observer/observers/rll-placeholders/index.js +208 -0
- package/dist/cjs/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +129 -16
- package/dist/cjs/vc/vc-observer-new/viewport-observer/index.js +16 -0
- package/dist/es2019/ssr/index.js +5 -1
- package/dist/es2019/vc/index.js +2 -0
- package/dist/es2019/vc/vc-observer/getVCRevisionDebugDetails.js +71 -9
- package/dist/es2019/vc/vc-observer/index.js +39 -22
- package/dist/es2019/vc/vc-observer/observers/index.js +4 -0
- package/dist/es2019/vc/vc-observer/observers/rll-placeholders/index.js +182 -0
- package/dist/es2019/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +102 -11
- package/dist/es2019/vc/vc-observer-new/viewport-observer/index.js +16 -0
- package/dist/esm/ssr/index.js +8 -2
- package/dist/esm/vc/index.js +2 -0
- package/dist/esm/vc/vc-observer/getVCRevisionDebugDetails.js +108 -11
- package/dist/esm/vc/vc-observer/index.js +56 -43
- package/dist/esm/vc/vc-observer/observers/index.js +4 -0
- package/dist/esm/vc/vc-observer/observers/rll-placeholders/index.js +201 -0
- package/dist/esm/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +130 -16
- package/dist/esm/vc/vc-observer-new/viewport-observer/index.js +16 -0
- package/dist/types/common/vc/types.d.ts +1 -1
- package/dist/types/ssr/index.d.ts +1 -0
- package/dist/types/vc/index.d.ts +1 -0
- package/dist/types/vc/vc-observer/getVCRevisionDebugDetails.d.ts +19 -18
- package/dist/types/vc/vc-observer/index.d.ts +7 -0
- package/dist/types/vc/vc-observer/observers/rll-placeholders/index.d.ts +49 -0
- package/dist/types/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +7 -0
- package/dist/types/vc/vc-observer-new/types.d.ts +1 -1
- package/dist/types-ts4.5/common/vc/types.d.ts +1 -1
- package/dist/types-ts4.5/ssr/index.d.ts +1 -0
- package/dist/types-ts4.5/vc/index.d.ts +1 -0
- package/dist/types-ts4.5/vc/vc-observer/getVCRevisionDebugDetails.d.ts +19 -18
- package/dist/types-ts4.5/vc/vc-observer/index.d.ts +7 -0
- package/dist/types-ts4.5/vc/vc-observer/observers/rll-placeholders/index.d.ts +49 -0
- package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.d.ts +7 -0
- package/dist/types-ts4.5/vc/vc-observer-new/types.d.ts +1 -1
- package/package.json +10 -1
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
3
|
+
const GLOBAL_RLL_HANDLERS_KEY = '__REACT_UFO_RLL_PLACEHOLDER_HANDLERS__';
|
|
4
|
+
export class RLLPlaceholderHandlers {
|
|
5
|
+
constructor() {
|
|
6
|
+
_defineProperty(this, "placeholders", []);
|
|
7
|
+
if (typeof window !== 'undefined' && typeof document !== 'undefined' && typeof window.document !== 'undefined') {
|
|
8
|
+
this.collectRLLPlaceholders();
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Gets the global singleton instance of RLLPlaceholderHandlers.
|
|
14
|
+
* Creates the instance if it doesn't exist and stores it in globalThis.
|
|
15
|
+
* @returns The singleton instance of RLLPlaceholderHandlers
|
|
16
|
+
*/
|
|
17
|
+
static getInstance() {
|
|
18
|
+
if (typeof globalThis !== 'undefined') {
|
|
19
|
+
if (!globalThis[GLOBAL_RLL_HANDLERS_KEY]) {
|
|
20
|
+
globalThis[GLOBAL_RLL_HANDLERS_KEY] = new RLLPlaceholderHandlers();
|
|
21
|
+
}
|
|
22
|
+
return globalThis[GLOBAL_RLL_HANDLERS_KEY];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Fallback for environments without globalThis (should be rare)
|
|
26
|
+
return new RLLPlaceholderHandlers();
|
|
27
|
+
}
|
|
28
|
+
reset() {
|
|
29
|
+
this.placeholders = [];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Collects all React Loosely Lazy (RLL) placeholders from the DOM and caches their viewport intersecting rectangles.
|
|
34
|
+
* RLL placeholders are marked with data-lazy-begin and data-lazy-end attributes on hidden input elements.
|
|
35
|
+
* Performance optimized to batch getBoundingClientRect calls and minimize layout thrashing.
|
|
36
|
+
* Only stores the intersecting portions of rectangles that are currently visible in the viewport.
|
|
37
|
+
*/
|
|
38
|
+
collectRLLPlaceholders() {
|
|
39
|
+
if (typeof window === 'undefined' || typeof document === 'undefined' || typeof window.document === 'undefined') {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (!fg('platform_ufo_rll_placeholder_ignore')) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const beginElements = document.querySelectorAll('input[data-lazy-begin]');
|
|
46
|
+
const beginCount = beginElements.length;
|
|
47
|
+
if (beginCount === 0) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Performance optimization: pre-allocate array with estimated size
|
|
52
|
+
const allElements = [];
|
|
53
|
+
|
|
54
|
+
// Performance optimization: use traditional for loop instead of forEach
|
|
55
|
+
for (let i = 0; i < beginCount; i++) {
|
|
56
|
+
const beginEl = beginElements[i];
|
|
57
|
+
const id = beginEl.getAttribute('data-lazy-begin');
|
|
58
|
+
if (!id) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const elements = this.refElements(beginEl, id);
|
|
62
|
+
if (elements.length > 0) {
|
|
63
|
+
allElements.push(...elements);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Second pass: batch all getBoundingClientRect calls to minimize reflow cycles
|
|
68
|
+
const allElementsLength = allElements.length;
|
|
69
|
+
if (allElementsLength > 0) {
|
|
70
|
+
// Performance optimization: pre-allocate array with exact size
|
|
71
|
+
const intersectingRects = [];
|
|
72
|
+
const windowWidth = window.innerWidth;
|
|
73
|
+
const windowHeight = window.innerHeight;
|
|
74
|
+
for (let i = 0; i < allElementsLength; i++) {
|
|
75
|
+
const rect = allElements[i].getBoundingClientRect();
|
|
76
|
+
|
|
77
|
+
// Performance optimization: inline intersection calculation to avoid function call overhead
|
|
78
|
+
const left = Math.max(rect.left, 0);
|
|
79
|
+
const top = Math.max(rect.top, 0);
|
|
80
|
+
const right = Math.min(rect.right, windowWidth);
|
|
81
|
+
const bottom = Math.min(rect.bottom, windowHeight);
|
|
82
|
+
|
|
83
|
+
// Check if there's a valid intersection with non-zero width and height
|
|
84
|
+
if (left < right && top < bottom) {
|
|
85
|
+
intersectingRects.push(new DOMRect(left, top, right - left, bottom - top));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
this.placeholders = intersectingRects;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Traverses DOM siblings to find all elements between RLL begin and end markers.
|
|
94
|
+
* Based on the refElements pattern from react-loosely-lazy's platform/packages/async/react-loosely-lazy/src/collect/hydrate.ts
|
|
95
|
+
* Performance optimized to minimize iterations and early exit conditions.
|
|
96
|
+
*
|
|
97
|
+
* @param fromEl - The input element with data-lazy-begin attribute
|
|
98
|
+
* @param id - The placeholder ID to match against data-lazy-end
|
|
99
|
+
* @returns Array of DOM elements between the begin/end markers
|
|
100
|
+
*/
|
|
101
|
+
refElements(fromEl, id) {
|
|
102
|
+
const result = [];
|
|
103
|
+
let el = fromEl.nextSibling;
|
|
104
|
+
|
|
105
|
+
// Performance optimization: use while loop instead of recursive calls
|
|
106
|
+
while (el) {
|
|
107
|
+
var _el$dataset;
|
|
108
|
+
if (((_el$dataset = el.dataset) === null || _el$dataset === void 0 ? void 0 : _el$dataset.lazyEnd) === id) {
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
if (el.nodeType === Node.ELEMENT_NODE) {
|
|
112
|
+
result.push(el);
|
|
113
|
+
}
|
|
114
|
+
el = el.nextSibling;
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Returns the cached intersecting viewport rectangles for all RLL placeholder elements.
|
|
121
|
+
* @returns Array of DOMRect objects representing the intersecting portions of placeholders within the viewport
|
|
122
|
+
*/
|
|
123
|
+
getPlaceholders() {
|
|
124
|
+
return this.placeholders;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Checks if the given intersecting rectangle matches any of the cached RLL placeholder intersecting rectangles.
|
|
129
|
+
* This is designed to be called from IntersectionObserver with the intersectionRect.
|
|
130
|
+
* Performance optimized with early exits and minimal calculations.
|
|
131
|
+
* @param intersectingRect - The intersecting rectangle from IntersectionObserver
|
|
132
|
+
* @returns true if the intersecting rectangle matches a cached placeholder rectangle and hasn't exceeded match limit, false otherwise
|
|
133
|
+
*/
|
|
134
|
+
isRLLPlaceholderHydration(intersectingRect) {
|
|
135
|
+
const placeholdersLength = this.placeholders.length;
|
|
136
|
+
if (placeholdersLength === 0) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Performance optimization: cache array length and use traditional for loop
|
|
141
|
+
for (let i = 0; i < placeholdersLength; i++) {
|
|
142
|
+
const placeholderRect = this.placeholders[i];
|
|
143
|
+
if (this.areRectsEqual(intersectingRect, placeholderRect)) {
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Compares two DOMRect objects for equality with ±1 pixel tolerance.
|
|
152
|
+
* Performance optimized to minimize calculations and early exit on major differences.
|
|
153
|
+
* @param rect1 - First rectangle to compare
|
|
154
|
+
* @param rect2 - Second rectangle to compare
|
|
155
|
+
* @returns true if rectangles are within 1 pixel tolerance for all properties
|
|
156
|
+
*/
|
|
157
|
+
areRectsEqual(rect1, rect2) {
|
|
158
|
+
// Early exit for exact matches (most common case)
|
|
159
|
+
if (rect1.left === rect2.left && rect1.top === rect2.top && rect1.right === rect2.right && rect1.bottom === rect2.bottom) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Performance optimization: check largest differences first for early exit
|
|
164
|
+
const leftDiff = rect1.left - rect2.left;
|
|
165
|
+
if (leftDiff > 1 || leftDiff < -1) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
const rightDiff = rect1.right - rect2.right;
|
|
169
|
+
if (rightDiff > 1 || rightDiff < -1) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
const topDiff = rect1.top - rect2.top;
|
|
173
|
+
if (topDiff > 1 || topDiff < -1) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
const bottomDiff = rect1.bottom - rect2.bottom;
|
|
177
|
+
if (bottomDiff > 1 || bottomDiff < -1) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -2,6 +2,9 @@ import { fg } from '@atlaskit/platform-feature-flags';
|
|
|
2
2
|
import { calculateTTVCPercentilesWithDebugInfo } from './percentile-calc';
|
|
3
3
|
import getViewportHeight from './utils/get-viewport-height';
|
|
4
4
|
import getViewportWidth from './utils/get-viewport-width';
|
|
5
|
+
|
|
6
|
+
// Create comprehensive debug details including ignored entries
|
|
7
|
+
|
|
5
8
|
export default class AbstractVCCalculatorBase {
|
|
6
9
|
constructor(revisionNo) {
|
|
7
10
|
this.revisionNo = revisionNo;
|
|
@@ -36,7 +39,8 @@ export default class AbstractVCCalculatorBase {
|
|
|
36
39
|
}
|
|
37
40
|
return ratios;
|
|
38
41
|
}
|
|
39
|
-
async calculateWithDebugInfo(filteredEntries, startTime, stopTime, isPostInteraction, isVCClean, interactionId, dirtyReason) {
|
|
42
|
+
async calculateWithDebugInfo(filteredEntries, startTime, stopTime, isPostInteraction, isVCClean, interactionId, dirtyReason, allEntries) {
|
|
43
|
+
var _window, _window2, _window3, _window5;
|
|
40
44
|
const percentiles = [25, 50, 75, 80, 85, 90, 95, 98, 99];
|
|
41
45
|
const viewportEntries = this.filterViewportEntries(filteredEntries);
|
|
42
46
|
const vcLogs = await calculateTTVCPercentilesWithDebugInfo({
|
|
@@ -99,24 +103,111 @@ export default class AbstractVCCalculatorBase {
|
|
|
99
103
|
previousResult = vcDetails[`${percentile}`];
|
|
100
104
|
}
|
|
101
105
|
}
|
|
106
|
+
let enhancedVcLogs = vcLogs ? vcLogs.map(log => ({
|
|
107
|
+
...log,
|
|
108
|
+
viewportPercentage: log.viewportPercentage
|
|
109
|
+
})) : [];
|
|
110
|
+
|
|
111
|
+
// Only calculate enhanced debug details if devtool callbacks exist
|
|
112
|
+
const shouldCalculateDebugDetails = !isPostInteraction && (typeof ((_window = window) === null || _window === void 0 ? void 0 : _window.__ufo_devtool_onVCRevisionReady__) === 'function' || typeof ((_window2 = window) === null || _window2 === void 0 ? void 0 : _window2.__on_ufo_vc_debug_data_ready) === 'function' && fg('platform_ufo_emit_vc_debug_data'));
|
|
113
|
+
if (shouldCalculateDebugDetails && allEntries && vcLogs) {
|
|
114
|
+
// Pre-sort vcLogs by time for efficient lookups
|
|
115
|
+
const sortedVcLogs = [...vcLogs].sort((a, b) => a.time - b.time);
|
|
116
|
+
|
|
117
|
+
// Pre-calculate max viewport percentage up to each time for efficient lookups
|
|
118
|
+
const maxViewportPercentageAtTime = new Map();
|
|
119
|
+
let maxSoFar = 0;
|
|
120
|
+
for (const log of sortedVcLogs) {
|
|
121
|
+
if (log.viewportPercentage !== null) {
|
|
122
|
+
maxSoFar = Math.max(maxSoFar, log.viewportPercentage);
|
|
123
|
+
maxViewportPercentageAtTime.set(log.time, maxSoFar);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Helper function to find the biggest previous viewport percentage
|
|
128
|
+
const getBiggestPreviousViewportPercentage = targetTime => {
|
|
129
|
+
// Binary search for the largest time <= targetTime
|
|
130
|
+
let left = 0;
|
|
131
|
+
let right = sortedVcLogs.length - 1;
|
|
132
|
+
let result = -1;
|
|
133
|
+
while (left <= right) {
|
|
134
|
+
const mid = Math.floor((left + right) / 2);
|
|
135
|
+
if (sortedVcLogs[mid].time <= targetTime) {
|
|
136
|
+
result = mid;
|
|
137
|
+
left = mid + 1;
|
|
138
|
+
} else {
|
|
139
|
+
right = mid - 1;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return result >= 0 ? maxViewportPercentageAtTime.get(sortedVcLogs[result].time) || null : null;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Group ignored entries by timestamp
|
|
146
|
+
const ignoredEntriesByTime = new Map();
|
|
147
|
+
for (const entry of allEntries) {
|
|
148
|
+
if ('rect' in entry.data && !this.isEntryIncluded(entry)) {
|
|
149
|
+
var _ignoredEntriesByTime;
|
|
150
|
+
const viewportData = entry.data;
|
|
151
|
+
const timestamp = Math.round(entry.time);
|
|
152
|
+
if (!ignoredEntriesByTime.has(timestamp)) {
|
|
153
|
+
ignoredEntriesByTime.set(timestamp, []);
|
|
154
|
+
}
|
|
155
|
+
(_ignoredEntriesByTime = ignoredEntriesByTime.get(timestamp)) === null || _ignoredEntriesByTime === void 0 ? void 0 : _ignoredEntriesByTime.push({
|
|
156
|
+
...viewportData,
|
|
157
|
+
ignoreReason: viewportData.visible ? viewportData.type : 'not-visible'
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Add ignored entries to vcLogs
|
|
163
|
+
const additionalVcLogs = [];
|
|
164
|
+
for (const [timestamp, ignoredEntries] of ignoredEntriesByTime) {
|
|
165
|
+
if (ignoredEntries.length > 0) {
|
|
166
|
+
const viewportPercentage = getBiggestPreviousViewportPercentage(timestamp);
|
|
167
|
+
additionalVcLogs.push({
|
|
168
|
+
time: timestamp,
|
|
169
|
+
viewportPercentage,
|
|
170
|
+
entries: ignoredEntries
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Combine and sort all vcLogs
|
|
176
|
+
enhancedVcLogs = [...enhancedVcLogs, ...additionalVcLogs].sort((a, b) => a.time - b.time);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Only create debug details if callbacks exist
|
|
180
|
+
let v3RevisionDebugDetails = null;
|
|
181
|
+
if (shouldCalculateDebugDetails) {
|
|
182
|
+
v3RevisionDebugDetails = {
|
|
183
|
+
revision: this.revisionNo,
|
|
184
|
+
isClean: isVCClean,
|
|
185
|
+
abortReason: dirtyReason,
|
|
186
|
+
vcLogs: enhancedVcLogs,
|
|
187
|
+
interactionId
|
|
188
|
+
};
|
|
189
|
+
}
|
|
102
190
|
|
|
103
191
|
// Handle devtool callback
|
|
104
|
-
if (
|
|
192
|
+
if (v3RevisionDebugDetails && typeof ((_window3 = window) === null || _window3 === void 0 ? void 0 : _window3.__ufo_devtool_onVCRevisionReady__) === 'function') {
|
|
105
193
|
try {
|
|
106
|
-
var
|
|
107
|
-
(
|
|
108
|
-
revision: this.revisionNo,
|
|
109
|
-
isClean: isVCClean,
|
|
110
|
-
abortReason: dirtyReason,
|
|
111
|
-
vcLogs,
|
|
112
|
-
interactionId
|
|
113
|
-
});
|
|
194
|
+
var _window4, _window4$__ufo_devtoo;
|
|
195
|
+
(_window4 = window) === null || _window4 === void 0 ? void 0 : (_window4$__ufo_devtoo = _window4.__ufo_devtool_onVCRevisionReady__) === null || _window4$__ufo_devtoo === void 0 ? void 0 : _window4$__ufo_devtoo.call(_window4, v3RevisionDebugDetails);
|
|
114
196
|
} catch (e) {
|
|
115
197
|
// if any error communicating with devtool, we don't want to break the app
|
|
116
198
|
// eslint-disable-next-line no-console
|
|
117
199
|
console.error('Error in onVCRevisionReady', e);
|
|
118
200
|
}
|
|
119
201
|
}
|
|
202
|
+
if (v3RevisionDebugDetails && typeof ((_window5 = window) === null || _window5 === void 0 ? void 0 : _window5.__on_ufo_vc_debug_data_ready) === 'function' && fg('platform_ufo_emit_vc_debug_data')) {
|
|
203
|
+
try {
|
|
204
|
+
var _window6, _window6$__on_ufo_vc_;
|
|
205
|
+
(_window6 = window) === null || _window6 === void 0 ? void 0 : (_window6$__on_ufo_vc_ = _window6.__on_ufo_vc_debug_data_ready) === null || _window6$__on_ufo_vc_ === void 0 ? void 0 : _window6$__on_ufo_vc_.call(_window6, v3RevisionDebugDetails);
|
|
206
|
+
} catch (e) {
|
|
207
|
+
// eslint-disable-next-line no-console
|
|
208
|
+
console.error('Error in onVCRevisionReady', e);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
120
211
|
return vcDetails;
|
|
121
212
|
}
|
|
122
213
|
async calculate({
|
|
@@ -143,7 +234,7 @@ export default class AbstractVCCalculatorBase {
|
|
|
143
234
|
abortReason: dirtyReason
|
|
144
235
|
};
|
|
145
236
|
}
|
|
146
|
-
const vcDetails = await this.calculateWithDebugInfo(filteredEntries, startTime, stopTime, isPostInteraction, isVCClean, interactionId, dirtyReason);
|
|
237
|
+
const vcDetails = await this.calculateWithDebugInfo(filteredEntries, startTime, stopTime, isPostInteraction, isVCClean, interactionId, dirtyReason, orderedEntries);
|
|
147
238
|
const result = {
|
|
148
239
|
revision: this.revisionNo,
|
|
149
240
|
clean: true,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
2
|
import { isContainedWithinMediaWrapper } from '../../vc-observer/media-wrapper/vc-utils';
|
|
3
3
|
import isNonVisualStyleMutation from '../../vc-observer/observers/non-visual-styles/is-non-visual-style-mutation';
|
|
4
|
+
import { RLLPlaceholderHandlers } from '../../vc-observer/observers/rll-placeholders';
|
|
4
5
|
import { createIntersectionObserver } from './intersection-observer';
|
|
5
6
|
import createMutationObserver from './mutation-observer';
|
|
6
7
|
import createPerformanceObserver from './performance-observer';
|
|
@@ -36,6 +37,10 @@ function sameRectDimensions(a, b) {
|
|
|
36
37
|
const createElementMutationsWatcher = removedNodeRects => ({
|
|
37
38
|
rect
|
|
38
39
|
}) => {
|
|
40
|
+
const isRLLPlaceholder = RLLPlaceholderHandlers.getInstance().isRLLPlaceholderHydration(rect);
|
|
41
|
+
if (isRLLPlaceholder) {
|
|
42
|
+
return 'mutation:rll-placeholder';
|
|
43
|
+
}
|
|
39
44
|
const wasDeleted = removedNodeRects.some(nr => sameRectDimensions(nr, rect));
|
|
40
45
|
if (wasDeleted) {
|
|
41
46
|
return 'mutation:element-replacement';
|
|
@@ -141,6 +146,17 @@ export default class ViewportObserver {
|
|
|
141
146
|
}
|
|
142
147
|
};
|
|
143
148
|
}
|
|
149
|
+
const isRLLPlaceholder = RLLPlaceholderHandlers.getInstance().isRLLPlaceholderHydration(rect);
|
|
150
|
+
if (isRLLPlaceholder) {
|
|
151
|
+
return {
|
|
152
|
+
type: 'mutation:rll-placeholder',
|
|
153
|
+
mutationData: {
|
|
154
|
+
attributeName,
|
|
155
|
+
oldValue,
|
|
156
|
+
newValue
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
144
160
|
const lastElementRect = this.mapVisibleNodeRects.get(target);
|
|
145
161
|
if (lastElementRect && sameRectSize(rect, lastElementRect)) {
|
|
146
162
|
return {
|
package/dist/esm/ssr/index.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
1
2
|
import _typeof from "@babel/runtime/helpers/typeof";
|
|
3
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
4
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
5
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
2
6
|
var NESTED_METRIC_SEPARATOR = '/';
|
|
3
7
|
function filterEntry(entry) {
|
|
4
8
|
return !(!entry || _typeof(entry) !== 'object' || entry.startTime < 0 || entry.duration < 0);
|
|
5
9
|
}
|
|
6
10
|
function mapEntry(entry) {
|
|
7
|
-
return {
|
|
11
|
+
return _objectSpread({
|
|
8
12
|
startTime: Math.round(entry.startTime),
|
|
9
13
|
duration: Math.round(entry.duration)
|
|
10
|
-
}
|
|
14
|
+
}, entry.size && fg('platform_ufo_ssr_size_field') ? {
|
|
15
|
+
size: Math.round(entry.size)
|
|
16
|
+
} : {});
|
|
11
17
|
}
|
|
12
18
|
var SSR_PREFIX = 'ssr';
|
|
13
19
|
function mapKey(key) {
|
package/dist/esm/vc/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { isVCRevisionEnabled } from '../config';
|
|
|
11
11
|
import { VCObserverNOOP } from './no-op-vc-observer';
|
|
12
12
|
import { VCObserver } from './vc-observer';
|
|
13
13
|
import VCObserverNew from './vc-observer-new';
|
|
14
|
+
import { RLLPlaceholderHandlers } from './vc-observer/observers/rll-placeholders';
|
|
14
15
|
export var VCObserverWrapper = /*#__PURE__*/function () {
|
|
15
16
|
function VCObserverWrapper() {
|
|
16
17
|
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
@@ -81,6 +82,7 @@ export var VCObserverWrapper = /*#__PURE__*/function () {
|
|
|
81
82
|
var _this$newVCObserver2;
|
|
82
83
|
(_this$newVCObserver2 = this.newVCObserver) === null || _this$newVCObserver2 === void 0 || _this$newVCObserver2.stop();
|
|
83
84
|
}
|
|
85
|
+
RLLPlaceholderHandlers.getInstance().reset();
|
|
84
86
|
}
|
|
85
87
|
}, {
|
|
86
88
|
key: "getVCRawData",
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
3
|
+
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; } } }; }
|
|
4
|
+
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; } }
|
|
5
|
+
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; }
|
|
1
6
|
export function getVCRevisionDebugDetails(_ref) {
|
|
2
7
|
var revision = _ref.revision,
|
|
3
8
|
isClean = _ref.isClean,
|
|
@@ -5,17 +10,59 @@ export function getVCRevisionDebugDetails(_ref) {
|
|
|
5
10
|
VCEntries = _ref.VCEntries,
|
|
6
11
|
componentsLog = _ref.componentsLog,
|
|
7
12
|
interactionId = _ref.interactionId;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
// Pre-sort VCEntries by time for efficient lookups
|
|
14
|
+
var sortedVCEntries = _toConsumableArray(VCEntries).sort(function (a, b) {
|
|
15
|
+
return a.time - b.time;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Pre-calculate max viewport percentage up to each time for efficient lookups
|
|
19
|
+
var maxViewportPercentageAtTime = new Map();
|
|
20
|
+
var maxSoFar = 0;
|
|
21
|
+
var _iterator = _createForOfIteratorHelper(sortedVCEntries),
|
|
22
|
+
_step;
|
|
23
|
+
try {
|
|
24
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
25
|
+
var entry = _step.value;
|
|
26
|
+
maxSoFar = Math.max(maxSoFar, entry.vc);
|
|
27
|
+
maxViewportPercentageAtTime.set(entry.time, maxSoFar);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Helper function to find the biggest previous viewport percentage
|
|
31
|
+
} catch (err) {
|
|
32
|
+
_iterator.e(err);
|
|
33
|
+
} finally {
|
|
34
|
+
_iterator.f();
|
|
35
|
+
}
|
|
36
|
+
var getBiggestPreviousViewportPercentage = function getBiggestPreviousViewportPercentage(targetTime) {
|
|
37
|
+
// Binary search for the largest time <= targetTime
|
|
38
|
+
var left = 0;
|
|
39
|
+
var right = sortedVCEntries.length - 1;
|
|
40
|
+
var result = -1;
|
|
41
|
+
while (left <= right) {
|
|
42
|
+
var mid = Math.floor((left + right) / 2);
|
|
43
|
+
if (sortedVCEntries[mid].time <= targetTime) {
|
|
44
|
+
result = mid;
|
|
45
|
+
left = mid + 1;
|
|
46
|
+
} else {
|
|
47
|
+
right = mid - 1;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return result >= 0 ? maxViewportPercentageAtTime.get(sortedVCEntries[result].time) || null : null;
|
|
51
|
+
};
|
|
52
|
+
var allVcLogs = [];
|
|
53
|
+
|
|
54
|
+
// Add regular VC entries
|
|
55
|
+
var _iterator2 = _createForOfIteratorHelper(VCEntries),
|
|
56
|
+
_step2;
|
|
57
|
+
try {
|
|
58
|
+
var _loop = function _loop() {
|
|
59
|
+
var entry = _step2.value;
|
|
60
|
+
var timeLogEntries = componentsLog[entry.time];
|
|
61
|
+
allVcLogs.push({
|
|
14
62
|
time: entry.time,
|
|
15
63
|
viewportPercentage: entry.vc,
|
|
16
64
|
entries: entry.elements.map(function (element) {
|
|
17
|
-
var
|
|
18
|
-
var logEntry = (_componentsLog$entry$ = componentsLog[entry.time]) === null || _componentsLog$entry$ === void 0 ? void 0 : _componentsLog$entry$.find(function (log) {
|
|
65
|
+
var logEntry = timeLogEntries === null || timeLogEntries === void 0 ? void 0 : timeLogEntries.find(function (log) {
|
|
19
66
|
return log.targetName === element;
|
|
20
67
|
});
|
|
21
68
|
return {
|
|
@@ -25,11 +72,61 @@ export function getVCRevisionDebugDetails(_ref) {
|
|
|
25
72
|
visible: true,
|
|
26
73
|
attributeName: logEntry === null || logEntry === void 0 ? void 0 : logEntry.attributeName,
|
|
27
74
|
oldValue: logEntry === null || logEntry === void 0 ? void 0 : logEntry.oldValue,
|
|
28
|
-
newValue: logEntry === null || logEntry === void 0 ? void 0 : logEntry.newValue
|
|
75
|
+
newValue: logEntry === null || logEntry === void 0 ? void 0 : logEntry.newValue,
|
|
76
|
+
ignoreReason: logEntry === null || logEntry === void 0 ? void 0 : logEntry.ignoreReason
|
|
29
77
|
};
|
|
30
78
|
})
|
|
31
|
-
};
|
|
32
|
-
}
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
82
|
+
_loop();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Add ignored elements - only process timestamps that have ignored elements
|
|
86
|
+
} catch (err) {
|
|
87
|
+
_iterator2.e(err);
|
|
88
|
+
} finally {
|
|
89
|
+
_iterator2.f();
|
|
90
|
+
}
|
|
91
|
+
for (var _i = 0, _Object$entries = Object.entries(componentsLog); _i < _Object$entries.length; _i++) {
|
|
92
|
+
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
|
|
93
|
+
timestamp = _Object$entries$_i[0],
|
|
94
|
+
timeLogEntries = _Object$entries$_i[1];
|
|
95
|
+
var ignoredElements = timeLogEntries.filter(function (log) {
|
|
96
|
+
return log.ignoreReason;
|
|
97
|
+
});
|
|
98
|
+
if (ignoredElements.length === 0) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
var time = Number(timestamp);
|
|
102
|
+
var viewportPercentage = getBiggestPreviousViewportPercentage(time);
|
|
103
|
+
allVcLogs.push({
|
|
104
|
+
time: time,
|
|
105
|
+
viewportPercentage: viewportPercentage,
|
|
106
|
+
entries: ignoredElements.map(function (logEntry) {
|
|
107
|
+
return {
|
|
108
|
+
elementName: logEntry.targetName,
|
|
109
|
+
type: logEntry.type,
|
|
110
|
+
rect: logEntry.intersectionRect,
|
|
111
|
+
visible: false,
|
|
112
|
+
attributeName: logEntry.attributeName,
|
|
113
|
+
oldValue: logEntry.oldValue,
|
|
114
|
+
newValue: logEntry.newValue,
|
|
115
|
+
ignoreReason: logEntry.ignoreReason
|
|
116
|
+
};
|
|
117
|
+
})
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Sort once at the end
|
|
122
|
+
allVcLogs.sort(function (a, b) {
|
|
123
|
+
return a.time - b.time;
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
revision: revision,
|
|
127
|
+
isClean: isClean,
|
|
128
|
+
abortReason: abortReason,
|
|
129
|
+
vcLogs: allVcLogs,
|
|
33
130
|
interactionId: interactionId
|
|
34
131
|
};
|
|
35
132
|
}
|