@atlaskit/react-ufo 2.14.3 → 2.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/create-payload/index.js +10 -0
  3. package/dist/cjs/experience-trace-id-context/index.js +5 -1
  4. package/dist/cjs/segment/segment.js +7 -1
  5. package/dist/cjs/vc/vc-observer/heatmap/heatmap.js +275 -0
  6. package/dist/cjs/vc/vc-observer/index.js +115 -26
  7. package/dist/cjs/vc/vc-observer/revisions/ViewportUpdateClassifier.js +52 -0
  8. package/dist/cjs/vc/vc-observer/revisions/fy24_01.js +39 -0
  9. package/dist/cjs/vc/vc-observer/revisions/fy25_01.js +39 -0
  10. package/dist/cjs/vc/vc-observer/revisions/revisions.js +23 -0
  11. package/dist/cjs/vc/vc-observer/revisions/types.js +5 -0
  12. package/dist/es2019/create-payload/index.js +10 -0
  13. package/dist/es2019/experience-trace-id-context/index.js +4 -0
  14. package/dist/es2019/segment/segment.js +11 -3
  15. package/dist/es2019/vc/vc-observer/heatmap/heatmap.js +238 -0
  16. package/dist/es2019/vc/vc-observer/index.js +94 -3
  17. package/dist/es2019/vc/vc-observer/revisions/ViewportUpdateClassifier.js +35 -0
  18. package/dist/es2019/vc/vc-observer/revisions/fy24_01.js +21 -0
  19. package/dist/es2019/vc/vc-observer/revisions/fy25_01.js +21 -0
  20. package/dist/es2019/vc/vc-observer/revisions/revisions.js +19 -0
  21. package/dist/es2019/vc/vc-observer/revisions/types.js +1 -0
  22. package/dist/esm/create-payload/index.js +10 -0
  23. package/dist/esm/experience-trace-id-context/index.js +4 -0
  24. package/dist/esm/segment/segment.js +7 -1
  25. package/dist/esm/vc/vc-observer/heatmap/heatmap.js +268 -0
  26. package/dist/esm/vc/vc-observer/index.js +115 -26
  27. package/dist/esm/vc/vc-observer/revisions/ViewportUpdateClassifier.js +45 -0
  28. package/dist/esm/vc/vc-observer/revisions/fy24_01.js +32 -0
  29. package/dist/esm/vc/vc-observer/revisions/fy25_01.js +32 -0
  30. package/dist/esm/vc/vc-observer/revisions/revisions.js +17 -0
  31. package/dist/esm/vc/vc-observer/revisions/types.js +1 -0
  32. package/dist/types/common/vc/types.d.ts +27 -8
  33. package/dist/types/experience-trace-id-context/index.d.ts +1 -0
  34. package/dist/types/vc/vc-observer/heatmap/heatmap.d.ts +75 -0
  35. package/dist/types/vc/vc-observer/index.d.ts +5 -1
  36. package/dist/types/vc/vc-observer/revisions/ViewportUpdateClassifier.d.ts +25 -0
  37. package/dist/types/vc/vc-observer/revisions/fy24_01.d.ts +11 -0
  38. package/dist/types/vc/vc-observer/revisions/fy25_01.d.ts +13 -0
  39. package/dist/types/vc/vc-observer/revisions/revisions.d.ts +2 -0
  40. package/dist/types/vc/vc-observer/revisions/types.d.ts +5 -0
  41. package/dist/types-ts4.5/common/vc/types.d.ts +27 -8
  42. package/dist/types-ts4.5/experience-trace-id-context/index.d.ts +1 -0
  43. package/dist/types-ts4.5/vc/vc-observer/heatmap/heatmap.d.ts +75 -0
  44. package/dist/types-ts4.5/vc/vc-observer/index.d.ts +15 -1
  45. package/dist/types-ts4.5/vc/vc-observer/revisions/ViewportUpdateClassifier.d.ts +25 -0
  46. package/dist/types-ts4.5/vc/vc-observer/revisions/fy24_01.d.ts +11 -0
  47. package/dist/types-ts4.5/vc/vc-observer/revisions/fy25_01.d.ts +13 -0
  48. package/dist/types-ts4.5/vc/vc-observer/revisions/revisions.d.ts +2 -0
  49. package/dist/types-ts4.5/vc/vc-observer/revisions/types.d.ts +5 -0
  50. package/package.json +8 -2
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.revFY24_01Classifier = exports.FY24_01Classifier = void 0;
8
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
9
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
10
+ var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
11
+ var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
12
+ var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
13
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
14
+ var _ViewportUpdateClassifier = require("./ViewportUpdateClassifier");
15
+ function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
16
+ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
17
+ var legacyIgnoreReasons = ['image', 'ssr-hydration', 'editor-lazy-node-view', 'editor-container-mutation'];
18
+ var FY24_01Classifier = exports.FY24_01Classifier = /*#__PURE__*/function (_ViewportUpdateClassi) {
19
+ function FY24_01Classifier() {
20
+ var _this;
21
+ (0, _classCallCheck2.default)(this, FY24_01Classifier);
22
+ _this = _callSuper(this, FY24_01Classifier);
23
+ (0, _defineProperty2.default)(_this, "revision", 'fy24.01');
24
+ (0, _defineProperty2.default)(_this, "types", ['html', 'text']);
25
+ (0, _defineProperty2.default)(_this, "filters", [{
26
+ name: 'default-ignore-reasons',
27
+ filter: function filter(_ref) {
28
+ var type = _ref.type,
29
+ ignoreReason = _ref.ignoreReason;
30
+ return !ignoreReason || !legacyIgnoreReasons.includes(ignoreReason);
31
+ }
32
+ }]);
33
+ _this.mergeConfig();
34
+ return _this;
35
+ }
36
+ (0, _inherits2.default)(FY24_01Classifier, _ViewportUpdateClassi);
37
+ return (0, _createClass2.default)(FY24_01Classifier);
38
+ }(_ViewportUpdateClassifier.ViewportUpdateClassifier);
39
+ var revFY24_01Classifier = exports.revFY24_01Classifier = new FY24_01Classifier();
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.revFY25_01Classifier = exports.FY25_01Classifier = void 0;
8
+ var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
9
+ var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
10
+ var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
11
+ var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
12
+ var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
13
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
14
+ var _fy24_ = require("./fy24_01");
15
+ function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
16
+ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
17
+ var FY25_01Classifier = exports.FY25_01Classifier = /*#__PURE__*/function (_FY24_01Classifier) {
18
+ function FY25_01Classifier() {
19
+ var _this;
20
+ (0, _classCallCheck2.default)(this, FY25_01Classifier);
21
+ _this = _callSuper(this, FY25_01Classifier);
22
+ (0, _defineProperty2.default)(_this, "revision", 'fy25.01');
23
+ (0, _defineProperty2.default)(_this, "types", ['attr']);
24
+ (0, _defineProperty2.default)(_this, "filters", [{
25
+ name: 'not-visible',
26
+ filter: function filter(_ref) {
27
+ var type = _ref.type,
28
+ ignoreReason = _ref.ignoreReason;
29
+ return !(ignoreReason !== null && ignoreReason !== void 0 && ignoreReason.includes('not-visible'));
30
+ }
31
+ }]);
32
+ (0, _defineProperty2.default)(_this, "removedFilters", []);
33
+ _this.mergeConfig();
34
+ return _this;
35
+ }
36
+ (0, _inherits2.default)(FY25_01Classifier, _FY24_01Classifier);
37
+ return (0, _createClass2.default)(FY25_01Classifier);
38
+ }(_fy24_.FY24_01Classifier);
39
+ var revFY25_01Classifier = exports.revFY25_01Classifier = new FY25_01Classifier();
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getRevisions = void 0;
7
+ var _fy24_ = require("./fy24_01");
8
+ var _fy25_ = require("./fy25_01");
9
+ var Revisions = [{
10
+ name: 'fy24.01',
11
+ classifier: _fy24_.revFY24_01Classifier
12
+ }, {
13
+ name: 'fy25.01',
14
+ classifier: _fy25_.revFY25_01Classifier
15
+ }];
16
+ var revisionResultCache = null;
17
+ var getRevisions = exports.getRevisions = function getRevisions() {
18
+ if (revisionResultCache !== null) {
19
+ return revisionResultCache;
20
+ }
21
+ revisionResultCache = [].concat(Revisions);
22
+ return revisionResultCache;
23
+ };
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
@@ -201,6 +201,16 @@ const getVCMetrics = interaction => {
201
201
  if (interactionStatus.originalInteractionStatus !== 'SUCCEEDED' || pageVisibilityUpToTTAI !== 'visible') {
202
202
  return result;
203
203
  }
204
+ if (fg('ufo_vc_multiheatmap')) {
205
+ var _result;
206
+ (_result = result[`${prefix}:vc:rev`]) === null || _result === void 0 ? void 0 : _result.forEach(element => {
207
+ var _element$vcDetails, _element$vcDetails$;
208
+ if ((_element$vcDetails = element.vcDetails) !== null && _element$vcDetails !== void 0 && (_element$vcDetails$ = _element$vcDetails['90']) !== null && _element$vcDetails$ !== void 0 && _element$vcDetails$.t) {
209
+ var _element$vcDetails$2;
210
+ element['metric:vc90'] = (_element$vcDetails$2 = element.vcDetails['90']) === null || _element$vcDetails$2 === void 0 ? void 0 : _element$vcDetails$2.t;
211
+ }
212
+ });
213
+ }
204
214
  return {
205
215
  ...result,
206
216
  'metric:vc90': VC['90']
@@ -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
- addSegment(labelStack);
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
- removeSegment(labelStack);
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,238 @@
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
+ ssr = UNUSED_SECTOR
115
+ }) {
116
+ return this.heatmaps.map((heatmap, i) => {
117
+ const lastUpdate = {};
118
+ let totalPainted = 0;
119
+ let componentsLog = this.componentsLogs[i];
120
+ if (ssr !== UNUSED_SECTOR) {
121
+ var _window$document;
122
+ const element = {
123
+ __debug__element: new WeakRef((_window$document = window.document) === null || _window$document === void 0 ? void 0 : _window$document.body),
124
+ intersectionRect: {
125
+ top: 0,
126
+ left: 0,
127
+ right: 0,
128
+ bottom: 0,
129
+ x: 0,
130
+ y: 0,
131
+ width: this.viewport.w,
132
+ height: this.viewport.h,
133
+ toJSON() {
134
+ return {};
135
+ }
136
+ },
137
+ targetName: 'SSR'
138
+ };
139
+ if (!componentsLog[ssr]) {
140
+ componentsLog[ssr] = [];
141
+ }
142
+ componentsLog[ssr].push(element);
143
+ }
144
+ for (let i = 0; i < heatmap.length; i++) {
145
+ const rounded = Math.floor(heatmap[i] === UNUSED_SECTOR && ssr !== UNUSED_SECTOR ? ssr : heatmap[i]);
146
+ totalPainted += rounded !== UNUSED_SECTOR ? 1 : 0;
147
+ if (rounded !== UNUSED_SECTOR) {
148
+ lastUpdate[rounded] = lastUpdate[rounded] ? lastUpdate[rounded] + 1 : 1;
149
+ }
150
+ }
151
+ const entries = Object.entries(lastUpdate).map(a => [parseInt(a[0], 10), a[1]]).sort((a, b) => a[0] > b[0] ? 1 : -1);
152
+ const VC = MultiRevisionHeatmap.makeVCReturnObj(VCParts);
153
+ const VCBox = MultiRevisionHeatmap.makeVCReturnObj(VCParts);
154
+ entries.reduce((acc = 0, v) => {
155
+ const VCRatio = v[1] / totalPainted + acc;
156
+ const time = v[0];
157
+ VCParts.forEach(value => {
158
+ if ((VC[value] === null || VC[value] === undefined) && VCRatio >= value / 100) {
159
+ var _componentsLog$time;
160
+ VC[value] = time;
161
+ VCBox[value] = new Set();
162
+ (_componentsLog$time = componentsLog[time]) === null || _componentsLog$time === void 0 ? void 0 : _componentsLog$time.forEach(v => {
163
+ var _VCBox$value;
164
+ return (_VCBox$value = VCBox[value]) === null || _VCBox$value === void 0 ? void 0 : _VCBox$value.add(v.targetName);
165
+ });
166
+ }
167
+ });
168
+ return VCRatio;
169
+ }, 0);
170
+ const VCEntries = entries.reduce((acc, [timestamp, entryPainted], i) => {
171
+ var _acc$abs, _componentsLog$timest;
172
+ const currentlyPainted = entryPainted + (((_acc$abs = acc.abs[i - 1]) === null || _acc$abs === void 0 ? void 0 : _acc$abs[1]) || 0);
173
+ const currentlyPaintedRatio = Math.round(currentlyPainted / totalPainted * 1000) / 10;
174
+ const logEntry = (_componentsLog$timest = componentsLog[timestamp]) === null || _componentsLog$timest === void 0 ? void 0 : _componentsLog$timest.map(v => v.targetName);
175
+ acc.abs.push([timestamp, currentlyPainted]);
176
+ acc.rel.push({
177
+ time: timestamp,
178
+ vc: currentlyPaintedRatio,
179
+ elements: logEntry
180
+ });
181
+ return acc;
182
+ }, {
183
+ abs: [],
184
+ rel: []
185
+ });
186
+ return {
187
+ VC,
188
+ VCBox,
189
+ VCEntries,
190
+ totalPainted
191
+ };
192
+ });
193
+ }
194
+ applyChangesToHeatMap(a, time, classification) {
195
+ const {
196
+ l,
197
+ t,
198
+ r,
199
+ b
200
+ } = a;
201
+ const size = classification.length;
202
+ for (let row = t; row < b; row++) {
203
+ if (this.heatmaps[0][row] === undefined) {
204
+ try {
205
+ return {
206
+ error: `index - ${row}`,
207
+ time
208
+ };
209
+ } catch (e) {
210
+ return {
211
+ error: 'row error',
212
+ time
213
+ };
214
+ }
215
+ } else {
216
+ for (let heatmapIndex = 0; heatmapIndex < size; heatmapIndex++) {
217
+ if (classification[heatmapIndex]) {
218
+ this.heatmaps[heatmapIndex].fill(time, this.getIndex(l, row), this.getIndex(r, row));
219
+ }
220
+ }
221
+ }
222
+ }
223
+ return true;
224
+ }
225
+ getIndex(x, y) {
226
+ return x + this.arraySize.w * y;
227
+ }
228
+ getCleanHeatmap() {
229
+ return new Uint32Array(this.arraySize.w * this.arraySize.h);
230
+ }
231
+ static makeVCReturnObj(VCParts) {
232
+ const vc = {};
233
+ VCParts.forEach(v => {
234
+ vc[v] = null;
235
+ });
236
+ return vc;
237
+ }
238
+ }
@@ -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,15 @@ 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
+ ssr,
257
+ clean: !abortReasonInfo
258
+ })
259
+ } : null;
260
+ // eslint-disable-next-line @atlaskit/platform/ensure-feature-flag-prefix
225
261
  const isCalcSpeedIndexEnabled = fg('ufo-calc-speed-index');
226
262
  return {
227
263
  'metrics:vc': VC,
@@ -240,12 +276,27 @@ export class VCObserver {
240
276
  [`${fullPrefix}vc:next:dom`]: vcNext.VCBox,
241
277
  //...oldDomUpdates,
242
278
  [`${fullPrefix}vc:ignored`]: this.getIgnoredElements(componentsLog),
279
+ ...revisionsData,
243
280
  [`ufo:speedIndex`]: isCalcSpeedIndexEnabled ? VCEntries.speedIndex : undefined,
244
281
  [`ufo:next:speedIndex`]: isCalcSpeedIndexEnabled ? vcNext.VCEntries.speedIndex : undefined
245
282
  };
246
283
  });
247
284
  _defineProperty(this, "handleUpdate", (rawTime, intersectionRect, targetName, element, type, ignoreReason) => {
248
285
  this.measureStart();
286
+ this.legacyHandleUpdate(rawTime, intersectionRect, targetName, element, type, ignoreReason);
287
+ if (fg('ufo_vc_multiheatmap')) {
288
+ this.onViewportChangeDetected({
289
+ timestamp: rawTime,
290
+ intersectionRect,
291
+ targetName,
292
+ element,
293
+ type,
294
+ ignoreReason
295
+ });
296
+ }
297
+ this.measureStop();
298
+ });
299
+ _defineProperty(this, "legacyHandleUpdate", (rawTime, intersectionRect, targetName, element, type, ignoreReason) => {
249
300
  if (this.abortReason.reason === null || this.abortReason.blocking === false) {
250
301
  const time = Math.round(rawTime - this.startTime);
251
302
  const mappedValues = this.mapPixelsToHeatmap(intersectionRect.left, intersectionRect.top, intersectionRect.width, intersectionRect.height);
@@ -266,8 +317,39 @@ export class VCObserver {
266
317
  ignoreReason
267
318
  });
268
319
  }
269
- // devtools export
270
- this.measureStop();
320
+ });
321
+ _defineProperty(this, "onViewportChangeDetected", ({
322
+ element,
323
+ type,
324
+ ignoreReason,
325
+ timestamp,
326
+ targetName,
327
+ intersectionRect
328
+ }) => {
329
+ if (this.multiHeatmap === null) {
330
+ return;
331
+ }
332
+ // @todo add abort reason handling
333
+ const time = Math.round(timestamp - this.startTime);
334
+ const revisions = getRevisions();
335
+ const revisionsClassification = revisions.map(revision => {
336
+ return revision.classifier.classifyUpdate({
337
+ element,
338
+ type,
339
+ ignoreReason
340
+ });
341
+ }, []);
342
+ this.multiHeatmap.handleUpdate({
343
+ time,
344
+ targetName,
345
+ intersectionRect,
346
+ type,
347
+ element,
348
+ classification: revisionsClassification,
349
+ onError: error => {
350
+ this.setAbortReason(abortReason.error, error.time, error.error);
351
+ }
352
+ });
271
353
  });
272
354
  _defineProperty(this, "mapPixelsToHeatmap", (left, top, width, height) => {
273
355
  const {
@@ -352,6 +434,13 @@ export class VCObserver {
352
434
  });
353
435
  this.heatmap = this.getCleanHeatmap();
354
436
  this.heatmapNext = this.getCleanHeatmap();
437
+ if (fg('ufo_vc_multiheatmap')) {
438
+ this.multiHeatmap = new MultiRevisionHeatmap({
439
+ viewport: this.viewport,
440
+ revisions: getRevisions(),
441
+ devToolsEnabled: this.devToolsEnabled
442
+ });
443
+ }
355
444
  this.isPostInteraction = options.isPostInteraction || false;
356
445
  }
357
446
  start({
@@ -428,6 +517,8 @@ export class VCObserver {
428
517
  const entries = Object.entries(lastUpdate).map(a => [parseInt(a[0], 10), a[1]]).sort((a, b) => a[0] > b[0] ? 1 : -1);
429
518
  const VC = VCObserver.makeVCReturnObj();
430
519
  const VCBox = VCObserver.makeVCReturnObj();
520
+
521
+ // eslint-disable-next-line @atlaskit/platform/ensure-feature-flag-prefix
431
522
  const isCalcSpeedIndexEnabled = fg('ufo-calc-speed-index');
432
523
  entries.reduce((acc = 0, v) => {
433
524
  const currRatio = v[1] / totalPainted;
@@ -0,0 +1,35 @@
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
+ mergeConfig() {
11
+ this.__combinedTypes = [...this.types, ...((this === null || this === void 0 ? void 0 : this.__combinedTypes) || [])];
12
+ const previousFilters = this.removedFilters.length === 0 ? this.__combinedFilters : this.__combinedFilters.filter(filter => !this.removedFilters.includes(filter.name));
13
+ this.__combinedFilters = [...this.filters, ...previousFilters];
14
+ }
15
+ classifyUpdate({
16
+ element,
17
+ type,
18
+ tags,
19
+ ignoreReason
20
+ }) {
21
+ if (!this.__combinedTypes.includes(type)) {
22
+ return false;
23
+ }
24
+ return this.__combinedFilters.every(({
25
+ filter,
26
+ name
27
+ }) => {
28
+ return filter({
29
+ type,
30
+ tags,
31
+ ignoreReason
32
+ });
33
+ });
34
+ }
35
+ }
@@ -0,0 +1,21 @@
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
+ constructor() {
6
+ super();
7
+ _defineProperty(this, "revision", 'fy24.01');
8
+ _defineProperty(this, "types", ['html', 'text']);
9
+ _defineProperty(this, "filters", [{
10
+ name: 'default-ignore-reasons',
11
+ filter: ({
12
+ type,
13
+ ignoreReason
14
+ }) => {
15
+ return !ignoreReason || !legacyIgnoreReasons.includes(ignoreReason);
16
+ }
17
+ }]);
18
+ this.mergeConfig();
19
+ }
20
+ }
21
+ 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();