UNPKG

mixpanel-browser

Version:

The official Mixpanel JavaScript browser client library

99 lines (89 loc) 4.12 kB
import { Promise } from '../promise-polyfill'; import { IDBStorageWrapper, RECORDING_REGISTRY_STORE_NAME } from '../storage/indexed-db'; import { SessionRecording } from './session-recording'; import { isRecordingExpired } from './utils'; /** * Module for handling the storage and retrieval of recording metadata as well as any active recordings. * Makes sure that only one tab can be recording at a time. */ var RecordingRegistry = function (options) { this.idb = new IDBStorageWrapper(RECORDING_REGISTRY_STORE_NAME); this.errorReporter = options.errorReporter; this.mixpanelInstance = options.mixpanelInstance; this.sharedLockStorage = options.sharedLockStorage; }; RecordingRegistry.prototype.handleError = function (err) { this.errorReporter('IndexedDB error: ', err); }; /** * @param {import('./session-recording').SerializedRecording} serializedRecording */ RecordingRegistry.prototype.setActiveRecording = function (serializedRecording) { var tabId = serializedRecording['tabId']; if (!tabId) { console.warn('No tab ID is set, cannot persist recording metadata.'); return Promise.resolve(); } return this.idb.init() .then(function () { return this.idb.setItem(tabId, serializedRecording); }.bind(this)) .catch(this.handleError.bind(this)); }; /** * @returns {Promise<import('./session-recording').SerializedRecording>} */ RecordingRegistry.prototype.getActiveRecording = function () { return this.idb.init() .then(function () { return this.idb.getItem(this.mixpanelInstance.get_tab_id()); }.bind(this)) .then(function (serializedRecording) { return isRecordingExpired(serializedRecording) ? null : serializedRecording; }.bind(this)) .catch(this.handleError.bind(this)); }; RecordingRegistry.prototype.clearActiveRecording = function () { // mark recording as expired instead of deleting it in case the page unloads mid-flush and doesn't make it to ingestion. // this will ensure the next pageload will flush the remaining events, but not try to continue the recording. return this.getActiveRecording() .then(function (serializedRecording) { if (serializedRecording) { serializedRecording['maxExpires'] = 0; return this.setActiveRecording(serializedRecording); } }.bind(this)) .catch(this.handleError.bind(this)); }; /** * Flush any inactive recordings from the registry to minimize data loss. * The main idea here is that we can flush remaining rrweb events on the next page load if a tab is closed mid-batch. */ RecordingRegistry.prototype.flushInactiveRecordings = function () { return this.idb.init() .then(function() { return this.idb.getAll(); }.bind(this)) .then(function (serializedRecordings) { // clean up any expired recordings from the registry, non-expired ones may be active in other tabs var unloadPromises = serializedRecordings .filter(function (serializedRecording) { return isRecordingExpired(serializedRecording); }) .map(function (serializedRecording) { var sessionRecording = SessionRecording.deserialize(serializedRecording, { mixpanelInstance: this.mixpanelInstance, sharedLockStorage: this.sharedLockStorage }); return sessionRecording.unloadPersistedData() .then(function () { // expired recording was successfully flushed, we can clean it up from the registry return this.idb.removeItem(serializedRecording['tabId']); }.bind(this)) .catch(this.handleError.bind(this)); }.bind(this)); return Promise.all(unloadPromises); }.bind(this)) .catch(this.handleError.bind(this)); }; export { RecordingRegistry };