userlens-session-recorder 1.2.2 → 2.0.0
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/dist/api/index.d.ts +2 -2
- package/dist/index.cjs.js +134 -98
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -4
- package/dist/index.esm.js +134 -98
- package/dist/index.esm.js.map +1 -1
- package/dist/types/index.d.ts +2 -21
- package/dist/userlens-session-recorder.umd.js +134 -98
- package/dist/userlens-session-recorder.umd.js.map +1 -1
- package/dist/utils.d.ts +4 -0
- package/package.json +1 -1
- package/dist/index.js +0 -1
package/dist/types/index.d.ts
CHANGED
|
@@ -1,39 +1,20 @@
|
|
|
1
|
-
import { eventWithTime } from 'rrweb';
|
|
2
|
-
|
|
3
1
|
type MaskingOption = "passwords" | "all";
|
|
4
|
-
type RecorderMode = "auto" | "manual";
|
|
5
2
|
interface SessionRecordingOptions {
|
|
6
3
|
TIMEOUT?: number;
|
|
7
4
|
BUFFER_SIZE?: number;
|
|
8
5
|
maskingOptions?: MaskingOption[];
|
|
9
6
|
recordCrossOriginIframes?: boolean;
|
|
10
7
|
}
|
|
11
|
-
interface
|
|
12
|
-
sessionId: string;
|
|
13
|
-
events: eventWithTime[];
|
|
14
|
-
chunkTimestamp: number;
|
|
15
|
-
}
|
|
16
|
-
type OnEventsCallback = (batch: EventBatch) => void;
|
|
17
|
-
interface AutoModeConfig {
|
|
18
|
-
mode?: "auto";
|
|
8
|
+
interface SessionRecorderConfig {
|
|
19
9
|
WRITE_CODE: string;
|
|
20
10
|
userId: string;
|
|
21
11
|
recordingOptions?: SessionRecordingOptions;
|
|
22
12
|
debug?: boolean;
|
|
23
13
|
}
|
|
24
|
-
interface ManualModeConfig {
|
|
25
|
-
mode: "manual";
|
|
26
|
-
onEvents: OnEventsCallback;
|
|
27
|
-
recordingOptions?: SessionRecordingOptions;
|
|
28
|
-
debug?: boolean;
|
|
29
|
-
}
|
|
30
|
-
type SessionRecorderConfig = AutoModeConfig | ManualModeConfig;
|
|
31
14
|
|
|
32
15
|
declare class SessionRecorder {
|
|
33
16
|
#private;
|
|
34
|
-
private mode;
|
|
35
17
|
private userId?;
|
|
36
|
-
private onEvents?;
|
|
37
18
|
private TIMEOUT;
|
|
38
19
|
private BUFFER_SIZE;
|
|
39
20
|
private maskingOptions;
|
|
@@ -48,4 +29,4 @@ declare class SessionRecorder {
|
|
|
48
29
|
}
|
|
49
30
|
|
|
50
31
|
export { SessionRecorder as default };
|
|
51
|
-
export type {
|
|
32
|
+
export type { MaskingOption, SessionRecorderConfig };
|
|
@@ -20611,48 +20611,71 @@
|
|
|
20611
20611
|
return null;
|
|
20612
20612
|
}
|
|
20613
20613
|
};
|
|
20614
|
+
const STATE_KEY = "userlensSessionRec";
|
|
20615
|
+
function readSessionState() {
|
|
20616
|
+
try {
|
|
20617
|
+
const raw = window.sessionStorage.getItem(STATE_KEY);
|
|
20618
|
+
if (!raw)
|
|
20619
|
+
return null;
|
|
20620
|
+
const parsed = JSON.parse(raw);
|
|
20621
|
+
if (typeof parsed !== "object" || parsed === null ||
|
|
20622
|
+
typeof parsed.session_uuid !== "string" ||
|
|
20623
|
+
typeof parsed.last_active !== "number" ||
|
|
20624
|
+
typeof parsed.chunk_seq !== "number" ||
|
|
20625
|
+
typeof parsed.initial_url !== "string")
|
|
20626
|
+
return null;
|
|
20627
|
+
return parsed;
|
|
20628
|
+
}
|
|
20629
|
+
catch {
|
|
20630
|
+
return null;
|
|
20631
|
+
}
|
|
20632
|
+
}
|
|
20633
|
+
function writeSessionState(state) {
|
|
20634
|
+
try {
|
|
20635
|
+
window.sessionStorage.setItem(STATE_KEY, JSON.stringify(state));
|
|
20636
|
+
}
|
|
20637
|
+
catch { }
|
|
20638
|
+
}
|
|
20639
|
+
function clearSessionState() {
|
|
20640
|
+
try {
|
|
20641
|
+
window.sessionStorage.removeItem(STATE_KEY);
|
|
20642
|
+
}
|
|
20643
|
+
catch { }
|
|
20644
|
+
}
|
|
20614
20645
|
|
|
20615
|
-
const
|
|
20616
|
-
|
|
20617
|
-
|
|
20618
|
-
const jsonStr = JSON.stringify(data);
|
|
20619
|
-
const stream = new Blob([new TextEncoder().encode(jsonStr)])
|
|
20620
|
-
.stream()
|
|
20621
|
-
.pipeThrough(new CompressionStream("gzip"));
|
|
20622
|
-
const buffer = await new Response(stream).arrayBuffer();
|
|
20623
|
-
const bytes = new Uint8Array(buffer);
|
|
20624
|
-
let binary = "";
|
|
20625
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
20626
|
-
binary += String.fromCharCode(bytes[i]);
|
|
20627
|
-
}
|
|
20628
|
-
return btoa(binary);
|
|
20629
|
-
}
|
|
20630
|
-
const uploadSessionEvents = async (userId, sessionUuid, events, chunkTimestamp) => {
|
|
20646
|
+
const INGEST_BASE_URL = "https://ul-ingest.userlens.io";
|
|
20647
|
+
const uploadSessionEvents = async (args) => {
|
|
20648
|
+
var _a;
|
|
20631
20649
|
const writeCode = getWriteCode();
|
|
20632
20650
|
if (!writeCode) {
|
|
20633
20651
|
return;
|
|
20634
20652
|
}
|
|
20635
|
-
|
|
20636
|
-
|
|
20637
|
-
|
|
20638
|
-
|
|
20639
|
-
|
|
20653
|
+
const body = {
|
|
20654
|
+
session_uuid: args.session_uuid,
|
|
20655
|
+
chunk_seq: args.chunk_seq,
|
|
20656
|
+
chunk_start_ts: args.chunk_start_ts,
|
|
20657
|
+
chunk_end_ts: args.chunk_end_ts,
|
|
20658
|
+
user_id: args.user_id,
|
|
20659
|
+
events: args.events,
|
|
20640
20660
|
};
|
|
20641
|
-
|
|
20642
|
-
|
|
20661
|
+
if (args.initial_url !== undefined) {
|
|
20662
|
+
body.initial_url = args.initial_url;
|
|
20663
|
+
}
|
|
20664
|
+
const res = await fetch(`${INGEST_BASE_URL}/session-recording`, {
|
|
20643
20665
|
method: "POST",
|
|
20644
20666
|
headers: {
|
|
20645
|
-
"Content-Type": "
|
|
20667
|
+
"Content-Type": "application/json",
|
|
20646
20668
|
Authorization: `Basic ${writeCode}`,
|
|
20647
20669
|
},
|
|
20648
|
-
body:
|
|
20670
|
+
body: JSON.stringify(body),
|
|
20671
|
+
keepalive: (_a = args.keepalive) !== null && _a !== void 0 ? _a : false,
|
|
20649
20672
|
});
|
|
20650
20673
|
if (!res.ok)
|
|
20651
20674
|
throw new Error("Userlens HTTP error: failed to track");
|
|
20652
20675
|
return "ok";
|
|
20653
20676
|
};
|
|
20654
20677
|
|
|
20655
|
-
var _SessionRecorder_instances, _SessionRecorder_trackEventsThrottled, _SessionRecorder_log, _SessionRecorder_initRecorder, _SessionRecorder_isUserInteraction, _SessionRecorder_handleEvent, _SessionRecorder_resetSession, _SessionRecorder_createSession,
|
|
20678
|
+
var _SessionRecorder_instances, _SessionRecorder_trackEventsThrottled, _SessionRecorder_uploading, _SessionRecorder_log, _SessionRecorder_initRecorder, _SessionRecorder_isUserInteraction, _SessionRecorder_handleEvent, _SessionRecorder_resetSession, _SessionRecorder_createSession, _SessionRecorder_handlePageHide, _SessionRecorder_initListeners, _SessionRecorder_throttle, _SessionRecorder_trackEvents, _SessionRecorder_clearEvents;
|
|
20656
20679
|
class SessionRecorder {
|
|
20657
20680
|
constructor(config) {
|
|
20658
20681
|
var _a, _b, _c;
|
|
@@ -20661,65 +20684,61 @@
|
|
|
20661
20684
|
this.rrwebStop = null;
|
|
20662
20685
|
this.debug = false;
|
|
20663
20686
|
_SessionRecorder_trackEventsThrottled.set(this, void 0);
|
|
20664
|
-
|
|
20687
|
+
_SessionRecorder_uploading.set(this, false);
|
|
20688
|
+
_SessionRecorder_handlePageHide.set(this, () => {
|
|
20665
20689
|
try {
|
|
20666
|
-
if (
|
|
20667
|
-
|
|
20668
|
-
|
|
20669
|
-
|
|
20670
|
-
|
|
20671
|
-
|
|
20672
|
-
|
|
20673
|
-
|
|
20674
|
-
|
|
20675
|
-
|
|
20690
|
+
if (this.sessionEvents.length === 0)
|
|
20691
|
+
return;
|
|
20692
|
+
const events = [...this.sessionEvents];
|
|
20693
|
+
const state = readSessionState();
|
|
20694
|
+
if (!state)
|
|
20695
|
+
return;
|
|
20696
|
+
const chunk_seq = __classPrivateFieldGet(this, _SessionRecorder_uploading, "f") ? state.chunk_seq + 1 : state.chunk_seq;
|
|
20697
|
+
writeSessionState({ ...state, chunk_seq: chunk_seq + 1 });
|
|
20698
|
+
const start_ts_ms = events[0].timestamp;
|
|
20699
|
+
const end_ts_ms = events[events.length - 1].timestamp;
|
|
20700
|
+
uploadSessionEvents({
|
|
20701
|
+
user_id: this.userId,
|
|
20702
|
+
session_uuid: this.sessionUuid,
|
|
20703
|
+
chunk_seq,
|
|
20704
|
+
chunk_start_ts: new Date(start_ts_ms).toISOString(),
|
|
20705
|
+
chunk_end_ts: new Date(end_ts_ms).toISOString(),
|
|
20706
|
+
initial_url: chunk_seq === 0 ? state.initial_url : undefined,
|
|
20707
|
+
events,
|
|
20708
|
+
keepalive: true,
|
|
20709
|
+
}).catch(() => { });
|
|
20710
|
+
this.sessionEvents = [];
|
|
20676
20711
|
}
|
|
20677
20712
|
catch (err) {
|
|
20678
|
-
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_log).call(this, "
|
|
20713
|
+
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_log).call(this, "Page hide handling failed", err);
|
|
20679
20714
|
}
|
|
20680
20715
|
});
|
|
20681
20716
|
try {
|
|
20682
|
-
// Check for browser environment
|
|
20683
20717
|
if (typeof window === "undefined")
|
|
20684
20718
|
return;
|
|
20685
20719
|
if (typeof document === "undefined")
|
|
20686
20720
|
return;
|
|
20687
20721
|
if (typeof localStorage === "undefined")
|
|
20688
20722
|
return;
|
|
20689
|
-
|
|
20690
|
-
if (typeof CompressionStream === "undefined")
|
|
20723
|
+
if (typeof sessionStorage === "undefined")
|
|
20691
20724
|
return;
|
|
20692
20725
|
if (typeof MutationObserver === "undefined")
|
|
20693
20726
|
return;
|
|
20694
|
-
if (typeof TextEncoder === "undefined")
|
|
20695
|
-
return;
|
|
20696
20727
|
if (typeof fetch === "undefined")
|
|
20697
20728
|
return;
|
|
20698
|
-
if (typeof Blob === "undefined")
|
|
20699
|
-
return;
|
|
20700
20729
|
if (typeof crypto === "undefined" || !crypto.getRandomValues)
|
|
20701
20730
|
return;
|
|
20702
|
-
// Check localStorage actually works (can be blocked even if defined)
|
|
20703
20731
|
const testKey = "__userlens_test__";
|
|
20704
20732
|
localStorage.setItem(testKey, "1");
|
|
20705
20733
|
localStorage.removeItem(testKey);
|
|
20706
|
-
|
|
20734
|
+
sessionStorage.setItem(testKey, "1");
|
|
20735
|
+
sessionStorage.removeItem(testKey);
|
|
20707
20736
|
this.debug = (_a = config.debug) !== null && _a !== void 0 ? _a : false;
|
|
20708
|
-
if (config.
|
|
20709
|
-
|
|
20710
|
-
if (!config.onEvents || typeof config.onEvents !== "function") {
|
|
20711
|
-
return;
|
|
20712
|
-
}
|
|
20713
|
-
this.onEvents = config.onEvents;
|
|
20714
|
-
}
|
|
20715
|
-
else {
|
|
20716
|
-
this.mode = "auto";
|
|
20717
|
-
if (!((_b = config.WRITE_CODE) === null || _b === void 0 ? void 0 : _b.trim()) || !((_c = config.userId) === null || _c === void 0 ? void 0 : _c.trim())) {
|
|
20718
|
-
return;
|
|
20719
|
-
}
|
|
20720
|
-
saveWriteCode(config.WRITE_CODE);
|
|
20721
|
-
this.userId = config.userId;
|
|
20737
|
+
if (!((_b = config.WRITE_CODE) === null || _b === void 0 ? void 0 : _b.trim()) || !((_c = config.userId) === null || _c === void 0 ? void 0 : _c.trim())) {
|
|
20738
|
+
return;
|
|
20722
20739
|
}
|
|
20740
|
+
saveWriteCode(config.WRITE_CODE);
|
|
20741
|
+
this.userId = config.userId;
|
|
20723
20742
|
const { recordingOptions = {} } = config;
|
|
20724
20743
|
const { TIMEOUT = 30 * 60 * 1000, BUFFER_SIZE = 10, maskingOptions = ["passwords"], recordCrossOriginIframes = false, } = recordingOptions;
|
|
20725
20744
|
this.TIMEOUT = TIMEOUT;
|
|
@@ -20748,15 +20767,15 @@
|
|
|
20748
20767
|
this.rrwebStop();
|
|
20749
20768
|
this.rrwebStop = null;
|
|
20750
20769
|
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_clearEvents).call(this);
|
|
20751
|
-
|
|
20752
|
-
window.removeEventListener("
|
|
20770
|
+
clearSessionState();
|
|
20771
|
+
window.removeEventListener("pagehide", __classPrivateFieldGet(this, _SessionRecorder_handlePageHide, "f"));
|
|
20753
20772
|
}
|
|
20754
20773
|
catch (err) {
|
|
20755
20774
|
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_log).call(this, "Stop failed", err);
|
|
20756
20775
|
}
|
|
20757
20776
|
}
|
|
20758
20777
|
}
|
|
20759
|
-
_SessionRecorder_trackEventsThrottled = new WeakMap(),
|
|
20778
|
+
_SessionRecorder_trackEventsThrottled = new WeakMap(), _SessionRecorder_uploading = new WeakMap(), _SessionRecorder_handlePageHide = new WeakMap(), _SessionRecorder_instances = new WeakSet(), _SessionRecorder_log = function _SessionRecorder_log(message, error) {
|
|
20760
20779
|
if (!this.debug)
|
|
20761
20780
|
return;
|
|
20762
20781
|
if (error) {
|
|
@@ -20781,7 +20800,7 @@
|
|
|
20781
20800
|
plugins: [getRecordConsolePlugin()],
|
|
20782
20801
|
checkoutEveryNth: 100,
|
|
20783
20802
|
});
|
|
20784
|
-
__classPrivateFieldGet(this, _SessionRecorder_instances, "m",
|
|
20803
|
+
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_initListeners).call(this);
|
|
20785
20804
|
}, _SessionRecorder_isUserInteraction = function _SessionRecorder_isUserInteraction(event) {
|
|
20786
20805
|
var _a;
|
|
20787
20806
|
if (event.type === 3) {
|
|
@@ -20793,15 +20812,16 @@
|
|
|
20793
20812
|
var _a;
|
|
20794
20813
|
try {
|
|
20795
20814
|
const now = Date.now();
|
|
20796
|
-
const
|
|
20797
|
-
|
|
20798
|
-
if (lastActive && now - lastActive > this.TIMEOUT) {
|
|
20815
|
+
const state = readSessionState();
|
|
20816
|
+
if (state && now - state.last_active > this.TIMEOUT) {
|
|
20799
20817
|
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_resetSession).call(this);
|
|
20800
20818
|
takeFullSnapshot(true);
|
|
20801
20819
|
}
|
|
20802
|
-
// only update lastActive on actual user interactions, not DOM mutations
|
|
20803
20820
|
if (__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_isUserInteraction).call(this, event)) {
|
|
20804
|
-
|
|
20821
|
+
const latest = readSessionState();
|
|
20822
|
+
if (latest) {
|
|
20823
|
+
writeSessionState({ ...latest, last_active: now });
|
|
20824
|
+
}
|
|
20805
20825
|
}
|
|
20806
20826
|
this.sessionEvents.push(event);
|
|
20807
20827
|
if (this.sessionEvents.length >= this.BUFFER_SIZE) {
|
|
@@ -20812,24 +20832,29 @@
|
|
|
20812
20832
|
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_log).call(this, "Event handling failed", err);
|
|
20813
20833
|
}
|
|
20814
20834
|
}, _SessionRecorder_resetSession = function _SessionRecorder_resetSession() {
|
|
20815
|
-
|
|
20835
|
+
clearSessionState();
|
|
20816
20836
|
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_clearEvents).call(this);
|
|
20817
20837
|
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_createSession).call(this);
|
|
20818
20838
|
}, _SessionRecorder_createSession = function _SessionRecorder_createSession() {
|
|
20819
20839
|
const now = Date.now();
|
|
20820
|
-
const
|
|
20821
|
-
const
|
|
20822
|
-
|
|
20823
|
-
if (!storedUuid || isExpired) {
|
|
20840
|
+
const state = readSessionState();
|
|
20841
|
+
const isExpired = !state || now - state.last_active > this.TIMEOUT;
|
|
20842
|
+
if (!state || isExpired) {
|
|
20824
20843
|
this.sessionUuid = generateUuid();
|
|
20825
|
-
|
|
20844
|
+
const initial_url = typeof window !== "undefined" ? window.location.href : "";
|
|
20845
|
+
writeSessionState({
|
|
20846
|
+
session_uuid: this.sessionUuid,
|
|
20847
|
+
last_active: now,
|
|
20848
|
+
chunk_seq: 0,
|
|
20849
|
+
initial_url,
|
|
20850
|
+
});
|
|
20826
20851
|
}
|
|
20827
20852
|
else {
|
|
20828
|
-
this.sessionUuid =
|
|
20853
|
+
this.sessionUuid = state.session_uuid;
|
|
20854
|
+
writeSessionState({ ...state, last_active: now });
|
|
20829
20855
|
}
|
|
20830
|
-
|
|
20831
|
-
|
|
20832
|
-
window.addEventListener("visibilitychange", __classPrivateFieldGet(this, _SessionRecorder_handleVisibilityChange, "f"));
|
|
20856
|
+
}, _SessionRecorder_initListeners = function _SessionRecorder_initListeners() {
|
|
20857
|
+
window.addEventListener("pagehide", __classPrivateFieldGet(this, _SessionRecorder_handlePageHide, "f"));
|
|
20833
20858
|
}, _SessionRecorder_throttle = function _SessionRecorder_throttle(func, delay) {
|
|
20834
20859
|
let lastCall = 0;
|
|
20835
20860
|
return (...args) => {
|
|
@@ -20840,34 +20865,45 @@
|
|
|
20840
20865
|
}
|
|
20841
20866
|
};
|
|
20842
20867
|
}, _SessionRecorder_trackEvents = async function _SessionRecorder_trackEvents() {
|
|
20868
|
+
if (__classPrivateFieldGet(this, _SessionRecorder_uploading, "f"))
|
|
20869
|
+
return;
|
|
20870
|
+
if (this.sessionEvents.length === 0)
|
|
20871
|
+
return;
|
|
20872
|
+
__classPrivateFieldSet(this, _SessionRecorder_uploading, true, "f");
|
|
20843
20873
|
try {
|
|
20844
|
-
if (this.sessionEvents.length === 0) {
|
|
20845
|
-
return;
|
|
20846
|
-
}
|
|
20847
|
-
const chunkTimestamp = this.sessionEvents[this.sessionEvents.length - 1].timestamp;
|
|
20848
20874
|
const events = [...this.sessionEvents];
|
|
20849
|
-
|
|
20850
|
-
|
|
20851
|
-
|
|
20852
|
-
|
|
20853
|
-
|
|
20854
|
-
|
|
20855
|
-
|
|
20856
|
-
});
|
|
20857
|
-
}
|
|
20875
|
+
const snapshot_count = events.length;
|
|
20876
|
+
const start_ts_ms = events[0].timestamp;
|
|
20877
|
+
const end_ts_ms = events[events.length - 1].timestamp;
|
|
20878
|
+
const state = readSessionState();
|
|
20879
|
+
if (!state) {
|
|
20880
|
+
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_log).call(this, "No session state during upload — skipping chunk");
|
|
20881
|
+
return;
|
|
20858
20882
|
}
|
|
20859
|
-
|
|
20860
|
-
|
|
20883
|
+
const chunk_seq = state.chunk_seq;
|
|
20884
|
+
await uploadSessionEvents({
|
|
20885
|
+
user_id: this.userId,
|
|
20886
|
+
session_uuid: this.sessionUuid,
|
|
20887
|
+
chunk_seq,
|
|
20888
|
+
chunk_start_ts: new Date(start_ts_ms).toISOString(),
|
|
20889
|
+
chunk_end_ts: new Date(end_ts_ms).toISOString(),
|
|
20890
|
+
initial_url: chunk_seq === 0 ? state.initial_url : undefined,
|
|
20891
|
+
events,
|
|
20892
|
+
});
|
|
20893
|
+
this.sessionEvents = this.sessionEvents.slice(snapshot_count);
|
|
20894
|
+
const after = readSessionState();
|
|
20895
|
+
if (after && after.session_uuid === state.session_uuid) {
|
|
20896
|
+
writeSessionState({ ...after, chunk_seq: after.chunk_seq + 1 });
|
|
20861
20897
|
}
|
|
20862
20898
|
}
|
|
20863
20899
|
catch (err) {
|
|
20864
20900
|
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_log).call(this, "Event tracking failed", err);
|
|
20865
20901
|
}
|
|
20902
|
+
finally {
|
|
20903
|
+
__classPrivateFieldSet(this, _SessionRecorder_uploading, false, "f");
|
|
20904
|
+
}
|
|
20866
20905
|
}, _SessionRecorder_clearEvents = function _SessionRecorder_clearEvents() {
|
|
20867
20906
|
this.sessionEvents = [];
|
|
20868
|
-
}, _SessionRecorder_removeLocalSessionData = function _SessionRecorder_removeLocalSessionData() {
|
|
20869
|
-
localStorage.removeItem("userlensSessionUuid");
|
|
20870
|
-
localStorage.removeItem("userlensSessionLastActive");
|
|
20871
20907
|
};
|
|
20872
20908
|
|
|
20873
20909
|
exports.default = SessionRecorder;
|