@atlaskit/insm 0.0.2 → 0.1.1

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 (38) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +203 -5
  3. package/dist/cjs/index.js +55 -1
  4. package/dist/cjs/inp-measurers/inp.js +184 -0
  5. package/dist/cjs/insm-period.js +345 -0
  6. package/dist/cjs/insm-session.js +183 -0
  7. package/dist/cjs/insm.js +129 -0
  8. package/dist/cjs/period-measurers/afps.js +193 -0
  9. package/dist/cjs/types.js +5 -0
  10. package/dist/es2019/index.js +54 -1
  11. package/dist/es2019/inp-measurers/inp.js +142 -0
  12. package/dist/es2019/insm-period.js +246 -0
  13. package/dist/es2019/insm-session.js +149 -0
  14. package/dist/es2019/insm.js +105 -0
  15. package/dist/es2019/period-measurers/afps.js +153 -0
  16. package/dist/es2019/types.js +1 -0
  17. package/dist/esm/index.js +54 -1
  18. package/dist/esm/inp-measurers/inp.js +177 -0
  19. package/dist/esm/insm-period.js +339 -0
  20. package/dist/esm/insm-session.js +177 -0
  21. package/dist/esm/insm.js +122 -0
  22. package/dist/esm/period-measurers/afps.js +186 -0
  23. package/dist/esm/types.js +1 -0
  24. package/dist/types/index.d.ts +10 -1
  25. package/dist/types/inp-measurers/inp.d.ts +37 -0
  26. package/dist/types/insm-period.d.ts +72 -0
  27. package/dist/types/insm-session.d.ts +91 -0
  28. package/dist/types/insm.d.ts +61 -0
  29. package/dist/types/period-measurers/afps.d.ts +57 -0
  30. package/dist/types/types.d.ts +81 -0
  31. package/dist/types-ts4.5/index.d.ts +10 -1
  32. package/dist/types-ts4.5/inp-measurers/inp.d.ts +37 -0
  33. package/dist/types-ts4.5/insm-period.d.ts +72 -0
  34. package/dist/types-ts4.5/insm-session.d.ts +91 -0
  35. package/dist/types-ts4.5/insm.d.ts +61 -0
  36. package/dist/types-ts4.5/period-measurers/afps.d.ts +57 -0
  37. package/dist/types-ts4.5/types.d.ts +81 -0
  38. package/package.json +6 -5
@@ -0,0 +1,149 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import { PeriodTracking } from './insm-period';
3
+
4
+ /**
5
+ * Only intended for internal use.
6
+ *
7
+ * Exported for consumers who may require the type.
8
+ */
9
+ export class INSMSession {
10
+ constructor(experienceKey, experienceProperties, insm) {
11
+ _defineProperty(this, "startedAt", performance.now());
12
+ _defineProperty(this, "running", true);
13
+ _defineProperty(this, "addedProperties", []);
14
+ _defineProperty(this, "runningFeatures", new Set());
15
+ this.experienceKey = experienceKey;
16
+ this.experienceProperties = experienceProperties;
17
+ this.insm = insm;
18
+ this.periodTracking = new PeriodTracking(this);
19
+
20
+ /**
21
+ * Note: Events are not reliably fired from mobile browsers (ie. when a browser is closed when not in use)
22
+ */
23
+ }
24
+
25
+ /**
26
+ * Adds a feature to the currently running session
27
+ */
28
+ startFeature(featureName) {
29
+ var _this$periodTracking;
30
+ this.runningFeatures.add(featureName);
31
+ (_this$periodTracking = this.periodTracking) === null || _this$periodTracking === void 0 ? void 0 : _this$periodTracking.startFeature(featureName);
32
+ }
33
+
34
+ /**
35
+ * Ends a features usage in the currently running session
36
+ */
37
+ endFeature(featureName) {
38
+ this.runningFeatures.delete(featureName);
39
+ }
40
+
41
+ /**
42
+ * Returns details on the current session.
43
+ */
44
+ get details() {
45
+ return {
46
+ experienceKey: this.experienceKey,
47
+ experienceProperties: this.experienceProperties,
48
+ paused: this.periodTracking.pauses.size > 0,
49
+ periodState: this.periodTracking.state,
50
+ /**
51
+ * The only scenario where this value should return false is when
52
+ * the experience has been stopped early.
53
+ */
54
+ running: this.running
55
+ };
56
+ }
57
+
58
+ /**
59
+ * This api takes either a static single-level key-value object, or callbacks which return the same and
60
+ * will be evaluated on session end.
61
+ *
62
+ * When ending a session, all properties received via this api are merged, in order, into the resulting
63
+ * insm event’s properties; last write wins.
64
+ *
65
+ * Callback values are evaluated at session end.
66
+ *
67
+ * For example, for the following
68
+ *
69
+ * ```ts
70
+ * insm.experience.addProperties({ one: 1, two: 2 });
71
+ * insm.experience.addProperties(() => ({ one: 'one' }));
72
+ * insm.experience.addProperties({ three: 3 });
73
+ * ```
74
+ *
75
+ * The resulting added properties will be
76
+ *
77
+ * ```ts
78
+ * { one: 'one', two: 2, three: 3 }
79
+ * ```
80
+ */
81
+ addProperties(propertiesToAdd) {
82
+ this.addedProperties.push(propertiesToAdd);
83
+ }
84
+
85
+ /**
86
+ * In some scenarios (ie. when a page error boundary is hit), you will want to exit early.
87
+ * This is api supports these scenarios
88
+ *
89
+ * ```ts
90
+ * insm.stopEarly(reasonKey: string, description: string);
91
+ * ```
92
+ *
93
+ * Sessions closed early are identifiable by their end details
94
+ * `"endDetails": { stoppedBy: "early-stop", reasonKey, description }`.
95
+ *
96
+ * **Note**: The session is ended as soon as this is called, and any `addProperties` handlers will
97
+ * called immediately.
98
+ */
99
+ earlyStop(reason, description) {
100
+ this.end({
101
+ stoppedBy: 'early-stop',
102
+ reason,
103
+ description
104
+ });
105
+ }
106
+ end(endDetails) {
107
+ var _this$insm$analyticsW;
108
+ if (this.running === false) {
109
+ // If an experience has already been ended -- don't repeat the ending
110
+ return;
111
+ }
112
+ this.running = false;
113
+ const endedAt = performance.now();
114
+ const duration = endedAt - this.startedAt;
115
+ const evaluatedAddedProperties = {};
116
+ for (const addedProperty of this.addedProperties) {
117
+ if (typeof addedProperty === 'function') {
118
+ Object.assign(evaluatedAddedProperties, addedProperty());
119
+ } else {
120
+ Object.assign(evaluatedAddedProperties, addedProperty);
121
+ }
122
+ }
123
+ const periodResults = this.periodTracking.endResults;
124
+
125
+ /**
126
+ * This event ends up as "insm measured"
127
+ */
128
+ const operationalEvent = {
129
+ actionSubject: 'insm',
130
+ action: 'measured',
131
+ attributes: {
132
+ // Added first to ensure these don't overwrite any insm properties
133
+ ...evaluatedAddedProperties,
134
+ experienceKey: this.experienceKey,
135
+ initial: this.experienceProperties.initial,
136
+ contentId: this.experienceProperties.contentId,
137
+ timing: {
138
+ startedAt: this.startedAt,
139
+ duration
140
+ },
141
+ periods: periodResults,
142
+ endDetails: endDetails
143
+ },
144
+ tags: ['insm'],
145
+ source: 'insm'
146
+ };
147
+ (_this$insm$analyticsW = this.insm.analyticsWebClient) === null || _this$insm$analyticsW === void 0 ? void 0 : _this$insm$analyticsW.sendOperationalEvent(operationalEvent);
148
+ }
149
+ }
@@ -0,0 +1,105 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import { INSMSession } from './insm-session';
3
+ import { AnimationFPSIM } from './period-measurers/afps';
4
+ import { INPTracker } from './inp-measurers/inp';
5
+ export class INSM {
6
+ constructor(options) {
7
+ /**
8
+ * Heavy tasks are tracked at the insm layer as heavy tasks
9
+ * are expected at times to be unrelated to the current
10
+ * page session.
11
+ */
12
+ _defineProperty(this, "runningHeavyTasks", new Set());
13
+ this.periodMeasurers = [new AnimationFPSIM(), new INPTracker(), new INPTracker({
14
+ includedInteractions: ['pointerup']
15
+ }), new INPTracker({
16
+ includedInteractions: ['pointerdown']
17
+ }), new INPTracker({
18
+ includedInteractions: ['click']
19
+ }), new INPTracker({
20
+ includedInteractions: ['keydown']
21
+ }), new INPTracker({
22
+ includedInteractions: ['keyup']
23
+ })];
24
+ this.options = options;
25
+
26
+ // If this does throw -- we do want an unhandledRejection rejection to be passed to the window
27
+ // this is to ease debugging.
28
+ options.getAnalyticsWebClient.then(analyticsWebClient => this.analyticsWebClient = analyticsWebClient);
29
+
30
+ // No cleanup needs to be performed -- as this is intended to run until the tab is closed
31
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
32
+ window.addEventListener('pagehide', () => {
33
+ var _this$runningSession;
34
+ (_this$runningSession = this.runningSession) === null || _this$runningSession === void 0 ? void 0 : _this$runningSession.end({
35
+ stoppedBy: 'pagehide'
36
+ });
37
+ });
38
+ }
39
+
40
+ /**
41
+ * Starts a heavy task in the currently running session.
42
+ *
43
+ * This also pauses measurement.
44
+ */
45
+ startHeavyTask(heavyTaskName) {
46
+ var _this$runningSession2, _this$runningSession3, _this$runningSession4;
47
+ this.runningHeavyTasks.add(heavyTaskName);
48
+ (_this$runningSession2 = this.runningSession) === null || _this$runningSession2 === void 0 ? void 0 : (_this$runningSession3 = _this$runningSession2.periodTracking) === null || _this$runningSession3 === void 0 ? void 0 : _this$runningSession3.startHeavyTask(heavyTaskName);
49
+ (_this$runningSession4 = this.runningSession) === null || _this$runningSession4 === void 0 ? void 0 : _this$runningSession4.periodTracking.pause(heavyTaskName);
50
+ }
51
+
52
+ /**
53
+ * Ends a heavy task in the currently running session
54
+ */
55
+ endHeavyTask(heavyTaskName) {
56
+ var _this$runningSession5;
57
+ this.runningHeavyTasks.delete(heavyTaskName);
58
+ (_this$runningSession5 = this.runningSession) === null || _this$runningSession5 === void 0 ? void 0 : _this$runningSession5.periodTracking.resume(heavyTaskName);
59
+ }
60
+
61
+ /**
62
+ * Call this when starting a new experience. This is expected to be wired to the product
63
+ * routing solution.
64
+ *
65
+ * It's expected this call will be paired with a `insm.session.startHeavyTask('page-load')` and subsequent `insm.session.endHeavyTask('page-load')`
66
+ * so that performance degradations linked to the page initialisation are excluded from the active interactivity monitoring.
67
+ *
68
+ *
69
+ * ```ts
70
+ * insm.start('edit-page', { initial: true, contentId: '9001' })
71
+ * insm.session.startHeavyTask(''page-load')
72
+ * // ... heavy initialisation work
73
+ * insm.session.endHeavyTask(''page-load')
74
+ * ```
75
+ */
76
+ start(experienceKey, experienceProperties) {
77
+ var _this$options$experie;
78
+ if (this.runningSession !== undefined) {
79
+ this.runningSession.end({
80
+ stoppedBy: 'new-experience',
81
+ experienceKey: experienceKey,
82
+ contentId: experienceProperties.contentId
83
+ });
84
+ }
85
+ if ((_this$options$experie = this.options.experiences[experienceKey]) !== null && _this$options$experie !== void 0 && _this$options$experie.enabled) {
86
+ this.runningSession = new INSMSession(experienceKey, experienceProperties, this);
87
+ }
88
+ }
89
+
90
+ /**
91
+ * This prematurely halts any running experience measurement. It's expected to be used in
92
+ * scenarios such as when error boundaries are hit.
93
+ */
94
+ stopEarly(reasonKey, description) {
95
+ var _this$runningSession6;
96
+ (_this$runningSession6 = this.runningSession) === null || _this$runningSession6 === void 0 ? void 0 : _this$runningSession6.earlyStop(reasonKey, description);
97
+ }
98
+
99
+ /**
100
+ * Gets the current running session details
101
+ */
102
+ get session() {
103
+ return this.runningSession;
104
+ }
105
+ }
@@ -0,0 +1,153 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ export class AnimationFPSIM {
3
+ constructor() {
4
+ /**
5
+ * AFPS stands for Animation Frames Per Second
6
+ */
7
+ _defineProperty(this, "name", 'afps');
8
+ _defineProperty(this, "monitor", new AnimationFPSMonitor());
9
+ }
10
+ start(paused) {
11
+ const result = this.monitor.startNewWindow(paused);
12
+ return result;
13
+ }
14
+ end() {
15
+ const result = this.monitor.end();
16
+ return result;
17
+ }
18
+ pause() {
19
+ this.monitor.pause();
20
+ }
21
+ resume() {
22
+ this.monitor.resume();
23
+ }
24
+ }
25
+ class AnimationFPSMonitor {
26
+ constructor() {
27
+ _defineProperty(this, "paused", false);
28
+ _defineProperty(this, "currentState", {
29
+ numerator: 0,
30
+ denominator: 0,
31
+ max: 0,
32
+ average: 0,
33
+ min: 0
34
+ });
35
+ // Current measurement window
36
+ _defineProperty(this, "windowFrameCount", 0);
37
+ _defineProperty(this, "windowTotalTime", 0);
38
+ _defineProperty(this, "currentFrameStart", 0);
39
+ }
40
+ measureWindowFPS() {
41
+ // Calculate FPS for this window
42
+ // Note: windowFrameCount and windowTotalTime is reset after each window measurement
43
+ const windowSeconds = this.windowTotalTime / 1000;
44
+ // frames / seconds
45
+ const windowFPS = this.windowFrameCount / windowSeconds;
46
+
47
+ // Update overall tracking
48
+ this.currentState.numerator += this.windowFrameCount;
49
+ // The denominator is seconds not ms
50
+ this.currentState.denominator += windowSeconds;
51
+
52
+ // Calculate overall average FPS: (total frames / total time measured in seconds
53
+ this.currentState.average = this.currentState.numerator / this.currentState.denominator;
54
+ if (this.currentState.max === 0 || windowFPS > this.currentState.max) {
55
+ this.currentState.max = windowFPS;
56
+ }
57
+ if (this.currentState.min === 0 || windowFPS < this.currentState.min) {
58
+ this.currentState.min = windowFPS;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * If there is running tracking - it will be reset
64
+ */
65
+ startWindowTracking() {
66
+ // Clear any previous tracking state
67
+ this.resetOverallTracking();
68
+ const measureFrame = () => {
69
+ this.currentFrameStart = global.performance.now();
70
+ this.animationFrame = requestAnimationFrame(() => {
71
+ // While measurement is paused -- we don't count the animation frame towards the monitored
72
+ // period.
73
+ if (!this.paused) {
74
+ const frameDuration = performance.now() - this.currentFrameStart;
75
+
76
+ // Add frame to current window
77
+ this.windowFrameCount++;
78
+ this.windowTotalTime += frameDuration;
79
+
80
+ // Check if window is >= 1 second (1000ms)
81
+ // When the page is running smoothly it's expected this will not be hit frequently
82
+ if (this.windowTotalTime >= 1000) {
83
+ this.measureWindowFPS();
84
+ // Reset window for next measurement
85
+ this.resetWindow();
86
+ }
87
+ }
88
+ measureFrame();
89
+ });
90
+ };
91
+ measureFrame();
92
+ }
93
+ startNewWindow(paused) {
94
+ const lastWindowResult = {
95
+ numerator: this.currentState.numerator,
96
+ denominator: this.currentState.denominator,
97
+ max: this.currentState.max,
98
+ min: this.currentState.min,
99
+ average: this.currentState.average
100
+ };
101
+ this.paused = paused;
102
+ this.startWindowTracking();
103
+ return lastWindowResult;
104
+ }
105
+ resetWindow() {
106
+ this.windowFrameCount = 0;
107
+ this.windowTotalTime = 0;
108
+ // Note this.currentFrameStart is over ridden
109
+ // as part of tracking - so does not need to be
110
+ // reset.
111
+ }
112
+ endWindowTracking() {
113
+ if (this.animationFrame) {
114
+ cancelAnimationFrame(this.animationFrame);
115
+ }
116
+ }
117
+ resetOverallTracking() {
118
+ this.resetWindow();
119
+ this.endWindowTracking();
120
+ this.currentState = {
121
+ numerator: 0,
122
+ denominator: 0,
123
+ max: 0,
124
+ min: 0,
125
+ average: 0
126
+ };
127
+ }
128
+ end() {
129
+ try {
130
+ this.measureWindowFPS();
131
+ return this.currentState;
132
+ } finally {
133
+ this.resetOverallTracking();
134
+ }
135
+ }
136
+ pause() {
137
+ // Note - we leave the tracking animationFrame monitoring running, and on resume
138
+ // simply set the currentFrameStart and paused to true
139
+ // This works because the tracking builds windows of tracked time rather than
140
+ // basing itself off a window start time and time elapsed time since then.
141
+ this.paused = true;
142
+ }
143
+ resume() {
144
+ // Note - We don't need to handle the gap in measurement - as the raf logic builds/increments a window time
145
+ // based on the currentFrameStart (and any previous measurements which can only happen while the tracking
146
+ // is not paused).
147
+ this.currentFrameStart = performance.now();
148
+ this.paused = false;
149
+ if (!this.animationFrame) {
150
+ this.startWindowTracking();
151
+ }
152
+ }
153
+ }
@@ -0,0 +1 @@
1
+ export {};
package/dist/esm/index.js CHANGED
@@ -1,4 +1,57 @@
1
+ import { INSM } from './insm';
2
+ var initialisedInsm;
3
+
1
4
  /**
2
5
  * Initializes the INSM (Interactivity Session Measurement) tooling
3
6
  */
4
- export function init() {}
7
+ export function init(options) {
8
+ initialisedInsm = new INSM(options);
9
+ }
10
+ function insmInitialised() {
11
+ if (!initialisedInsm) {
12
+ // eslint-disable-next-line no-console
13
+ console.error('INSM used when not initialised');
14
+ return false;
15
+ }
16
+ return true;
17
+ }
18
+
19
+ /**
20
+ * **In**teractivity **s**ession **m**onitoring
21
+ */
22
+ export var insm = {
23
+ startHeavyTask: function startHeavyTask(heavyTaskName) {
24
+ if (insmInitialised()) {
25
+ initialisedInsm.startHeavyTask(heavyTaskName);
26
+ }
27
+ },
28
+ endHeavyTask: function endHeavyTask(heavyTaskName) {
29
+ if (insmInitialised()) {
30
+ initialisedInsm.endHeavyTask(heavyTaskName);
31
+ }
32
+ },
33
+ start: function start(experienceKey, experienceProperties) {
34
+ if (insmInitialised()) {
35
+ initialisedInsm.start(experienceKey, experienceProperties);
36
+ }
37
+ },
38
+ stopEarly: function stopEarly(reasonKey, description) {
39
+ if (insmInitialised()) {
40
+ initialisedInsm.stopEarly(reasonKey, description);
41
+ }
42
+ },
43
+ // We only expose details and feature start/stop to consumers
44
+ // as the other properties are internals for the insm and InsmPeriod
45
+ // to interact with the running session.
46
+ get session() {
47
+ if (insmInitialised()) {
48
+ return initialisedInsm.runningSession;
49
+ }
50
+ },
51
+ // @ts-expect-error Private method for testing purposes
52
+ __setAnalyticsWebClient: function __setAnalyticsWebClient(analyticsWebClient) {
53
+ if (initialisedInsm) {
54
+ initialisedInsm.analyticsWebClient = analyticsWebClient;
55
+ }
56
+ }
57
+ };
@@ -0,0 +1,177 @@
1
+ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
2
+ import _createClass from "@babel/runtime/helpers/createClass";
3
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
4
+ export var INPTracker = /*#__PURE__*/function () {
5
+ function INPTracker(options) {
6
+ _classCallCheck(this, INPTracker);
7
+ /**
8
+ * INP stands for Interaction to Next Paint
9
+ */
10
+ _defineProperty(this, "name", 'inp');
11
+ this.includedInteractions = (options === null || options === void 0 ? void 0 : options.includedInteractions) || ['pointerdown', 'pointerup', 'click', 'keydown', 'keyup'];
12
+ this.monitor = new InteractionTracker(this.includedInteractions);
13
+ }
14
+ return _createClass(INPTracker, [{
15
+ key: "start",
16
+ value: function start(paused) {
17
+ var result = this.monitor.start(paused);
18
+ return result;
19
+ }
20
+ }, {
21
+ key: "end",
22
+ value: function end() {
23
+ var result = this.monitor.end();
24
+ return result;
25
+ }
26
+ }, {
27
+ key: "pause",
28
+ value: function pause() {
29
+ this.monitor.pause();
30
+ }
31
+ }, {
32
+ key: "resume",
33
+ value: function resume() {
34
+ this.monitor.resume();
35
+ }
36
+ }]);
37
+ }();
38
+ var InteractionResult = /*#__PURE__*/function () {
39
+ function InteractionResult() {
40
+ _classCallCheck(this, InteractionResult);
41
+ this.min = Infinity;
42
+ this.max = 0;
43
+ this.average = 0;
44
+ this.numerator = 0;
45
+ this.count = 0;
46
+ }
47
+ return _createClass(InteractionResult, [{
48
+ key: "update",
49
+ value: function update(duration) {
50
+ if (duration > this.max) {
51
+ this.max = duration;
52
+ }
53
+ if (duration < this.min) {
54
+ this.min = duration;
55
+ }
56
+ this.numerator += this.average * (this.count - 1) + duration;
57
+ this.count += 1;
58
+ this.average = this.numerator / this.count;
59
+ }
60
+ }, {
61
+ key: "toMeasure",
62
+ value: function toMeasure() {
63
+ return {
64
+ min: this.count === 0 ? 0 : this.min,
65
+ max: this.max,
66
+ average: this.average,
67
+ numerator: this.numerator,
68
+ denominator: this.count
69
+ };
70
+ }
71
+ }]);
72
+ }();
73
+ var InteractionTracker = /*#__PURE__*/function () {
74
+ function InteractionTracker(includedInteractions) {
75
+ _classCallCheck(this, InteractionTracker);
76
+ _defineProperty(this, "paused", false);
77
+ this.performanceObserver = null;
78
+ this.interactionResult = new InteractionResult();
79
+ this.includedInteractions = includedInteractions;
80
+ }
81
+ return _createClass(InteractionTracker, [{
82
+ key: "stopTracking",
83
+ value: function stopTracking() {
84
+ var _this$performanceObse;
85
+ (_this$performanceObse = this.performanceObserver) === null || _this$performanceObse === void 0 || _this$performanceObse.disconnect();
86
+ this.performanceObserver = null;
87
+ }
88
+ }, {
89
+ key: "startTracking",
90
+ value: function startTracking() {
91
+ var _this = this;
92
+ this.performanceObserver = new PerformanceObserver(function (list) {
93
+ // Note: find link to actual safari issue .. good to get rid of this if Safari has fixed this
94
+ // Delay by a microtask to workaround a bug in Safari where the
95
+ // callback is invoked immediately, rather than in a separate task.
96
+ // See: https://github.com/GoogleChrome/web-vitals/issues/277
97
+ Promise.resolve().then(function () {
98
+ var entries = list.getEntries();
99
+ entries.forEach(function (entry) {
100
+ // Skip further processing for entries that cannot be INP candidates.
101
+ //
102
+ // When a user interacts with a web page, a user interaction (for example a click) usually triggers a sequence of events.
103
+ // To measure the latency of this series of events, the events share the same interactionId.
104
+ // An interactionId is only computed for the following event types belonging to a user interaction. It is 0 otherwise.
105
+ // * click / tap / drag events: 'pointerdown', 'pointerup', 'click'
106
+ // * keypress events: 'keydown', 'keyup'
107
+ //
108
+ if (!entry.interactionId) {
109
+ return;
110
+ }
111
+ if (_this.includedInteractions.includes(entry.name)) {
112
+ _this.interactionResult.update(entry.duration);
113
+ }
114
+ });
115
+ });
116
+ });
117
+
118
+ // Event Timing entries have their durations rounded to the nearest 8ms,
119
+ // so a duration of 40ms would be any event that spans 2.5 or more frames
120
+ // at 60Hz. This threshold is chosen to strike a balance between usefulness
121
+ // and performance. Running this callback for any interaction that spans
122
+ // just one or two frames is likely not worth the insight that could be
123
+ // gained.
124
+ if (PerformanceObserver.supportedEntryTypes.includes('event')) {
125
+ this.performanceObserver.observe({
126
+ type: 'event',
127
+ buffered: true,
128
+ durationThreshold: 40
129
+ });
130
+ }
131
+ }
132
+ }, {
133
+ key: "reset",
134
+ value: function reset() {
135
+ this.stopTracking();
136
+ this.interactionResult = new InteractionResult();
137
+ }
138
+ }, {
139
+ key: "start",
140
+ value: function start(paused) {
141
+ var lastResult = this.interactionResult.toMeasure();
142
+ this.reset();
143
+ this.paused = paused;
144
+ if (!paused) {
145
+ this.startTracking();
146
+ }
147
+ return lastResult;
148
+ }
149
+ }, {
150
+ key: "end",
151
+ value: function end() {
152
+ try {
153
+ return this.interactionResult.toMeasure();
154
+ } finally {
155
+ this.reset();
156
+ }
157
+ }
158
+ }, {
159
+ key: "pause",
160
+ value: function pause() {
161
+ if (this.paused) {
162
+ return;
163
+ }
164
+ this.paused = true;
165
+ this.stopTracking();
166
+ }
167
+ }, {
168
+ key: "resume",
169
+ value: function resume() {
170
+ if (!this.paused) {
171
+ return;
172
+ }
173
+ this.paused = false;
174
+ this.startTracking();
175
+ }
176
+ }]);
177
+ }();