userlens-session-recorder 2.0.1 → 2.1.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.d.ts CHANGED
@@ -1,13 +1,16 @@
1
- import { MaskingOption, SessionRecorderConfig } from "./types";
2
- export type { SessionRecorderConfig, MaskingOption, };
1
+ import { MaskingOption, RecorderMode, EventBatch, OnEventsCallback, SessionRecorderConfig, AutoModeConfig, ManualModeConfig } from "./types";
2
+ export type { SessionRecorderConfig, AutoModeConfig, ManualModeConfig, RecorderMode, EventBatch, OnEventsCallback, MaskingOption, };
3
3
  export default class SessionRecorder {
4
4
  #private;
5
+ private mode;
5
6
  private userId?;
7
+ private onEvents?;
6
8
  private TIMEOUT;
7
9
  private BUFFER_SIZE;
8
10
  private maskingOptions;
9
11
  private sessionUuid;
10
12
  private sessionEvents;
13
+ private bufferBytes;
11
14
  private rrwebStop;
12
15
  private debug;
13
16
  private recordCrossOriginIframes;
package/dist/index.esm.js CHANGED
@@ -20669,66 +20669,81 @@ const uploadSessionEvents = async (args) => {
20669
20669
  return "ok";
20670
20670
  };
20671
20671
 
20672
- var _SessionRecorder_instances, _SessionRecorder_trackEventsThrottled, _SessionRecorder_uploading, _SessionRecorder_uploadingMaxTs, _SessionRecorder_log, _SessionRecorder_initRecorder, _SessionRecorder_isUserInteraction, _SessionRecorder_handleEvent, _SessionRecorder_resetSession, _SessionRecorder_createSession, _SessionRecorder_handlePageHide, _SessionRecorder_initListeners, _SessionRecorder_throttle, _SessionRecorder_trackEvents, _SessionRecorder_clearEvents;
20672
+ var _SessionRecorder_instances, _SessionRecorder_uploading, _SessionRecorder_uploadingMaxTs, _SessionRecorder_bufferTimer, _SessionRecorder_log, _SessionRecorder_initRecorder, _SessionRecorder_isUserInteraction, _SessionRecorder_handleEvent, _SessionRecorder_armBufferTimer, _SessionRecorder_flushBuffer, _SessionRecorder_resetSession, _SessionRecorder_createSession, _SessionRecorder_handlePageHide, _SessionRecorder_initListeners, _SessionRecorder_trackEvents, _SessionRecorder_clearEvents;
20673
+ const BUFFER_TIMEOUT_MS = 2000;
20674
+ const MAX_BUFFER_BYTES = 900 * 1024;
20675
+ function estimateEventSize(event) {
20676
+ try {
20677
+ return JSON.stringify(event).length;
20678
+ }
20679
+ catch {
20680
+ return 0;
20681
+ }
20682
+ }
20673
20683
  class SessionRecorder {
20674
20684
  constructor(config) {
20675
20685
  var _a, _b, _c;
20676
20686
  _SessionRecorder_instances.add(this);
20677
20687
  this.sessionEvents = [];
20688
+ this.bufferBytes = 0;
20678
20689
  this.rrwebStop = null;
20679
20690
  this.debug = false;
20680
- _SessionRecorder_trackEventsThrottled.set(this, void 0);
20681
20691
  _SessionRecorder_uploading.set(this, false);
20682
20692
  _SessionRecorder_uploadingMaxTs.set(this, 0);
20693
+ _SessionRecorder_bufferTimer.set(this, null);
20683
20694
  _SessionRecorder_handlePageHide.set(this, () => {
20684
20695
  try {
20685
20696
  if (this.sessionEvents.length === 0)
20686
20697
  return;
20687
- let toUpload;
20698
+ let events;
20688
20699
  if (__classPrivateFieldGet(this, _SessionRecorder_uploading, "f") && __classPrivateFieldGet(this, _SessionRecorder_uploadingMaxTs, "f") > 0) {
20689
- toUpload = this.sessionEvents.filter((e) => e.timestamp > __classPrivateFieldGet(this, _SessionRecorder_uploadingMaxTs, "f"));
20700
+ events = this.sessionEvents.filter((e) => e.timestamp > __classPrivateFieldGet(this, _SessionRecorder_uploadingMaxTs, "f"));
20690
20701
  }
20691
20702
  else {
20692
- toUpload = [...this.sessionEvents];
20703
+ events = [...this.sessionEvents];
20693
20704
  }
20694
- if (toUpload.length === 0)
20705
+ if (events.length === 0)
20695
20706
  return;
20696
- let events;
20697
- if (toUpload[0].type !== 4) {
20698
- let snapshotPair = [];
20699
- for (let i = this.sessionEvents.length - 2; i >= 0; i--) {
20700
- if (this.sessionEvents[i].type === 4 &&
20701
- this.sessionEvents[i + 1].type === 2) {
20702
- snapshotPair = [
20703
- this.sessionEvents[i],
20704
- this.sessionEvents[i + 1],
20705
- ];
20706
- break;
20707
- }
20708
- }
20709
- events = [...snapshotPair, ...toUpload];
20710
- }
20711
- else {
20712
- events = toUpload;
20713
- }
20714
20707
  const state = readSessionState();
20715
20708
  if (!state)
20716
20709
  return;
20717
20710
  const chunk_seq = __classPrivateFieldGet(this, _SessionRecorder_uploading, "f") ? state.chunk_seq + 1 : state.chunk_seq;
20718
20711
  writeSessionState({ ...state, chunk_seq: chunk_seq + 1 });
20719
- const start_ts_ms = toUpload[0].timestamp;
20720
- const end_ts_ms = toUpload[toUpload.length - 1].timestamp;
20721
- uploadSessionEvents({
20722
- user_id: this.userId,
20723
- session_uuid: this.sessionUuid,
20724
- chunk_seq,
20725
- chunk_start_ts: new Date(start_ts_ms).toISOString(),
20726
- chunk_end_ts: new Date(end_ts_ms).toISOString(),
20727
- initial_url: chunk_seq === 0 ? state.initial_url : undefined,
20728
- events,
20729
- keepalive: true,
20730
- }).catch(() => { });
20712
+ const start_ts_ms = events[0].timestamp;
20713
+ const end_ts_ms = events[events.length - 1].timestamp;
20714
+ const initial_url = chunk_seq === 0 ? state.initial_url : undefined;
20715
+ const chunk_start_ts = new Date(start_ts_ms).toISOString();
20716
+ const chunk_end_ts = new Date(end_ts_ms).toISOString();
20717
+ if (this.mode === "manual") {
20718
+ try {
20719
+ void this.onEvents({
20720
+ session_uuid: this.sessionUuid,
20721
+ chunk_seq,
20722
+ chunk_start_ts,
20723
+ chunk_end_ts,
20724
+ initial_url,
20725
+ events,
20726
+ keepalive: true,
20727
+ });
20728
+ }
20729
+ catch {
20730
+ // ignore
20731
+ }
20732
+ }
20733
+ else {
20734
+ uploadSessionEvents({
20735
+ user_id: this.userId,
20736
+ session_uuid: this.sessionUuid,
20737
+ chunk_seq,
20738
+ chunk_start_ts,
20739
+ chunk_end_ts,
20740
+ initial_url,
20741
+ events,
20742
+ keepalive: true,
20743
+ }).catch(() => { });
20744
+ }
20731
20745
  this.sessionEvents = [];
20746
+ this.bufferBytes = 0;
20732
20747
  }
20733
20748
  catch (err) {
20734
20749
  __classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_log).call(this, "Page hide handling failed", err);
@@ -20755,21 +20770,29 @@ class SessionRecorder {
20755
20770
  sessionStorage.setItem(testKey, "1");
20756
20771
  sessionStorage.removeItem(testKey);
20757
20772
  this.debug = (_a = config.debug) !== null && _a !== void 0 ? _a : false;
20758
- if (!((_b = config.WRITE_CODE) === null || _b === void 0 ? void 0 : _b.trim()) || !((_c = config.userId) === null || _c === void 0 ? void 0 : _c.trim())) {
20759
- return;
20773
+ if (config.mode === "manual") {
20774
+ this.mode = "manual";
20775
+ if (!config.onEvents || typeof config.onEvents !== "function") {
20776
+ __classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_log).call(this, "onEvents callback is required in manual mode");
20777
+ return;
20778
+ }
20779
+ this.onEvents = config.onEvents;
20780
+ }
20781
+ else {
20782
+ this.mode = "auto";
20783
+ if (!((_b = config.WRITE_CODE) === null || _b === void 0 ? void 0 : _b.trim()) || !((_c = config.userId) === null || _c === void 0 ? void 0 : _c.trim())) {
20784
+ return;
20785
+ }
20786
+ saveWriteCode(config.WRITE_CODE);
20787
+ this.userId = config.userId;
20760
20788
  }
20761
- saveWriteCode(config.WRITE_CODE);
20762
- this.userId = config.userId;
20763
20789
  const { recordingOptions = {} } = config;
20764
- const { TIMEOUT = 30 * 60 * 1000, BUFFER_SIZE = 10, maskingOptions = ["passwords"], recordCrossOriginIframes = false, } = recordingOptions;
20790
+ const { TIMEOUT = 30 * 60 * 1000, BUFFER_SIZE = 30, maskingOptions = ["passwords"], recordCrossOriginIframes = false, } = recordingOptions;
20765
20791
  this.TIMEOUT = TIMEOUT;
20766
20792
  this.BUFFER_SIZE = BUFFER_SIZE;
20767
20793
  this.maskingOptions = maskingOptions;
20768
20794
  this.recordCrossOriginIframes = recordCrossOriginIframes;
20769
20795
  this.sessionEvents = [];
20770
- __classPrivateFieldSet(this, _SessionRecorder_trackEventsThrottled, __classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_throttle).call(this, () => {
20771
- __classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_trackEvents).call(this);
20772
- }, 5000), "f");
20773
20796
  __classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_initRecorder).call(this);
20774
20797
  }
20775
20798
  catch (err) {
@@ -20796,7 +20819,7 @@ class SessionRecorder {
20796
20819
  }
20797
20820
  }
20798
20821
  }
20799
- _SessionRecorder_trackEventsThrottled = new WeakMap(), _SessionRecorder_uploading = new WeakMap(), _SessionRecorder_uploadingMaxTs = new WeakMap(), _SessionRecorder_handlePageHide = new WeakMap(), _SessionRecorder_instances = new WeakSet(), _SessionRecorder_log = function _SessionRecorder_log(message, error) {
20822
+ _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) {
20800
20823
  if (!this.debug)
20801
20824
  return;
20802
20825
  if (error) {
@@ -20819,6 +20842,7 @@ _SessionRecorder_trackEventsThrottled = new WeakMap(), _SessionRecorder_uploadin
20819
20842
  },
20820
20843
  recordCrossOriginIframes: this.recordCrossOriginIframes,
20821
20844
  plugins: [getRecordConsolePlugin()],
20845
+ checkoutEveryNth: 100,
20822
20846
  });
20823
20847
  __classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_initListeners).call(this);
20824
20848
  }, _SessionRecorder_isUserInteraction = function _SessionRecorder_isUserInteraction(event) {
@@ -20829,7 +20853,6 @@ _SessionRecorder_trackEventsThrottled = new WeakMap(), _SessionRecorder_uploadin
20829
20853
  }
20830
20854
  return false;
20831
20855
  }, _SessionRecorder_handleEvent = function _SessionRecorder_handleEvent(event, _isCheckout) {
20832
- var _a;
20833
20856
  try {
20834
20857
  const now = Date.now();
20835
20858
  const state = readSessionState();
@@ -20844,13 +20867,29 @@ _SessionRecorder_trackEventsThrottled = new WeakMap(), _SessionRecorder_uploadin
20844
20867
  }
20845
20868
  }
20846
20869
  this.sessionEvents.push(event);
20847
- if (this.sessionEvents.length >= this.BUFFER_SIZE) {
20848
- (_a = __classPrivateFieldGet(this, _SessionRecorder_trackEventsThrottled, "f")) === null || _a === void 0 ? void 0 : _a.call(this);
20870
+ this.bufferBytes += estimateEventSize(event);
20871
+ __classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_armBufferTimer).call(this);
20872
+ if (this.bufferBytes >= MAX_BUFFER_BYTES) {
20873
+ __classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_flushBuffer).call(this);
20849
20874
  }
20850
20875
  }
20851
20876
  catch (err) {
20852
20877
  __classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_log).call(this, "Event handling failed", err);
20853
20878
  }
20879
+ }, _SessionRecorder_armBufferTimer = function _SessionRecorder_armBufferTimer() {
20880
+ if (__classPrivateFieldGet(this, _SessionRecorder_bufferTimer, "f") !== null) {
20881
+ clearTimeout(__classPrivateFieldGet(this, _SessionRecorder_bufferTimer, "f"));
20882
+ }
20883
+ __classPrivateFieldSet(this, _SessionRecorder_bufferTimer, setTimeout(() => {
20884
+ __classPrivateFieldSet(this, _SessionRecorder_bufferTimer, null, "f");
20885
+ __classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_flushBuffer).call(this);
20886
+ }, BUFFER_TIMEOUT_MS), "f");
20887
+ }, _SessionRecorder_flushBuffer = function _SessionRecorder_flushBuffer() {
20888
+ if (__classPrivateFieldGet(this, _SessionRecorder_bufferTimer, "f") !== null) {
20889
+ clearTimeout(__classPrivateFieldGet(this, _SessionRecorder_bufferTimer, "f"));
20890
+ __classPrivateFieldSet(this, _SessionRecorder_bufferTimer, null, "f");
20891
+ }
20892
+ void __classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_trackEvents).call(this);
20854
20893
  }, _SessionRecorder_resetSession = function _SessionRecorder_resetSession() {
20855
20894
  clearSessionState();
20856
20895
  __classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_clearEvents).call(this);
@@ -20875,15 +20914,6 @@ _SessionRecorder_trackEventsThrottled = new WeakMap(), _SessionRecorder_uploadin
20875
20914
  }
20876
20915
  }, _SessionRecorder_initListeners = function _SessionRecorder_initListeners() {
20877
20916
  window.addEventListener("pagehide", __classPrivateFieldGet(this, _SessionRecorder_handlePageHide, "f"));
20878
- }, _SessionRecorder_throttle = function _SessionRecorder_throttle(func, delay) {
20879
- let lastCall = 0;
20880
- return (...args) => {
20881
- const now = Date.now();
20882
- if (now - lastCall >= delay) {
20883
- lastCall = now;
20884
- func.apply(this, args);
20885
- }
20886
- };
20887
20917
  }, _SessionRecorder_trackEvents = async function _SessionRecorder_trackEvents() {
20888
20918
  if (__classPrivateFieldGet(this, _SessionRecorder_uploading, "f"))
20889
20919
  return;
@@ -20902,26 +20932,33 @@ _SessionRecorder_trackEventsThrottled = new WeakMap(), _SessionRecorder_uploadin
20902
20932
  return;
20903
20933
  }
20904
20934
  const chunk_seq = state.chunk_seq;
20905
- await uploadSessionEvents({
20906
- user_id: this.userId,
20907
- session_uuid: this.sessionUuid,
20908
- chunk_seq,
20909
- chunk_start_ts: new Date(start_ts_ms).toISOString(),
20910
- chunk_end_ts: new Date(end_ts_ms).toISOString(),
20911
- initial_url: chunk_seq === 0 ? state.initial_url : undefined,
20912
- events,
20913
- });
20914
- const remaining = this.sessionEvents.slice(snapshot_count);
20915
- this.sessionEvents = [];
20916
- try {
20917
- takeFullSnapshot(true);
20918
- }
20919
- catch (err) {
20920
- __classPrivateFieldGet(this, _SessionRecorder_instances, "m", _SessionRecorder_log).call(this, "takeFullSnapshot failed", err);
20935
+ const initial_url = chunk_seq === 0 ? state.initial_url : undefined;
20936
+ const chunk_start_ts = new Date(start_ts_ms).toISOString();
20937
+ const chunk_end_ts = new Date(end_ts_ms).toISOString();
20938
+ if (this.mode === "manual") {
20939
+ await this.onEvents({
20940
+ session_uuid: this.sessionUuid,
20941
+ chunk_seq,
20942
+ chunk_start_ts,
20943
+ chunk_end_ts,
20944
+ initial_url,
20945
+ events,
20946
+ });
20921
20947
  }
20922
- if (remaining.length > 0) {
20923
- this.sessionEvents.push(...remaining);
20948
+ else {
20949
+ await uploadSessionEvents({
20950
+ user_id: this.userId,
20951
+ session_uuid: this.sessionUuid,
20952
+ chunk_seq,
20953
+ chunk_start_ts,
20954
+ chunk_end_ts,
20955
+ initial_url,
20956
+ events,
20957
+ });
20924
20958
  }
20959
+ const removedBytes = events.reduce((sum, e) => sum + estimateEventSize(e), 0);
20960
+ this.sessionEvents = this.sessionEvents.slice(snapshot_count);
20961
+ this.bufferBytes = Math.max(0, this.bufferBytes - removedBytes);
20925
20962
  const after = readSessionState();
20926
20963
  if (after &&
20927
20964
  after.session_uuid === state.session_uuid &&
@@ -20938,6 +20975,11 @@ _SessionRecorder_trackEventsThrottled = new WeakMap(), _SessionRecorder_uploadin
20938
20975
  }
20939
20976
  }, _SessionRecorder_clearEvents = function _SessionRecorder_clearEvents() {
20940
20977
  this.sessionEvents = [];
20978
+ this.bufferBytes = 0;
20979
+ if (__classPrivateFieldGet(this, _SessionRecorder_bufferTimer, "f") !== null) {
20980
+ clearTimeout(__classPrivateFieldGet(this, _SessionRecorder_bufferTimer, "f"));
20981
+ __classPrivateFieldSet(this, _SessionRecorder_bufferTimer, null, "f");
20982
+ }
20941
20983
  };
20942
20984
 
20943
20985
  export { SessionRecorder as default };