@atlaskit/react-ufo 2.14.3 → 2.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/cjs/assets/index.js +96 -0
- package/dist/cjs/assets/utils.js +32 -0
- package/dist/cjs/common/assets/types.js +5 -0
- package/dist/cjs/create-payload/index.js +42 -5
- package/dist/cjs/experience-trace-id-context/index.js +5 -1
- package/dist/cjs/segment/segment.js +7 -1
- package/dist/cjs/vc/vc-observer/heatmap/heatmap.js +264 -0
- package/dist/cjs/vc/vc-observer/index.js +119 -26
- package/dist/cjs/vc/vc-observer/revisions/ViewportUpdateClassifier.js +60 -0
- package/dist/cjs/vc/vc-observer/revisions/fy24_01.js +71 -0
- package/dist/cjs/vc/vc-observer/revisions/fy25_01.js +39 -0
- package/dist/cjs/vc/vc-observer/revisions/revisions.js +23 -0
- package/dist/cjs/vc/vc-observer/revisions/types.js +5 -0
- package/dist/es2019/assets/index.js +82 -0
- package/dist/es2019/assets/utils.js +26 -0
- package/dist/es2019/common/assets/types.js +1 -0
- package/dist/es2019/create-payload/index.js +43 -3
- package/dist/es2019/experience-trace-id-context/index.js +4 -0
- package/dist/es2019/segment/segment.js +11 -3
- package/dist/es2019/vc/vc-observer/heatmap/heatmap.js +230 -0
- package/dist/es2019/vc/vc-observer/index.js +97 -3
- package/dist/es2019/vc/vc-observer/revisions/ViewportUpdateClassifier.js +41 -0
- package/dist/es2019/vc/vc-observer/revisions/fy24_01.js +50 -0
- package/dist/es2019/vc/vc-observer/revisions/fy25_01.js +21 -0
- package/dist/es2019/vc/vc-observer/revisions/revisions.js +19 -0
- package/dist/es2019/vc/vc-observer/revisions/types.js +1 -0
- package/dist/esm/assets/index.js +89 -0
- package/dist/esm/assets/utils.js +26 -0
- package/dist/esm/common/assets/types.js +1 -0
- package/dist/esm/create-payload/index.js +42 -5
- package/dist/esm/experience-trace-id-context/index.js +4 -0
- package/dist/esm/segment/segment.js +7 -1
- package/dist/esm/vc/vc-observer/heatmap/heatmap.js +257 -0
- package/dist/esm/vc/vc-observer/index.js +119 -26
- package/dist/esm/vc/vc-observer/revisions/ViewportUpdateClassifier.js +53 -0
- package/dist/esm/vc/vc-observer/revisions/fy24_01.js +64 -0
- package/dist/esm/vc/vc-observer/revisions/fy25_01.js +32 -0
- package/dist/esm/vc/vc-observer/revisions/revisions.js +17 -0
- package/dist/esm/vc/vc-observer/revisions/types.js +1 -0
- package/dist/types/assets/index.d.ts +25 -0
- package/dist/types/assets/utils.d.ts +6 -0
- package/dist/types/common/assets/types.d.ts +18 -0
- package/dist/types/common/index.d.ts +1 -0
- package/dist/types/common/vc/types.d.ts +27 -8
- package/dist/types/config/index.d.ts +2 -1
- package/dist/types/create-payload/index.d.ts +11616 -0
- package/dist/types/experience-trace-id-context/index.d.ts +1 -0
- package/dist/types/resource-timing/index.d.ts +1 -1
- package/dist/types/vc/vc-observer/heatmap/heatmap.d.ts +72 -0
- package/dist/types/vc/vc-observer/index.d.ts +5 -1
- package/dist/types/vc/vc-observer/revisions/ViewportUpdateClassifier.d.ts +30 -0
- package/dist/types/vc/vc-observer/revisions/fy24_01.d.ts +13 -0
- package/dist/types/vc/vc-observer/revisions/fy25_01.d.ts +13 -0
- package/dist/types/vc/vc-observer/revisions/revisions.d.ts +2 -0
- package/dist/types/vc/vc-observer/revisions/types.d.ts +23 -0
- package/dist/types-ts4.5/assets/index.d.ts +25 -0
- package/dist/types-ts4.5/assets/utils.d.ts +6 -0
- package/dist/types-ts4.5/common/assets/types.d.ts +18 -0
- package/dist/types-ts4.5/common/index.d.ts +1 -0
- package/dist/types-ts4.5/common/vc/types.d.ts +27 -8
- package/dist/types-ts4.5/config/index.d.ts +2 -1
- package/dist/types-ts4.5/create-payload/index.d.ts +11616 -0
- package/dist/types-ts4.5/experience-trace-id-context/index.d.ts +1 -0
- package/dist/types-ts4.5/resource-timing/index.d.ts +1 -1
- package/dist/types-ts4.5/vc/vc-observer/heatmap/heatmap.d.ts +72 -0
- package/dist/types-ts4.5/vc/vc-observer/index.d.ts +15 -1
- package/dist/types-ts4.5/vc/vc-observer/revisions/ViewportUpdateClassifier.d.ts +30 -0
- package/dist/types-ts4.5/vc/vc-observer/revisions/fy24_01.d.ts +13 -0
- package/dist/types-ts4.5/vc/vc-observer/revisions/fy25_01.d.ts +13 -0
- package/dist/types-ts4.5/vc/vc-observer/revisions/revisions.d.ts +2 -0
- package/dist/types-ts4.5/vc/vc-observer/revisions/types.d.ts +23 -0
- package/package.json +11 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Bowser from 'bowser-ultralight';
|
|
2
2
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
3
3
|
import { getLighthouseMetrics } from '../additional-payload';
|
|
4
|
+
import { CHRReporter } from '../assets';
|
|
4
5
|
import * as bundleEvalTiming from '../bundle-eval-timing';
|
|
5
6
|
import coinflip from '../coinflip';
|
|
6
7
|
import { REACT_UFO_VERSION } from '../common/constants';
|
|
@@ -201,6 +202,16 @@ const getVCMetrics = interaction => {
|
|
|
201
202
|
if (interactionStatus.originalInteractionStatus !== 'SUCCEEDED' || pageVisibilityUpToTTAI !== 'visible') {
|
|
202
203
|
return result;
|
|
203
204
|
}
|
|
205
|
+
if (fg('ufo_vc_multiheatmap')) {
|
|
206
|
+
var _result;
|
|
207
|
+
(_result = result[`${prefix}:vc:rev`]) === null || _result === void 0 ? void 0 : _result.forEach(element => {
|
|
208
|
+
var _element$vcDetails, _element$vcDetails$;
|
|
209
|
+
if ((_element$vcDetails = element.vcDetails) !== null && _element$vcDetails !== void 0 && (_element$vcDetails$ = _element$vcDetails['90']) !== null && _element$vcDetails$ !== void 0 && _element$vcDetails$.t) {
|
|
210
|
+
var _element$vcDetails$2;
|
|
211
|
+
element['metric:vc90'] = (_element$vcDetails$2 = element.vcDetails['90']) === null || _element$vcDetails$2 === void 0 ? void 0 : _element$vcDetails$2.t;
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
204
215
|
return {
|
|
205
216
|
...result,
|
|
206
217
|
'metric:vc90': VC['90']
|
|
@@ -288,6 +299,33 @@ function getSSRProperties(type) {
|
|
|
288
299
|
'ssr:featureFlags': getSSRFeatureFlags(type)
|
|
289
300
|
};
|
|
290
301
|
}
|
|
302
|
+
const getAssetsMetrics = (interaction, resourceTimings) => {
|
|
303
|
+
try {
|
|
304
|
+
const config = getConfig();
|
|
305
|
+
const {
|
|
306
|
+
type
|
|
307
|
+
} = interaction;
|
|
308
|
+
const isCHREnabled = fg('ufo_chr_config');
|
|
309
|
+
const allowedTypes = ['page_load', 'transition'];
|
|
310
|
+
const assetsClassification = config === null || config === void 0 ? void 0 : config.assetsClassification;
|
|
311
|
+
if (!isCHREnabled || !allowedTypes.includes(type) || !assetsClassification) {
|
|
312
|
+
// Skip if: FG disabled, not allowed type or assetsClassification isn't configured
|
|
313
|
+
return {};
|
|
314
|
+
}
|
|
315
|
+
const reporter = new CHRReporter();
|
|
316
|
+
const assets = reporter.get(resourceTimings, assetsClassification);
|
|
317
|
+
if (assets) {
|
|
318
|
+
// Only add assets in case it exists
|
|
319
|
+
return {
|
|
320
|
+
'event:assets': assets
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
return {};
|
|
324
|
+
} catch (error) {
|
|
325
|
+
// Skip CHR in case of error
|
|
326
|
+
return {};
|
|
327
|
+
}
|
|
328
|
+
};
|
|
291
329
|
const getBrowserMetadata = () => {
|
|
292
330
|
const data = {};
|
|
293
331
|
const now = new Date();
|
|
@@ -713,7 +751,7 @@ function createInteractionMetricsPayload(interaction, interactionId, experimenta
|
|
|
713
751
|
};
|
|
714
752
|
};
|
|
715
753
|
// Detailed payload. Page visibility = visible
|
|
716
|
-
const getDetailedInteractionMetrics =
|
|
754
|
+
const getDetailedInteractionMetrics = resourceTimings => {
|
|
717
755
|
if (experimental || window.__UFO_COMPACT_PAYLOAD__ || !isDetailedPayload) {
|
|
718
756
|
return {};
|
|
719
757
|
}
|
|
@@ -734,7 +772,7 @@ function createInteractionMetricsPayload(interaction, interactionId, experimenta
|
|
|
734
772
|
requestInfo: optimizeRequestInfo(interaction.requestInfo, start),
|
|
735
773
|
customTimings: optimizeCustomTimings(interaction.customTimings, start),
|
|
736
774
|
bundleEvalTimings: objectToArray(getBundleEvalTimings(start)),
|
|
737
|
-
resourceTimings: objectToArray(
|
|
775
|
+
resourceTimings: objectToArray(resourceTimings)
|
|
738
776
|
};
|
|
739
777
|
};
|
|
740
778
|
// Page load & detailed payload
|
|
@@ -755,6 +793,7 @@ function createInteractionMetricsPayload(interaction, interactionId, experimenta
|
|
|
755
793
|
regularTTAI = getTTAI(interaction);
|
|
756
794
|
}
|
|
757
795
|
const newUFOName = sanitizeUfoName(ufoName);
|
|
796
|
+
const resourceTimings = getResourceTimings(start, end);
|
|
758
797
|
const payload = {
|
|
759
798
|
actionSubject: 'experience',
|
|
760
799
|
action: 'measured',
|
|
@@ -779,6 +818,7 @@ function createInteractionMetricsPayload(interaction, interactionId, experimenta
|
|
|
779
818
|
// root
|
|
780
819
|
...getBrowserMetadata(),
|
|
781
820
|
...getSSRProperties(type),
|
|
821
|
+
...getAssetsMetrics(interaction, resourceTimings),
|
|
782
822
|
...getPPSMetrics(interaction),
|
|
783
823
|
...getPaintMetrics(type),
|
|
784
824
|
...getNavigationMetrics(type),
|
|
@@ -821,7 +861,7 @@ function createInteractionMetricsPayload(interaction, interactionId, experimenta
|
|
|
821
861
|
reactProfilerTimings: optimizeReactProfilerTimings(interaction.reactProfilerTimings, start),
|
|
822
862
|
...labelStack,
|
|
823
863
|
...getPageLoadInteractionMetrics(),
|
|
824
|
-
...getDetailedInteractionMetrics(),
|
|
864
|
+
...getDetailedInteractionMetrics(resourceTimings),
|
|
825
865
|
...getPageLoadDetailedInteractionMetrics(),
|
|
826
866
|
...getBm3TrackerTimings(interaction),
|
|
827
867
|
'metric:ttai': experimental ? regularTTAI || expTTAI : undefined,
|
|
@@ -28,4 +28,8 @@ export const getActiveTraceHttpRequestHeaders = _url => {
|
|
|
28
28
|
spanId
|
|
29
29
|
} = state.context;
|
|
30
30
|
return makeTraceHttpRequestHeaders(traceId, spanId);
|
|
31
|
+
};
|
|
32
|
+
export const getActiveTraceAsQueryParams = _url => {
|
|
33
|
+
const traceHeaders = getActiveTraceHttpRequestHeaders(_url);
|
|
34
|
+
return traceHeaders ? new URLSearchParams(traceHeaders).toString().toLowerCase() : null;
|
|
31
35
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { lazy, Profiler, Suspense, useCallback, useContext, useEffect, useMemo, useRef } from 'react';
|
|
2
2
|
import { unstable_NormalPriority as NormalPriority, unstable_scheduleCallback as scheduleCallback } from 'scheduler';
|
|
3
3
|
import { v4 as createUUID } from 'uuid';
|
|
4
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
4
5
|
import coinflip from '../coinflip';
|
|
5
6
|
import { getConfig, getInteractionRate } from '../config';
|
|
6
7
|
import { getActiveTrace, setInteractionActiveTrace } from '../experience-trace-id-context';
|
|
@@ -31,7 +32,9 @@ export default function UFOSegment({
|
|
|
31
32
|
}], [parentContext, segmentName, segmentId]);
|
|
32
33
|
const interactionId = useContext(UFOInteractionIDContext);
|
|
33
34
|
const interactionContext = useMemo(() => {
|
|
34
|
-
|
|
35
|
+
if (!fg('platform-ufo-add-segment-use-effect')) {
|
|
36
|
+
addSegment(labelStack);
|
|
37
|
+
}
|
|
35
38
|
let lastCompleteEndTime = 0;
|
|
36
39
|
function complete(endTime = performance.now()) {
|
|
37
40
|
if (interactionId.current) {
|
|
@@ -205,8 +208,13 @@ export default function UFOSegment({
|
|
|
205
208
|
interactionContext.onRender(phase, actualDuration, baseDuration, startTime, commitTime);
|
|
206
209
|
}
|
|
207
210
|
}, [interactionContext]);
|
|
208
|
-
useEffect(() =>
|
|
209
|
-
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
if (fg('platform-ufo-add-segment-use-effect')) {
|
|
213
|
+
addSegment(labelStack);
|
|
214
|
+
}
|
|
215
|
+
return () => {
|
|
216
|
+
removeSegment(labelStack);
|
|
217
|
+
};
|
|
210
218
|
}, [interactionId, parentContext, interactionContext, labelStack]);
|
|
211
219
|
const reactProfilerId = useMemo(() => labelStack.map(l => l.name).join('/'), [labelStack]);
|
|
212
220
|
const enableSegmentHighlighting = (_getConfig2 = getConfig()) === null || _getConfig2 === void 0 ? void 0 : _getConfig2.enableSegmentHighlighting;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
const UNUSED_SECTOR = 0;
|
|
3
|
+
export class MultiRevisionHeatmap {
|
|
4
|
+
constructor({
|
|
5
|
+
viewport,
|
|
6
|
+
revisions,
|
|
7
|
+
arraySize,
|
|
8
|
+
devToolsEnabled
|
|
9
|
+
}) {
|
|
10
|
+
_defineProperty(this, "arraySize", {
|
|
11
|
+
w: 200,
|
|
12
|
+
h: 200
|
|
13
|
+
});
|
|
14
|
+
_defineProperty(this, "mapPixelsToHeatmap", (left, top, width, height) => {
|
|
15
|
+
const {
|
|
16
|
+
w,
|
|
17
|
+
h
|
|
18
|
+
} = this.viewport;
|
|
19
|
+
const l = Math.floor(left / w * this.arraySize.w);
|
|
20
|
+
const t = Math.floor(top / h * this.arraySize.h);
|
|
21
|
+
const r = Math.ceil((left + width) / w * this.arraySize.w);
|
|
22
|
+
const b = Math.ceil((top + height) / h * this.arraySize.h);
|
|
23
|
+
|
|
24
|
+
// correct values to min - 0, max - arraySize
|
|
25
|
+
const result = {
|
|
26
|
+
l: Math.max(0, l),
|
|
27
|
+
t: Math.max(0, t),
|
|
28
|
+
r: Math.min(this.arraySize.w, r),
|
|
29
|
+
b: Math.min(this.arraySize.h, b)
|
|
30
|
+
};
|
|
31
|
+
return result;
|
|
32
|
+
});
|
|
33
|
+
_defineProperty(this, "getElementRatio", mappedValues => {
|
|
34
|
+
const {
|
|
35
|
+
r,
|
|
36
|
+
l,
|
|
37
|
+
b,
|
|
38
|
+
t
|
|
39
|
+
} = mappedValues;
|
|
40
|
+
return (r - l) * (b - t) / (this.arraySize.w * this.arraySize.h);
|
|
41
|
+
});
|
|
42
|
+
this.viewport = viewport;
|
|
43
|
+
this.revisions = revisions;
|
|
44
|
+
if (arraySize) {
|
|
45
|
+
this.arraySize = arraySize;
|
|
46
|
+
}
|
|
47
|
+
this.heatmaps = new Array(revisions.length);
|
|
48
|
+
this.componentsLogs = new Array(revisions.length);
|
|
49
|
+
this.vcRatios = new Array(revisions.length);
|
|
50
|
+
this.devToolsEnabled = devToolsEnabled || false;
|
|
51
|
+
revisions.forEach(({}, i) => {
|
|
52
|
+
this.heatmaps[i] = this.getCleanHeatmap();
|
|
53
|
+
this.componentsLogs[i] = {};
|
|
54
|
+
this.vcRatios[i] = {};
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
handleUpdate({
|
|
58
|
+
time,
|
|
59
|
+
type,
|
|
60
|
+
classification,
|
|
61
|
+
intersectionRect,
|
|
62
|
+
element,
|
|
63
|
+
targetName,
|
|
64
|
+
ignoreReason,
|
|
65
|
+
onError
|
|
66
|
+
}) {
|
|
67
|
+
const mappedValues = this.mapPixelsToHeatmap(intersectionRect.left, intersectionRect.top, intersectionRect.width, intersectionRect.height);
|
|
68
|
+
const result = this.applyChangesToHeatMap(mappedValues, time, classification);
|
|
69
|
+
if (result !== true) {
|
|
70
|
+
onError(result);
|
|
71
|
+
}
|
|
72
|
+
const componentRatio = this.getElementRatio(mappedValues);
|
|
73
|
+
this.revisions.forEach((_, i) => {
|
|
74
|
+
if (classification[i]) {
|
|
75
|
+
this.vcRatios[i][targetName] = componentRatio;
|
|
76
|
+
}
|
|
77
|
+
if (!this.componentsLogs[i][time]) {
|
|
78
|
+
this.componentsLogs[i][time] = [];
|
|
79
|
+
}
|
|
80
|
+
this.componentsLogs[i][time].push({
|
|
81
|
+
__debug__element: this.devToolsEnabled ? new WeakRef(element) : null,
|
|
82
|
+
intersectionRect,
|
|
83
|
+
targetName,
|
|
84
|
+
ignoreReason
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
getData() {
|
|
89
|
+
return {
|
|
90
|
+
heatmaps: this.heatmaps
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
getPayloadShapedData(args) {
|
|
94
|
+
const result = this.processData(args);
|
|
95
|
+
const payload = this.revisions.map((rev, i) => {
|
|
96
|
+
const vcDetails = {};
|
|
97
|
+
args.VCParts.forEach(VCPart => {
|
|
98
|
+
vcDetails[VCPart] = {
|
|
99
|
+
t: result[i].VC[VCPart] || 0,
|
|
100
|
+
e: Array.from(result[i].VCBox[VCPart] || [])
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
return {
|
|
104
|
+
revision: rev.name,
|
|
105
|
+
vcDetails,
|
|
106
|
+
clean: args.clean,
|
|
107
|
+
'metric:vc90': null // will be set or not in the payload generator
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
return payload;
|
|
111
|
+
}
|
|
112
|
+
processData({
|
|
113
|
+
VCParts,
|
|
114
|
+
VCCalculationMethods,
|
|
115
|
+
ssr = UNUSED_SECTOR
|
|
116
|
+
}) {
|
|
117
|
+
return this.heatmaps.map((heatmap, i) => {
|
|
118
|
+
const lastUpdate = {};
|
|
119
|
+
let totalPainted = 0;
|
|
120
|
+
let componentsLog = this.componentsLogs[i];
|
|
121
|
+
if (ssr !== UNUSED_SECTOR) {
|
|
122
|
+
var _window$document;
|
|
123
|
+
const element = {
|
|
124
|
+
__debug__element: new WeakRef((_window$document = window.document) === null || _window$document === void 0 ? void 0 : _window$document.body),
|
|
125
|
+
intersectionRect: {
|
|
126
|
+
top: 0,
|
|
127
|
+
left: 0,
|
|
128
|
+
right: 0,
|
|
129
|
+
bottom: 0,
|
|
130
|
+
x: 0,
|
|
131
|
+
y: 0,
|
|
132
|
+
width: this.viewport.w,
|
|
133
|
+
height: this.viewport.h,
|
|
134
|
+
toJSON() {
|
|
135
|
+
return {};
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
targetName: 'SSR'
|
|
139
|
+
};
|
|
140
|
+
if (!componentsLog[ssr]) {
|
|
141
|
+
componentsLog[ssr] = [];
|
|
142
|
+
}
|
|
143
|
+
componentsLog[ssr].push(element);
|
|
144
|
+
}
|
|
145
|
+
for (let i = 0; i < heatmap.length; i++) {
|
|
146
|
+
const rounded = Math.floor(heatmap[i] === UNUSED_SECTOR && ssr !== UNUSED_SECTOR ? ssr : heatmap[i]);
|
|
147
|
+
totalPainted += rounded !== UNUSED_SECTOR ? 1 : 0;
|
|
148
|
+
if (rounded !== UNUSED_SECTOR) {
|
|
149
|
+
lastUpdate[rounded] = lastUpdate[rounded] ? lastUpdate[rounded] + 1 : 1;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const entries = Object.entries(lastUpdate).map(a => [parseInt(a[0], 10), a[1]]).sort((a, b) => a[0] > b[0] ? 1 : -1);
|
|
153
|
+
const {
|
|
154
|
+
VC,
|
|
155
|
+
VCBox
|
|
156
|
+
} = VCCalculationMethods[i]({
|
|
157
|
+
VCParts,
|
|
158
|
+
componentsLog,
|
|
159
|
+
entries,
|
|
160
|
+
totalPainted
|
|
161
|
+
});
|
|
162
|
+
const VCEntries = entries.reduce((acc, [timestamp, entryPainted], i) => {
|
|
163
|
+
var _acc$abs, _componentsLog$timest;
|
|
164
|
+
const currentlyPainted = entryPainted + (((_acc$abs = acc.abs[i - 1]) === null || _acc$abs === void 0 ? void 0 : _acc$abs[1]) || 0);
|
|
165
|
+
const currentlyPaintedRatio = Math.round(currentlyPainted / totalPainted * 1000) / 10;
|
|
166
|
+
const logEntry = (_componentsLog$timest = componentsLog[timestamp]) === null || _componentsLog$timest === void 0 ? void 0 : _componentsLog$timest.map(v => v.targetName);
|
|
167
|
+
acc.abs.push([timestamp, currentlyPainted]);
|
|
168
|
+
acc.rel.push({
|
|
169
|
+
time: timestamp,
|
|
170
|
+
vc: currentlyPaintedRatio,
|
|
171
|
+
elements: logEntry
|
|
172
|
+
});
|
|
173
|
+
return acc;
|
|
174
|
+
}, {
|
|
175
|
+
abs: [],
|
|
176
|
+
rel: []
|
|
177
|
+
});
|
|
178
|
+
return {
|
|
179
|
+
VC,
|
|
180
|
+
VCBox,
|
|
181
|
+
VCEntries,
|
|
182
|
+
totalPainted
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
applyChangesToHeatMap(a, time, classification) {
|
|
187
|
+
const {
|
|
188
|
+
l,
|
|
189
|
+
t,
|
|
190
|
+
r,
|
|
191
|
+
b
|
|
192
|
+
} = a;
|
|
193
|
+
const size = classification.length;
|
|
194
|
+
for (let row = t; row < b; row++) {
|
|
195
|
+
if (this.heatmaps[0][row] === undefined) {
|
|
196
|
+
try {
|
|
197
|
+
return {
|
|
198
|
+
error: `index - ${row}`,
|
|
199
|
+
time
|
|
200
|
+
};
|
|
201
|
+
} catch (e) {
|
|
202
|
+
return {
|
|
203
|
+
error: 'row error',
|
|
204
|
+
time
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
} else {
|
|
208
|
+
for (let heatmapIndex = 0; heatmapIndex < size; heatmapIndex++) {
|
|
209
|
+
if (classification[heatmapIndex]) {
|
|
210
|
+
this.heatmaps[heatmapIndex].fill(time, this.getIndex(l, row), this.getIndex(r, row));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
getIndex(x, y) {
|
|
218
|
+
return x + this.arraySize.w * y;
|
|
219
|
+
}
|
|
220
|
+
getCleanHeatmap() {
|
|
221
|
+
return new Uint32Array(this.arraySize.w * this.arraySize.h);
|
|
222
|
+
}
|
|
223
|
+
static makeVCReturnObj(VCParts) {
|
|
224
|
+
const vc = {};
|
|
225
|
+
VCParts.forEach(v => {
|
|
226
|
+
vc[v] = null;
|
|
227
|
+
});
|
|
228
|
+
return vc;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
@@ -2,7 +2,9 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
|
2
2
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
3
3
|
import { attachAbortListeners } from './attachAbortListeners';
|
|
4
4
|
import { getViewportHeight, getViewportWidth } from './getViewport';
|
|
5
|
+
import { MultiRevisionHeatmap } from './heatmap/heatmap';
|
|
5
6
|
import { Observers } from './observers';
|
|
7
|
+
import { getRevisions } from './revisions/revisions';
|
|
6
8
|
const abortReason = {
|
|
7
9
|
scroll: 'scroll',
|
|
8
10
|
keypress: 'keypress',
|
|
@@ -35,6 +37,7 @@ export class VCObserver {
|
|
|
35
37
|
});
|
|
36
38
|
/* heatmap */
|
|
37
39
|
_defineProperty(this, "arraySize", 0);
|
|
40
|
+
_defineProperty(this, "multiHeatmap", null);
|
|
38
41
|
_defineProperty(this, "componentsLog", {});
|
|
39
42
|
_defineProperty(this, "vcRatios", {});
|
|
40
43
|
_defineProperty(this, "active", false);
|
|
@@ -68,6 +71,7 @@ export class VCObserver {
|
|
|
68
71
|
},
|
|
69
72
|
heatmap: this.heatmap,
|
|
70
73
|
heatmapNext: this.heatmapNext,
|
|
74
|
+
multiHeatmap: this.multiHeatmap,
|
|
71
75
|
outOfBoundaryInfo: this.outOfBoundaryInfo,
|
|
72
76
|
totalTime: Math.round(this.totalTime + this.observers.getTotalTime()),
|
|
73
77
|
componentsLog: {
|
|
@@ -106,7 +110,8 @@ export class VCObserver {
|
|
|
106
110
|
componentsLog,
|
|
107
111
|
viewport,
|
|
108
112
|
devToolsEnabled,
|
|
109
|
-
ratios
|
|
113
|
+
ratios,
|
|
114
|
+
multiHeatmap
|
|
110
115
|
} = rawData;
|
|
111
116
|
if (abortReasonInfo !== null && abortReason.blocking) {
|
|
112
117
|
// exposing data to devtools
|
|
@@ -150,6 +155,7 @@ export class VCObserver {
|
|
|
150
155
|
/* empty */
|
|
151
156
|
}
|
|
152
157
|
let _componentsLog = {};
|
|
158
|
+
// eslint-disable-next-line @atlaskit/platform/ensure-feature-flag-prefix
|
|
153
159
|
if (fg('ufo-remove-vc-component-observations-after-ttai')) {
|
|
154
160
|
Object.entries(this.componentsLog).forEach(([_timestamp, value]) => {
|
|
155
161
|
const timestamp = Number(_timestamp);
|
|
@@ -207,9 +213,30 @@ export class VCObserver {
|
|
|
207
213
|
tti,
|
|
208
214
|
ttai: stop - start
|
|
209
215
|
},
|
|
216
|
+
start,
|
|
217
|
+
stop,
|
|
210
218
|
heatmap,
|
|
211
219
|
ratios
|
|
212
220
|
};
|
|
221
|
+
window.__vcNext = {
|
|
222
|
+
entries: vcNext.VCEntries.rel,
|
|
223
|
+
log: componentsLog,
|
|
224
|
+
metrics: {
|
|
225
|
+
'75': vcNext.VC['75'],
|
|
226
|
+
'80': vcNext.VC['80'],
|
|
227
|
+
'85': vcNext.VC['85'],
|
|
228
|
+
'90': vcNext.VC['90'],
|
|
229
|
+
'95': vcNext.VC['95'],
|
|
230
|
+
'98': vcNext.VC['98'],
|
|
231
|
+
'99': vcNext.VC['99'],
|
|
232
|
+
tti,
|
|
233
|
+
ttai: stop - start
|
|
234
|
+
},
|
|
235
|
+
start,
|
|
236
|
+
stop,
|
|
237
|
+
heatmap: heatmapNext,
|
|
238
|
+
ratios
|
|
239
|
+
};
|
|
213
240
|
|
|
214
241
|
// Emitting a custom event to make it available in the Chrome extension
|
|
215
242
|
window.dispatchEvent(new CustomEvent('vcReady', {
|
|
@@ -222,6 +249,18 @@ export class VCObserver {
|
|
|
222
249
|
} catch (e) {
|
|
223
250
|
/* do nothing */
|
|
224
251
|
}
|
|
252
|
+
const isMultiHeatmapEnabled = fg('ufo_vc_multiheatmap');
|
|
253
|
+
const revisionsData = isMultiHeatmapEnabled && multiHeatmap !== null ? {
|
|
254
|
+
[`${fullPrefix}vc:rev`]: multiHeatmap.getPayloadShapedData({
|
|
255
|
+
VCParts: VCObserver.VCParts.map(v => parseInt(v)),
|
|
256
|
+
VCCalculationMethods: getRevisions().map(({
|
|
257
|
+
classifier
|
|
258
|
+
}) => classifier.VCCalculationMethod),
|
|
259
|
+
ssr,
|
|
260
|
+
clean: !abortReasonInfo
|
|
261
|
+
})
|
|
262
|
+
} : null;
|
|
263
|
+
// eslint-disable-next-line @atlaskit/platform/ensure-feature-flag-prefix
|
|
225
264
|
const isCalcSpeedIndexEnabled = fg('ufo-calc-speed-index');
|
|
226
265
|
return {
|
|
227
266
|
'metrics:vc': VC,
|
|
@@ -240,12 +279,27 @@ export class VCObserver {
|
|
|
240
279
|
[`${fullPrefix}vc:next:dom`]: vcNext.VCBox,
|
|
241
280
|
//...oldDomUpdates,
|
|
242
281
|
[`${fullPrefix}vc:ignored`]: this.getIgnoredElements(componentsLog),
|
|
282
|
+
...revisionsData,
|
|
243
283
|
[`ufo:speedIndex`]: isCalcSpeedIndexEnabled ? VCEntries.speedIndex : undefined,
|
|
244
284
|
[`ufo:next:speedIndex`]: isCalcSpeedIndexEnabled ? vcNext.VCEntries.speedIndex : undefined
|
|
245
285
|
};
|
|
246
286
|
});
|
|
247
287
|
_defineProperty(this, "handleUpdate", (rawTime, intersectionRect, targetName, element, type, ignoreReason) => {
|
|
248
288
|
this.measureStart();
|
|
289
|
+
this.legacyHandleUpdate(rawTime, intersectionRect, targetName, element, type, ignoreReason);
|
|
290
|
+
if (fg('ufo_vc_multiheatmap')) {
|
|
291
|
+
this.onViewportChangeDetected({
|
|
292
|
+
timestamp: rawTime,
|
|
293
|
+
intersectionRect,
|
|
294
|
+
targetName,
|
|
295
|
+
element,
|
|
296
|
+
type,
|
|
297
|
+
ignoreReason
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
this.measureStop();
|
|
301
|
+
});
|
|
302
|
+
_defineProperty(this, "legacyHandleUpdate", (rawTime, intersectionRect, targetName, element, type, ignoreReason) => {
|
|
249
303
|
if (this.abortReason.reason === null || this.abortReason.blocking === false) {
|
|
250
304
|
const time = Math.round(rawTime - this.startTime);
|
|
251
305
|
const mappedValues = this.mapPixelsToHeatmap(intersectionRect.left, intersectionRect.top, intersectionRect.width, intersectionRect.height);
|
|
@@ -266,8 +320,39 @@ export class VCObserver {
|
|
|
266
320
|
ignoreReason
|
|
267
321
|
});
|
|
268
322
|
}
|
|
269
|
-
|
|
270
|
-
|
|
323
|
+
});
|
|
324
|
+
_defineProperty(this, "onViewportChangeDetected", ({
|
|
325
|
+
element,
|
|
326
|
+
type,
|
|
327
|
+
ignoreReason,
|
|
328
|
+
timestamp,
|
|
329
|
+
targetName,
|
|
330
|
+
intersectionRect
|
|
331
|
+
}) => {
|
|
332
|
+
if (this.multiHeatmap === null) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
// @todo add abort reason handling
|
|
336
|
+
const time = Math.round(timestamp - this.startTime);
|
|
337
|
+
const revisions = getRevisions();
|
|
338
|
+
const revisionsClassification = revisions.map(revision => {
|
|
339
|
+
return revision.classifier.classifyUpdate({
|
|
340
|
+
element,
|
|
341
|
+
type,
|
|
342
|
+
ignoreReason
|
|
343
|
+
});
|
|
344
|
+
}, []);
|
|
345
|
+
this.multiHeatmap.handleUpdate({
|
|
346
|
+
time,
|
|
347
|
+
targetName,
|
|
348
|
+
intersectionRect,
|
|
349
|
+
type,
|
|
350
|
+
element,
|
|
351
|
+
classification: revisionsClassification,
|
|
352
|
+
onError: error => {
|
|
353
|
+
this.setAbortReason(abortReason.error, error.time, error.error);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
271
356
|
});
|
|
272
357
|
_defineProperty(this, "mapPixelsToHeatmap", (left, top, width, height) => {
|
|
273
358
|
const {
|
|
@@ -352,6 +437,13 @@ export class VCObserver {
|
|
|
352
437
|
});
|
|
353
438
|
this.heatmap = this.getCleanHeatmap();
|
|
354
439
|
this.heatmapNext = this.getCleanHeatmap();
|
|
440
|
+
if (fg('ufo_vc_multiheatmap')) {
|
|
441
|
+
this.multiHeatmap = new MultiRevisionHeatmap({
|
|
442
|
+
viewport: this.viewport,
|
|
443
|
+
revisions: getRevisions(),
|
|
444
|
+
devToolsEnabled: this.devToolsEnabled
|
|
445
|
+
});
|
|
446
|
+
}
|
|
355
447
|
this.isPostInteraction = options.isPostInteraction || false;
|
|
356
448
|
}
|
|
357
449
|
start({
|
|
@@ -428,6 +520,8 @@ export class VCObserver {
|
|
|
428
520
|
const entries = Object.entries(lastUpdate).map(a => [parseInt(a[0], 10), a[1]]).sort((a, b) => a[0] > b[0] ? 1 : -1);
|
|
429
521
|
const VC = VCObserver.makeVCReturnObj();
|
|
430
522
|
const VCBox = VCObserver.makeVCReturnObj();
|
|
523
|
+
|
|
524
|
+
// eslint-disable-next-line @atlaskit/platform/ensure-feature-flag-prefix
|
|
431
525
|
const isCalcSpeedIndexEnabled = fg('ufo-calc-speed-index');
|
|
432
526
|
entries.reduce((acc = 0, v) => {
|
|
433
527
|
const currRatio = v[1] / totalPainted;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
export class ViewportUpdateClassifier {
|
|
3
|
+
constructor() {
|
|
4
|
+
_defineProperty(this, "types", []);
|
|
5
|
+
_defineProperty(this, "filters", []);
|
|
6
|
+
_defineProperty(this, "removedFilters", []);
|
|
7
|
+
_defineProperty(this, "__combinedTypes", []);
|
|
8
|
+
_defineProperty(this, "__combinedFilters", []);
|
|
9
|
+
}
|
|
10
|
+
VCCalculationMethod(_) {
|
|
11
|
+
return {
|
|
12
|
+
VC: {},
|
|
13
|
+
VCBox: {}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
mergeConfig() {
|
|
17
|
+
this.__combinedTypes = [...this.types, ...((this === null || this === void 0 ? void 0 : this.__combinedTypes) || [])];
|
|
18
|
+
const previousFilters = this.removedFilters.length === 0 ? this.__combinedFilters : this.__combinedFilters.filter(filter => !this.removedFilters.includes(filter.name));
|
|
19
|
+
this.__combinedFilters = [...this.filters, ...previousFilters];
|
|
20
|
+
}
|
|
21
|
+
classifyUpdate({
|
|
22
|
+
element,
|
|
23
|
+
type,
|
|
24
|
+
tags,
|
|
25
|
+
ignoreReason
|
|
26
|
+
}) {
|
|
27
|
+
if (!this.__combinedTypes.includes(type)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
return this.__combinedFilters.every(({
|
|
31
|
+
filter,
|
|
32
|
+
name
|
|
33
|
+
}) => {
|
|
34
|
+
return filter({
|
|
35
|
+
type,
|
|
36
|
+
tags,
|
|
37
|
+
ignoreReason
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import { ViewportUpdateClassifier } from './ViewportUpdateClassifier';
|
|
3
|
+
const legacyIgnoreReasons = ['image', 'ssr-hydration', 'editor-lazy-node-view', 'editor-container-mutation'];
|
|
4
|
+
export class FY24_01Classifier extends ViewportUpdateClassifier {
|
|
5
|
+
VCCalculationMethod({
|
|
6
|
+
VCParts,
|
|
7
|
+
entries,
|
|
8
|
+
totalPainted,
|
|
9
|
+
componentsLog
|
|
10
|
+
}) {
|
|
11
|
+
const VC = {};
|
|
12
|
+
const VCBox = {};
|
|
13
|
+
entries.reduce((acc = 0, v) => {
|
|
14
|
+
const VCRatio = v[1] / totalPainted + acc;
|
|
15
|
+
const time = v[0];
|
|
16
|
+
VCParts.forEach(value => {
|
|
17
|
+
if ((VC[value] === null || VC[value] === undefined) && VCRatio >= value / 100) {
|
|
18
|
+
var _componentsLog$time;
|
|
19
|
+
VC[value] = time;
|
|
20
|
+
VCBox[value] = new Set();
|
|
21
|
+
(_componentsLog$time = componentsLog[time]) === null || _componentsLog$time === void 0 ? void 0 : _componentsLog$time.forEach(v => {
|
|
22
|
+
var _VCBox$value;
|
|
23
|
+
return (_VCBox$value = VCBox[value]) === null || _VCBox$value === void 0 ? void 0 : _VCBox$value.add(v.targetName);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return VCRatio;
|
|
28
|
+
}, 0);
|
|
29
|
+
return {
|
|
30
|
+
VC,
|
|
31
|
+
VCBox
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
constructor() {
|
|
35
|
+
super();
|
|
36
|
+
_defineProperty(this, "revision", 'fy24.01');
|
|
37
|
+
_defineProperty(this, "types", ['html', 'text']);
|
|
38
|
+
_defineProperty(this, "filters", [{
|
|
39
|
+
name: 'default-ignore-reasons',
|
|
40
|
+
filter: ({
|
|
41
|
+
type,
|
|
42
|
+
ignoreReason
|
|
43
|
+
}) => {
|
|
44
|
+
return !ignoreReason || !legacyIgnoreReasons.includes(ignoreReason);
|
|
45
|
+
}
|
|
46
|
+
}]);
|
|
47
|
+
this.mergeConfig();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export const revFY24_01Classifier = new FY24_01Classifier();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import { FY24_01Classifier } from './fy24_01';
|
|
3
|
+
export class FY25_01Classifier extends FY24_01Classifier {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
_defineProperty(this, "revision", 'fy25.01');
|
|
7
|
+
_defineProperty(this, "types", ['attr']);
|
|
8
|
+
_defineProperty(this, "filters", [{
|
|
9
|
+
name: 'not-visible',
|
|
10
|
+
filter: ({
|
|
11
|
+
type,
|
|
12
|
+
ignoreReason
|
|
13
|
+
}) => {
|
|
14
|
+
return !(ignoreReason !== null && ignoreReason !== void 0 && ignoreReason.includes('not-visible'));
|
|
15
|
+
}
|
|
16
|
+
}]);
|
|
17
|
+
_defineProperty(this, "removedFilters", []);
|
|
18
|
+
this.mergeConfig();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export const revFY25_01Classifier = new FY25_01Classifier();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { revFY24_01Classifier } from './fy24_01';
|
|
2
|
+
import { revFY25_01Classifier } from './fy25_01';
|
|
3
|
+
const Revisions = [{
|
|
4
|
+
name: 'fy24.01',
|
|
5
|
+
classifier: revFY24_01Classifier
|
|
6
|
+
}, {
|
|
7
|
+
name: 'fy25.01',
|
|
8
|
+
classifier: revFY25_01Classifier
|
|
9
|
+
}];
|
|
10
|
+
let revisionResultCache = null;
|
|
11
|
+
export const getRevisions = () => {
|
|
12
|
+
if (revisionResultCache !== null) {
|
|
13
|
+
return revisionResultCache;
|
|
14
|
+
}
|
|
15
|
+
revisionResultCache = [...Revisions
|
|
16
|
+
//...(fg('next_available') ? [{name: 'next', classifier:NEXT}] : [])
|
|
17
|
+
];
|
|
18
|
+
return revisionResultCache;
|
|
19
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|