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