@atlaskit/insm 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +7 -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,339 @@
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
+ 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
+ /* eslint-disable @repo/internal/dom-events/no-unsafe-event-listeners */
8
+
9
+ export var PeriodTracking = /*#__PURE__*/function () {
10
+ function PeriodTracking(session) {
11
+ _classCallCheck(this, PeriodTracking);
12
+ _defineProperty(this, "periodMeasurements", {
13
+ active: {
14
+ features: new Set(),
15
+ heavyTasks: new Set(),
16
+ measurements: {},
17
+ duration: 0,
18
+ count: 0
19
+ },
20
+ inactive: {
21
+ features: new Set(),
22
+ heavyTasks: new Set(),
23
+ measurements: {},
24
+ duration: 0,
25
+ count: 0
26
+ }
27
+ });
28
+ _defineProperty(this, "state", 'inactive');
29
+ _defineProperty(this, "pauses", new Set());
30
+ /**
31
+ * Warning: this can be reset mid period when pausing/resuming.
32
+ * It's intended use is to build the `periodMeasurements` duration.
33
+ */
34
+ _defineProperty(this, "currentPeriodStart", performance.now());
35
+ _defineProperty(this, "activeStartListeners", []);
36
+ _defineProperty(this, "activeEndListeners", []);
37
+ /**
38
+ * This works by;
39
+ * On setup
40
+ * - starts activity listeners
41
+ * - on activity received
42
+ * - possible end active count down started.
43
+ * 3 animation frames or 3 seconds of inactivity - whichever comes first
44
+ * - any existing end count down ended
45
+ */
46
+ _defineProperty(this, "activeEndCountDownAbortController", new AbortController());
47
+ this.session = session;
48
+ this.latestPeriodFeatures = new Set(session.runningFeatures);
49
+ this.latestHeavyTasks = new Set(session.insm.runningHeavyTasks);
50
+ this.pauses = new Set(session.insm.runningHeavyTasks);
51
+ var startInteractivityMeasuresPaused = this.latestHeavyTasks.size !== 0;
52
+ var _iterator = _createForOfIteratorHelper(session.insm.periodMeasurers),
53
+ _step;
54
+ try {
55
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
56
+ var periodMeasurer = _step.value;
57
+ periodMeasurer.start(startInteractivityMeasuresPaused);
58
+ this.periodMeasurements.active.measurements[periodMeasurer.name] = {
59
+ numerator: 0,
60
+ denominator: 0,
61
+ max: 0,
62
+ min: 0,
63
+ average: 0
64
+ };
65
+ this.periodMeasurements.inactive.measurements[periodMeasurer.name] = {
66
+ numerator: 0,
67
+ denominator: 0,
68
+ max: 0,
69
+ min: 0,
70
+ average: 0
71
+ };
72
+ }
73
+ } catch (err) {
74
+ _iterator.e(err);
75
+ } finally {
76
+ _iterator.f();
77
+ }
78
+ this.setupActiveStartInteractionListeners();
79
+ }
80
+ return _createClass(PeriodTracking, [{
81
+ key: "startFeature",
82
+ value: function startFeature(featureName) {
83
+ this.latestPeriodFeatures.add(featureName);
84
+ this.periodMeasurements[this.state].features.add(featureName);
85
+ }
86
+ }, {
87
+ key: "startHeavyTask",
88
+ value: function startHeavyTask(heavyTaskName) {
89
+ this.latestHeavyTasks.add(heavyTaskName);
90
+ this.periodMeasurements[this.state].heavyTasks.add(heavyTaskName);
91
+ this.pause(heavyTaskName);
92
+ }
93
+ }, {
94
+ key: "endHeavyTask",
95
+ value: function endHeavyTask(heavyTaskName) {
96
+ this.resume(heavyTaskName);
97
+ }
98
+
99
+ /**
100
+ * Sets a pause based on a key. If this is the first pause, then it will also halt
101
+ * running interactivity measures, and update the current measurements duration.
102
+ */
103
+ }, {
104
+ key: "pause",
105
+ value: function pause(pauseName) {
106
+ if (this.pauses.size === 0) {
107
+ this.periodMeasurements[this.state].duration = this.periodMeasurements[this.state].duration + (performance.now() - this.currentPeriodStart);
108
+ var _iterator2 = _createForOfIteratorHelper(this.session.insm.periodMeasurers),
109
+ _step2;
110
+ try {
111
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
112
+ var periodMeasurer = _step2.value;
113
+ periodMeasurer.pause();
114
+ }
115
+ } catch (err) {
116
+ _iterator2.e(err);
117
+ } finally {
118
+ _iterator2.f();
119
+ }
120
+ }
121
+ this.pauses.add(pauseName);
122
+ }
123
+
124
+ /**
125
+ * Releases a pause for a key.
126
+ *
127
+ * **NOTE**: The session will only resume if this was the only
128
+ * currently tracked pause key.
129
+ */
130
+ }, {
131
+ key: "resume",
132
+ value: function resume(pauseName) {
133
+ this.pauses.delete(pauseName);
134
+ if (this.pauses.size === 0) {
135
+ this.currentPeriodStart = performance.now();
136
+ var _iterator3 = _createForOfIteratorHelper(this.session.insm.periodMeasurers),
137
+ _step3;
138
+ try {
139
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
140
+ var periodMeasurer = _step3.value;
141
+ periodMeasurer.resume();
142
+ }
143
+ } catch (err) {
144
+ _iterator3.e(err);
145
+ } finally {
146
+ _iterator3.f();
147
+ }
148
+ }
149
+ }
150
+ }, {
151
+ key: "endResults",
152
+ get: function get() {
153
+ this.changePeriodAndTrackLast(this.state);
154
+ return this.periodMeasurements;
155
+ }
156
+ }, {
157
+ key: "setupActiveStartInteractionListeners",
158
+ value: function setupActiveStartInteractionListeners() {
159
+ var _this = this;
160
+ var events = ['mousemove', 'pointerdown', 'pointerup', 'click', 'keydown', 'keyup', 'scroll'];
161
+ for (var _i = 0, _events = events; _i < _events.length; _i++) {
162
+ var event = _events[_i];
163
+ var eventListener = function eventListener() {
164
+ // start event
165
+ _this.changePeriodAndTrackLast('inactive', 'active');
166
+ };
167
+ window.addEventListener(event, eventListener, {
168
+ once: true
169
+ });
170
+ this.activeStartListeners.push([event, eventListener]);
171
+ }
172
+ }
173
+ }, {
174
+ key: "setupEndActiveInteractionListeners",
175
+ value: function setupEndActiveInteractionListeners() {
176
+ var _this2 = this;
177
+ this.activeEndCountDownVisibilityListener = function () {
178
+ if (document.visibilityState === 'hidden') {
179
+ endSession();
180
+ }
181
+ };
182
+ window.addEventListener('visibilitychange', this.activeEndCountDownVisibilityListener);
183
+ var events = ['mousemove', 'pointerdown', 'pointerup', 'click', 'keydown', 'keyup', 'scroll'];
184
+ for (var _i2 = 0, _events2 = events; _i2 < _events2.length; _i2++) {
185
+ var event = _events2[_i2];
186
+ var eventListener = function eventListener() {
187
+ _this2.activeEndCountDownAbortController.abort();
188
+ _this2.activeEndCountDownAbortController = new AbortController();
189
+ startPeriodEndCountDown(_this2.activeEndCountDownAbortController.signal, function () {
190
+ endSession();
191
+ });
192
+ };
193
+ window.addEventListener(event, eventListener);
194
+ this.activeEndListeners.push([event, eventListener]);
195
+ }
196
+ var endSession = function endSession() {
197
+ _this2.activeEndCountDownAbortController.abort();
198
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
199
+ window.removeEventListener('visibilitychange', _this2.activeEndCountDownVisibilityListener);
200
+ var _iterator4 = _createForOfIteratorHelper(_this2.activeEndListeners),
201
+ _step4;
202
+ try {
203
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
204
+ var activeEndListener = _step4.value;
205
+ window.removeEventListener(activeEndListener[0], activeEndListener[1]);
206
+ }
207
+ } catch (err) {
208
+ _iterator4.e(err);
209
+ } finally {
210
+ _iterator4.f();
211
+ }
212
+ _this2.changePeriodAndTrackLast('active', 'inactive');
213
+ };
214
+
215
+ // When first setting up -- there has just been an interaction -- so we start a countdown
216
+ this.activeEndCountDownAbortController.abort();
217
+ this.activeEndCountDownAbortController = new AbortController();
218
+ startPeriodEndCountDown(this.activeEndCountDownAbortController.signal, function () {
219
+ endSession();
220
+ });
221
+ }
222
+ }, {
223
+ key: "changePeriodAndTrackLast",
224
+ value: function changePeriodAndTrackLast(lastPeriod, newPeriod) {
225
+ var _this$activeEndCountD,
226
+ _this3 = this;
227
+ this.periodMeasurements[lastPeriod].duration = this.periodMeasurements[lastPeriod].duration + (performance.now() - this.currentPeriodStart);
228
+ this.periodMeasurements[lastPeriod].count++;
229
+ var interactivityMeasuresPaused = this.latestHeavyTasks.size !== 0;
230
+ var _iterator5 = _createForOfIteratorHelper(this.session.insm.periodMeasurers),
231
+ _step5;
232
+ try {
233
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
234
+ var interactivityMeasure = _step5.value;
235
+ var finalResult = newPeriod ? interactivityMeasure.start(interactivityMeasuresPaused) : interactivityMeasure.end();
236
+ var tracked = this.periodMeasurements[lastPeriod].measurements[interactivityMeasure.name];
237
+ if (finalResult.average === 0) {
238
+ // In this case -- we haven't had any measurements complete since last measurement
239
+ // so we do nothing
240
+ } else if (tracked.average === 0) {
241
+ // In this case -- we haven't had any entries come through yet - so we replace any existing entries
242
+ this.periodMeasurements[lastPeriod].measurements[interactivityMeasure.name] = finalResult;
243
+ } else {
244
+ // In this case there is a new measurement which we merge with the previous measurement
245
+ var merged = {
246
+ numerator: finalResult.numerator + tracked.numerator,
247
+ denominator: finalResult.denominator + tracked.denominator,
248
+ average: (finalResult.numerator + tracked.numerator) / (finalResult.denominator + tracked.denominator),
249
+ max: tracked.max === 0 || finalResult.max > tracked.max ? finalResult.max : tracked.max,
250
+ min: tracked.min === 0 || finalResult.min < tracked.min ? finalResult.min : tracked.min
251
+ };
252
+ this.periodMeasurements[lastPeriod].measurements[interactivityMeasure.name] = merged;
253
+ }
254
+ }
255
+ } catch (err) {
256
+ _iterator5.e(err);
257
+ } finally {
258
+ _iterator5.f();
259
+ }
260
+ var _iterator6 = _createForOfIteratorHelper(this.activeStartListeners),
261
+ _step6;
262
+ try {
263
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
264
+ var listener = _step6.value;
265
+ window.removeEventListener(listener[0], listener[1]);
266
+ }
267
+ } catch (err) {
268
+ _iterator6.e(err);
269
+ } finally {
270
+ _iterator6.f();
271
+ }
272
+ var _iterator7 = _createForOfIteratorHelper(this.activeEndListeners),
273
+ _step7;
274
+ try {
275
+ for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
276
+ var _listener = _step7.value;
277
+ window.removeEventListener(_listener[0], _listener[1]);
278
+ }
279
+ } catch (err) {
280
+ _iterator7.e(err);
281
+ } finally {
282
+ _iterator7.f();
283
+ }
284
+ (_this$activeEndCountD = this.activeEndCountDownAbortController) === null || _this$activeEndCountD === void 0 || _this$activeEndCountD.abort();
285
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
286
+ window.removeEventListener('visibilitychange', this.activeEndCountDownVisibilityListener);
287
+ if (newPeriod) {
288
+ this.latestPeriodFeatures = new Set(this.session.runningFeatures);
289
+ this.session.runningFeatures.forEach(function (featureName) {
290
+ _this3.periodMeasurements[newPeriod].features.add(featureName);
291
+ });
292
+ this.latestHeavyTasks = new Set(this.session.insm.runningHeavyTasks);
293
+ this.currentPeriodStart = performance.now();
294
+ switch (newPeriod) {
295
+ case 'active':
296
+ this.setupEndActiveInteractionListeners();
297
+ this.state = 'active';
298
+ break;
299
+ case 'inactive':
300
+ this.setupActiveStartInteractionListeners();
301
+ this.state = 'inactive';
302
+ break;
303
+ }
304
+ }
305
+ }
306
+ }]);
307
+ }();
308
+ function startPeriodEndCountDown(signal, handleEnd) {
309
+ var animationFramesSinceLastInteraction = 0;
310
+ var inactiveTimeReached = false;
311
+ var timer;
312
+ var animationFrameHandler;
313
+ var _monitorAnimationFrames = function monitorAnimationFrames() {
314
+ animationFrameHandler = requestAnimationFrame(function () {
315
+ animationFramesSinceLastInteraction++;
316
+ if (animationFramesSinceLastInteraction < 3) {
317
+ _monitorAnimationFrames();
318
+ } else {
319
+ if (inactiveTimeReached) {
320
+ handleEnd();
321
+ }
322
+ }
323
+ });
324
+ };
325
+ var monitorTime = function monitorTime() {
326
+ timer = setTimeout(function () {
327
+ inactiveTimeReached = true;
328
+ if (animationFramesSinceLastInteraction === 3) {
329
+ handleEnd();
330
+ }
331
+ }, 3000);
332
+ };
333
+ monitorTime();
334
+ _monitorAnimationFrames();
335
+ signal.addEventListener('abort', function () {
336
+ clearTimeout(timer);
337
+ cancelAnimationFrame(animationFrameHandler);
338
+ });
339
+ }
@@ -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
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
5
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
6
+ 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; } } }; }
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
+ 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
+ import { PeriodTracking } from './insm-period';
10
+
11
+ /**
12
+ * Only intended for internal use.
13
+ *
14
+ * Exported for consumers who may require the type.
15
+ */
16
+ export var INSMSession = /*#__PURE__*/function () {
17
+ function INSMSession(experienceKey, experienceProperties, insm) {
18
+ _classCallCheck(this, INSMSession);
19
+ _defineProperty(this, "startedAt", performance.now());
20
+ _defineProperty(this, "running", true);
21
+ _defineProperty(this, "addedProperties", []);
22
+ _defineProperty(this, "runningFeatures", new Set());
23
+ this.experienceKey = experienceKey;
24
+ this.experienceProperties = experienceProperties;
25
+ this.insm = insm;
26
+ this.periodTracking = new PeriodTracking(this);
27
+
28
+ /**
29
+ * Note: Events are not reliably fired from mobile browsers (ie. when a browser is closed when not in use)
30
+ */
31
+ }
32
+
33
+ /**
34
+ * Adds a feature to the currently running session
35
+ */
36
+ return _createClass(INSMSession, [{
37
+ key: "startFeature",
38
+ value: function startFeature(featureName) {
39
+ var _this$periodTracking;
40
+ this.runningFeatures.add(featureName);
41
+ (_this$periodTracking = this.periodTracking) === null || _this$periodTracking === void 0 || _this$periodTracking.startFeature(featureName);
42
+ }
43
+
44
+ /**
45
+ * Ends a features usage in the currently running session
46
+ */
47
+ }, {
48
+ key: "endFeature",
49
+ value: function endFeature(featureName) {
50
+ this.runningFeatures.delete(featureName);
51
+ }
52
+
53
+ /**
54
+ * Returns details on the current session.
55
+ */
56
+ }, {
57
+ key: "details",
58
+ get: function get() {
59
+ return {
60
+ experienceKey: this.experienceKey,
61
+ experienceProperties: this.experienceProperties,
62
+ paused: this.periodTracking.pauses.size > 0,
63
+ periodState: this.periodTracking.state,
64
+ /**
65
+ * The only scenario where this value should return false is when
66
+ * the experience has been stopped early.
67
+ */
68
+ running: this.running
69
+ };
70
+ }
71
+
72
+ /**
73
+ * This api takes either a static single-level key-value object, or callbacks which return the same and
74
+ * will be evaluated on session end.
75
+ *
76
+ * When ending a session, all properties received via this api are merged, in order, into the resulting
77
+ * insm event’s properties; last write wins.
78
+ *
79
+ * Callback values are evaluated at session end.
80
+ *
81
+ * For example, for the following
82
+ *
83
+ * ```ts
84
+ * insm.experience.addProperties({ one: 1, two: 2 });
85
+ * insm.experience.addProperties(() => ({ one: 'one' }));
86
+ * insm.experience.addProperties({ three: 3 });
87
+ * ```
88
+ *
89
+ * The resulting added properties will be
90
+ *
91
+ * ```ts
92
+ * { one: 'one', two: 2, three: 3 }
93
+ * ```
94
+ */
95
+ }, {
96
+ key: "addProperties",
97
+ value: function addProperties(propertiesToAdd) {
98
+ this.addedProperties.push(propertiesToAdd);
99
+ }
100
+
101
+ /**
102
+ * In some scenarios (ie. when a page error boundary is hit), you will want to exit early.
103
+ * This is api supports these scenarios
104
+ *
105
+ * ```ts
106
+ * insm.stopEarly(reasonKey: string, description: string);
107
+ * ```
108
+ *
109
+ * Sessions closed early are identifiable by their end details
110
+ * `"endDetails": { stoppedBy: "early-stop", reasonKey, description }`.
111
+ *
112
+ * **Note**: The session is ended as soon as this is called, and any `addProperties` handlers will
113
+ * called immediately.
114
+ */
115
+ }, {
116
+ key: "earlyStop",
117
+ value: function earlyStop(reason, description) {
118
+ this.end({
119
+ stoppedBy: 'early-stop',
120
+ reason: reason,
121
+ description: description
122
+ });
123
+ }
124
+ }, {
125
+ key: "end",
126
+ value: function end(endDetails) {
127
+ var _this$insm$analyticsW;
128
+ if (this.running === false) {
129
+ // If an experience has already been ended -- don't repeat the ending
130
+ return;
131
+ }
132
+ this.running = false;
133
+ var endedAt = performance.now();
134
+ var duration = endedAt - this.startedAt;
135
+ var evaluatedAddedProperties = {};
136
+ var _iterator = _createForOfIteratorHelper(this.addedProperties),
137
+ _step;
138
+ try {
139
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
140
+ var addedProperty = _step.value;
141
+ if (typeof addedProperty === 'function') {
142
+ Object.assign(evaluatedAddedProperties, addedProperty());
143
+ } else {
144
+ Object.assign(evaluatedAddedProperties, addedProperty);
145
+ }
146
+ }
147
+ } catch (err) {
148
+ _iterator.e(err);
149
+ } finally {
150
+ _iterator.f();
151
+ }
152
+ var periodResults = this.periodTracking.endResults;
153
+
154
+ /**
155
+ * This event ends up as "insm measured"
156
+ */
157
+ var operationalEvent = {
158
+ actionSubject: 'insm',
159
+ action: 'measured',
160
+ attributes: _objectSpread(_objectSpread({}, evaluatedAddedProperties), {}, {
161
+ experienceKey: this.experienceKey,
162
+ initial: this.experienceProperties.initial,
163
+ contentId: this.experienceProperties.contentId,
164
+ timing: {
165
+ startedAt: this.startedAt,
166
+ duration: duration
167
+ },
168
+ periods: periodResults,
169
+ endDetails: endDetails
170
+ }),
171
+ tags: ['insm'],
172
+ source: 'insm'
173
+ };
174
+ (_this$insm$analyticsW = this.insm.analyticsWebClient) === null || _this$insm$analyticsW === void 0 || _this$insm$analyticsW.sendOperationalEvent(operationalEvent);
175
+ }
176
+ }]);
177
+ }();
@@ -0,0 +1,122 @@
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
+ import { INSMSession } from './insm-session';
5
+ import { AnimationFPSIM } from './period-measurers/afps';
6
+ import { INPTracker } from './inp-measurers/inp';
7
+ export var INSM = /*#__PURE__*/function () {
8
+ function INSM(options) {
9
+ var _this = this;
10
+ _classCallCheck(this, INSM);
11
+ /**
12
+ * Heavy tasks are tracked at the insm layer as heavy tasks
13
+ * are expected at times to be unrelated to the current
14
+ * page session.
15
+ */
16
+ _defineProperty(this, "runningHeavyTasks", new Set());
17
+ this.periodMeasurers = [new AnimationFPSIM(), new INPTracker(), new INPTracker({
18
+ includedInteractions: ['pointerup']
19
+ }), new INPTracker({
20
+ includedInteractions: ['pointerdown']
21
+ }), new INPTracker({
22
+ includedInteractions: ['click']
23
+ }), new INPTracker({
24
+ includedInteractions: ['keydown']
25
+ }), new INPTracker({
26
+ includedInteractions: ['keyup']
27
+ })];
28
+ this.options = options;
29
+
30
+ // If this does throw -- we do want an unhandledRejection rejection to be passed to the window
31
+ // this is to ease debugging.
32
+ options.getAnalyticsWebClient.then(function (analyticsWebClient) {
33
+ return _this.analyticsWebClient = analyticsWebClient;
34
+ });
35
+
36
+ // No cleanup needs to be performed -- as this is intended to run until the tab is closed
37
+ // eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
38
+ window.addEventListener('pagehide', function () {
39
+ var _this$runningSession;
40
+ (_this$runningSession = _this.runningSession) === null || _this$runningSession === void 0 || _this$runningSession.end({
41
+ stoppedBy: 'pagehide'
42
+ });
43
+ });
44
+ }
45
+
46
+ /**
47
+ * Starts a heavy task in the currently running session.
48
+ *
49
+ * This also pauses measurement.
50
+ */
51
+ return _createClass(INSM, [{
52
+ key: "startHeavyTask",
53
+ value: function startHeavyTask(heavyTaskName) {
54
+ var _this$runningSession2, _this$runningSession3;
55
+ this.runningHeavyTasks.add(heavyTaskName);
56
+ (_this$runningSession2 = this.runningSession) === null || _this$runningSession2 === void 0 || (_this$runningSession2 = _this$runningSession2.periodTracking) === null || _this$runningSession2 === void 0 || _this$runningSession2.startHeavyTask(heavyTaskName);
57
+ (_this$runningSession3 = this.runningSession) === null || _this$runningSession3 === void 0 || _this$runningSession3.periodTracking.pause(heavyTaskName);
58
+ }
59
+
60
+ /**
61
+ * Ends a heavy task in the currently running session
62
+ */
63
+ }, {
64
+ key: "endHeavyTask",
65
+ value: function endHeavyTask(heavyTaskName) {
66
+ var _this$runningSession4;
67
+ this.runningHeavyTasks.delete(heavyTaskName);
68
+ (_this$runningSession4 = this.runningSession) === null || _this$runningSession4 === void 0 || _this$runningSession4.periodTracking.resume(heavyTaskName);
69
+ }
70
+
71
+ /**
72
+ * Call this when starting a new experience. This is expected to be wired to the product
73
+ * routing solution.
74
+ *
75
+ * It's expected this call will be paired with a `insm.session.startHeavyTask('page-load')` and subsequent `insm.session.endHeavyTask('page-load')`
76
+ * so that performance degradations linked to the page initialisation are excluded from the active interactivity monitoring.
77
+ *
78
+ *
79
+ * ```ts
80
+ * insm.start('edit-page', { initial: true, contentId: '9001' })
81
+ * insm.session.startHeavyTask(''page-load')
82
+ * // ... heavy initialisation work
83
+ * insm.session.endHeavyTask(''page-load')
84
+ * ```
85
+ */
86
+ }, {
87
+ key: "start",
88
+ value: function start(experienceKey, experienceProperties) {
89
+ var _this$options$experie;
90
+ if (this.runningSession !== undefined) {
91
+ this.runningSession.end({
92
+ stoppedBy: 'new-experience',
93
+ experienceKey: experienceKey,
94
+ contentId: experienceProperties.contentId
95
+ });
96
+ }
97
+ if ((_this$options$experie = this.options.experiences[experienceKey]) !== null && _this$options$experie !== void 0 && _this$options$experie.enabled) {
98
+ this.runningSession = new INSMSession(experienceKey, experienceProperties, this);
99
+ }
100
+ }
101
+
102
+ /**
103
+ * This prematurely halts any running experience measurement. It's expected to be used in
104
+ * scenarios such as when error boundaries are hit.
105
+ */
106
+ }, {
107
+ key: "stopEarly",
108
+ value: function stopEarly(reasonKey, description) {
109
+ var _this$runningSession5;
110
+ (_this$runningSession5 = this.runningSession) === null || _this$runningSession5 === void 0 || _this$runningSession5.earlyStop(reasonKey, description);
111
+ }
112
+
113
+ /**
114
+ * Gets the current running session details
115
+ */
116
+ }, {
117
+ key: "session",
118
+ get: function get() {
119
+ return this.runningSession;
120
+ }
121
+ }]);
122
+ }();