userlens-session-recorder 2.1.0 → 2.2.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/index.cjs.js +269 -93
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +269 -93
- package/dist/index.esm.js.map +1 -1
- package/dist/sessionManager.d.ts +61 -0
- package/dist/storage.d.ts +10 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/userlens-session-recorder.umd.js +269 -93
- package/dist/userlens-session-recorder.umd.js.map +1 -1
- package/dist/utils.d.ts +1 -5
- package/package.json +1 -1
package/dist/index.cjs.js
CHANGED
|
@@ -12188,6 +12188,8 @@ var n$1;
|
|
|
12188
12188
|
!function(t2) {
|
|
12189
12189
|
t2[t2.NotStarted = 0] = "NotStarted", t2[t2.Running = 1] = "Running", t2[t2.Stopped = 2] = "Stopped";
|
|
12190
12190
|
}(n$1 || (n$1 = {}));
|
|
12191
|
+
const { addCustomEvent } = record;
|
|
12192
|
+
const { freezePage } = record;
|
|
12191
12193
|
const { takeFullSnapshot } = record;
|
|
12192
12194
|
|
|
12193
12195
|
var __defProp = Object.defineProperty;
|
|
@@ -20592,7 +20594,7 @@ function generateUuid() {
|
|
|
20592
20594
|
function saveWriteCode(writeCode) {
|
|
20593
20595
|
window.localStorage.setItem("$ul_WRITE_CODE", btoa(`${writeCode}:`));
|
|
20594
20596
|
}
|
|
20595
|
-
|
|
20597
|
+
function getWriteCode() {
|
|
20596
20598
|
try {
|
|
20597
20599
|
const raw = window.localStorage.getItem("$ul_WRITE_CODE");
|
|
20598
20600
|
if (raw == null)
|
|
@@ -20608,37 +20610,6 @@ const getWriteCode = () => {
|
|
|
20608
20610
|
catch {
|
|
20609
20611
|
return null;
|
|
20610
20612
|
}
|
|
20611
|
-
};
|
|
20612
|
-
const STATE_KEY = "userlensSessionRec";
|
|
20613
|
-
function readSessionState() {
|
|
20614
|
-
try {
|
|
20615
|
-
const raw = window.sessionStorage.getItem(STATE_KEY);
|
|
20616
|
-
if (!raw)
|
|
20617
|
-
return null;
|
|
20618
|
-
const parsed = JSON.parse(raw);
|
|
20619
|
-
if (typeof parsed !== "object" || parsed === null ||
|
|
20620
|
-
typeof parsed.session_uuid !== "string" ||
|
|
20621
|
-
typeof parsed.last_active !== "number" ||
|
|
20622
|
-
typeof parsed.chunk_seq !== "number" ||
|
|
20623
|
-
typeof parsed.initial_url !== "string")
|
|
20624
|
-
return null;
|
|
20625
|
-
return parsed;
|
|
20626
|
-
}
|
|
20627
|
-
catch {
|
|
20628
|
-
return null;
|
|
20629
|
-
}
|
|
20630
|
-
}
|
|
20631
|
-
function writeSessionState(state) {
|
|
20632
|
-
try {
|
|
20633
|
-
window.sessionStorage.setItem(STATE_KEY, JSON.stringify(state));
|
|
20634
|
-
}
|
|
20635
|
-
catch { }
|
|
20636
|
-
}
|
|
20637
|
-
function clearSessionState() {
|
|
20638
|
-
try {
|
|
20639
|
-
window.sessionStorage.removeItem(STATE_KEY);
|
|
20640
|
-
}
|
|
20641
|
-
catch { }
|
|
20642
20613
|
}
|
|
20643
20614
|
|
|
20644
20615
|
const INGEST_BASE_URL = "https://ul-ingest.userlens.io";
|
|
@@ -20650,15 +20621,13 @@ const uploadSessionEvents = async (args) => {
|
|
|
20650
20621
|
}
|
|
20651
20622
|
const body = {
|
|
20652
20623
|
session_uuid: args.session_uuid,
|
|
20624
|
+
window_id: args.window_id,
|
|
20653
20625
|
chunk_seq: args.chunk_seq,
|
|
20654
20626
|
chunk_start_ts: args.chunk_start_ts,
|
|
20655
20627
|
chunk_end_ts: args.chunk_end_ts,
|
|
20656
20628
|
user_id: args.user_id,
|
|
20657
20629
|
events: args.events,
|
|
20658
20630
|
};
|
|
20659
|
-
if (args.initial_url !== undefined) {
|
|
20660
|
-
body.initial_url = args.initial_url;
|
|
20661
|
-
}
|
|
20662
20631
|
const res = await fetch(`${INGEST_BASE_URL}/session-recording`, {
|
|
20663
20632
|
method: "POST",
|
|
20664
20633
|
headers: {
|
|
@@ -20673,7 +20642,224 @@ const uploadSessionEvents = async (args) => {
|
|
|
20673
20642
|
return "ok";
|
|
20674
20643
|
};
|
|
20675
20644
|
|
|
20676
|
-
|
|
20645
|
+
const SHARED_KEY = "userlensSession";
|
|
20646
|
+
const WINDOW_KEY = "userlensWindow";
|
|
20647
|
+
// Set in sessionStorage on construct, cleared on pagehide. If we read it on
|
|
20648
|
+
// init and it's already set, sessionStorage was cloned (Cmd+click, duplicate,
|
|
20649
|
+
// window.open) — discard inherited window_id and mint a fresh one.
|
|
20650
|
+
const PRIMARY_WINDOW_KEY = "userlensPrimaryWindow";
|
|
20651
|
+
function readSharedSessionState() {
|
|
20652
|
+
try {
|
|
20653
|
+
const raw = window.localStorage.getItem(SHARED_KEY);
|
|
20654
|
+
if (!raw)
|
|
20655
|
+
return null;
|
|
20656
|
+
const parsed = JSON.parse(raw);
|
|
20657
|
+
if (typeof parsed !== "object" || parsed === null ||
|
|
20658
|
+
typeof parsed.session_uuid !== "string" ||
|
|
20659
|
+
typeof parsed.last_active !== "number")
|
|
20660
|
+
return null;
|
|
20661
|
+
return parsed;
|
|
20662
|
+
}
|
|
20663
|
+
catch {
|
|
20664
|
+
return null;
|
|
20665
|
+
}
|
|
20666
|
+
}
|
|
20667
|
+
function writeSharedSessionState(state) {
|
|
20668
|
+
try {
|
|
20669
|
+
window.localStorage.setItem(SHARED_KEY, JSON.stringify(state));
|
|
20670
|
+
}
|
|
20671
|
+
catch { }
|
|
20672
|
+
}
|
|
20673
|
+
function clearSharedSessionState() {
|
|
20674
|
+
try {
|
|
20675
|
+
window.localStorage.removeItem(SHARED_KEY);
|
|
20676
|
+
}
|
|
20677
|
+
catch { }
|
|
20678
|
+
}
|
|
20679
|
+
function readWindowState() {
|
|
20680
|
+
try {
|
|
20681
|
+
const raw = window.sessionStorage.getItem(WINDOW_KEY);
|
|
20682
|
+
if (!raw)
|
|
20683
|
+
return null;
|
|
20684
|
+
const parsed = JSON.parse(raw);
|
|
20685
|
+
if (typeof parsed !== "object" || parsed === null ||
|
|
20686
|
+
typeof parsed.window_id !== "string" ||
|
|
20687
|
+
typeof parsed.chunk_seq !== "number")
|
|
20688
|
+
return null;
|
|
20689
|
+
return parsed;
|
|
20690
|
+
}
|
|
20691
|
+
catch {
|
|
20692
|
+
return null;
|
|
20693
|
+
}
|
|
20694
|
+
}
|
|
20695
|
+
function writeWindowState(state) {
|
|
20696
|
+
try {
|
|
20697
|
+
window.sessionStorage.setItem(WINDOW_KEY, JSON.stringify(state));
|
|
20698
|
+
}
|
|
20699
|
+
catch { }
|
|
20700
|
+
}
|
|
20701
|
+
function clearWindowState() {
|
|
20702
|
+
try {
|
|
20703
|
+
window.sessionStorage.removeItem(WINDOW_KEY);
|
|
20704
|
+
}
|
|
20705
|
+
catch { }
|
|
20706
|
+
}
|
|
20707
|
+
function wasPrimaryWindowFlagAlreadySet() {
|
|
20708
|
+
try {
|
|
20709
|
+
return window.sessionStorage.getItem(PRIMARY_WINDOW_KEY) === "1";
|
|
20710
|
+
}
|
|
20711
|
+
catch {
|
|
20712
|
+
return false;
|
|
20713
|
+
}
|
|
20714
|
+
}
|
|
20715
|
+
function markPrimaryWindow() {
|
|
20716
|
+
try {
|
|
20717
|
+
window.sessionStorage.setItem(PRIMARY_WINDOW_KEY, "1");
|
|
20718
|
+
}
|
|
20719
|
+
catch { }
|
|
20720
|
+
}
|
|
20721
|
+
function clearPrimaryWindowFlag() {
|
|
20722
|
+
try {
|
|
20723
|
+
window.sessionStorage.removeItem(PRIMARY_WINDOW_KEY);
|
|
20724
|
+
}
|
|
20725
|
+
catch { }
|
|
20726
|
+
}
|
|
20727
|
+
|
|
20728
|
+
var _SessionManager_instances, _SessionManager_initWindow, _SessionManager_joinOrStartSession, _SessionManager_startFresh;
|
|
20729
|
+
/**
|
|
20730
|
+
* Owns the (session_uuid, window_id, chunk_seq, last_active) lifecycle.
|
|
20731
|
+
*
|
|
20732
|
+
* Two storage scopes:
|
|
20733
|
+
* - shared (localStorage): session_uuid + last_active — visible to every tab
|
|
20734
|
+
* of the same origin so tabs can adopt one logical session and a single
|
|
20735
|
+
* idle timeout governs the whole user.
|
|
20736
|
+
* - per-tab (sessionStorage): window_id + chunk_seq — independent per tab so
|
|
20737
|
+
* concurrent tabs can upload chunks without colliding on chunk_seq.
|
|
20738
|
+
*
|
|
20739
|
+
* The recorder talks to this class instead of touching storage directly.
|
|
20740
|
+
*/
|
|
20741
|
+
class SessionManager {
|
|
20742
|
+
constructor(timeoutMs) {
|
|
20743
|
+
_SessionManager_instances.add(this);
|
|
20744
|
+
this.timeoutMs = timeoutMs;
|
|
20745
|
+
}
|
|
20746
|
+
initialize() {
|
|
20747
|
+
__classPrivateFieldGet(this, _SessionManager_instances, "m", _SessionManager_initWindow).call(this);
|
|
20748
|
+
__classPrivateFieldGet(this, _SessionManager_instances, "m", _SessionManager_joinOrStartSession).call(this);
|
|
20749
|
+
}
|
|
20750
|
+
get sessionUuid() {
|
|
20751
|
+
return this._sessionUuid;
|
|
20752
|
+
}
|
|
20753
|
+
get windowId() {
|
|
20754
|
+
return this._windowId;
|
|
20755
|
+
}
|
|
20756
|
+
/**
|
|
20757
|
+
* Reconciles in-memory session_uuid with whatever shared storage says.
|
|
20758
|
+
* Called on every event so cross-tab session changes are picked up quickly.
|
|
20759
|
+
*
|
|
20760
|
+
* Returns:
|
|
20761
|
+
* "expired" — shared session has been idle past timeout; we rotated.
|
|
20762
|
+
* "rotated" — another tab rotated the session_uuid; we adopted it.
|
|
20763
|
+
* "none" — no change, business as usual.
|
|
20764
|
+
*
|
|
20765
|
+
* Caller takes a fresh full snapshot on "expired" or "rotated" so this
|
|
20766
|
+
* window's first chunk in the new session is replayable on its own.
|
|
20767
|
+
*/
|
|
20768
|
+
syncWithSharedState(now) {
|
|
20769
|
+
const shared = readSharedSessionState();
|
|
20770
|
+
if (shared && now - shared.last_active > this.timeoutMs) {
|
|
20771
|
+
__classPrivateFieldGet(this, _SessionManager_instances, "m", _SessionManager_startFresh).call(this, now);
|
|
20772
|
+
return "expired";
|
|
20773
|
+
}
|
|
20774
|
+
if (shared && shared.session_uuid !== this._sessionUuid) {
|
|
20775
|
+
this._sessionUuid = shared.session_uuid;
|
|
20776
|
+
writeWindowState({ window_id: this._windowId, chunk_seq: 0 });
|
|
20777
|
+
return "rotated";
|
|
20778
|
+
}
|
|
20779
|
+
return "none";
|
|
20780
|
+
}
|
|
20781
|
+
/** Bump shared last_active. Call only on real user interactions. */
|
|
20782
|
+
bumpActivity(now) {
|
|
20783
|
+
const latest = readSharedSessionState();
|
|
20784
|
+
if (latest) {
|
|
20785
|
+
writeSharedSessionState({ ...latest, last_active: now });
|
|
20786
|
+
}
|
|
20787
|
+
}
|
|
20788
|
+
/** Read this window's current chunk_seq for a pending upload. */
|
|
20789
|
+
currentChunkSeq() {
|
|
20790
|
+
var _a, _b;
|
|
20791
|
+
return (_b = (_a = readWindowState()) === null || _a === void 0 ? void 0 : _a.chunk_seq) !== null && _b !== void 0 ? _b : null;
|
|
20792
|
+
}
|
|
20793
|
+
/**
|
|
20794
|
+
* Mark a chunk_seq as committed after a successful upload. No-ops if the
|
|
20795
|
+
* stored seq has moved on (e.g. a session rotation reset it to 0 mid-flight).
|
|
20796
|
+
*/
|
|
20797
|
+
commitChunkSeq(uploadedSeq) {
|
|
20798
|
+
const after = readWindowState();
|
|
20799
|
+
if (after && after.window_id === this._windowId && after.chunk_seq === uploadedSeq) {
|
|
20800
|
+
writeWindowState({ window_id: this._windowId, chunk_seq: uploadedSeq + 1 });
|
|
20801
|
+
}
|
|
20802
|
+
}
|
|
20803
|
+
/**
|
|
20804
|
+
* Reserve and return the current chunk_seq for a pagehide flush, advancing
|
|
20805
|
+
* storage so the next normal upload uses seq+1. Returns null if window state
|
|
20806
|
+
* is missing.
|
|
20807
|
+
*/
|
|
20808
|
+
reserveChunkSeq() {
|
|
20809
|
+
const win = readWindowState();
|
|
20810
|
+
if (!win)
|
|
20811
|
+
return null;
|
|
20812
|
+
writeWindowState({ window_id: this._windowId, chunk_seq: win.chunk_seq + 1 });
|
|
20813
|
+
return win.chunk_seq;
|
|
20814
|
+
}
|
|
20815
|
+
/** Hard reset: wipe shared session and start a fresh one. */
|
|
20816
|
+
reset() {
|
|
20817
|
+
clearSharedSessionState();
|
|
20818
|
+
__classPrivateFieldGet(this, _SessionManager_instances, "m", _SessionManager_joinOrStartSession).call(this);
|
|
20819
|
+
}
|
|
20820
|
+
/**
|
|
20821
|
+
* Called from pagehide. Lets a future reload of the same tab reuse the
|
|
20822
|
+
* window_id (because the flag is gone), while a cloned tab still sees the
|
|
20823
|
+
* flag set in its inherited sessionStorage and treats itself as a duplicate.
|
|
20824
|
+
*/
|
|
20825
|
+
notifyPageHide() {
|
|
20826
|
+
clearPrimaryWindowFlag();
|
|
20827
|
+
}
|
|
20828
|
+
/** Full teardown — call on stop(). */
|
|
20829
|
+
dispose() {
|
|
20830
|
+
clearSharedSessionState();
|
|
20831
|
+
clearWindowState();
|
|
20832
|
+
clearPrimaryWindowFlag();
|
|
20833
|
+
}
|
|
20834
|
+
}
|
|
20835
|
+
_SessionManager_instances = new WeakSet(), _SessionManager_initWindow = function _SessionManager_initWindow() {
|
|
20836
|
+
const cloned = wasPrimaryWindowFlagAlreadySet();
|
|
20837
|
+
const existing = readWindowState();
|
|
20838
|
+
if (existing && !cloned) {
|
|
20839
|
+
this._windowId = existing.window_id;
|
|
20840
|
+
}
|
|
20841
|
+
else {
|
|
20842
|
+
this._windowId = generateUuid();
|
|
20843
|
+
writeWindowState({ window_id: this._windowId, chunk_seq: 0 });
|
|
20844
|
+
}
|
|
20845
|
+
markPrimaryWindow();
|
|
20846
|
+
}, _SessionManager_joinOrStartSession = function _SessionManager_joinOrStartSession() {
|
|
20847
|
+
const now = Date.now();
|
|
20848
|
+
const shared = readSharedSessionState();
|
|
20849
|
+
if (!shared || now - shared.last_active > this.timeoutMs) {
|
|
20850
|
+
__classPrivateFieldGet(this, _SessionManager_instances, "m", _SessionManager_startFresh).call(this, now);
|
|
20851
|
+
}
|
|
20852
|
+
else {
|
|
20853
|
+
this._sessionUuid = shared.session_uuid;
|
|
20854
|
+
writeSharedSessionState({ ...shared, last_active: now });
|
|
20855
|
+
}
|
|
20856
|
+
}, _SessionManager_startFresh = function _SessionManager_startFresh(now) {
|
|
20857
|
+
this._sessionUuid = generateUuid();
|
|
20858
|
+
writeSharedSessionState({ session_uuid: this._sessionUuid, last_active: now });
|
|
20859
|
+
writeWindowState({ window_id: this._windowId, chunk_seq: 0 });
|
|
20860
|
+
};
|
|
20861
|
+
|
|
20862
|
+
var _SessionRecorder_instances, _SessionRecorder_uploading, _SessionRecorder_uploadingMaxTs, _SessionRecorder_bufferTimer, _SessionRecorder_log, _SessionRecorder_initRecorder, _SessionRecorder_isUserInteraction, _SessionRecorder_handleEvent, _SessionRecorder_armBufferTimer, _SessionRecorder_flushBuffer, _SessionRecorder_handlePageHide, _SessionRecorder_handleVisibilityChange, _SessionRecorder_initListeners, _SessionRecorder_trackEvents, _SessionRecorder_clearEvents;
|
|
20677
20863
|
const BUFFER_TIMEOUT_MS = 2000;
|
|
20678
20864
|
const MAX_BUFFER_BYTES = 900 * 1024;
|
|
20679
20865
|
function estimateEventSize(event) {
|
|
@@ -20697,6 +20883,7 @@ class SessionRecorder {
|
|
|
20697
20883
|
_SessionRecorder_bufferTimer.set(this, null);
|
|
20698
20884
|
_SessionRecorder_handlePageHide.set(this, () => {
|
|
20699
20885
|
try {
|
|
20886
|
+
this.sessionManager.notifyPageHide();
|
|
20700
20887
|
if (this.sessionEvents.length === 0)
|
|
20701
20888
|
return;
|
|
20702
20889
|
let events;
|
|
@@ -20708,24 +20895,21 @@ class SessionRecorder {
|
|
|
20708
20895
|
}
|
|
20709
20896
|
if (events.length === 0)
|
|
20710
20897
|
return;
|
|
20711
|
-
const
|
|
20712
|
-
if (
|
|
20898
|
+
const chunk_seq = this.sessionManager.reserveChunkSeq();
|
|
20899
|
+
if (chunk_seq === null)
|
|
20713
20900
|
return;
|
|
20714
|
-
const chunk_seq = __classPrivateFieldGet(this, _SessionRecorder_uploading, "f") ? state.chunk_seq + 1 : state.chunk_seq;
|
|
20715
|
-
writeSessionState({ ...state, chunk_seq: chunk_seq + 1 });
|
|
20716
20901
|
const start_ts_ms = events[0].timestamp;
|
|
20717
20902
|
const end_ts_ms = events[events.length - 1].timestamp;
|
|
20718
|
-
const initial_url = chunk_seq === 0 ? state.initial_url : undefined;
|
|
20719
20903
|
const chunk_start_ts = new Date(start_ts_ms).toISOString();
|
|
20720
20904
|
const chunk_end_ts = new Date(end_ts_ms).toISOString();
|
|
20721
20905
|
if (this.mode === "manual") {
|
|
20722
20906
|
try {
|
|
20723
20907
|
void this.onEvents({
|
|
20724
|
-
session_uuid: this.sessionUuid,
|
|
20908
|
+
session_uuid: this.sessionManager.sessionUuid,
|
|
20909
|
+
window_id: this.sessionManager.windowId,
|
|
20725
20910
|
chunk_seq,
|
|
20726
20911
|
chunk_start_ts,
|
|
20727
20912
|
chunk_end_ts,
|
|
20728
|
-
initial_url,
|
|
20729
20913
|
events,
|
|
20730
20914
|
keepalive: true,
|
|
20731
20915
|
});
|
|
@@ -20737,11 +20921,11 @@ class SessionRecorder {
|
|
|
20737
20921
|
else {
|
|
20738
20922
|
uploadSessionEvents({
|
|
20739
20923
|
user_id: this.userId,
|
|
20740
|
-
session_uuid: this.sessionUuid,
|
|
20924
|
+
session_uuid: this.sessionManager.sessionUuid,
|
|
20925
|
+
window_id: this.sessionManager.windowId,
|
|
20741
20926
|
chunk_seq,
|
|
20742
20927
|
chunk_start_ts,
|
|
20743
20928
|
chunk_end_ts,
|
|
20744
|
-
initial_url,
|
|
20745
20929
|
events,
|
|
20746
20930
|
keepalive: true,
|
|
20747
20931
|
}).catch(() => { });
|
|
@@ -20753,6 +20937,19 @@ class SessionRecorder {
|
|
|
20753
20937
|
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_log).call(this, "Page hide handling failed", err);
|
|
20754
20938
|
}
|
|
20755
20939
|
});
|
|
20940
|
+
// Player groups multi-tab recordings into segments by which tab was visible
|
|
20941
|
+
// when. Without these markers it has to guess from chunk timestamps and
|
|
20942
|
+
// active-source heuristics, which is unreliable when two tabs overlap.
|
|
20943
|
+
_SessionRecorder_handleVisibilityChange.set(this, () => {
|
|
20944
|
+
try {
|
|
20945
|
+
if (typeof document === "undefined" || !document.visibilityState)
|
|
20946
|
+
return;
|
|
20947
|
+
record.addCustomEvent("window " + document.visibilityState, {});
|
|
20948
|
+
}
|
|
20949
|
+
catch (err) {
|
|
20950
|
+
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_log).call(this, "Visibility change handling failed", err);
|
|
20951
|
+
}
|
|
20952
|
+
});
|
|
20756
20953
|
try {
|
|
20757
20954
|
if (typeof window === "undefined")
|
|
20758
20955
|
return;
|
|
@@ -20797,6 +20994,7 @@ class SessionRecorder {
|
|
|
20797
20994
|
this.maskingOptions = maskingOptions;
|
|
20798
20995
|
this.recordCrossOriginIframes = recordCrossOriginIframes;
|
|
20799
20996
|
this.sessionEvents = [];
|
|
20997
|
+
this.sessionManager = new SessionManager(this.TIMEOUT);
|
|
20800
20998
|
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_initRecorder).call(this);
|
|
20801
20999
|
}
|
|
20802
21000
|
catch (err) {
|
|
@@ -20805,7 +21003,8 @@ class SessionRecorder {
|
|
|
20805
21003
|
}
|
|
20806
21004
|
}
|
|
20807
21005
|
getSessionId() {
|
|
20808
|
-
|
|
21006
|
+
var _a;
|
|
21007
|
+
return (_a = this.sessionManager) === null || _a === void 0 ? void 0 : _a.sessionUuid;
|
|
20809
21008
|
}
|
|
20810
21009
|
stop() {
|
|
20811
21010
|
try {
|
|
@@ -20815,15 +21014,16 @@ class SessionRecorder {
|
|
|
20815
21014
|
this.rrwebStop();
|
|
20816
21015
|
this.rrwebStop = null;
|
|
20817
21016
|
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_clearEvents).call(this);
|
|
20818
|
-
|
|
21017
|
+
this.sessionManager.dispose();
|
|
20819
21018
|
window.removeEventListener("pagehide", __classPrivateFieldGet(this, _SessionRecorder_handlePageHide, "f"));
|
|
21019
|
+
document.removeEventListener("visibilitychange", __classPrivateFieldGet(this, _SessionRecorder_handleVisibilityChange, "f"));
|
|
20820
21020
|
}
|
|
20821
21021
|
catch (err) {
|
|
20822
21022
|
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_log).call(this, "Stop failed", err);
|
|
20823
21023
|
}
|
|
20824
21024
|
}
|
|
20825
21025
|
}
|
|
20826
|
-
_SessionRecorder_uploading = new WeakMap(), _SessionRecorder_uploadingMaxTs = new WeakMap(), _SessionRecorder_bufferTimer = new WeakMap(), _SessionRecorder_handlePageHide = new WeakMap(), _SessionRecorder_instances = new WeakSet(), _SessionRecorder_log = function _SessionRecorder_log(message, error) {
|
|
21026
|
+
_SessionRecorder_uploading = new WeakMap(), _SessionRecorder_uploadingMaxTs = new WeakMap(), _SessionRecorder_bufferTimer = new WeakMap(), _SessionRecorder_handlePageHide = new WeakMap(), _SessionRecorder_handleVisibilityChange = new WeakMap(), _SessionRecorder_instances = new WeakSet(), _SessionRecorder_log = function _SessionRecorder_log(message, error) {
|
|
20827
21027
|
if (!this.debug)
|
|
20828
21028
|
return;
|
|
20829
21029
|
if (error) {
|
|
@@ -20835,7 +21035,7 @@ _SessionRecorder_uploading = new WeakMap(), _SessionRecorder_uploadingMaxTs = ne
|
|
|
20835
21035
|
}, _SessionRecorder_initRecorder = function _SessionRecorder_initRecorder() {
|
|
20836
21036
|
if (this.rrwebStop)
|
|
20837
21037
|
return;
|
|
20838
|
-
|
|
21038
|
+
this.sessionManager.initialize();
|
|
20839
21039
|
this.rrwebStop = record({
|
|
20840
21040
|
emit: (event, isCheckout) => {
|
|
20841
21041
|
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_handleEvent).call(this, event, isCheckout);
|
|
@@ -20859,16 +21059,16 @@ _SessionRecorder_uploading = new WeakMap(), _SessionRecorder_uploadingMaxTs = ne
|
|
|
20859
21059
|
}, _SessionRecorder_handleEvent = function _SessionRecorder_handleEvent(event, _isCheckout) {
|
|
20860
21060
|
try {
|
|
20861
21061
|
const now = Date.now();
|
|
20862
|
-
const
|
|
20863
|
-
if (
|
|
20864
|
-
__classPrivateFieldGet(this, _SessionRecorder_instances, "m",
|
|
21062
|
+
const change = this.sessionManager.syncWithSharedState(now);
|
|
21063
|
+
if (change === "expired") {
|
|
21064
|
+
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_clearEvents).call(this);
|
|
21065
|
+
takeFullSnapshot(true);
|
|
21066
|
+
}
|
|
21067
|
+
else if (change === "rotated") {
|
|
20865
21068
|
takeFullSnapshot(true);
|
|
20866
21069
|
}
|
|
20867
21070
|
if (__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_isUserInteraction).call(this, event)) {
|
|
20868
|
-
|
|
20869
|
-
if (latest) {
|
|
20870
|
-
writeSessionState({ ...latest, last_active: now });
|
|
20871
|
-
}
|
|
21071
|
+
this.sessionManager.bumpActivity(now);
|
|
20872
21072
|
}
|
|
20873
21073
|
this.sessionEvents.push(event);
|
|
20874
21074
|
this.bufferBytes += estimateEventSize(event);
|
|
@@ -20894,30 +21094,13 @@ _SessionRecorder_uploading = new WeakMap(), _SessionRecorder_uploadingMaxTs = ne
|
|
|
20894
21094
|
__classPrivateFieldSet(this, _SessionRecorder_bufferTimer, null, "f");
|
|
20895
21095
|
}
|
|
20896
21096
|
void __classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_trackEvents).call(this);
|
|
20897
|
-
}, _SessionRecorder_resetSession = function _SessionRecorder_resetSession() {
|
|
20898
|
-
clearSessionState();
|
|
20899
|
-
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_clearEvents).call(this);
|
|
20900
|
-
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_createSession).call(this);
|
|
20901
|
-
}, _SessionRecorder_createSession = function _SessionRecorder_createSession() {
|
|
20902
|
-
const now = Date.now();
|
|
20903
|
-
const state = readSessionState();
|
|
20904
|
-
const isExpired = !state || now - state.last_active > this.TIMEOUT;
|
|
20905
|
-
if (!state || isExpired) {
|
|
20906
|
-
this.sessionUuid = generateUuid();
|
|
20907
|
-
const initial_url = typeof window !== "undefined" ? window.location.href : "";
|
|
20908
|
-
writeSessionState({
|
|
20909
|
-
session_uuid: this.sessionUuid,
|
|
20910
|
-
last_active: now,
|
|
20911
|
-
chunk_seq: 0,
|
|
20912
|
-
initial_url,
|
|
20913
|
-
});
|
|
20914
|
-
}
|
|
20915
|
-
else {
|
|
20916
|
-
this.sessionUuid = state.session_uuid;
|
|
20917
|
-
writeSessionState({ ...state, last_active: now });
|
|
20918
|
-
}
|
|
20919
21097
|
}, _SessionRecorder_initListeners = function _SessionRecorder_initListeners() {
|
|
20920
21098
|
window.addEventListener("pagehide", __classPrivateFieldGet(this, _SessionRecorder_handlePageHide, "f"));
|
|
21099
|
+
document.addEventListener("visibilitychange", __classPrivateFieldGet(this, _SessionRecorder_handleVisibilityChange, "f"));
|
|
21100
|
+
// Emit the initial state so the player knows whether this tab started
|
|
21101
|
+
// visible or hidden (a tab opened in the background never fires
|
|
21102
|
+
// visibilitychange until the user focuses it).
|
|
21103
|
+
__classPrivateFieldGet(this, _SessionRecorder_handleVisibilityChange, "f").call(this);
|
|
20921
21104
|
}, _SessionRecorder_trackEvents = async function _SessionRecorder_trackEvents() {
|
|
20922
21105
|
if (__classPrivateFieldGet(this, _SessionRecorder_uploading, "f"))
|
|
20923
21106
|
return;
|
|
@@ -20930,45 +21113,38 @@ _SessionRecorder_uploading = new WeakMap(), _SessionRecorder_uploadingMaxTs = ne
|
|
|
20930
21113
|
__classPrivateFieldSet(this, _SessionRecorder_uploadingMaxTs, events[events.length - 1].timestamp, "f");
|
|
20931
21114
|
const start_ts_ms = events[0].timestamp;
|
|
20932
21115
|
const end_ts_ms = events[events.length - 1].timestamp;
|
|
20933
|
-
const
|
|
20934
|
-
if (
|
|
20935
|
-
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_log).call(this, "No
|
|
21116
|
+
const chunk_seq = this.sessionManager.currentChunkSeq();
|
|
21117
|
+
if (chunk_seq === null) {
|
|
21118
|
+
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_log).call(this, "No window state during upload — skipping chunk");
|
|
20936
21119
|
return;
|
|
20937
21120
|
}
|
|
20938
|
-
const chunk_seq = state.chunk_seq;
|
|
20939
|
-
const initial_url = chunk_seq === 0 ? state.initial_url : undefined;
|
|
20940
21121
|
const chunk_start_ts = new Date(start_ts_ms).toISOString();
|
|
20941
21122
|
const chunk_end_ts = new Date(end_ts_ms).toISOString();
|
|
20942
21123
|
if (this.mode === "manual") {
|
|
20943
21124
|
await this.onEvents({
|
|
20944
|
-
session_uuid: this.sessionUuid,
|
|
21125
|
+
session_uuid: this.sessionManager.sessionUuid,
|
|
21126
|
+
window_id: this.sessionManager.windowId,
|
|
20945
21127
|
chunk_seq,
|
|
20946
21128
|
chunk_start_ts,
|
|
20947
21129
|
chunk_end_ts,
|
|
20948
|
-
initial_url,
|
|
20949
21130
|
events,
|
|
20950
21131
|
});
|
|
20951
21132
|
}
|
|
20952
21133
|
else {
|
|
20953
21134
|
await uploadSessionEvents({
|
|
20954
21135
|
user_id: this.userId,
|
|
20955
|
-
session_uuid: this.sessionUuid,
|
|
21136
|
+
session_uuid: this.sessionManager.sessionUuid,
|
|
21137
|
+
window_id: this.sessionManager.windowId,
|
|
20956
21138
|
chunk_seq,
|
|
20957
21139
|
chunk_start_ts,
|
|
20958
21140
|
chunk_end_ts,
|
|
20959
|
-
initial_url,
|
|
20960
21141
|
events,
|
|
20961
21142
|
});
|
|
20962
21143
|
}
|
|
20963
21144
|
const removedBytes = events.reduce((sum, e) => sum + estimateEventSize(e), 0);
|
|
20964
21145
|
this.sessionEvents = this.sessionEvents.slice(snapshot_count);
|
|
20965
21146
|
this.bufferBytes = Math.max(0, this.bufferBytes - removedBytes);
|
|
20966
|
-
|
|
20967
|
-
if (after &&
|
|
20968
|
-
after.session_uuid === state.session_uuid &&
|
|
20969
|
-
after.chunk_seq === chunk_seq) {
|
|
20970
|
-
writeSessionState({ ...after, chunk_seq: chunk_seq + 1 });
|
|
20971
|
-
}
|
|
21147
|
+
this.sessionManager.commitChunkSeq(chunk_seq);
|
|
20972
21148
|
}
|
|
20973
21149
|
catch (err) {
|
|
20974
21150
|
__classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_log).call(this, "Event tracking failed", err);
|