@atlaskit/editor-plugin-interactivity 1.1.0 → 1.2.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 +17 -0
- package/README.md +46 -1
- package/dist/cjs/collections/bounded-list.js +42 -0
- package/dist/cjs/collections/bounded-map.js +42 -0
- package/dist/cjs/collector/interaction-group.js +31 -4
- package/dist/cjs/collector/interaction-tracker.js +207 -67
- package/dist/cjs/collector/interactivity-collector.js +45 -35
- package/dist/cjs/collector/long-animation-frame-observer.js +62 -0
- package/dist/cjs/collector/slow-interaction-list.js +297 -56
- package/dist/es2019/collections/bounded-list.js +24 -0
- package/dist/es2019/collections/bounded-map.js +25 -0
- package/dist/es2019/collector/interaction-group.js +28 -5
- package/dist/es2019/collector/interaction-tracker.js +198 -54
- package/dist/es2019/collector/interactivity-collector.js +30 -34
- package/dist/es2019/collector/long-animation-frame-observer.js +42 -0
- package/dist/es2019/collector/slow-interaction-list.js +251 -57
- package/dist/esm/collections/bounded-list.js +35 -0
- package/dist/esm/collections/bounded-map.js +35 -0
- package/dist/esm/collector/interaction-group.js +31 -5
- package/dist/esm/collector/interaction-tracker.js +207 -67
- package/dist/esm/collector/interactivity-collector.js +45 -35
- package/dist/esm/collector/long-animation-frame-observer.js +55 -0
- package/dist/esm/collector/slow-interaction-list.js +297 -56
- package/dist/types/analytics/interactivity-snapshot.d.ts +32 -1
- package/dist/types/collections/bounded-list.d.ts +9 -0
- package/dist/types/collections/bounded-map.d.ts +9 -0
- package/dist/types/collector/interaction-group.d.ts +18 -5
- package/dist/types/collector/interaction-tracker.d.ts +58 -9
- package/dist/types/collector/interactivity-collector.d.ts +3 -0
- package/dist/types/collector/long-animation-frame-observer.d.ts +28 -0
- package/dist/types/collector/slow-interaction-list.d.ts +44 -18
- package/package.json +3 -3
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
1
2
|
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
2
3
|
import _createClass from "@babel/runtime/helpers/createClass";
|
|
3
4
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
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; }
|
|
5
|
+
import { BoundedList } from '../collections/bounded-list';
|
|
6
|
+
import { BoundedMap } from '../collections/bounded-map';
|
|
7
7
|
import { interactionEventKind } from './interaction-events';
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -12,10 +12,43 @@ import { interactionEventKind } from './interaction-events';
|
|
|
12
12
|
* `PerformanceEventTiming` altogether, and the rest are only on it.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* One paint, and the processing of every event it presented.
|
|
17
|
+
*
|
|
18
|
+
* One paint can present several events, and the handlers of all of them ran before it: a
|
|
19
|
+
* `pointerover` handler that was still running when the user clicked held up the paint that showed
|
|
20
|
+
* the click. So an interaction's processing is the processing of its whole paint, not of its own
|
|
21
|
+
* events only — otherwise a handler that is not its own reads as time the user waited for nothing.
|
|
22
|
+
*
|
|
23
|
+
* Event Timing gives a paint no identity. The only thing an entry says about it is
|
|
24
|
+
* `startTime + duration`, the moment it happened, so that is what identifies it.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The paint an entry was presented by, and whether the entry moved the processing that paint covers.
|
|
29
|
+
* An entry whose handlers ran inside what the paint already covered changes nothing for any
|
|
30
|
+
* interaction reading its boundaries from it.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The four moments an interaction's latency divides at, in order: the user acted, its handlers
|
|
35
|
+
* started running, they finished, the screen updated.
|
|
36
|
+
*/
|
|
37
|
+
|
|
15
38
|
/**
|
|
16
39
|
* What an entry did to the interaction it belongs to: either it is the first entry of a new
|
|
17
|
-
* interaction, or it
|
|
18
|
-
*
|
|
40
|
+
* interaction, or it changed an interaction already known. Both carry the editor group of the
|
|
41
|
+
* interaction, if it is one of the editor's, and the boundaries it now has.
|
|
42
|
+
*
|
|
43
|
+
* A `remeasured` where `previousLatencyMs` equals `latencyMs` is an interaction whose latency stayed
|
|
44
|
+
* as it was and whose boundaries moved: the entry ran in the same paint without being the slowest
|
|
45
|
+
* of them.
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* What is kept per interaction. The boundaries are not among these: they are derived from the paint
|
|
50
|
+
* whenever the interaction is reported, because the paint keeps growing as the browser reports the
|
|
51
|
+
* remaining events it presented.
|
|
19
52
|
*/
|
|
20
53
|
|
|
21
54
|
/**
|
|
@@ -24,11 +57,17 @@ import { interactionEventKind } from './interaction-events';
|
|
|
24
57
|
* anything older than the last few hundred is not needed.
|
|
25
58
|
*/
|
|
26
59
|
var MAX_TRACKED = 256;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* How many paints entries can still be placed in. The entries of a paint arrive within a batch or
|
|
63
|
+
* two of each other, so this only has to cover the paints in flight; it is what `web-vitals` keeps.
|
|
64
|
+
*/
|
|
65
|
+
var MAX_RECENT_PAINTS = 10;
|
|
27
66
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
67
|
+
* Event Timing rounds `duration` down to 8 ms, so two events presented by one paint report that
|
|
68
|
+
* paint up to this far apart — and nothing else in Event Timing says they share it.
|
|
30
69
|
*/
|
|
31
|
-
var
|
|
70
|
+
var PRESENTATION_ROUNDING_MS = 8;
|
|
32
71
|
|
|
33
72
|
/**
|
|
34
73
|
* Identifies the event an entry measured: an entry's `startTime` is that event's timestamp and its
|
|
@@ -54,6 +93,9 @@ function eventKey(type, timeStamp) {
|
|
|
54
93
|
*
|
|
55
94
|
* The editor's events answer what an entry cannot: which interactions were with the editor, and
|
|
56
95
|
* how many there were, including the ones below the Event Timing reporting threshold.
|
|
96
|
+
*
|
|
97
|
+
* Every entry is also placed in the paint that presented it, which is what says how an
|
|
98
|
+
* interaction's latency divides into waiting, processing and presentation. See `Paint`.
|
|
57
99
|
*/
|
|
58
100
|
export var InteractionTracker = /*#__PURE__*/function () {
|
|
59
101
|
/**
|
|
@@ -66,8 +108,9 @@ export var InteractionTracker = /*#__PURE__*/function () {
|
|
|
66
108
|
function InteractionTracker() {
|
|
67
109
|
var startsAfterInteractionId = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
|
68
110
|
_classCallCheck(this, InteractionTracker);
|
|
69
|
-
_defineProperty(this, "interactions", new
|
|
70
|
-
_defineProperty(this, "groupByEvent", new
|
|
111
|
+
_defineProperty(this, "interactions", new BoundedMap(MAX_TRACKED));
|
|
112
|
+
_defineProperty(this, "groupByEvent", new BoundedMap(MAX_TRACKED));
|
|
113
|
+
_defineProperty(this, "recentPaints", new BoundedList(MAX_RECENT_PAINTS));
|
|
71
114
|
_defineProperty(this, "highestInteractionId", 0);
|
|
72
115
|
this.startsAfterInteractionId = startsAfterInteractionId;
|
|
73
116
|
}
|
|
@@ -82,60 +125,67 @@ export var InteractionTracker = /*#__PURE__*/function () {
|
|
|
82
125
|
/**
|
|
83
126
|
* Merges an entry into the interaction it belongs to.
|
|
84
127
|
*
|
|
85
|
-
* @returns what that
|
|
86
|
-
*
|
|
128
|
+
* @returns what that changed about the interactions this tracker knows, the entry's own first.
|
|
129
|
+
* More than one of them when the paint the entry ran in presented several.
|
|
87
130
|
*/
|
|
88
131
|
}, {
|
|
89
132
|
key: "merge",
|
|
90
133
|
value: function merge(entry) {
|
|
134
|
+
if (!Number.isFinite(entry.duration) || entry.duration < 0) {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
var placement = this.paintOf(entry);
|
|
138
|
+
var paint = placement === null || placement === void 0 ? void 0 : placement.paint;
|
|
91
139
|
var interactionId = entry.interactionId;
|
|
92
|
-
|
|
140
|
+
|
|
141
|
+
// Reported only when the entry grew the paint, because otherwise nothing an interaction reads
|
|
142
|
+
// from it moved. Every interaction the paint presented is here, not only the entry's own: a
|
|
143
|
+
// `first-input` or non-interaction event reports `interactionId` 0 and has none of its own,
|
|
144
|
+
// and a second press of the same paint moved where the first one spent its latency.
|
|
145
|
+
//
|
|
146
|
+
// The check below is neither reached with a `0` nor needed: the interactions reported are the
|
|
147
|
+
// ones this tracker holds, and the only way into that map is past the check.
|
|
148
|
+
var remeasuredOthers = placement !== null && placement !== void 0 && placement.grew ? this.remeasuredUpdatesIn(placement.paint, {
|
|
149
|
+
except: interactionId
|
|
150
|
+
}) : [];
|
|
93
151
|
if (!interactionId) {
|
|
94
|
-
return
|
|
152
|
+
return remeasuredOthers;
|
|
95
153
|
}
|
|
96
154
|
if (interactionId <= this.startsAfterInteractionId) {
|
|
97
|
-
|
|
155
|
+
// The entry belongs to the session before this one, but its handlers still ran before a
|
|
156
|
+
// paint of this one.
|
|
157
|
+
return remeasuredOthers;
|
|
98
158
|
}
|
|
99
159
|
this.highestInteractionId = Math.max(this.highestInteractionId, interactionId);
|
|
100
|
-
|
|
101
|
-
// Every latency is counted through here, so this is where one that cannot be measured
|
|
102
|
-
// is rejected: a `NaN` getting through becomes a `NaN` bucket key and a `NaN` `sumMs`
|
|
103
|
-
// for the rest of the session.
|
|
104
|
-
if (!Number.isFinite(entry.duration) || entry.duration < 0) {
|
|
105
|
-
return undefined;
|
|
106
|
-
}
|
|
107
160
|
var tracked = this.interactions.get(interactionId);
|
|
108
161
|
if (tracked === undefined) {
|
|
109
162
|
// Taken once: an entry that only makes the interaction slower has to move its count
|
|
110
163
|
// within the group it was counted in, not into another one.
|
|
111
164
|
var group = this.groupByEvent.get(eventKey(entry.name, entry.startTime));
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
group: group
|
|
115
|
-
});
|
|
116
|
-
this.prune(this.interactions);
|
|
117
|
-
return {
|
|
118
|
-
type: 'new',
|
|
119
|
-
interactionId: interactionId,
|
|
165
|
+
var interaction = {
|
|
166
|
+
group: group,
|
|
120
167
|
latencyMs: entry.duration,
|
|
121
|
-
|
|
168
|
+
presentedIn: paint,
|
|
169
|
+
startedAt: entry.startTime
|
|
122
170
|
};
|
|
171
|
+
this.interactions.set(interactionId, interaction);
|
|
172
|
+
return [this.newUpdate(interactionId, interaction)].concat(_toConsumableArray(remeasuredOthers));
|
|
123
173
|
}
|
|
124
|
-
if (entry.duration
|
|
125
|
-
|
|
174
|
+
if (entry.duration > tracked.latencyMs) {
|
|
175
|
+
var previousLatencyMs = tracked.latencyMs;
|
|
176
|
+
tracked.latencyMs = entry.duration;
|
|
177
|
+
tracked.presentedIn = paint;
|
|
178
|
+
tracked.startedAt = entry.startTime;
|
|
179
|
+
return [this.remeasuredUpdate(interactionId, tracked, previousLatencyMs)].concat(_toConsumableArray(remeasuredOthers));
|
|
126
180
|
}
|
|
127
|
-
var fromMs = tracked.latencyMs;
|
|
128
|
-
tracked.latencyMs = entry.duration;
|
|
129
|
-
return {
|
|
130
|
-
type: 'remeasured',
|
|
131
|
-
interactionId: interactionId,
|
|
132
|
-
fromMs: fromMs,
|
|
133
|
-
toMs: entry.duration,
|
|
134
|
-
group: tracked.group
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
181
|
|
|
138
|
-
|
|
182
|
+
// Not the slowest entry of the interaction, so its latency stands. Its handlers still ran
|
|
183
|
+
// before the same paint, if this is that paint, and so moved where that latency went.
|
|
184
|
+
if (!(placement !== null && placement !== void 0 && placement.grew) || paint !== tracked.presentedIn) {
|
|
185
|
+
return remeasuredOthers;
|
|
186
|
+
}
|
|
187
|
+
return [this.remeasuredUpdate(interactionId, tracked, tracked.latencyMs)].concat(_toConsumableArray(remeasuredOthers));
|
|
188
|
+
}
|
|
139
189
|
}, {
|
|
140
190
|
key: "recordEditorEvent",
|
|
141
191
|
value: function recordEditorEvent(event) {
|
|
@@ -147,35 +197,125 @@ export var InteractionTracker = /*#__PURE__*/function () {
|
|
|
147
197
|
// Every event of the interaction, because any of them can be the one Event Timing reports
|
|
148
198
|
// as the slowest: for a pointer press that is usually the click.
|
|
149
199
|
this.groupByEvent.set(eventKey(event.type, event.timeStamp), kind.group);
|
|
150
|
-
this.prune(this.groupByEvent);
|
|
151
200
|
return kind.counts ? kind.group : undefined;
|
|
152
201
|
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Every interaction whose boundaries are read from this paint, reported as measured again at the
|
|
205
|
+
* latency it already had.
|
|
206
|
+
*
|
|
207
|
+
* @param except the interaction the entry measured, which the caller reports itself. `0` or
|
|
208
|
+
* nothing when the entry measured none, and then no interaction is left out.
|
|
209
|
+
*/
|
|
210
|
+
}, {
|
|
211
|
+
key: "remeasuredUpdatesIn",
|
|
212
|
+
value: function remeasuredUpdatesIn(paint, _ref) {
|
|
213
|
+
var _this = this;
|
|
214
|
+
var except = _ref.except;
|
|
215
|
+
var remeasured = [];
|
|
216
|
+
this.interactions.forEach(function (interaction, interactionId) {
|
|
217
|
+
if (interaction.presentedIn === paint && interactionId !== except) {
|
|
218
|
+
remeasured.push(_this.remeasuredUpdate(interactionId, interaction, interaction.latencyMs));
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
return remeasured;
|
|
222
|
+
}
|
|
223
|
+
}, {
|
|
224
|
+
key: "newUpdate",
|
|
225
|
+
value: function newUpdate(interactionId, tracked) {
|
|
226
|
+
return {
|
|
227
|
+
type: 'new',
|
|
228
|
+
interactionId: interactionId,
|
|
229
|
+
latencyMs: tracked.latencyMs,
|
|
230
|
+
group: tracked.group,
|
|
231
|
+
boundaries: this.boundariesOf(tracked)
|
|
232
|
+
};
|
|
233
|
+
}
|
|
153
234
|
}, {
|
|
154
|
-
key: "
|
|
155
|
-
value: function
|
|
156
|
-
|
|
157
|
-
|
|
235
|
+
key: "remeasuredUpdate",
|
|
236
|
+
value: function remeasuredUpdate(interactionId, tracked, previousLatencyMs) {
|
|
237
|
+
return {
|
|
238
|
+
type: 'remeasured',
|
|
239
|
+
interactionId: interactionId,
|
|
240
|
+
previousLatencyMs: previousLatencyMs,
|
|
241
|
+
latencyMs: tracked.latencyMs,
|
|
242
|
+
group: tracked.group,
|
|
243
|
+
boundaries: this.boundariesOf(tracked)
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* The four moments of an interaction, read from the paint as it stands now.
|
|
249
|
+
*
|
|
250
|
+
* Limited the way `web-vitals` limits its INP attribution, so the four stay in order: the paint's
|
|
251
|
+
* handlers can have started before the event arrived, and can have finished after the paint the
|
|
252
|
+
* event's rounded-down `duration` points at.
|
|
253
|
+
*
|
|
254
|
+
* @returns nothing when the browser reported no processing timestamps for the interaction, which
|
|
255
|
+
* leaves it in no paint.
|
|
256
|
+
*/
|
|
257
|
+
}, {
|
|
258
|
+
key: "boundariesOf",
|
|
259
|
+
value: function boundariesOf(_ref2) {
|
|
260
|
+
var latencyMs = _ref2.latencyMs,
|
|
261
|
+
presentedIn = _ref2.presentedIn,
|
|
262
|
+
startedAt = _ref2.startedAt;
|
|
263
|
+
if (!presentedIn) {
|
|
264
|
+
return undefined;
|
|
158
265
|
}
|
|
266
|
+
var processingStartedAt = Math.max(presentedIn.processingStartedAt, startedAt);
|
|
267
|
+
var presentedAt = Math.max(startedAt + latencyMs, processingStartedAt);
|
|
268
|
+
var processingEndedAt = Math.min(presentedIn.processingEndedAt, presentedAt);
|
|
269
|
+
return {
|
|
270
|
+
startedAt: startedAt,
|
|
271
|
+
processingStartedAt: processingStartedAt,
|
|
272
|
+
processingEndedAt: processingEndedAt,
|
|
273
|
+
presentedAt: presentedAt
|
|
274
|
+
};
|
|
275
|
+
}
|
|
159
276
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
277
|
+
/**
|
|
278
|
+
* The paint that presented this entry, grown to cover this entry's own processing.
|
|
279
|
+
*
|
|
280
|
+
* The moment being matched is always the one the first entry of the paint reported, so that a
|
|
281
|
+
* run of entries 8 ms apart cannot walk one paint across the next.
|
|
282
|
+
*
|
|
283
|
+
* @returns nothing when the browser reported no processing timestamps for the entry, which
|
|
284
|
+
* leaves nothing to place it by.
|
|
285
|
+
*/
|
|
286
|
+
}, {
|
|
287
|
+
key: "paintOf",
|
|
288
|
+
value: function paintOf(entry) {
|
|
289
|
+
var startTime = entry.startTime,
|
|
290
|
+
duration = entry.duration,
|
|
291
|
+
processingStart = entry.processingStart,
|
|
292
|
+
processingEnd = entry.processingEnd;
|
|
293
|
+
if (typeof processingStart !== 'number' || typeof processingEnd !== 'number') {
|
|
294
|
+
return undefined;
|
|
178
295
|
}
|
|
296
|
+
var presentedAt = startTime + duration;
|
|
297
|
+
var knownPaint = this.recentPaints.findLast(function (paint) {
|
|
298
|
+
return Math.abs(presentedAt - paint.presentedAt) <= PRESENTATION_ROUNDING_MS;
|
|
299
|
+
});
|
|
300
|
+
if (knownPaint) {
|
|
301
|
+
var grew = processingStart < knownPaint.processingStartedAt || processingEnd > knownPaint.processingEndedAt;
|
|
302
|
+
knownPaint.processingStartedAt = Math.min(processingStart, knownPaint.processingStartedAt);
|
|
303
|
+
knownPaint.processingEndedAt = Math.max(processingEnd, knownPaint.processingEndedAt);
|
|
304
|
+
return {
|
|
305
|
+
grew: grew,
|
|
306
|
+
paint: knownPaint
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
var newPaint = {
|
|
310
|
+
presentedAt: presentedAt,
|
|
311
|
+
processingStartedAt: processingStart,
|
|
312
|
+
processingEndedAt: processingEnd
|
|
313
|
+
};
|
|
314
|
+
this.recentPaints.push(newPaint);
|
|
315
|
+
return {
|
|
316
|
+
grew: true,
|
|
317
|
+
paint: newPaint
|
|
318
|
+
};
|
|
179
319
|
}
|
|
180
320
|
}]);
|
|
181
321
|
}();
|
|
@@ -7,11 +7,13 @@ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol
|
|
|
7
7
|
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; } }
|
|
8
8
|
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; }
|
|
9
9
|
import { getDocument } from '@atlaskit/browser-apis';
|
|
10
|
+
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
10
11
|
import { EditorEventObserver } from './editor-event-observer';
|
|
11
12
|
import { InteractionObserver } from './interaction-observer';
|
|
12
13
|
import { InteractivitySession } from './interactivity-session';
|
|
13
14
|
import { SCHEMA_VERSION } from './bucket-boundaries';
|
|
14
15
|
import { LifecycleObserver } from './lifecycle-observer';
|
|
16
|
+
import { LongAnimationFrameObserver } from './long-animation-frame-observer';
|
|
15
17
|
import { SnapshotScheduler } from './snapshot-scheduler';
|
|
16
18
|
/**
|
|
17
19
|
* Collects interaction latencies for one editor mount and emits session-to-date snapshots.
|
|
@@ -38,6 +40,9 @@ export var InteractivityCollector = /*#__PURE__*/function () {
|
|
|
38
40
|
this.interactionObserver = new InteractionObserver(function (entries) {
|
|
39
41
|
return _this.recordEntries(entries);
|
|
40
42
|
});
|
|
43
|
+
this.longAnimationFrameObserver = isExperimentEnabled('platform_editor_editor_interactivity_slowest') ? new LongAnimationFrameObserver(function (frames) {
|
|
44
|
+
return _this.recordFrames(frames);
|
|
45
|
+
}) : undefined;
|
|
41
46
|
this.editorEvents = new EditorEventObserver(function (event) {
|
|
42
47
|
return _this.recordEditorEvent(event);
|
|
43
48
|
});
|
|
@@ -70,6 +75,7 @@ export var InteractivityCollector = /*#__PURE__*/function () {
|
|
|
70
75
|
return _createClass(InteractivityCollector, [{
|
|
71
76
|
key: "start",
|
|
72
77
|
value: function start() {
|
|
78
|
+
var _this$longAnimationFr;
|
|
73
79
|
// Collecting is over for good once stopped: the session it covered has been reported.
|
|
74
80
|
if (this.stopped) {
|
|
75
81
|
return false;
|
|
@@ -85,6 +91,7 @@ export var InteractivityCollector = /*#__PURE__*/function () {
|
|
|
85
91
|
// this object was constructed.
|
|
86
92
|
this.session = this.createSession(0);
|
|
87
93
|
this.interactionObserver.start();
|
|
94
|
+
(_this$longAnimationFr = this.longAnimationFrameObserver) === null || _this$longAnimationFr === void 0 || _this$longAnimationFr.start();
|
|
88
95
|
this.editorEvents.observe(this.editorRoot);
|
|
89
96
|
this.snapshotScheduler.start();
|
|
90
97
|
this.lifecycleObserver.start();
|
|
@@ -96,12 +103,14 @@ export var InteractivityCollector = /*#__PURE__*/function () {
|
|
|
96
103
|
}, {
|
|
97
104
|
key: "stop",
|
|
98
105
|
value: function stop() {
|
|
106
|
+
var _this$longAnimationFr2;
|
|
99
107
|
if (this.stopped) {
|
|
100
108
|
return;
|
|
101
109
|
}
|
|
102
110
|
this.takeSnapshot('unmount');
|
|
103
111
|
this.stopped = true;
|
|
104
112
|
this.interactionObserver.stop();
|
|
113
|
+
(_this$longAnimationFr2 = this.longAnimationFrameObserver) === null || _this$longAnimationFr2 === void 0 || _this$longAnimationFr2.stop();
|
|
105
114
|
this.editorEvents.stop();
|
|
106
115
|
this.snapshotScheduler.stop();
|
|
107
116
|
this.lifecycleObserver.stop();
|
|
@@ -249,47 +258,47 @@ export var InteractivityCollector = /*#__PURE__*/function () {
|
|
|
249
258
|
this.session[group].countTotal();
|
|
250
259
|
this.session.revision += 1;
|
|
251
260
|
}
|
|
261
|
+
|
|
262
|
+
/** Long Animation Frames say where the latency of a slow interaction went. */
|
|
263
|
+
}, {
|
|
264
|
+
key: "recordFrames",
|
|
265
|
+
value: function recordFrames(frames) {
|
|
266
|
+
var _this$session$slowest;
|
|
267
|
+
if (this.stopped) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if ((_this$session$slowest = this.session.slowest) !== null && _this$session$slowest !== void 0 && _this$session$slowest.trackLongAnimationFrames(frames)) {
|
|
271
|
+
this.session.revision += 1;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
252
274
|
}, {
|
|
253
275
|
key: "recordEntries",
|
|
254
276
|
value: function recordEntries(entries) {
|
|
255
|
-
var _this$session = this.session,
|
|
256
|
-
tracker = _this$session.tracker,
|
|
257
|
-
page = _this$session.page,
|
|
258
|
-
slowest = _this$session.slowest;
|
|
259
277
|
var _iterator = _createForOfIteratorHelper(entries),
|
|
260
278
|
_step;
|
|
261
279
|
try {
|
|
262
280
|
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
263
|
-
var _update$group;
|
|
264
281
|
var entry = _step.value;
|
|
265
|
-
var
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (update.group) {
|
|
280
|
-
this.session[update.group].remeasure(update.fromMs, latencyMs);
|
|
282
|
+
var _iterator2 = _createForOfIteratorHelper(this.session.tracker.merge(entry)),
|
|
283
|
+
_step2;
|
|
284
|
+
try {
|
|
285
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
286
|
+
var _this$session$slowest2;
|
|
287
|
+
var update = _step2.value;
|
|
288
|
+
var groupsChanged = this.session.page.trackInteractionUpdate(update);
|
|
289
|
+
if (update.group) {
|
|
290
|
+
groupsChanged = this.session[update.group].trackInteractionUpdate(update) || groupsChanged;
|
|
291
|
+
}
|
|
292
|
+
var slowestChanged = (_this$session$slowest2 = this.session.slowest) === null || _this$session$slowest2 === void 0 ? void 0 : _this$session$slowest2.trackInteractionUpdate(entry, update);
|
|
293
|
+
if (groupsChanged || slowestChanged) {
|
|
294
|
+
this.session.revision += 1;
|
|
295
|
+
}
|
|
281
296
|
}
|
|
297
|
+
} catch (err) {
|
|
298
|
+
_iterator2.e(err);
|
|
299
|
+
} finally {
|
|
300
|
+
_iterator2.f();
|
|
282
301
|
}
|
|
283
|
-
|
|
284
|
-
// An interaction the editor never reported an event for is not the editor's as far as we
|
|
285
|
-
// know.
|
|
286
|
-
slowest === null || slowest === void 0 || slowest.track({
|
|
287
|
-
entry: entry,
|
|
288
|
-
interactionId: update.interactionId,
|
|
289
|
-
latencyMs: latencyMs,
|
|
290
|
-
group: (_update$group = update.group) !== null && _update$group !== void 0 ? _update$group : 'outsideEditor'
|
|
291
|
-
});
|
|
292
|
-
this.session.revision += 1;
|
|
293
302
|
}
|
|
294
303
|
} catch (err) {
|
|
295
304
|
_iterator.e(err);
|
|
@@ -300,7 +309,7 @@ export var InteractivityCollector = /*#__PURE__*/function () {
|
|
|
300
309
|
}, {
|
|
301
310
|
key: "takeSnapshot",
|
|
302
311
|
value: function takeSnapshot(reason) {
|
|
303
|
-
var _session$slowest;
|
|
312
|
+
var _this$longAnimationFr3, _session$slowest;
|
|
304
313
|
if (this.stopped) {
|
|
305
314
|
return;
|
|
306
315
|
}
|
|
@@ -314,10 +323,11 @@ export var InteractivityCollector = /*#__PURE__*/function () {
|
|
|
314
323
|
return;
|
|
315
324
|
}
|
|
316
325
|
|
|
317
|
-
// Must run before the change check: the browser may be holding entries that have
|
|
318
|
-
// reached the observer
|
|
319
|
-
// pick them up.
|
|
326
|
+
// Must run before the change check: the browser may be holding entries and frames that have
|
|
327
|
+
// not reached the observer callbacks yet, and on `pagehide` there is no later chance to
|
|
328
|
+
// pick them up. Entries first, so a record the frames answer for exists by then.
|
|
320
329
|
this.interactionObserver.drain();
|
|
330
|
+
(_this$longAnimationFr3 = this.longAnimationFrameObserver) === null || _this$longAnimationFr3 === void 0 || _this$longAnimationFr3.drain();
|
|
321
331
|
if (session.revision === session.emittedRevision) {
|
|
322
332
|
return;
|
|
323
333
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
2
|
+
import _createClass from "@babel/runtime/helpers/createClass";
|
|
3
|
+
/**
|
|
4
|
+
* Reports the frames the browser took longer than 50 ms to render to `onFrames`. They are what says
|
|
5
|
+
* where the time of a slow interaction went — which script ran the longest while the user waited,
|
|
6
|
+
* and how much of the wait was style and layout — which Event Timing cannot answer.
|
|
7
|
+
*
|
|
8
|
+
* What they say about an interaction is `SlowInteractionList`'s to work out; this only observes.
|
|
9
|
+
*/
|
|
10
|
+
export var LongAnimationFrameObserver = /*#__PURE__*/function () {
|
|
11
|
+
function LongAnimationFrameObserver(onFrames) {
|
|
12
|
+
_classCallCheck(this, LongAnimationFrameObserver);
|
|
13
|
+
this.onFrames = onFrames;
|
|
14
|
+
}
|
|
15
|
+
return _createClass(LongAnimationFrameObserver, [{
|
|
16
|
+
key: "start",
|
|
17
|
+
value: function start() {
|
|
18
|
+
var _this = this;
|
|
19
|
+
if (this.observer || !LongAnimationFrameObserver.isSupported()) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
this.observer = new PerformanceObserver(function (list) {
|
|
23
|
+
_this.onFrames(list.getEntries());
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Buffered, as `web-vitals` observes them: a frame reported before the editor mounted can
|
|
27
|
+
// still be the one an interaction right after it ran in.
|
|
28
|
+
this.observer.observe({
|
|
29
|
+
type: 'long-animation-frame',
|
|
30
|
+
buffered: true
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}, {
|
|
34
|
+
key: "drain",
|
|
35
|
+
value: function drain() {
|
|
36
|
+
var _this$observer;
|
|
37
|
+
var frames = (_this$observer = this.observer) === null || _this$observer === void 0 ? void 0 : _this$observer.takeRecords();
|
|
38
|
+
if (frames) {
|
|
39
|
+
this.onFrames(frames);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}, {
|
|
43
|
+
key: "stop",
|
|
44
|
+
value: function stop() {
|
|
45
|
+
var _this$observer2;
|
|
46
|
+
(_this$observer2 = this.observer) === null || _this$observer2 === void 0 || _this$observer2.disconnect();
|
|
47
|
+
this.observer = undefined;
|
|
48
|
+
}
|
|
49
|
+
}], [{
|
|
50
|
+
key: "isSupported",
|
|
51
|
+
value: function isSupported() {
|
|
52
|
+
return typeof PerformanceObserver !== 'undefined' && PerformanceObserver.supportedEntryTypes.includes('long-animation-frame');
|
|
53
|
+
}
|
|
54
|
+
}]);
|
|
55
|
+
}();
|