@atlaskit/insm 0.1.3 → 0.2.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.
@@ -1,7 +1,9 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ // Script timing data we want to track
3
+
2
4
  export class LongAnimationFrameMeasurer {
3
5
  constructor(options) {
4
- _defineProperty(this, "longestAnimationFrames", []);
6
+ _defineProperty(this, "longestScriptTimings", []);
5
7
  _defineProperty(this, "minimumIndex", -1);
6
8
  _defineProperty(this, "minimumDuration", Infinity);
7
9
  this.options = options;
@@ -20,48 +22,72 @@ export class LongAnimationFrameMeasurer {
20
22
  }
21
23
  }
22
24
  handleBatch(batch) {
23
- let len = this.longestAnimationFrames.length;
24
25
  for (let batchIndex = 0; batchIndex < batch.length; batchIndex++) {
25
- if (batch[batchIndex].duration < this.options.reportingThreshold) {
26
- // If the long frame entry had a duration less than the reporting
27
- // threshold it's not eligible for tracking
26
+ const animationFrame = batch[batchIndex];
27
+
28
+ // Only process frames that exceed the reporting threshold
29
+ if (animationFrame.duration < this.options.reportingThreshold) {
28
30
  continue;
29
31
  }
30
- const longAnimationFrameEntry = Object.assign(batch[batchIndex], {
31
- runningFeatures: Array.from(this.options.insmSession.runningFeatures)
32
- });
33
32
 
34
- // Not yet at capacity: append and update min tracking incrementally
35
- if (len < this.options.limit) {
36
- this.longestAnimationFrames[len] = longAnimationFrameEntry;
37
- if (len === 0 || longAnimationFrameEntry.duration < this.minimumDuration) {
38
- this.minimumDuration = longAnimationFrameEntry.duration;
39
- this.minimumIndex = len;
33
+ // Process all scripts in this long animation frame
34
+ if (animationFrame.scripts && animationFrame.scripts.length > 0) {
35
+ for (const script of animationFrame.scripts) {
36
+ this.processScript(script, animationFrame);
40
37
  }
41
- len++;
42
- continue;
43
38
  }
39
+ }
40
+ }
41
+ createScriptTiming(script, animationFrame) {
42
+ return {
43
+ duration: script.duration,
44
+ forcedStyleAndLayoutDuration: script.forcedStyleAndLayoutDuration,
45
+ invoker: script.invoker,
46
+ invokerType: script.invokerType,
47
+ sourceCharPosition: script.sourceCharPosition,
48
+ sourceFunctionName: script.sourceFunctionName,
49
+ sourceURL: script.sourceURL,
50
+ features: Array.from(this.options.insmSession.runningFeatures),
51
+ afDuration: animationFrame.duration
52
+ };
53
+ }
54
+ processScript(script, animationFrame) {
55
+ const scriptDuration = script.duration;
56
+ const len = this.longestScriptTimings.length;
44
57
 
45
- // At capacity: only do work if we beat the current min
46
- if (longAnimationFrameEntry.duration <= this.minimumDuration) {
47
- continue;
58
+ // Not yet at capacity: append and update min tracking incrementally
59
+ if (len < this.options.limit) {
60
+ const scriptTiming = this.createScriptTiming(script, animationFrame);
61
+ this.longestScriptTimings[len] = scriptTiming;
62
+ if (len === 0 || scriptDuration < this.minimumDuration) {
63
+ this.minimumDuration = scriptDuration;
64
+ this.minimumIndex = len;
48
65
  }
66
+ return;
67
+ }
49
68
 
50
- // Replace the current min
51
- this.longestAnimationFrames[this.minimumIndex] = longAnimationFrameEntry;
52
- let _possiblyNewMinimumIndex = 0;
53
- let _possibleNewMinimumDuration = this.longestAnimationFrames[0].duration;
54
- for (let i = 1; i < this.options.limit; i++) {
55
- if (this.longestAnimationFrames[i].duration < _possibleNewMinimumDuration) {
56
- _possiblyNewMinimumIndex = i;
57
- _possibleNewMinimumDuration = this.longestAnimationFrames[i].duration;
58
- }
59
- }
69
+ // At capacity: only do work if we beat the current min
70
+ if (scriptDuration <= this.minimumDuration) {
71
+ return;
72
+ }
73
+ const scriptTiming = this.createScriptTiming(script, animationFrame);
60
74
 
61
- // Update the min tracking
62
- this.minimumIndex = _possiblyNewMinimumIndex;
63
- this.minimumDuration = _possibleNewMinimumDuration;
75
+ // Replace the current min
76
+ this.longestScriptTimings[this.minimumIndex] = scriptTiming;
77
+
78
+ // Find new minimum
79
+ let newMinimumIndex = 0;
80
+ let newMinimumDuration = this.longestScriptTimings[0].duration;
81
+ for (let i = 1; i < this.options.limit; i++) {
82
+ if (this.longestScriptTimings[i].duration < newMinimumDuration) {
83
+ newMinimumIndex = i;
84
+ newMinimumDuration = this.longestScriptTimings[i].duration;
85
+ }
64
86
  }
87
+
88
+ // Update the min tracking
89
+ this.minimumIndex = newMinimumIndex;
90
+ this.minimumDuration = newMinimumDuration;
65
91
  }
66
92
 
67
93
  /**
@@ -79,10 +105,10 @@ export class LongAnimationFrameMeasurer {
79
105
  }
80
106
 
81
107
  /**
82
- * Returns the current tracked longest animation frames sorted by duration
108
+ * Returns the current tracked longest script timings sorted by duration
83
109
  */
84
110
  get current() {
85
- const copy = this.longestAnimationFrames.slice();
111
+ const copy = this.longestScriptTimings.slice();
86
112
  copy.sort((a, b) => b.duration - a.duration);
87
113
  return copy;
88
114
  }
@@ -97,5 +123,6 @@ export class LongAnimationFrameMeasurer {
97
123
  }
98
124
 
99
125
  // Based on https://github.com/GoogleChrome/web-vitals/blob/1b872cf5f2159e8ace0e98d55d8eb54fb09adfbe/src/types.ts#L129
126
+ // exported for use in test file
100
127
 
101
128
  // Based on https://github.com/GoogleChrome/web-vitals/blob/1b872cf5f2159e8ace0e98d55d8eb54fb09adfbe/src/types.ts#L129
@@ -5,7 +5,7 @@ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol
5
5
  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; } }
6
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; }
7
7
  /* eslint-disable @repo/internal/dom-events/no-unsafe-event-listeners */
8
-
8
+ import { fg } from '@atlaskit/platform-feature-flags';
9
9
  export var PeriodTracking = /*#__PURE__*/function () {
10
10
  function PeriodTracking(session) {
11
11
  _classCallCheck(this, PeriodTracking);
@@ -151,6 +151,24 @@ export var PeriodTracking = /*#__PURE__*/function () {
151
151
  key: "endResults",
152
152
  get: function get() {
153
153
  this.changePeriodAndTrackLast(this.state);
154
+ if (fg('cc_editor_insm_fix_attributes')) {
155
+ return {
156
+ active: {
157
+ features: Array.from(this.periodMeasurements.active.features),
158
+ heavyTasks: Array.from(this.periodMeasurements.active.heavyTasks),
159
+ measurements: this.periodMeasurements.active.measurements,
160
+ duration: this.periodMeasurements.active.duration,
161
+ count: this.periodMeasurements.active.count
162
+ },
163
+ inactive: {
164
+ features: Array.from(this.periodMeasurements.inactive.features),
165
+ heavyTasks: Array.from(this.periodMeasurements.inactive.heavyTasks),
166
+ measurements: this.periodMeasurements.inactive.measurements,
167
+ duration: this.periodMeasurements.inactive.duration,
168
+ count: this.periodMeasurements.inactive.count
169
+ }
170
+ };
171
+ }
154
172
  return this.periodMeasurements;
155
173
  }
156
174
  }, {
@@ -15,11 +15,14 @@ import { LongAnimationFrameMeasurer } from './session-measurers/LongAnimationFra
15
15
  * Only intended for internal use.
16
16
  *
17
17
  * Exported for consumers who may require the type.
18
+ *
19
+ * Note: Events are not reliably fired from mobile browsers (ie. when a browser is closed when not in use)
18
20
  */
19
21
  export var INSMSession = /*#__PURE__*/function () {
20
22
  function INSMSession(experienceKey, experienceProperties, insm) {
21
23
  _classCallCheck(this, INSMSession);
22
24
  _defineProperty(this, "startedAt", performance.now());
25
+ _defineProperty(this, "pageLoadTime", null);
23
26
  _defineProperty(this, "running", true);
24
27
  _defineProperty(this, "addedProperties", []);
25
28
  _defineProperty(this, "runningFeatures", new Set());
@@ -33,12 +36,18 @@ export var INSMSession = /*#__PURE__*/function () {
33
36
  insmSession: this,
34
37
  reportingThreshold: 500
35
38
  });
36
-
37
- /**
38
- * Note: Events are not reliably fired from mobile browsers (ie. when a browser is closed when not in use)
39
- */
40
39
  }
40
+
41
+ /**
42
+ * Completes the page load timing. This is called automatically when ending a heavy task
43
+ * with the key 'PageLoad'.
44
+ */
41
45
  return _createClass(INSMSession, [{
46
+ key: "completePageLoad",
47
+ value: function completePageLoad() {
48
+ this.pageLoadTime = performance.now() - this.startedAt;
49
+ }
50
+ }, {
42
51
  key: "updateExperienceKey",
43
52
  value: function updateExperienceKey(experienceKey) {
44
53
  this.experienceKey = experienceKey;
@@ -138,7 +147,7 @@ export var INSMSession = /*#__PURE__*/function () {
138
147
  }, {
139
148
  key: "end",
140
149
  value: function end(endDetails) {
141
- var _this$insm$analyticsW;
150
+ var _periodResults$active, _periodResults$active2, _periodResults$active3, _periodResults$active4, _this$insm$analyticsW;
142
151
  if (this.running === false) {
143
152
  // If an experience has already been ended -- don't repeat the ending
144
153
  return;
@@ -171,19 +180,27 @@ export var INSMSession = /*#__PURE__*/function () {
171
180
  var operationalEvent = {
172
181
  actionSubject: 'insm',
173
182
  action: 'measured',
174
- attributes: _objectSpread(_objectSpread({}, evaluatedAddedProperties), {}, {
183
+ attributes: _objectSpread(_objectSpread(_objectSpread({}, evaluatedAddedProperties), {}, {
175
184
  experienceKey: this.experienceKey,
176
185
  initial: this.experienceProperties.initial,
177
186
  contentId: this.experienceProperties.contentId,
178
187
  timing: {
179
188
  startedAt: this.startedAt,
189
+ // Note: this will not match up with the periods sum of durations, as it includes
190
+ // time paused when heavy tasks were running, and/or the page is in the background.
180
191
  duration: duration
181
192
  },
182
193
  periods: periodResults,
183
- endDetails: endDetails,
184
- longAnimationFrames: this.longAnimationFrameMeasurer.current
194
+ endDetails: endDetails
195
+ }, expValEquals('cc_editor_insm_outlier_events', 'cohort', 'test') ? {
196
+ longScripts: this.longAnimationFrameMeasurer.current,
197
+ pageLoadTime: this.pageLoadTime,
198
+ deviceDetails: getDeviceDetails()
199
+ } : {}), {}, {
200
+ // these health attributes drive our SLOs
201
+ healthAFPS: (_periodResults$active = periodResults.active.measurements.afps) !== null && _periodResults$active !== void 0 && _periodResults$active.average ? ((_periodResults$active2 = periodResults.active.measurements.afps) === null || _periodResults$active2 === void 0 ? void 0 : _periodResults$active2.average) >= 50 : undefined,
202
+ healthINP: (_periodResults$active3 = periodResults.active.measurements.inp) !== null && _periodResults$active3 !== void 0 && _periodResults$active3.average ? ((_periodResults$active4 = periodResults.active.measurements.inp) === null || _periodResults$active4 === void 0 ? void 0 : _periodResults$active4.average) >= 200 : undefined
185
203
  }),
186
- deviceDetails: expValEquals('cc_editor_interactivity_monitoring', 'isEnabled', true) ? getDeviceDetails() : undefined,
187
204
  highPriority: true,
188
205
  tags: ['editor'],
189
206
  source: 'unknown'
package/dist/esm/insm.js CHANGED
@@ -4,6 +4,7 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
4
4
  import { INSMSession } from './insm-session';
5
5
  import { AnimationFPSIM } from './period-measurers/afps';
6
6
  import { INPTracker } from './inp-measurers/inp';
7
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
7
8
  export var INSM = /*#__PURE__*/function () {
8
9
  function INSM(options) {
9
10
  var _this = this;
@@ -46,15 +47,18 @@ export var INSM = /*#__PURE__*/function () {
46
47
  * Starts a heavy task in the currently running session.
47
48
  *
48
49
  * This also pauses measurement.
50
+ *
51
+ * For PageLoads using the key 'PageLoad' will mean the heavy task duration
52
+ * is added to the insm session event as pageLoadTime.
49
53
  */
50
54
  return _createClass(INSM, [{
51
55
  key: "startHeavyTask",
52
56
  value: function startHeavyTask(heavyTaskName) {
53
- var _this$runningSession2, _this$runningSession3, _this$session;
57
+ var _this$runningSession2, _this$runningSession3, _this$runningSession4;
54
58
  this.runningHeavyTasks.add(heavyTaskName);
55
59
  (_this$runningSession2 = this.runningSession) === null || _this$runningSession2 === void 0 || (_this$runningSession2 = _this$runningSession2.periodTracking) === null || _this$runningSession2 === void 0 || _this$runningSession2.startHeavyTask(heavyTaskName);
56
60
  (_this$runningSession3 = this.runningSession) === null || _this$runningSession3 === void 0 || _this$runningSession3.periodTracking.pause(heavyTaskName);
57
- (_this$session = this.session) === null || _this$session === void 0 || _this$session.longAnimationFrameMeasurer.pause();
61
+ (_this$runningSession4 = this.runningSession) === null || _this$runningSession4 === void 0 || _this$runningSession4.longAnimationFrameMeasurer.pause();
58
62
  }
59
63
 
60
64
  /**
@@ -63,12 +67,18 @@ export var INSM = /*#__PURE__*/function () {
63
67
  }, {
64
68
  key: "endHeavyTask",
65
69
  value: function endHeavyTask(heavyTaskName) {
66
- var _this$runningSession4;
70
+ var _this$runningSession5;
67
71
  this.runningHeavyTasks.delete(heavyTaskName);
68
- (_this$runningSession4 = this.runningSession) === null || _this$runningSession4 === void 0 || _this$runningSession4.periodTracking.resume(heavyTaskName);
72
+ (_this$runningSession5 = this.runningSession) === null || _this$runningSession5 === void 0 || _this$runningSession5.periodTracking.resume(heavyTaskName);
69
73
  if (this.runningHeavyTasks.size === 0) {
70
- var _this$session2;
71
- (_this$session2 = this.session) === null || _this$session2 === void 0 || _this$session2.longAnimationFrameMeasurer.resume();
74
+ var _this$runningSession6;
75
+ (_this$runningSession6 = this.runningSession) === null || _this$runningSession6 === void 0 || _this$runningSession6.longAnimationFrameMeasurer.resume();
76
+ }
77
+ if (expValEquals('cc_editor_insm_outlier_events', 'cohort', 'test')) {
78
+ if (heavyTaskName === 'PageLoad') {
79
+ var _this$runningSession7;
80
+ (_this$runningSession7 = this.runningSession) === null || _this$runningSession7 === void 0 || _this$runningSession7.completePageLoad();
81
+ }
72
82
  }
73
83
  }
74
84
 
@@ -76,15 +86,19 @@ export var INSM = /*#__PURE__*/function () {
76
86
  * Call this when starting a new experience. This is expected to be wired to the product
77
87
  * routing solution.
78
88
  *
79
- * It's expected this call will be paired with a `insm.session.startHeavyTask('page-load')` and subsequent `insm.session.endHeavyTask('page-load')`
80
- * so that performance degradations linked to the page initialisation are excluded from the active interactivity monitoring.
89
+ * It's expected this call will be paired with a `insm.session.startHeavyTask('PageLoad')` and
90
+ * subsequent `insm.session.endHeavyTask('PageLoad')` so that performance degradations linked
91
+ * to the page initialisation are excluded from the active interactivity monitoring.
92
+ *
93
+ * Using the key 'PageLoad' is special and will result in the heavy task duration being added to the
94
+ * insm session event as pageLoadTime.
81
95
  *
82
96
  *
83
97
  * ```ts
84
98
  * insm.start('edit-page', { initial: true, contentId: '9001' })
85
- * insm.session.startHeavyTask(''page-load')
99
+ * insm.session.startHeavyTask('PageLoad')
86
100
  * // ... heavy initialisation work
87
- * insm.session.endHeavyTask(''page-load')
101
+ * insm.session.endHeavyTask('PageLoad')
88
102
  * ```
89
103
  */
90
104
  }, {
@@ -131,8 +145,8 @@ export var INSM = /*#__PURE__*/function () {
131
145
  }, {
132
146
  key: "stopEarly",
133
147
  value: function stopEarly(reasonKey, description) {
134
- var _this$runningSession5;
135
- (_this$runningSession5 = this.runningSession) === null || _this$runningSession5 === void 0 || _this$runningSession5.earlyStop(reasonKey, description);
148
+ var _this$runningSession8;
149
+ (_this$runningSession8 = this.runningSession) === null || _this$runningSession8 === void 0 || _this$runningSession8.earlyStop(reasonKey, description);
136
150
  }
137
151
 
138
152
  /**
@@ -1,11 +1,16 @@
1
1
  import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
2
2
  import _createClass from "@babel/runtime/helpers/createClass";
3
3
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
4
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
5
+ 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; } }
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; }
7
+ // Script timing data we want to track
8
+
4
9
  export var LongAnimationFrameMeasurer = /*#__PURE__*/function () {
5
10
  function LongAnimationFrameMeasurer(options) {
6
11
  var _this = this;
7
12
  _classCallCheck(this, LongAnimationFrameMeasurer);
8
- _defineProperty(this, "longestAnimationFrames", []);
13
+ _defineProperty(this, "longestScriptTimings", []);
9
14
  _defineProperty(this, "minimumIndex", -1);
10
15
  _defineProperty(this, "minimumDuration", Infinity);
11
16
  this.options = options;
@@ -26,48 +31,85 @@ export var LongAnimationFrameMeasurer = /*#__PURE__*/function () {
26
31
  return _createClass(LongAnimationFrameMeasurer, [{
27
32
  key: "handleBatch",
28
33
  value: function handleBatch(batch) {
29
- var len = this.longestAnimationFrames.length;
30
34
  for (var batchIndex = 0; batchIndex < batch.length; batchIndex++) {
31
- if (batch[batchIndex].duration < this.options.reportingThreshold) {
32
- // If the long frame entry had a duration less than the reporting
33
- // threshold it's not eligible for tracking
35
+ var animationFrame = batch[batchIndex];
36
+
37
+ // Only process frames that exceed the reporting threshold
38
+ if (animationFrame.duration < this.options.reportingThreshold) {
34
39
  continue;
35
40
  }
36
- var longAnimationFrameEntry = Object.assign(batch[batchIndex], {
37
- runningFeatures: Array.from(this.options.insmSession.runningFeatures)
38
- });
39
41
 
40
- // Not yet at capacity: append and update min tracking incrementally
41
- if (len < this.options.limit) {
42
- this.longestAnimationFrames[len] = longAnimationFrameEntry;
43
- if (len === 0 || longAnimationFrameEntry.duration < this.minimumDuration) {
44
- this.minimumDuration = longAnimationFrameEntry.duration;
45
- this.minimumIndex = len;
42
+ // Process all scripts in this long animation frame
43
+ if (animationFrame.scripts && animationFrame.scripts.length > 0) {
44
+ var _iterator = _createForOfIteratorHelper(animationFrame.scripts),
45
+ _step;
46
+ try {
47
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
48
+ var script = _step.value;
49
+ this.processScript(script, animationFrame);
50
+ }
51
+ } catch (err) {
52
+ _iterator.e(err);
53
+ } finally {
54
+ _iterator.f();
46
55
  }
47
- len++;
48
- continue;
49
56
  }
57
+ }
58
+ }
59
+ }, {
60
+ key: "createScriptTiming",
61
+ value: function createScriptTiming(script, animationFrame) {
62
+ return {
63
+ duration: script.duration,
64
+ forcedStyleAndLayoutDuration: script.forcedStyleAndLayoutDuration,
65
+ invoker: script.invoker,
66
+ invokerType: script.invokerType,
67
+ sourceCharPosition: script.sourceCharPosition,
68
+ sourceFunctionName: script.sourceFunctionName,
69
+ sourceURL: script.sourceURL,
70
+ features: Array.from(this.options.insmSession.runningFeatures),
71
+ afDuration: animationFrame.duration
72
+ };
73
+ }
74
+ }, {
75
+ key: "processScript",
76
+ value: function processScript(script, animationFrame) {
77
+ var scriptDuration = script.duration;
78
+ var len = this.longestScriptTimings.length;
50
79
 
51
- // At capacity: only do work if we beat the current min
52
- if (longAnimationFrameEntry.duration <= this.minimumDuration) {
53
- continue;
80
+ // Not yet at capacity: append and update min tracking incrementally
81
+ if (len < this.options.limit) {
82
+ var _scriptTiming = this.createScriptTiming(script, animationFrame);
83
+ this.longestScriptTimings[len] = _scriptTiming;
84
+ if (len === 0 || scriptDuration < this.minimumDuration) {
85
+ this.minimumDuration = scriptDuration;
86
+ this.minimumIndex = len;
54
87
  }
88
+ return;
89
+ }
55
90
 
56
- // Replace the current min
57
- this.longestAnimationFrames[this.minimumIndex] = longAnimationFrameEntry;
58
- var _possiblyNewMinimumIndex = 0;
59
- var _possibleNewMinimumDuration = this.longestAnimationFrames[0].duration;
60
- for (var i = 1; i < this.options.limit; i++) {
61
- if (this.longestAnimationFrames[i].duration < _possibleNewMinimumDuration) {
62
- _possiblyNewMinimumIndex = i;
63
- _possibleNewMinimumDuration = this.longestAnimationFrames[i].duration;
64
- }
65
- }
91
+ // At capacity: only do work if we beat the current min
92
+ if (scriptDuration <= this.minimumDuration) {
93
+ return;
94
+ }
95
+ var scriptTiming = this.createScriptTiming(script, animationFrame);
96
+
97
+ // Replace the current min
98
+ this.longestScriptTimings[this.minimumIndex] = scriptTiming;
66
99
 
67
- // Update the min tracking
68
- this.minimumIndex = _possiblyNewMinimumIndex;
69
- this.minimumDuration = _possibleNewMinimumDuration;
100
+ // Find new minimum
101
+ var newMinimumIndex = 0;
102
+ var newMinimumDuration = this.longestScriptTimings[0].duration;
103
+ for (var i = 1; i < this.options.limit; i++) {
104
+ if (this.longestScriptTimings[i].duration < newMinimumDuration) {
105
+ newMinimumIndex = i;
106
+ newMinimumDuration = this.longestScriptTimings[i].duration;
107
+ }
70
108
  }
109
+
110
+ // Update the min tracking
111
+ this.minimumIndex = newMinimumIndex;
112
+ this.minimumDuration = newMinimumDuration;
71
113
  }
72
114
 
73
115
  /**
@@ -89,12 +131,12 @@ export var LongAnimationFrameMeasurer = /*#__PURE__*/function () {
89
131
  }
90
132
 
91
133
  /**
92
- * Returns the current tracked longest animation frames sorted by duration
134
+ * Returns the current tracked longest script timings sorted by duration
93
135
  */
94
136
  }, {
95
137
  key: "current",
96
138
  get: function get() {
97
- var copy = this.longestAnimationFrames.slice();
139
+ var copy = this.longestScriptTimings.slice();
98
140
  copy.sort(function (a, b) {
99
141
  return b.duration - a.duration;
100
142
  });
@@ -114,5 +156,6 @@ export var LongAnimationFrameMeasurer = /*#__PURE__*/function () {
114
156
  }();
115
157
 
116
158
  // Based on https://github.com/GoogleChrome/web-vitals/blob/1b872cf5f2159e8ace0e98d55d8eb54fb09adfbe/src/types.ts#L129
159
+ // exported for use in test file
117
160
 
118
161
  // Based on https://github.com/GoogleChrome/web-vitals/blob/1b872cf5f2159e8ace0e98d55d8eb54fb09adfbe/src/types.ts#L129
@@ -35,20 +35,39 @@ export declare class PeriodTracking {
35
35
  resume(pauseName: string): void;
36
36
  get endResults(): {
37
37
  active: {
38
+ count: number;
39
+ duration: number;
38
40
  features: Set<string>;
39
41
  heavyTasks: Set<string>;
40
42
  measurements: {
41
43
  [key: string]: Measure;
42
44
  };
43
- duration: number;
44
- count: number;
45
45
  };
46
46
  inactive: {
47
+ count: number;
48
+ duration: number;
47
49
  features: Set<string>;
48
50
  heavyTasks: Set<string>;
49
51
  measurements: {
50
52
  [key: string]: Measure;
51
53
  };
54
+ };
55
+ } | {
56
+ active: {
57
+ features: string[];
58
+ heavyTasks: string[];
59
+ measurements: {
60
+ [key: string]: Measure;
61
+ };
62
+ duration: number;
63
+ count: number;
64
+ };
65
+ inactive: {
66
+ features: string[];
67
+ heavyTasks: string[];
68
+ measurements: {
69
+ [key: string]: Measure;
70
+ };
52
71
  duration: number;
53
72
  count: number;
54
73
  };
@@ -6,11 +6,14 @@ import { LongAnimationFrameMeasurer } from './session-measurers/LongAnimationFra
6
6
  * Only intended for internal use.
7
7
  *
8
8
  * Exported for consumers who may require the type.
9
+ *
10
+ * Note: Events are not reliably fired from mobile browsers (ie. when a browser is closed when not in use)
9
11
  */
10
12
  export declare class INSMSession {
11
13
  private experienceKey;
12
14
  private experienceProperties;
13
15
  private startedAt;
16
+ private pageLoadTime;
14
17
  insm: INSM;
15
18
  private running;
16
19
  private addedProperties;
@@ -18,6 +21,11 @@ export declare class INSMSession {
18
21
  periodTracking: PeriodTracking;
19
22
  longAnimationFrameMeasurer: LongAnimationFrameMeasurer;
20
23
  constructor(experienceKey: string, experienceProperties: ExperienceProperties, insm: INSM);
24
+ /**
25
+ * Completes the page load timing. This is called automatically when ending a heavy task
26
+ * with the key 'PageLoad'.
27
+ */
28
+ completePageLoad(): void;
21
29
  updateExperienceKey(experienceKey: string): void;
22
30
  /**
23
31
  * Adds a feature to the currently running session
@@ -19,6 +19,9 @@ export declare class INSM {
19
19
  * Starts a heavy task in the currently running session.
20
20
  *
21
21
  * This also pauses measurement.
22
+ *
23
+ * For PageLoads using the key 'PageLoad' will mean the heavy task duration
24
+ * is added to the insm session event as pageLoadTime.
22
25
  */
23
26
  startHeavyTask(heavyTaskName: string): void;
24
27
  /**
@@ -29,15 +32,19 @@ export declare class INSM {
29
32
  * Call this when starting a new experience. This is expected to be wired to the product
30
33
  * routing solution.
31
34
  *
32
- * It's expected this call will be paired with a `insm.session.startHeavyTask('page-load')` and subsequent `insm.session.endHeavyTask('page-load')`
33
- * so that performance degradations linked to the page initialisation are excluded from the active interactivity monitoring.
35
+ * It's expected this call will be paired with a `insm.session.startHeavyTask('PageLoad')` and
36
+ * subsequent `insm.session.endHeavyTask('PageLoad')` so that performance degradations linked
37
+ * to the page initialisation are excluded from the active interactivity monitoring.
38
+ *
39
+ * Using the key 'PageLoad' is special and will result in the heavy task duration being added to the
40
+ * insm session event as pageLoadTime.
34
41
  *
35
42
  *
36
43
  * ```ts
37
44
  * insm.start('edit-page', { initial: true, contentId: '9001' })
38
- * insm.session.startHeavyTask(''page-load')
45
+ * insm.session.startHeavyTask('PageLoad')
39
46
  * // ... heavy initialisation work
40
- * insm.session.endHeavyTask(''page-load')
47
+ * insm.session.endHeavyTask('PageLoad')
41
48
  * ```
42
49
  */
43
50
  start(experienceKey: string, experienceProperties: ExperienceProperties): void;