@grafana/faro-web-sdk
Version:
Faro instrumentations, metas, transports for web.
121 lines • 5.24 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserActionController = void 0;
// packages/web-sdk/src/instrumentations/userActions/userActionController.ts
const faro_core_1 = require("@grafana/faro-core");
const domMutationMonitor_1 = require("../_internal/monitors/domMutationMonitor");
const httpRequestMonitor_1 = require("../_internal/monitors/httpRequestMonitor");
const performanceEntriesMonitor_1 = require("../_internal/monitors/performanceEntriesMonitor");
const util_1 = require("./util");
const defaultFollowUpActionTimeRange = 100;
const defaultHaltTimeout = 10 * 1000;
class UserActionController {
constructor(userAction) {
this.userAction = userAction;
this.http = (0, httpRequestMonitor_1.monitorHttpRequests)();
this.dom = (0, domMutationMonitor_1.monitorDomMutations)();
this.perf = (0, performanceEntriesMonitor_1.monitorPerformanceEntries)();
this.isValid = false;
this.runningRequests = new Map();
}
attach() {
// Subscribe to monitors while action is active/halting
this.allMonitorsSub = new faro_core_1.Observable()
.merge(this.http, this.dom, this.perf)
.takeWhile(() => [faro_core_1.UserActionState.Started, faro_core_1.UserActionState.Halted].includes(this.userAction.getState()))
.filter((msg) => {
// If the user action is in halt state, we only keep listening to ended http requests
if (this.userAction.getState() === faro_core_1.UserActionState.Halted &&
!((0, util_1.isRequestEndMessage)(msg) && this.runningRequests.has(msg.request.requestId))) {
return false;
}
return true;
})
.subscribe((msg) => {
if ((0, util_1.isRequestStartMessage)(msg)) {
// An action is on halt if it has pending items, like pending HTTP requests.
// In this case we start a separate timeout to wait for the requests to finish
// If in the halt state, we stop adding Faro signals to the action's buffer (see userActionLifecycleHandler.ts)
// But we are still subscribed to
this.runningRequests.set(msg.request.requestId, msg.request);
}
if ((0, util_1.isRequestEndMessage)(msg)) {
this.runningRequests.delete(msg.request.requestId);
}
if (!(0, util_1.isRequestEndMessage)(msg)) {
if (!this.isValid) {
this.isValid = true;
}
this.scheduleFollowUp();
}
else if (this.userAction.getState() === faro_core_1.UserActionState.Halted && this.runningRequests.size === 0) {
this.endAction();
}
});
// When UA ends or cancels, cleanup timers/subscriptions
this.stateSub = this.userAction
.filter((s) => [faro_core_1.UserActionState.Ended, faro_core_1.UserActionState.Cancelled].includes(s))
.first()
.subscribe(() => this.cleanup());
// initial follow-up window in case nothing else happens
this.scheduleFollowUp();
}
scheduleFollowUp() {
this.clearTimer(this.followUpTid);
this.followUpTid = setTimeout(() => {
// If action just started and there's pending work, go to halted
if (this.userAction.getState() === faro_core_1.UserActionState.Started && this.runningRequests.size > 0) {
this.haltAction();
return;
}
// If we saw any relevant activity in the window, finish as ended
if (this.isValid) {
this.endAction();
return;
}
// Otherwise, no signals => cancel
this.cancelAction();
}, defaultFollowUpActionTimeRange);
}
haltAction() {
if (this.userAction.getState() !== faro_core_1.UserActionState.Started) {
return;
}
this.userAction.halt();
this.startHaltTimeout();
}
startHaltTimeout() {
this.clearTimer(this.haltTid);
this.haltTid = (0, util_1.startTimeout)(this.haltTid, () => {
// If still halted after timeout, end
if (this.userAction.getState() === faro_core_1.UserActionState.Halted) {
this.endAction();
}
}, defaultHaltTimeout);
}
endAction() {
this.userAction.end();
this.cleanup();
}
cancelAction() {
this.userAction.cancel();
this.cleanup();
}
cleanup() {
var _a, _b;
this.clearTimer(this.followUpTid);
this.clearTimer(this.haltTid);
(_a = this.allMonitorsSub) === null || _a === void 0 ? void 0 : _a.unsubscribe();
(_b = this.stateSub) === null || _b === void 0 ? void 0 : _b.unsubscribe();
this.allMonitorsSub = undefined;
this.stateSub = undefined;
this.runningRequests.clear();
}
clearTimer(id) {
if (id) {
clearTimeout(id);
}
}
}
exports.UserActionController = UserActionController;
//# sourceMappingURL=userActionController.js.map