@atlaskit/insm 0.1.1 → 0.1.3
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 +15 -0
- package/README.md +14 -0
- package/dist/cjs/index.js +5 -2
- package/dist/cjs/inp-measurers/inp.js +1 -1
- package/dist/cjs/insm-session.js +66 -8
- package/dist/cjs/insm.js +44 -19
- package/dist/cjs/session-measurers/LongAnimationFrameMeasurer.js +122 -0
- package/dist/es2019/index.js +5 -2
- package/dist/es2019/inp-measurers/inp.js +1 -1
- package/dist/es2019/insm-session.js +61 -3
- package/dist/es2019/insm.js +41 -19
- package/dist/es2019/session-measurers/LongAnimationFrameMeasurer.js +101 -0
- package/dist/esm/index.js +5 -2
- package/dist/esm/inp-measurers/inp.js +1 -1
- package/dist/esm/insm-session.js +68 -8
- package/dist/esm/insm.js +44 -19
- package/dist/esm/session-measurers/LongAnimationFrameMeasurer.js +118 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/insm-session.d.ts +8 -5
- package/dist/types/insm.d.ts +8 -9
- package/dist/types/session-measurers/LongAnimationFrameMeasurer.d.ts +61 -0
- package/dist/types-ts4.5/index.d.ts +1 -1
- package/dist/types-ts4.5/insm-session.d.ts +8 -5
- package/dist/types-ts4.5/insm.d.ts +7 -5
- package/dist/types-ts4.5/session-measurers/LongAnimationFrameMeasurer.d.ts +61 -0
- package/package.json +5 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @atlaskit/insm
|
|
2
2
|
|
|
3
|
+
## 0.1.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
|
|
9
|
+
## 0.1.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [`cf1cfd5a04c5c`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/cf1cfd5a04c5c) -
|
|
14
|
+
Remove INSM circular dependency
|
|
15
|
+
- [`78573b5014067`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/78573b5014067) -
|
|
16
|
+
Update numerator calculation in INP Measurers
|
|
17
|
+
|
|
3
18
|
## 0.1.1
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -169,6 +169,20 @@ insm.start(experienceKey, {
|
|
|
169
169
|
Note: calling this will end any existing experience (unless the experience key and properties
|
|
170
170
|
match - in which case no session change will occur).
|
|
171
171
|
|
|
172
|
+
#### Updating a page session experienceKey after it's started
|
|
173
|
+
|
|
174
|
+
In some scenarios you may need to update the running sessions experience key after it has started,
|
|
175
|
+
in these cases you can use `overrideExperienceKey`.
|
|
176
|
+
|
|
177
|
+
If there is a running experience, it will update the running experiences name.
|
|
178
|
+
|
|
179
|
+
If there is no running experience (ie. if the original experienceKey had not been whitelisted), it
|
|
180
|
+
will start a new experience using the last started experienceProperties.
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
insm.overrideExperienceKey('new-key');
|
|
184
|
+
```
|
|
185
|
+
|
|
172
186
|
#### Naming guidance
|
|
173
187
|
|
|
174
188
|
Choose an `experienceKey` that reflects the product and content type you want to analyze.
|
package/dist/cjs/index.js
CHANGED
|
@@ -16,8 +16,6 @@ function init(options) {
|
|
|
16
16
|
}
|
|
17
17
|
function insmInitialised() {
|
|
18
18
|
if (!initialisedInsm) {
|
|
19
|
-
// eslint-disable-next-line no-console
|
|
20
|
-
console.error('INSM used when not initialised');
|
|
21
19
|
return false;
|
|
22
20
|
}
|
|
23
21
|
return true;
|
|
@@ -42,6 +40,11 @@ var insm = exports.insm = {
|
|
|
42
40
|
initialisedInsm.start(experienceKey, experienceProperties);
|
|
43
41
|
}
|
|
44
42
|
},
|
|
43
|
+
overrideExperienceKey: function overrideExperienceKey(experienceKey) {
|
|
44
|
+
if (insmInitialised()) {
|
|
45
|
+
initialisedInsm.overrideExperienceKey(experienceKey);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
45
48
|
stopEarly: function stopEarly(reasonKey, description) {
|
|
46
49
|
if (insmInitialised()) {
|
|
47
50
|
initialisedInsm.stopEarly(reasonKey, description);
|
|
@@ -60,7 +60,7 @@ var InteractionResult = /*#__PURE__*/function () {
|
|
|
60
60
|
if (duration < this.min) {
|
|
61
61
|
this.min = duration;
|
|
62
62
|
}
|
|
63
|
-
this.numerator +=
|
|
63
|
+
this.numerator += duration;
|
|
64
64
|
this.count += 1;
|
|
65
65
|
this.average = this.numerator / this.count;
|
|
66
66
|
}
|
package/dist/cjs/insm-session.js
CHANGED
|
@@ -8,7 +8,10 @@ exports.INSMSession = 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 _bowserUltralight = _interopRequireDefault(require("bowser-ultralight"));
|
|
12
|
+
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
11
13
|
var _insmPeriod = require("./insm-period");
|
|
14
|
+
var _LongAnimationFrameMeasurer = require("./session-measurers/LongAnimationFrameMeasurer");
|
|
12
15
|
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
16
|
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
17
|
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; } } }; }
|
|
@@ -30,16 +33,27 @@ var INSMSession = exports.INSMSession = /*#__PURE__*/function () {
|
|
|
30
33
|
this.experienceProperties = experienceProperties;
|
|
31
34
|
this.insm = insm;
|
|
32
35
|
this.periodTracking = new _insmPeriod.PeriodTracking(this);
|
|
36
|
+
this.longAnimationFrameMeasurer = new _LongAnimationFrameMeasurer.LongAnimationFrameMeasurer({
|
|
37
|
+
initial: this.experienceProperties.initial,
|
|
38
|
+
limit: 3,
|
|
39
|
+
insmSession: this,
|
|
40
|
+
reportingThreshold: 500
|
|
41
|
+
});
|
|
33
42
|
|
|
34
43
|
/**
|
|
35
44
|
* Note: Events are not reliably fired from mobile browsers (ie. when a browser is closed when not in use)
|
|
36
45
|
*/
|
|
37
46
|
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Adds a feature to the currently running session
|
|
41
|
-
*/
|
|
42
47
|
return (0, _createClass2.default)(INSMSession, [{
|
|
48
|
+
key: "updateExperienceKey",
|
|
49
|
+
value: function updateExperienceKey(experienceKey) {
|
|
50
|
+
this.experienceKey = experienceKey;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Adds a feature to the currently running session
|
|
55
|
+
*/
|
|
56
|
+
}, {
|
|
43
57
|
key: "startFeature",
|
|
44
58
|
value: function startFeature(featureName) {
|
|
45
59
|
var _this$periodTracking;
|
|
@@ -172,12 +186,56 @@ var INSMSession = exports.INSMSession = /*#__PURE__*/function () {
|
|
|
172
186
|
duration: duration
|
|
173
187
|
},
|
|
174
188
|
periods: periodResults,
|
|
175
|
-
endDetails: endDetails
|
|
189
|
+
endDetails: endDetails,
|
|
190
|
+
longAnimationFrames: this.longAnimationFrameMeasurer.current
|
|
176
191
|
}),
|
|
177
|
-
|
|
178
|
-
|
|
192
|
+
deviceDetails: (0, _expValEquals.expValEquals)('cc_editor_interactivity_monitoring', 'isEnabled', true) ? getDeviceDetails() : undefined,
|
|
193
|
+
highPriority: true,
|
|
194
|
+
tags: ['editor'],
|
|
195
|
+
source: 'unknown'
|
|
179
196
|
};
|
|
180
197
|
(_this$insm$analyticsW = this.insm.analyticsWebClient) === null || _this$insm$analyticsW === void 0 || _this$insm$analyticsW.sendOperationalEvent(operationalEvent);
|
|
198
|
+
this.longAnimationFrameMeasurer.cleanup();
|
|
181
199
|
}
|
|
182
200
|
}]);
|
|
183
|
-
}();
|
|
201
|
+
}(); // Functionality vendored from UFO
|
|
202
|
+
function getDeviceDetails() {
|
|
203
|
+
var _navigator$connection, _navigator$connection2, _navigator$connection3;
|
|
204
|
+
var telemetry = {};
|
|
205
|
+
|
|
206
|
+
// /platform/packages/data/ufo-internal/src/core/publisher/plugins/browser.ts
|
|
207
|
+
if (_bowserUltralight.default.getParser) {
|
|
208
|
+
var _ref;
|
|
209
|
+
var browser = _bowserUltralight.default.getParser(((_ref = typeof window !== 'undefined' && !!window ? window : undefined) === null || _ref === void 0 || (_ref = _ref.navigator) === null || _ref === void 0 ? void 0 : _ref.userAgent) || '');
|
|
210
|
+
Object.assign(telemetry, {
|
|
211
|
+
'event:browser:name': browser.getBrowserName(),
|
|
212
|
+
'event:browser:version': browser.getBrowserVersion()
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// /platform/packages/data/ufo-internal/src/core/publisher/plugins/cpus.ts
|
|
217
|
+
Object.assign(telemetry, {
|
|
218
|
+
'event:cpus': navigator.hardwareConcurrency
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// /platform/packages/data/ufo-internal/src/core/publisher/plugins/memory.ts
|
|
222
|
+
// @ts-ignore: deviceMemory is exposed in some browsers
|
|
223
|
+
Object.assign(telemetry, {
|
|
224
|
+
'event:memory': navigator.deviceMemory
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// /platform/packages/data/ufo-internal/src/core/publisher/plugins/network.ts
|
|
228
|
+
Object.assign(telemetry, {
|
|
229
|
+
// Network
|
|
230
|
+
// @ts-ignore: connection is available in some browsers
|
|
231
|
+
// eslint-disable-next-line compat/compat
|
|
232
|
+
'event:network:effectiveType': (_navigator$connection = navigator.connection) === null || _navigator$connection === void 0 ? void 0 : _navigator$connection.effectiveType,
|
|
233
|
+
// @ts-ignore: connection is available in some browsers
|
|
234
|
+
// eslint-disable-next-line compat/compat
|
|
235
|
+
'event:network:rtt': (_navigator$connection2 = navigator.connection) === null || _navigator$connection2 === void 0 ? void 0 : _navigator$connection2.rtt,
|
|
236
|
+
// @ts-ignore: connection is available in some browsers
|
|
237
|
+
// eslint-disable-next-line compat/compat
|
|
238
|
+
'event:network:downlink': (_navigator$connection3 = navigator.connection) === null || _navigator$connection3 === void 0 ? void 0 : _navigator$connection3.downlink
|
|
239
|
+
});
|
|
240
|
+
return telemetry;
|
|
241
|
+
}
|
package/dist/cjs/insm.js
CHANGED
|
@@ -21,17 +21,7 @@ var INSM = exports.INSM = /*#__PURE__*/function () {
|
|
|
21
21
|
* page session.
|
|
22
22
|
*/
|
|
23
23
|
(0, _defineProperty2.default)(this, "runningHeavyTasks", new Set());
|
|
24
|
-
this.periodMeasurers = [new _afps.AnimationFPSIM(), 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
|
-
})];
|
|
24
|
+
this.periodMeasurers = [new _afps.AnimationFPSIM(), new _inp.INPTracker()];
|
|
35
25
|
this.options = options;
|
|
36
26
|
|
|
37
27
|
// If this does throw -- we do want an unhandledRejection rejection to be passed to the window
|
|
@@ -40,14 +30,23 @@ var INSM = exports.INSM = /*#__PURE__*/function () {
|
|
|
40
30
|
return _this.analyticsWebClient = analyticsWebClient;
|
|
41
31
|
});
|
|
42
32
|
|
|
43
|
-
// No cleanup needs to be performed -- as this is intended to run until the tab is closed
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
33
|
+
// No cleanup needs to be performed -- as this tooling is intended to run until the tab is closed
|
|
34
|
+
// The use of beforeunload here is because using pagehide does not reliably result in the analytics
|
|
35
|
+
// being fired by the analytics web client.
|
|
36
|
+
// The use of this means we will miss events from mobile safari which does not reliably call this api
|
|
37
|
+
// however mobile browsers do not call when a browser closes - so we expect limited mobile data from
|
|
38
|
+
// this tooling in its current form.
|
|
39
|
+
|
|
40
|
+
// window will not be defined in server envs - in these envs -- there can never be a session end
|
|
41
|
+
if (typeof window !== 'undefined') {
|
|
42
|
+
// eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
|
|
43
|
+
window.addEventListener('beforeunload', function () {
|
|
44
|
+
var _this$runningSession;
|
|
45
|
+
(_this$runningSession = _this.runningSession) === null || _this$runningSession === void 0 || _this$runningSession.end({
|
|
46
|
+
stoppedBy: 'beforeunload'
|
|
47
|
+
});
|
|
49
48
|
});
|
|
50
|
-
}
|
|
49
|
+
}
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
/**
|
|
@@ -58,10 +57,11 @@ var INSM = exports.INSM = /*#__PURE__*/function () {
|
|
|
58
57
|
return (0, _createClass2.default)(INSM, [{
|
|
59
58
|
key: "startHeavyTask",
|
|
60
59
|
value: function startHeavyTask(heavyTaskName) {
|
|
61
|
-
var _this$runningSession2, _this$runningSession3;
|
|
60
|
+
var _this$runningSession2, _this$runningSession3, _this$session;
|
|
62
61
|
this.runningHeavyTasks.add(heavyTaskName);
|
|
63
62
|
(_this$runningSession2 = this.runningSession) === null || _this$runningSession2 === void 0 || (_this$runningSession2 = _this$runningSession2.periodTracking) === null || _this$runningSession2 === void 0 || _this$runningSession2.startHeavyTask(heavyTaskName);
|
|
64
63
|
(_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();
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/**
|
|
@@ -73,6 +73,10 @@ var INSM = exports.INSM = /*#__PURE__*/function () {
|
|
|
73
73
|
var _this$runningSession4;
|
|
74
74
|
this.runningHeavyTasks.delete(heavyTaskName);
|
|
75
75
|
(_this$runningSession4 = this.runningSession) === null || _this$runningSession4 === void 0 || _this$runningSession4.periodTracking.resume(heavyTaskName);
|
|
76
|
+
if (this.runningHeavyTasks.size === 0) {
|
|
77
|
+
var _this$session2;
|
|
78
|
+
(_this$session2 = this.session) === null || _this$session2 === void 0 || _this$session2.longAnimationFrameMeasurer.resume();
|
|
79
|
+
}
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
/**
|
|
@@ -101,10 +105,31 @@ var INSM = exports.INSM = /*#__PURE__*/function () {
|
|
|
101
105
|
contentId: experienceProperties.contentId
|
|
102
106
|
});
|
|
103
107
|
}
|
|
108
|
+
this.lastStartedExperienceProperties = experienceProperties;
|
|
104
109
|
if ((_this$options$experie = this.options.experiences[experienceKey]) !== null && _this$options$experie !== void 0 && _this$options$experie.enabled) {
|
|
105
110
|
this.runningSession = new _insmSession.INSMSession(experienceKey, experienceProperties, this);
|
|
106
111
|
}
|
|
107
112
|
}
|
|
113
|
+
}, {
|
|
114
|
+
key: "overrideExperienceKey",
|
|
115
|
+
value:
|
|
116
|
+
/**
|
|
117
|
+
* Call this to update the name of the running session after it's started
|
|
118
|
+
* In the case it's been started with an unregistered name, and there is not running
|
|
119
|
+
* session. This will also trigger the session being started.
|
|
120
|
+
*/
|
|
121
|
+
function overrideExperienceKey(experienceKey) {
|
|
122
|
+
if (this.runningSession !== undefined) {
|
|
123
|
+
// If there is a running session - we update its name
|
|
124
|
+
this.runningSession.updateExperienceKey(experienceKey);
|
|
125
|
+
} else {
|
|
126
|
+
var _this$options$experie2;
|
|
127
|
+
// otherwise - we assume the last session was not registered, and start it with the new name
|
|
128
|
+
if ((_this$options$experie2 = this.options.experiences[experienceKey]) !== null && _this$options$experie2 !== void 0 && _this$options$experie2.enabled && this.lastStartedExperienceProperties) {
|
|
129
|
+
this.runningSession = new _insmSession.INSMSession(experienceKey, this.lastStartedExperienceProperties, this);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
108
133
|
|
|
109
134
|
/**
|
|
110
135
|
* This prematurely halts any running experience measurement. It's expected to be used in
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.LongAnimationFrameMeasurer = 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 LongAnimationFrameMeasurer = exports.LongAnimationFrameMeasurer = /*#__PURE__*/function () {
|
|
12
|
+
function LongAnimationFrameMeasurer(options) {
|
|
13
|
+
var _this = this;
|
|
14
|
+
(0, _classCallCheck2.default)(this, LongAnimationFrameMeasurer);
|
|
15
|
+
(0, _defineProperty2.default)(this, "longestAnimationFrames", []);
|
|
16
|
+
(0, _defineProperty2.default)(this, "minimumIndex", -1);
|
|
17
|
+
(0, _defineProperty2.default)(this, "minimumDuration", Infinity);
|
|
18
|
+
this.options = options;
|
|
19
|
+
this.paused = options.insmSession.insm.runningHeavyTasks.size !== 0;
|
|
20
|
+
if (PerformanceObserver.supportedEntryTypes.includes('long-animation-frame')) {
|
|
21
|
+
this.observer = new PerformanceObserver(function (list) {
|
|
22
|
+
if (!_this.paused) {
|
|
23
|
+
// only handle batches when tracking is not paused
|
|
24
|
+
_this.handleBatch(list.getEntries());
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
this.observer.observe({
|
|
28
|
+
type: 'long-animation-frame',
|
|
29
|
+
buffered: options.initial
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return (0, _createClass2.default)(LongAnimationFrameMeasurer, [{
|
|
34
|
+
key: "handleBatch",
|
|
35
|
+
value: function handleBatch(batch) {
|
|
36
|
+
var len = this.longestAnimationFrames.length;
|
|
37
|
+
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
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
var longAnimationFrameEntry = Object.assign(batch[batchIndex], {
|
|
44
|
+
runningFeatures: Array.from(this.options.insmSession.runningFeatures)
|
|
45
|
+
});
|
|
46
|
+
|
|
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;
|
|
53
|
+
}
|
|
54
|
+
len++;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// At capacity: only do work if we beat the current min
|
|
59
|
+
if (longAnimationFrameEntry.duration <= this.minimumDuration) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
|
|
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
|
+
}
|
|
73
|
+
|
|
74
|
+
// Update the min tracking
|
|
75
|
+
this.minimumIndex = _possiblyNewMinimumIndex;
|
|
76
|
+
this.minimumDuration = _possibleNewMinimumDuration;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Pauses tracking
|
|
82
|
+
*/
|
|
83
|
+
}, {
|
|
84
|
+
key: "pause",
|
|
85
|
+
value: function pause() {
|
|
86
|
+
this.paused = true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Resumes tracking
|
|
91
|
+
*/
|
|
92
|
+
}, {
|
|
93
|
+
key: "resume",
|
|
94
|
+
value: function resume() {
|
|
95
|
+
this.paused = false;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Returns the current tracked longest animation frames sorted by duration
|
|
100
|
+
*/
|
|
101
|
+
}, {
|
|
102
|
+
key: "current",
|
|
103
|
+
get: function get() {
|
|
104
|
+
var copy = this.longestAnimationFrames.slice();
|
|
105
|
+
copy.sort(function (a, b) {
|
|
106
|
+
return b.duration - a.duration;
|
|
107
|
+
});
|
|
108
|
+
return copy;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Cleans up the performance tracking (tracking cannot be resumed following this).
|
|
113
|
+
*/
|
|
114
|
+
}, {
|
|
115
|
+
key: "cleanup",
|
|
116
|
+
value: function cleanup() {
|
|
117
|
+
var _this$observer;
|
|
118
|
+
(_this$observer = this.observer) === null || _this$observer === void 0 || _this$observer.disconnect();
|
|
119
|
+
}
|
|
120
|
+
}]);
|
|
121
|
+
}(); // Based on https://github.com/GoogleChrome/web-vitals/blob/1b872cf5f2159e8ace0e98d55d8eb54fb09adfbe/src/types.ts#L129
|
|
122
|
+
// Based on https://github.com/GoogleChrome/web-vitals/blob/1b872cf5f2159e8ace0e98d55d8eb54fb09adfbe/src/types.ts#L129
|
package/dist/es2019/index.js
CHANGED
|
@@ -9,8 +9,6 @@ export function init(options) {
|
|
|
9
9
|
}
|
|
10
10
|
function insmInitialised() {
|
|
11
11
|
if (!initialisedInsm) {
|
|
12
|
-
// eslint-disable-next-line no-console
|
|
13
|
-
console.error('INSM used when not initialised');
|
|
14
12
|
return false;
|
|
15
13
|
}
|
|
16
14
|
return true;
|
|
@@ -35,6 +33,11 @@ export const insm = {
|
|
|
35
33
|
initialisedInsm.start(experienceKey, experienceProperties);
|
|
36
34
|
}
|
|
37
35
|
},
|
|
36
|
+
overrideExperienceKey(experienceKey) {
|
|
37
|
+
if (insmInitialised()) {
|
|
38
|
+
initialisedInsm.overrideExperienceKey(experienceKey);
|
|
39
|
+
}
|
|
40
|
+
},
|
|
38
41
|
stopEarly(reasonKey, description) {
|
|
39
42
|
if (insmInitialised()) {
|
|
40
43
|
initialisedInsm.stopEarly(reasonKey, description);
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import Bowser from 'bowser-ultralight';
|
|
3
|
+
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
2
4
|
import { PeriodTracking } from './insm-period';
|
|
5
|
+
import { LongAnimationFrameMeasurer } from './session-measurers/LongAnimationFrameMeasurer';
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* Only intended for internal use.
|
|
@@ -16,11 +19,20 @@ export class INSMSession {
|
|
|
16
19
|
this.experienceProperties = experienceProperties;
|
|
17
20
|
this.insm = insm;
|
|
18
21
|
this.periodTracking = new PeriodTracking(this);
|
|
22
|
+
this.longAnimationFrameMeasurer = new LongAnimationFrameMeasurer({
|
|
23
|
+
initial: this.experienceProperties.initial,
|
|
24
|
+
limit: 3,
|
|
25
|
+
insmSession: this,
|
|
26
|
+
reportingThreshold: 500
|
|
27
|
+
});
|
|
19
28
|
|
|
20
29
|
/**
|
|
21
30
|
* Note: Events are not reliably fired from mobile browsers (ie. when a browser is closed when not in use)
|
|
22
31
|
*/
|
|
23
32
|
}
|
|
33
|
+
updateExperienceKey(experienceKey) {
|
|
34
|
+
this.experienceKey = experienceKey;
|
|
35
|
+
}
|
|
24
36
|
|
|
25
37
|
/**
|
|
26
38
|
* Adds a feature to the currently running session
|
|
@@ -139,11 +151,57 @@ export class INSMSession {
|
|
|
139
151
|
duration
|
|
140
152
|
},
|
|
141
153
|
periods: periodResults,
|
|
142
|
-
endDetails: endDetails
|
|
154
|
+
endDetails: endDetails,
|
|
155
|
+
longAnimationFrames: this.longAnimationFrameMeasurer.current
|
|
143
156
|
},
|
|
144
|
-
|
|
145
|
-
|
|
157
|
+
deviceDetails: expValEquals('cc_editor_interactivity_monitoring', 'isEnabled', true) ? getDeviceDetails() : undefined,
|
|
158
|
+
highPriority: true,
|
|
159
|
+
tags: ['editor'],
|
|
160
|
+
source: 'unknown'
|
|
146
161
|
};
|
|
147
162
|
(_this$insm$analyticsW = this.insm.analyticsWebClient) === null || _this$insm$analyticsW === void 0 ? void 0 : _this$insm$analyticsW.sendOperationalEvent(operationalEvent);
|
|
163
|
+
this.longAnimationFrameMeasurer.cleanup();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Functionality vendored from UFO
|
|
168
|
+
function getDeviceDetails() {
|
|
169
|
+
var _navigator$connection, _navigator$connection2, _navigator$connection3;
|
|
170
|
+
const telemetry = {};
|
|
171
|
+
|
|
172
|
+
// /platform/packages/data/ufo-internal/src/core/publisher/plugins/browser.ts
|
|
173
|
+
if (Bowser.getParser) {
|
|
174
|
+
var _ref, _ref$navigator;
|
|
175
|
+
const browser = Bowser.getParser(((_ref = typeof window !== 'undefined' && !!window ? window : undefined) === null || _ref === void 0 ? void 0 : (_ref$navigator = _ref.navigator) === null || _ref$navigator === void 0 ? void 0 : _ref$navigator.userAgent) || '');
|
|
176
|
+
Object.assign(telemetry, {
|
|
177
|
+
'event:browser:name': browser.getBrowserName(),
|
|
178
|
+
'event:browser:version': browser.getBrowserVersion()
|
|
179
|
+
});
|
|
148
180
|
}
|
|
181
|
+
|
|
182
|
+
// /platform/packages/data/ufo-internal/src/core/publisher/plugins/cpus.ts
|
|
183
|
+
Object.assign(telemetry, {
|
|
184
|
+
'event:cpus': navigator.hardwareConcurrency
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// /platform/packages/data/ufo-internal/src/core/publisher/plugins/memory.ts
|
|
188
|
+
// @ts-ignore: deviceMemory is exposed in some browsers
|
|
189
|
+
Object.assign(telemetry, {
|
|
190
|
+
'event:memory': navigator.deviceMemory
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// /platform/packages/data/ufo-internal/src/core/publisher/plugins/network.ts
|
|
194
|
+
Object.assign(telemetry, {
|
|
195
|
+
// Network
|
|
196
|
+
// @ts-ignore: connection is available in some browsers
|
|
197
|
+
// eslint-disable-next-line compat/compat
|
|
198
|
+
'event:network:effectiveType': (_navigator$connection = navigator.connection) === null || _navigator$connection === void 0 ? void 0 : _navigator$connection.effectiveType,
|
|
199
|
+
// @ts-ignore: connection is available in some browsers
|
|
200
|
+
// eslint-disable-next-line compat/compat
|
|
201
|
+
'event:network:rtt': (_navigator$connection2 = navigator.connection) === null || _navigator$connection2 === void 0 ? void 0 : _navigator$connection2.rtt,
|
|
202
|
+
// @ts-ignore: connection is available in some browsers
|
|
203
|
+
// eslint-disable-next-line compat/compat
|
|
204
|
+
'event:network:downlink': (_navigator$connection3 = navigator.connection) === null || _navigator$connection3 === void 0 ? void 0 : _navigator$connection3.downlink
|
|
205
|
+
});
|
|
206
|
+
return telemetry;
|
|
149
207
|
}
|
package/dist/es2019/insm.js
CHANGED
|
@@ -10,31 +10,30 @@ export class INSM {
|
|
|
10
10
|
* page session.
|
|
11
11
|
*/
|
|
12
12
|
_defineProperty(this, "runningHeavyTasks", new Set());
|
|
13
|
-
this.periodMeasurers = [new AnimationFPSIM(), new INPTracker()
|
|
14
|
-
includedInteractions: ['pointerup']
|
|
15
|
-
}), new INPTracker({
|
|
16
|
-
includedInteractions: ['pointerdown']
|
|
17
|
-
}), new INPTracker({
|
|
18
|
-
includedInteractions: ['click']
|
|
19
|
-
}), new INPTracker({
|
|
20
|
-
includedInteractions: ['keydown']
|
|
21
|
-
}), new INPTracker({
|
|
22
|
-
includedInteractions: ['keyup']
|
|
23
|
-
})];
|
|
13
|
+
this.periodMeasurers = [new AnimationFPSIM(), new INPTracker()];
|
|
24
14
|
this.options = options;
|
|
25
15
|
|
|
26
16
|
// If this does throw -- we do want an unhandledRejection rejection to be passed to the window
|
|
27
17
|
// this is to ease debugging.
|
|
28
18
|
options.getAnalyticsWebClient.then(analyticsWebClient => this.analyticsWebClient = analyticsWebClient);
|
|
29
19
|
|
|
30
|
-
// No cleanup needs to be performed -- as this is intended to run until the tab is closed
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
20
|
+
// No cleanup needs to be performed -- as this tooling is intended to run until the tab is closed
|
|
21
|
+
// The use of beforeunload here is because using pagehide does not reliably result in the analytics
|
|
22
|
+
// being fired by the analytics web client.
|
|
23
|
+
// The use of this means we will miss events from mobile safari which does not reliably call this api
|
|
24
|
+
// however mobile browsers do not call when a browser closes - so we expect limited mobile data from
|
|
25
|
+
// this tooling in its current form.
|
|
26
|
+
|
|
27
|
+
// window will not be defined in server envs - in these envs -- there can never be a session end
|
|
28
|
+
if (typeof window !== 'undefined') {
|
|
29
|
+
// eslint-disable-next-line @repo/internal/dom-events/no-unsafe-event-listeners
|
|
30
|
+
window.addEventListener('beforeunload', () => {
|
|
31
|
+
var _this$runningSession;
|
|
32
|
+
(_this$runningSession = this.runningSession) === null || _this$runningSession === void 0 ? void 0 : _this$runningSession.end({
|
|
33
|
+
stoppedBy: 'beforeunload'
|
|
34
|
+
});
|
|
36
35
|
});
|
|
37
|
-
}
|
|
36
|
+
}
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
/**
|
|
@@ -43,10 +42,11 @@ export class INSM {
|
|
|
43
42
|
* This also pauses measurement.
|
|
44
43
|
*/
|
|
45
44
|
startHeavyTask(heavyTaskName) {
|
|
46
|
-
var _this$runningSession2, _this$runningSession3, _this$runningSession4;
|
|
45
|
+
var _this$runningSession2, _this$runningSession3, _this$runningSession4, _this$session;
|
|
47
46
|
this.runningHeavyTasks.add(heavyTaskName);
|
|
48
47
|
(_this$runningSession2 = this.runningSession) === null || _this$runningSession2 === void 0 ? void 0 : (_this$runningSession3 = _this$runningSession2.periodTracking) === null || _this$runningSession3 === void 0 ? void 0 : _this$runningSession3.startHeavyTask(heavyTaskName);
|
|
49
48
|
(_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();
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
@@ -56,6 +56,10 @@ export class INSM {
|
|
|
56
56
|
var _this$runningSession5;
|
|
57
57
|
this.runningHeavyTasks.delete(heavyTaskName);
|
|
58
58
|
(_this$runningSession5 = this.runningSession) === null || _this$runningSession5 === void 0 ? void 0 : _this$runningSession5.periodTracking.resume(heavyTaskName);
|
|
59
|
+
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();
|
|
62
|
+
}
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
/**
|
|
@@ -82,10 +86,28 @@ export class INSM {
|
|
|
82
86
|
contentId: experienceProperties.contentId
|
|
83
87
|
});
|
|
84
88
|
}
|
|
89
|
+
this.lastStartedExperienceProperties = experienceProperties;
|
|
85
90
|
if ((_this$options$experie = this.options.experiences[experienceKey]) !== null && _this$options$experie !== void 0 && _this$options$experie.enabled) {
|
|
86
91
|
this.runningSession = new INSMSession(experienceKey, experienceProperties, this);
|
|
87
92
|
}
|
|
88
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Call this to update the name of the running session after it's started
|
|
96
|
+
* In the case it's been started with an unregistered name, and there is not running
|
|
97
|
+
* session. This will also trigger the session being started.
|
|
98
|
+
*/
|
|
99
|
+
overrideExperienceKey(experienceKey) {
|
|
100
|
+
if (this.runningSession !== undefined) {
|
|
101
|
+
// If there is a running session - we update its name
|
|
102
|
+
this.runningSession.updateExperienceKey(experienceKey);
|
|
103
|
+
} else {
|
|
104
|
+
var _this$options$experie2;
|
|
105
|
+
// otherwise - we assume the last session was not registered, and start it with the new name
|
|
106
|
+
if ((_this$options$experie2 = this.options.experiences[experienceKey]) !== null && _this$options$experie2 !== void 0 && _this$options$experie2.enabled && this.lastStartedExperienceProperties) {
|
|
107
|
+
this.runningSession = new INSMSession(experienceKey, this.lastStartedExperienceProperties, this);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
89
111
|
|
|
90
112
|
/**
|
|
91
113
|
* This prematurely halts any running experience measurement. It's expected to be used in
|