UNPKG

clean-insights-sdk

Version:

A privacy-preserving measurement framework.

182 lines (180 loc) 6.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Insights = void 0; var consents_1 = require("./consents"); var dayjs_1 = __importDefault(require("dayjs")); var duration_1 = __importDefault(require("dayjs/plugin/duration")); dayjs_1.default.extend(duration_1.default); var Insights = /** @class */ (function () { /** * Create an `Insights` object according to configuration with all data from the store which * is due for offloading to the server. * * @param {Configuration} conf * The current configuration. * * @param {{consents: Consents, visits: Visit[], events: Event[]}} store * The current measurement and consents store. */ function Insights(conf, store) { var _this = this; /** * Matomo site ID. */ this.idsite = 0; /** * Preferred user languages as an HTTP Accept header. */ this.lang = undefined; /** * User Agent string. */ this.ua = undefined; /** * `Visit` data points. */ this.visits = []; /** * `Event` data points. */ this.events = []; this.idsite = conf.siteId; if (!conf.serverSideAnonymousUsage && store.consents.stateOfFeature(consents_1.Feature.lang) == consents_1.ConsentState.granted && typeof navigator === 'object' && navigator) { this.lang = Insights.acceptLanguages(); } if (!conf.serverSideAnonymousUsage && store.consents.stateOfFeature(consents_1.Feature.ua) == consents_1.ConsentState.granted && typeof navigator === 'object' && navigator) { this.ua = navigator.userAgent; } Insights.purge(conf, store); var now = (0, dayjs_1.default)(); store.visits.forEach(function (visit) { if (!conf.campaigns.hasOwnProperty(visit.campaignId)) { return; } // Only send, after aggregation period is over. `last` should contain that date! if (now.isAfter(visit.last)) { _this.visits.push({ action_name: visit.scenePath.join('/'), period_start: visit.first.unix(), period_end: visit.last.unix(), times: visit.times }); } }); store.events.forEach(function (event) { if (!conf.campaigns.hasOwnProperty(event.campaignId)) { return; } // Only send, after aggregation period is over. `last` should contain that date! if (now.isAfter(event.last)) { _this.events.push({ category: event.category, action: event.action, name: event.name, value: event.value, period_start: event.first.unix(), period_end: event.last.unix(), times: event.times }); } }); } Object.defineProperty(Insights.prototype, "isEmpty", { /** * @returns {boolean} */ get: function () { return this.visits.length < 1 && this.events.length < 1; }, enumerable: false, configurable: true }); /** * Removes all visits and events from the given `Store`, which are also available in here. * * This should be called, when all `Insights` were offloaded at the server successfully. * * @param {{consents: Consents, visits: Visit[], events: Event[]}} store * The store where the `Visit`s and `Event`s in here came from. */ Insights.prototype.clean = function (store) { this.visits.forEach(function (d) { var i = store.visits.length; while (i--) { var v = store.visits[i]; if (v.scenePath.join('/') === d.action_name && Insights.equals(v, d)) { store.visits.splice(i, 1); } } }); this.events.forEach(function (d) { var i = store.events.length; while (i--) { var e = store.events[i]; // noinspection EqualityComparisonWithCoercionJS if (e.category === d.category && e.action === d.action && e.name == d.name && e.value == d.value && Insights.equals(e, d)) { store.events.splice(i, 1); } } }); }; /** * * @param {DataPoint} dp * @param {{period_start: number, period_end: number, times: number}} data * @return {boolean} */ Insights.equals = function (dp, data) { return dp.first.unix() === data.period_start && dp.last.unix() === data.period_end && dp.times === data.times; }; /** * Create an HTTP-accept-language-header-like string from the information * we can get from inside JavaScript. * * @private * @return {string} */ Insights.acceptLanguages = function () { var components = []; var languages = navigator.languages || [navigator.language]; for (var i = 0; i < languages.length; i++) { var quality = 1.0 - (i * 0.1); components.push("".concat(languages[i], ";q=").concat(quality)); if (quality <= 0.5) { break; } } return components.join(','); }; /** Removes `DataPoint`s, which are too old. These were never been sent, otherwise, they would have been removed, already. Remove them now, if they're over the threshold, to not accumulate too many `DataPoints` and therefore reduce privacy. */ Insights.purge = function (conf, store) { var threshold = (0, dayjs_1.default)().subtract(dayjs_1.default.duration(conf.maxAgeOfOldData, 'days')); var i = store.visits.length; while (i--) { if (store.visits[i].last.isBefore(threshold)) { store.visits.splice(i, 1); } } i = store.events.length; while (i--) { if (store.events[i].last.isBefore(threshold)) { store.events.splice(i, 1); } } }; return Insights; }()); exports.Insights = Insights;