@brainfish-ai/web-tracker 0.0.29 → 0.0.31

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.
@@ -24468,6 +24468,212 @@ class Stasher {
24468
24468
  });
24469
24469
  }
24470
24470
  }
24471
+ const USER_ACTIONS = [
24472
+ IncrementalSource.MouseMove,
24473
+ IncrementalSource.MouseInteraction,
24474
+ IncrementalSource.Scroll,
24475
+ IncrementalSource.ViewportResize,
24476
+ IncrementalSource.Input,
24477
+ IncrementalSource.TouchMove,
24478
+ IncrementalSource.MediaInteraction,
24479
+ IncrementalSource.Drag
24480
+ ];
24481
+ class RecordingManager {
24482
+ constructor(agent) {
24483
+ __publicField(this, "agent");
24484
+ __publicField(this, "recorder");
24485
+ __publicField(this, "stasher");
24486
+ this.agent = agent;
24487
+ this.recorder = new Recorder(this.handleEvent.bind(this));
24488
+ this.stasher = new Stasher(this);
24489
+ }
24490
+ start() {
24491
+ this.recorder.start();
24492
+ }
24493
+ handleEvent(event) {
24494
+ const url2 = new URL(window.location.href);
24495
+ const cleanUrl = url2.origin + url2.pathname;
24496
+ if (this.agent.sessionManager.isIdle) {
24497
+ if (this.isInteractiveEvent(event)) {
24498
+ this.agent.handleActivityDuringIdle();
24499
+ } else {
24500
+ return;
24501
+ }
24502
+ }
24503
+ if (this.isClick(event)) {
24504
+ let clickedNode = record.mirror.getNode(event.data.id);
24505
+ if (clickedNode && this.nodeIsInteresting(clickedNode)) {
24506
+ event["conversionData"] = {};
24507
+ event.conversionData.eventType = "click";
24508
+ event.conversionData.textContent = clickedNode.textContent;
24509
+ event.conversionData.htmlElement = clickedNode.nodeName?.toLowerCase();
24510
+ event.conversionData.location = cleanUrl;
24511
+ }
24512
+ }
24513
+ if (this.isCustom(event)) {
24514
+ event["conversionData"] = {};
24515
+ event.conversionData.eventType = "custom";
24516
+ event.conversionData.customEventType = event.data.tag;
24517
+ event.conversionData.location = cleanUrl;
24518
+ }
24519
+ if (this.stasher.isRunning) {
24520
+ this.stasher.handle(event);
24521
+ return;
24522
+ }
24523
+ this.publishEvent(event);
24524
+ }
24525
+ publishEvent(event) {
24526
+ this.agent.sender.handle(event);
24527
+ if (this.agent.timer) {
24528
+ this.agent.timer.restart();
24529
+ }
24530
+ }
24531
+ startRecorderWithStasher() {
24532
+ this.stasher.start();
24533
+ this.recorder = new Recorder(this.handleEvent.bind(this));
24534
+ this.recorder.start();
24535
+ }
24536
+ isClick(event) {
24537
+ return event.type === EventType.IncrementalSnapshot && // incremental snapshot event
24538
+ event.data.source === IncrementalSource.MouseInteraction && // source of incremental snapshot is a mouse action
24539
+ event.data.type === MouseInteractions.Click;
24540
+ }
24541
+ nodeIsInteresting(clickedNode) {
24542
+ return clickedNode.nodeName === "BUTTON" || clickedNode.nodeName === "ANCHOR";
24543
+ }
24544
+ isCustom(event) {
24545
+ return event.type === EventType.Custom;
24546
+ }
24547
+ isInteractiveEvent(event) {
24548
+ return event.type === EventType.IncrementalSnapshot && USER_ACTIONS.includes(event.data?.source);
24549
+ }
24550
+ }
24551
+ class Timer {
24552
+ constructor(agent, MAX_IDLE_TIME2) {
24553
+ __publicField(this, "agent");
24554
+ __publicField(this, "MAX_IDLE_TIME");
24555
+ __publicField(this, "timeoutId");
24556
+ this.agent = agent;
24557
+ this.MAX_IDLE_TIME = MAX_IDLE_TIME2;
24558
+ this.timeoutId = 0;
24559
+ }
24560
+ restart() {
24561
+ this.stop();
24562
+ this.start();
24563
+ }
24564
+ stop() {
24565
+ clearTimeout(this.timeoutId);
24566
+ }
24567
+ start() {
24568
+ this.timeoutId = window.setTimeout(
24569
+ this.agent.handleTimeout.bind(this.agent),
24570
+ this.MAX_IDLE_TIME
24571
+ );
24572
+ }
24573
+ }
24574
+ const _SessionManager = class _SessionManager {
24575
+ constructor() {
24576
+ __publicField(this, "sessionId", null);
24577
+ __publicField(this, "lastScreenViewEventTime", 0);
24578
+ __publicField(this, "isIdle", false);
24579
+ }
24580
+ static getInstance() {
24581
+ if (!_SessionManager.instance) {
24582
+ _SessionManager.instance = new _SessionManager();
24583
+ }
24584
+ return _SessionManager.instance;
24585
+ }
24586
+ /**
24587
+ * Determines the current session state based on previous and current session IDs
24588
+ */
24589
+ determineSessionState(newSessionId) {
24590
+ if (!this.sessionId && newSessionId) {
24591
+ return "new";
24592
+ }
24593
+ if (this.sessionId && newSessionId && this.sessionId !== newSessionId) {
24594
+ return "changed";
24595
+ }
24596
+ if (this.sessionId === newSessionId && this.sessionId !== null) {
24597
+ return "active";
24598
+ }
24599
+ return "expired";
24600
+ }
24601
+ /**
24602
+ * Handles session state transitions and triggers appropriate actions
24603
+ */
24604
+ handleSessionTransition(state, callbacks) {
24605
+ switch (state) {
24606
+ case "new":
24607
+ case "changed":
24608
+ this.updateIdleStatus(false);
24609
+ callbacks.takeFullSnapshot?.();
24610
+ break;
24611
+ }
24612
+ }
24613
+ /**
24614
+ * Updates the session state based on a new session ID
24615
+ * @param newSessionId The new session ID from the server
24616
+ * @param callbacks Optional callbacks for session state changes
24617
+ */
24618
+ updateSession(newSessionId, callbacks = {}) {
24619
+ const sessionState = this.determineSessionState(newSessionId);
24620
+ this.handleSessionTransition(sessionState, callbacks);
24621
+ this.sessionId = newSessionId;
24622
+ }
24623
+ updateLastScreenViewEventTime() {
24624
+ this.lastScreenViewEventTime = Date.now();
24625
+ }
24626
+ updateIdleStatus(isIdle) {
24627
+ this.isIdle = isIdle;
24628
+ }
24629
+ getLastScreenViewEventTime() {
24630
+ return this.lastScreenViewEventTime;
24631
+ }
24632
+ getCurrentSessionId() {
24633
+ return this.sessionId;
24634
+ }
24635
+ };
24636
+ __publicField(_SessionManager, "instance");
24637
+ let SessionManager = _SessionManager;
24638
+ class UrlMonitor {
24639
+ constructor(callback) {
24640
+ __publicField(this, "currentUrl");
24641
+ __publicField(this, "callback");
24642
+ __publicField(this, "originalPushState");
24643
+ __publicField(this, "originalReplaceState");
24644
+ __publicField(this, "handlePopState", () => {
24645
+ this.checkUrlChange();
24646
+ });
24647
+ this.currentUrl = window.location.href;
24648
+ this.callback = callback;
24649
+ this.originalPushState = history.pushState;
24650
+ this.originalReplaceState = history.replaceState;
24651
+ this.setupListeners();
24652
+ }
24653
+ setupListeners() {
24654
+ window.addEventListener("popstate", this.handlePopState);
24655
+ history.pushState = (...args) => {
24656
+ this.originalPushState.apply(history, args);
24657
+ this.checkUrlChange();
24658
+ };
24659
+ history.replaceState = (...args) => {
24660
+ this.originalReplaceState.apply(history, args);
24661
+ this.checkUrlChange();
24662
+ };
24663
+ }
24664
+ checkUrlChange() {
24665
+ const newUrl = window.location.href;
24666
+ if (newUrl !== this.currentUrl) {
24667
+ this.currentUrl = newUrl;
24668
+ this.callback();
24669
+ }
24670
+ }
24671
+ destroy() {
24672
+ history.pushState = this.originalPushState;
24673
+ history.replaceState = this.originalReplaceState;
24674
+ window.removeEventListener("popstate", this.handlePopState);
24675
+ }
24676
+ }
24471
24677
  function lexer(str) {
24472
24678
  var tokens = [];
24473
24679
  var i = 0;
@@ -24807,181 +25013,9 @@ function canDiscover(recordingBlocklist, allowLocalhostRecording) {
24807
25013
  }
24808
25014
  return true;
24809
25015
  }
24810
- const USER_ACTIONS = [
24811
- IncrementalSource.MouseMove,
24812
- IncrementalSource.MouseInteraction,
24813
- IncrementalSource.Scroll,
24814
- IncrementalSource.ViewportResize,
24815
- IncrementalSource.Input,
24816
- IncrementalSource.TouchMove,
24817
- IncrementalSource.MediaInteraction,
24818
- IncrementalSource.Drag
24819
- ];
24820
- class RecordingManager {
24821
- constructor(agent) {
24822
- __publicField(this, "agent");
24823
- __publicField(this, "recorder");
24824
- __publicField(this, "stasher");
24825
- this.agent = agent;
24826
- this.recorder = new Recorder(this.handleEvent.bind(this));
24827
- this.stasher = new Stasher(this);
24828
- }
24829
- start() {
24830
- this.recorder.start();
24831
- }
24832
- handleEvent(event) {
24833
- const url2 = new URL(window.location.href);
24834
- const cleanUrl = url2.origin + url2.pathname;
24835
- if (!canDiscover(
24836
- this.agent.recordingBlocklist,
24837
- this.agent._allowLocalhostRecording
24838
- )) {
24839
- return;
24840
- }
24841
- if (this.agent.sessionManager.isIdle) {
24842
- if (this.isInteractiveEvent(event)) {
24843
- this.agent.handleActivityDuringIdle();
24844
- } else {
24845
- return;
24846
- }
24847
- }
24848
- if (this.isClick(event)) {
24849
- let clickedNode = record.mirror.getNode(event.data.id);
24850
- if (clickedNode && this.nodeIsInteresting(clickedNode)) {
24851
- event["conversionData"] = {};
24852
- event.conversionData.eventType = "click";
24853
- event.conversionData.textContent = clickedNode.textContent;
24854
- event.conversionData.htmlElement = clickedNode.nodeName?.toLowerCase();
24855
- event.conversionData.location = cleanUrl;
24856
- }
24857
- }
24858
- if (this.isCustom(event)) {
24859
- event["conversionData"] = {};
24860
- event.conversionData.eventType = "custom";
24861
- event.conversionData.customEventType = event.data.tag;
24862
- event.conversionData.location = cleanUrl;
24863
- }
24864
- if (this.stasher.isRunning) {
24865
- this.stasher.handle(event);
24866
- return;
24867
- }
24868
- this.publishEvent(event);
24869
- }
24870
- publishEvent(event) {
24871
- this.agent.sender.handle(event);
24872
- if (this.agent.timer) {
24873
- this.agent.timer.restart();
24874
- }
24875
- }
24876
- startRecorderWithStasher() {
24877
- this.stasher.start();
24878
- this.recorder = new Recorder(this.handleEvent.bind(this));
24879
- this.recorder.start();
24880
- }
24881
- isClick(event) {
24882
- return event.type === EventType.IncrementalSnapshot && // incremental snapshot event
24883
- event.data.source === IncrementalSource.MouseInteraction && // source of incremental snapshot is a mouse action
24884
- event.data.type === MouseInteractions.Click;
24885
- }
24886
- nodeIsInteresting(clickedNode) {
24887
- return clickedNode.nodeName === "BUTTON" || clickedNode.nodeName === "ANCHOR";
24888
- }
24889
- isCustom(event) {
24890
- return event.type === EventType.Custom;
24891
- }
24892
- isInteractiveEvent(event) {
24893
- return event.type === EventType.IncrementalSnapshot && USER_ACTIONS.includes(event.data?.source);
24894
- }
24895
- }
24896
- class Timer {
24897
- constructor(agent, MAX_IDLE_TIME2) {
24898
- __publicField(this, "agent");
24899
- __publicField(this, "MAX_IDLE_TIME");
24900
- __publicField(this, "timeoutId");
24901
- this.agent = agent;
24902
- this.MAX_IDLE_TIME = MAX_IDLE_TIME2;
24903
- this.timeoutId = 0;
24904
- }
24905
- restart() {
24906
- this.stop();
24907
- this.start();
24908
- }
24909
- stop() {
24910
- clearTimeout(this.timeoutId);
24911
- }
24912
- start() {
24913
- this.timeoutId = window.setTimeout(
24914
- this.agent.handleTimeout.bind(this.agent),
24915
- this.MAX_IDLE_TIME
24916
- );
24917
- }
24918
- }
24919
- const _SessionManager = class _SessionManager {
24920
- constructor() {
24921
- __publicField(this, "sessionId", null);
24922
- __publicField(this, "lastScreenViewEventTime", 0);
24923
- __publicField(this, "isIdle", false);
24924
- }
24925
- static getInstance() {
24926
- if (!_SessionManager.instance) {
24927
- _SessionManager.instance = new _SessionManager();
24928
- }
24929
- return _SessionManager.instance;
24930
- }
24931
- /**
24932
- * Determines the current session state based on previous and current session IDs
24933
- */
24934
- determineSessionState(newSessionId) {
24935
- if (!this.sessionId && newSessionId) {
24936
- return "new";
24937
- }
24938
- if (this.sessionId && newSessionId && this.sessionId !== newSessionId) {
24939
- return "changed";
24940
- }
24941
- if (this.sessionId === newSessionId && this.sessionId !== null) {
24942
- return "active";
24943
- }
24944
- return "expired";
24945
- }
24946
- /**
24947
- * Handles session state transitions and triggers appropriate actions
24948
- */
24949
- handleSessionTransition(state, callbacks) {
24950
- switch (state) {
24951
- case "new":
24952
- case "changed":
24953
- this.updateIdleStatus(false);
24954
- callbacks.takeFullSnapshot?.();
24955
- break;
24956
- }
24957
- }
24958
- /**
24959
- * Updates the session state based on a new session ID
24960
- * @param newSessionId The new session ID from the server
24961
- * @param callbacks Optional callbacks for session state changes
24962
- */
24963
- updateSession(newSessionId, callbacks = {}) {
24964
- const sessionState = this.determineSessionState(newSessionId);
24965
- this.handleSessionTransition(sessionState, callbacks);
24966
- this.sessionId = newSessionId;
24967
- }
24968
- updateLastScreenViewEventTime() {
24969
- this.lastScreenViewEventTime = Date.now();
24970
- }
24971
- updateIdleStatus(isIdle) {
24972
- this.isIdle = isIdle;
24973
- }
24974
- getLastScreenViewEventTime() {
24975
- return this.lastScreenViewEventTime;
24976
- }
24977
- getCurrentSessionId() {
24978
- return this.sessionId;
24979
- }
24980
- };
24981
- __publicField(_SessionManager, "instance");
24982
- let SessionManager = _SessionManager;
24983
25016
  const MAX_IDLE_TIME = 60 * 1e3;
24984
25017
  const SCREEN_VIEW_EVENT_COOLDOWN = 5 * 1e3;
25018
+ const RESUME_RECORDING_DEBOUNCE_MS = 3e3;
24985
25019
  const _Agent = class _Agent {
24986
25020
  constructor() {
24987
25021
  __publicField(this, "recordingManager");
@@ -24993,6 +25027,9 @@ const _Agent = class _Agent {
24993
25027
  __publicField(this, "recordingBlocklist", []);
24994
25028
  __publicField(this, "_allowLocalhostRecording");
24995
25029
  __publicField(this, "sessionCreationPromise", null);
25030
+ __publicField(this, "urlMonitor");
25031
+ __publicField(this, "isRecordingStopped", false);
25032
+ __publicField(this, "resumeRecordingTimer");
24996
25033
  this.recordingManager = new RecordingManager(this);
24997
25034
  this.sender = new Sender(this);
24998
25035
  }
@@ -25012,7 +25049,61 @@ const _Agent = class _Agent {
25012
25049
  this.MAX_IDLE_TIME = MAX_IDLE_TIME;
25013
25050
  this.timer = new Timer(this, this.MAX_IDLE_TIME);
25014
25051
  this.sender.start();
25015
- this.recordingManager.start();
25052
+ const shouldRecordInitialPage = canDiscover(
25053
+ this.recordingBlocklist,
25054
+ this._allowLocalhostRecording
25055
+ );
25056
+ if (shouldRecordInitialPage) {
25057
+ this.recordingManager.start();
25058
+ this.isRecordingStopped = false;
25059
+ } else {
25060
+ this.isRecordingStopped = true;
25061
+ }
25062
+ this.urlMonitor = new UrlMonitor(() => this.handleUrlChange());
25063
+ }
25064
+ handleUrlChange() {
25065
+ const shouldRecord = canDiscover(
25066
+ this.recordingBlocklist,
25067
+ this._allowLocalhostRecording
25068
+ );
25069
+ if (!shouldRecord) {
25070
+ if (this.resumeRecordingTimer) {
25071
+ clearTimeout(this.resumeRecordingTimer);
25072
+ this.resumeRecordingTimer = void 0;
25073
+ }
25074
+ if (!this.isRecordingStopped) {
25075
+ this.stopRecording();
25076
+ }
25077
+ } else {
25078
+ if (this.isRecordingStopped) {
25079
+ if (this.resumeRecordingTimer) {
25080
+ clearTimeout(this.resumeRecordingTimer);
25081
+ }
25082
+ this.resumeRecordingTimer = window.setTimeout(() => {
25083
+ this.resumeRecording();
25084
+ this.resumeRecordingTimer = void 0;
25085
+ }, RESUME_RECORDING_DEBOUNCE_MS);
25086
+ }
25087
+ }
25088
+ }
25089
+ stopRecording() {
25090
+ record.addCustomEvent("recording_stopped_blocklist", {
25091
+ url: window.location.href,
25092
+ timestamp: Date.now()
25093
+ });
25094
+ if (this.recordingManager.recorder.stop) {
25095
+ this.recordingManager.recorder.stop();
25096
+ }
25097
+ this.sender.send();
25098
+ this.isRecordingStopped = true;
25099
+ }
25100
+ resumeRecording() {
25101
+ this.recordingManager.recorder.start();
25102
+ record.addCustomEvent("recording_resumed_blocklist", {
25103
+ url: window.location.href,
25104
+ timestamp: Date.now()
25105
+ });
25106
+ this.isRecordingStopped = false;
25016
25107
  }
25017
25108
  handleTimeout() {
25018
25109
  if (this.recordingManager.recorder.stop) {
@@ -25056,6 +25147,22 @@ const _Agent = class _Agent {
25056
25147
  console.error("Failed to send screen_view event:", error);
25057
25148
  }
25058
25149
  }
25150
+ stop() {
25151
+ if (this.resumeRecordingTimer) {
25152
+ clearTimeout(this.resumeRecordingTimer);
25153
+ this.resumeRecordingTimer = void 0;
25154
+ }
25155
+ if (this.urlMonitor) {
25156
+ this.urlMonitor.destroy();
25157
+ this.urlMonitor = void 0;
25158
+ }
25159
+ if (!this.isRecordingStopped && this.recordingManager.recorder.stop) {
25160
+ this.recordingManager.recorder.stop();
25161
+ }
25162
+ if (this.timer) {
25163
+ this.timer.stop();
25164
+ }
25165
+ }
25059
25166
  };
25060
25167
  __publicField(_Agent, "_sessionManager", SessionManager.getInstance());
25061
25168
  let Agent = _Agent;
@@ -25078,7 +25185,7 @@ function toCamelCase(str) {
25078
25185
  ($1) => $1.toUpperCase().replace("-", "").replace("_", "")
25079
25186
  );
25080
25187
  }
25081
- const VERSION = "0.0.29";
25188
+ const VERSION = "0.0.31";
25082
25189
  class Tracker extends TrackerSdk {
25083
25190
  constructor(options) {
25084
25191
  super({
@@ -25277,10 +25384,9 @@ class Tracker extends TrackerSdk {
25277
25384
  */
25278
25385
  disableTracking() {
25279
25386
  super.disableTracking();
25387
+ console.log("Brainfish tracking has been disabled.");
25280
25388
  if (this.isRecordingActive) {
25281
- if (this.agent.recordingManager.recorder.stop) {
25282
- this.agent.recordingManager.recorder.stop();
25283
- }
25389
+ this.agent.stop();
25284
25390
  this.isRecordingActive = false;
25285
25391
  console.log("Brainfish has stopped ambient learning");
25286
25392
  }
Binary file