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