@journium/js 1.0.7 → 1.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.mjs CHANGED
@@ -697,15 +697,23 @@ Logger.isDebugEnabled = false;
697
697
 
698
698
  class JourniumClient {
699
699
  constructor(config) {
700
- var _a;
700
+ var _a, _b, _c, _d;
701
701
  this.queue = [];
702
+ this.stagedEvents = [];
702
703
  this.flushTimer = null;
703
- this.initialized = false;
704
+ this.initializationComplete = false;
705
+ this.initializationFailed = false;
706
+ this.disabled = false;
704
707
  this.optionsChangeCallbacks = new Set();
705
- // Validate required configuration
706
- if (!config.publishableKey) {
708
+ // Validate required configuration - put in disabled state if invalid
709
+ if (!config.publishableKey || config.publishableKey.trim() === '') {
710
+ this.disabled = true;
707
711
  Logger.setDebug(true);
708
- Logger.error('Journium: publishableKey is required but not provided. SDK will not function.');
712
+ Logger.error('Journium: publishableKey is required but not provided or is empty. SDK will not function.');
713
+ // Create minimal config to prevent crashes
714
+ this.config = { publishableKey: '', apiHost: 'https://events.journium.app' };
715
+ this.effectiveOptions = { debug: true };
716
+ this.optionsStorageKey = 'jrnm_invalid_options';
709
717
  return;
710
718
  }
711
719
  // Set default apiHost if not provided
@@ -715,25 +723,15 @@ class JourniumClient {
715
723
  };
716
724
  // Generate storage key for options caching
717
725
  this.optionsStorageKey = `jrnm_${config.publishableKey}_options`;
718
- // Generate default values
719
- const defaultOptions = {
720
- debug: false,
721
- flushAt: 20,
722
- flushInterval: 10000,
723
- sessionTimeout: 30 * 60 * 1000, // 30 minutes
724
- };
725
- // Initialize effective options with local options taking precedence over defaults
726
- this.effectiveOptions = { ...defaultOptions };
727
- if (this.config.options) {
728
- this.effectiveOptions = mergeOptions(defaultOptions, this.config.options);
729
- }
730
- // Initialize Logger with debug setting
731
- Logger.setDebug((_a = this.effectiveOptions.debug) !== null && _a !== void 0 ? _a : false);
732
- // Initialize identity manager
733
- this.identityManager = new BrowserIdentityManager(this.effectiveOptions.sessionTimeout, this.config.publishableKey);
734
- // Initialize synchronously with cached config, fetch fresh config in background
735
- this.initializeSync();
736
- this.fetchRemoteOptionsAsync();
726
+ // Initialize with minimal defaults for identity manager
727
+ const fallbackSessionTimeout = 30 * 60 * 1000; // 30 minutes
728
+ this.effectiveOptions = {}; // Will be set after remote config
729
+ // Initialize Logger with local debug setting or false
730
+ Logger.setDebug((_b = (_a = this.config.options) === null || _a === void 0 ? void 0 : _a.debug) !== null && _b !== void 0 ? _b : false);
731
+ // Initialize identity manager with fallback timeout
732
+ this.identityManager = new BrowserIdentityManager((_d = (_c = this.config.options) === null || _c === void 0 ? void 0 : _c.sessionTimeout) !== null && _d !== void 0 ? _d : fallbackSessionTimeout, this.config.publishableKey);
733
+ // Initialize asynchronously - wait for remote config first
734
+ this.initializeAsync();
737
735
  }
738
736
  loadCachedOptions() {
739
737
  if (typeof window === 'undefined' || !window.localStorage) {
@@ -759,70 +757,105 @@ class JourniumClient {
759
757
  Logger.warn('Journium: Failed to save config to cache:', error);
760
758
  }
761
759
  }
762
- initializeSync() {
763
- // Step 1: Load cached remote options from localStorage (synchronous)
764
- const cachedRemoteOptions = this.loadCachedOptions();
765
- // Step 2: Merge cached remote options with local options (if cached options exist)
766
- // Local options take precedence over cached remote options
767
- if (cachedRemoteOptions) {
768
- if (this.config.options) {
769
- // Merge: local options override cached remote options
770
- this.effectiveOptions = mergeOptions(cachedRemoteOptions, this.config.options);
771
- Logger.log('Journium: Using cached remote options merged with local options:', this.effectiveOptions);
760
+ async initializeAsync() {
761
+ var _a;
762
+ try {
763
+ Logger.log('Journium: Starting initialization - fetching fresh remote config...');
764
+ // Step 1: Try to fetch fresh remote config with timeout and retry
765
+ const remoteOptions = await this.fetchRemoteOptionsWithRetry();
766
+ if (remoteOptions) {
767
+ // Step 2: Cache the fresh remote config
768
+ this.saveCachedOptions(remoteOptions);
769
+ // Step 3: Merge local options over remote config (local overrides remote)
770
+ if (this.config.options) {
771
+ this.effectiveOptions = mergeOptions(this.config.options, remoteOptions);
772
+ Logger.log('Journium: Using fresh remote config merged with local options:', this.effectiveOptions);
773
+ }
774
+ else {
775
+ this.effectiveOptions = remoteOptions;
776
+ Logger.log('Journium: Using fresh remote config:', this.effectiveOptions);
777
+ }
772
778
  }
773
779
  else {
774
- // No local options, use cached remote options as-is
775
- this.effectiveOptions = cachedRemoteOptions;
776
- Logger.log('Journium: Using cached remote options:', cachedRemoteOptions);
780
+ // Step 4: Fallback to cached config if fresh fetch failed
781
+ const cachedRemoteOptions = this.loadCachedOptions();
782
+ if (cachedRemoteOptions) {
783
+ if (this.config.options) {
784
+ this.effectiveOptions = mergeOptions(this.config.options, cachedRemoteOptions);
785
+ Logger.log('Journium: Fresh config failed, using cached remote config merged with local options:', this.effectiveOptions);
786
+ }
787
+ else {
788
+ this.effectiveOptions = cachedRemoteOptions;
789
+ Logger.log('Journium: Fresh config failed, using cached remote config:', this.effectiveOptions);
790
+ }
791
+ }
792
+ else {
793
+ // Step 5: No remote config and no cached config - initialization fails
794
+ Logger.error('Journium: Initialization failed - no remote config available and no cached config found');
795
+ this.initializationFailed = true;
796
+ this.initializationComplete = false;
797
+ return;
798
+ }
777
799
  }
800
+ // Step 6: Update identity manager session timeout if provided
801
+ if (this.effectiveOptions.sessionTimeout) {
802
+ this.identityManager.updateSessionTimeout(this.effectiveOptions.sessionTimeout);
803
+ }
804
+ // Step 7: Update Logger debug setting
805
+ Logger.setDebug((_a = this.effectiveOptions.debug) !== null && _a !== void 0 ? _a : false);
806
+ // Step 8: Mark initialization as complete
807
+ this.initializationComplete = true;
808
+ this.initializationFailed = false;
809
+ // Step 9: Process any staged events
810
+ this.processStagedEvents();
811
+ // Step 10: Start flush timer
812
+ if (this.effectiveOptions.flushInterval && this.effectiveOptions.flushInterval > 0) {
813
+ this.startFlushTimer();
814
+ }
815
+ Logger.log('Journium: Initialization complete with options:', this.effectiveOptions);
816
+ // Step 11: Notify callbacks about options
817
+ this.notifyOptionsChange();
778
818
  }
779
- // If no cached options, effectiveOptions already has defaults merged with local options from constructor
780
- // Step 3: Mark as initialized immediately - no need to wait for remote fetch
781
- this.initialized = true;
782
- // Step 4: Start flush timer immediately
783
- if (this.effectiveOptions.flushInterval && this.effectiveOptions.flushInterval > 0) {
784
- this.startFlushTimer();
785
- }
786
- Logger.log('Journium: Client initialized with effective options:', this.effectiveOptions);
787
- }
788
- async fetchRemoteOptionsAsync() {
789
- // Fetch fresh config in background
790
- if (this.config.publishableKey) {
791
- await this.fetchAndCacheRemoteOptions();
819
+ catch (error) {
820
+ Logger.error('Journium: Initialization failed:', error);
821
+ this.initializationFailed = true;
822
+ this.initializationComplete = false;
792
823
  }
793
824
  }
794
- async fetchAndCacheRemoteOptions() {
795
- var _a;
796
- try {
797
- Logger.log('Journium: Fetching remote configuration in background...');
798
- const remoteOptionsResponse = await fetchRemoteOptions(this.config.apiHost, this.config.publishableKey);
799
- if (remoteOptionsResponse && remoteOptionsResponse.success) {
800
- // Save remote config to cache for next session
801
- this.saveCachedOptions(remoteOptionsResponse.config);
802
- // Update effective options: local options (if provided) overrides fresh remote options
803
- if (!this.config.options) {
804
- // No local options provided, use fresh remote options
805
- this.effectiveOptions = remoteOptionsResponse.config;
825
+ async fetchRemoteOptionsWithRetry() {
826
+ const maxRetries = 2;
827
+ const timeoutMs = 15000; // 15 seconds
828
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
829
+ try {
830
+ Logger.log(`Journium: Fetching remote config (attempt ${attempt}/${maxRetries})...`);
831
+ // Create timeout promise
832
+ const timeoutPromise = new Promise((_, reject) => {
833
+ setTimeout(() => reject(new Error('Timeout')), timeoutMs);
834
+ });
835
+ // Race fetch against timeout
836
+ const fetchPromise = fetchRemoteOptions(this.config.apiHost, this.config.publishableKey);
837
+ const remoteOptionsResponse = await Promise.race([fetchPromise, timeoutPromise]);
838
+ if (remoteOptionsResponse && remoteOptionsResponse.success) {
839
+ Logger.log('Journium: Successfully fetched fresh remote config:', remoteOptionsResponse.config);
840
+ return remoteOptionsResponse.config;
806
841
  }
807
842
  else {
808
- // Local options provided, merge it over fresh remote options
809
- this.effectiveOptions = mergeOptions(remoteOptionsResponse.config, this.config.options);
843
+ throw new Error('Remote config fetch unsuccessful');
844
+ }
845
+ }
846
+ catch (error) {
847
+ Logger.warn(`Journium: Remote config fetch attempt ${attempt} failed:`, error);
848
+ if (attempt === maxRetries) {
849
+ Logger.warn('Journium: All remote config fetch attempts failed, falling back to cached config');
850
+ return null;
810
851
  }
811
- // Update session timeout if provided in fresh effective options
812
- if (this.effectiveOptions.sessionTimeout) {
813
- this.identityManager.updateSessionTimeout(this.effectiveOptions.sessionTimeout);
852
+ // Wait 1 second before retry (except on last attempt)
853
+ if (attempt < maxRetries) {
854
+ await new Promise(resolve => setTimeout(resolve, 1000));
814
855
  }
815
- Logger.log('Journium: Background remote options applied:', remoteOptionsResponse.config);
816
- Logger.log('Journium: New effective options:', this.effectiveOptions);
817
- // Update Logger debug setting with new options
818
- Logger.setDebug((_a = this.effectiveOptions.debug) !== null && _a !== void 0 ? _a : false);
819
- // Notify all registered callbacks about the options change
820
- this.notifyOptionsChange();
821
856
  }
822
857
  }
823
- catch (error) {
824
- Logger.warn('Journium: Background remote options fetch failed:', error);
825
- }
858
+ return null;
826
859
  }
827
860
  /**
828
861
  * Register a callback to be notified when effective options change (e.g., when remote options are fetched)
@@ -844,12 +877,48 @@ class JourniumClient {
844
877
  }
845
878
  });
846
879
  }
880
+ processStagedEvents() {
881
+ if (this.stagedEvents.length === 0)
882
+ return;
883
+ Logger.log(`Journium: Processing ${this.stagedEvents.length} staged events`);
884
+ // Move staged events to main queue, adding identity properties now
885
+ const identity = this.identityManager.getIdentity();
886
+ const userAgentInfo = this.identityManager.getUserAgentInfo();
887
+ for (const stagedEvent of this.stagedEvents) {
888
+ // Add identity properties that weren't available during staging
889
+ const eventWithIdentity = {
890
+ ...stagedEvent,
891
+ properties: {
892
+ $device_id: identity === null || identity === void 0 ? void 0 : identity.$device_id,
893
+ distinct_id: identity === null || identity === void 0 ? void 0 : identity.distinct_id,
894
+ $session_id: identity === null || identity === void 0 ? void 0 : identity.$session_id,
895
+ $is_identified: (identity === null || identity === void 0 ? void 0 : identity.$user_state) === 'identified',
896
+ $current_url: typeof window !== 'undefined' ? window.location.href : '',
897
+ $pathname: typeof window !== 'undefined' ? window.location.pathname : '',
898
+ ...userAgentInfo,
899
+ $lib_version: '0.1.0', // TODO: Get from package.json
900
+ $platform: 'web',
901
+ ...stagedEvent.properties, // Original properties override system properties
902
+ },
903
+ };
904
+ this.queue.push(eventWithIdentity);
905
+ }
906
+ // Clear staged events
907
+ this.stagedEvents = [];
908
+ Logger.log('Journium: Staged events processed and moved to main queue');
909
+ // Check if we should flush immediately
910
+ if (this.queue.length >= this.effectiveOptions.flushAt) {
911
+ // console.log('1 Journium: Flushing events...'+JSON.stringify(this.effectiveOptions));
912
+ this.flush();
913
+ }
914
+ }
847
915
  startFlushTimer() {
848
916
  if (this.flushTimer) {
849
917
  clearInterval(this.flushTimer);
850
918
  }
851
919
  // Use universal setInterval (works in both browser and Node.js)
852
920
  this.flushTimer = setInterval(() => {
921
+ // console.log('2 Journium: Flushing events...'+JSON.stringify(this.effectiveOptions));
853
922
  this.flush();
854
923
  }, this.effectiveOptions.flushInterval);
855
924
  }
@@ -878,9 +947,14 @@ class JourniumClient {
878
947
  }
879
948
  }
880
949
  identify(distinctId, attributes = {}) {
881
- // Don't identify if SDK is not properly configured
882
- if (!this.config || !this.config.publishableKey || !this.initialized) {
883
- Logger.warn('Journium: identify() call rejected - SDK not ready');
950
+ // Don't identify if SDK is not properly configured or disabled
951
+ if (this.disabled || !this.config || !this.config.publishableKey) {
952
+ Logger.warn('Journium: identify() call rejected - SDK not ready or disabled');
953
+ return;
954
+ }
955
+ // Don't identify if initialization failed
956
+ if (this.initializationFailed) {
957
+ Logger.warn('Journium: identify() call rejected - initialization failed');
884
958
  return;
885
959
  }
886
960
  // Call identify on identity manager to get previous distinct ID
@@ -894,9 +968,14 @@ class JourniumClient {
894
968
  Logger.log('Journium: User identified', { distinctId, attributes, previousDistinctId });
895
969
  }
896
970
  reset() {
897
- // Don't reset if SDK is not properly configured
898
- if (!this.config || !this.config.publishableKey || !this.initialized) {
899
- Logger.warn('Journium: reset() call rejected - SDK not ready');
971
+ // Don't reset if SDK is not properly configured or disabled
972
+ if (this.disabled || !this.config || !this.config.publishableKey) {
973
+ Logger.warn('Journium: reset() call rejected - SDK not ready or disabled');
974
+ return;
975
+ }
976
+ // Don't reset if initialization failed
977
+ if (this.initializationFailed) {
978
+ Logger.warn('Journium: reset() call rejected - initialization failed');
900
979
  return;
901
980
  }
902
981
  // Reset identity in identity manager
@@ -904,36 +983,58 @@ class JourniumClient {
904
983
  Logger.log('Journium: User identity reset');
905
984
  }
906
985
  track(event, properties = {}) {
907
- // Don't track if SDK is not properly configured
908
- if (!this.config || !this.config.publishableKey || !this.initialized) {
909
- Logger.warn('Journium: track() call rejected - SDK not ready');
986
+ // Don't track if SDK is not properly configured or disabled
987
+ if (this.disabled || !this.config || !this.config.publishableKey) {
988
+ Logger.warn('Journium: track() call rejected - SDK not ready or disabled');
910
989
  return;
911
990
  }
912
- const identity = this.identityManager.getIdentity();
913
- const userAgentInfo = this.identityManager.getUserAgentInfo();
914
- // Create standardized event properties
915
- const eventProperties = {
916
- $device_id: identity === null || identity === void 0 ? void 0 : identity.$device_id,
917
- distinct_id: identity === null || identity === void 0 ? void 0 : identity.distinct_id,
918
- $session_id: identity === null || identity === void 0 ? void 0 : identity.$session_id,
919
- $is_identified: (identity === null || identity === void 0 ? void 0 : identity.$user_state) === 'identified',
920
- $current_url: typeof window !== 'undefined' ? window.location.href : '',
921
- $pathname: typeof window !== 'undefined' ? window.location.pathname : '',
922
- ...userAgentInfo,
923
- $lib_version: '0.1.0', // TODO: Get from package.json
924
- $platform: 'web',
925
- ...properties, // User-provided properties override defaults
926
- };
991
+ // Create minimal event without identity properties (will be added later if staging)
927
992
  const journiumEvent = {
928
993
  uuid: generateUuidv7(),
929
994
  ingestion_key: this.config.publishableKey,
930
995
  client_timestamp: getCurrentTimestamp(),
931
996
  event,
932
- properties: eventProperties,
997
+ properties: { ...properties }, // Only user properties for now
933
998
  };
934
- this.queue.push(journiumEvent);
935
- Logger.log('Journium: Event tracked', journiumEvent);
936
- if (this.queue.length >= this.effectiveOptions.flushAt) {
999
+ // Stage events during initialization, add to queue after initialization
1000
+ if (!this.initializationComplete) {
1001
+ // If initialization failed, reject events
1002
+ if (this.initializationFailed) {
1003
+ Logger.warn('Journium: track() call rejected - initialization failed');
1004
+ return;
1005
+ }
1006
+ this.stagedEvents.push(journiumEvent);
1007
+ Logger.log('Journium: Event staged during initialization', journiumEvent);
1008
+ return;
1009
+ }
1010
+ // If initialization failed, reject events
1011
+ if (this.initializationFailed) {
1012
+ Logger.warn('Journium: track() call rejected - initialization failed');
1013
+ return;
1014
+ }
1015
+ // Add identity properties for immediate events (after initialization)
1016
+ const identity = this.identityManager.getIdentity();
1017
+ const userAgentInfo = this.identityManager.getUserAgentInfo();
1018
+ const eventWithIdentity = {
1019
+ ...journiumEvent,
1020
+ properties: {
1021
+ $device_id: identity === null || identity === void 0 ? void 0 : identity.$device_id,
1022
+ distinct_id: identity === null || identity === void 0 ? void 0 : identity.distinct_id,
1023
+ $session_id: identity === null || identity === void 0 ? void 0 : identity.$session_id,
1024
+ $is_identified: (identity === null || identity === void 0 ? void 0 : identity.$user_state) === 'identified',
1025
+ $current_url: typeof window !== 'undefined' ? window.location.href : '',
1026
+ $pathname: typeof window !== 'undefined' ? window.location.pathname : '',
1027
+ ...userAgentInfo,
1028
+ $lib_version: '0.1.0', // TODO: Get from package.json
1029
+ $platform: 'web',
1030
+ ...properties, // User-provided properties override system properties
1031
+ },
1032
+ };
1033
+ this.queue.push(eventWithIdentity);
1034
+ Logger.log('Journium: Event tracked', eventWithIdentity);
1035
+ // Only flush if we have effective options (after initialization)
1036
+ if (this.effectiveOptions.flushAt && this.queue.length >= this.effectiveOptions.flushAt) {
1037
+ // console.log('3 Journium: Flushing events...'+JSON.stringify(this.effectiveOptions));
937
1038
  this.flush();
938
1039
  }
939
1040
  }
@@ -942,6 +1043,11 @@ class JourniumClient {
942
1043
  if (!this.config || !this.config.publishableKey) {
943
1044
  return;
944
1045
  }
1046
+ // Don't flush if initialization failed
1047
+ if (this.initializationFailed) {
1048
+ Logger.warn('Journium: flush() call rejected - initialization failed');
1049
+ return;
1050
+ }
945
1051
  if (this.queue.length === 0)
946
1052
  return;
947
1053
  const events = [...this.queue];
@@ -993,7 +1099,7 @@ class PageviewTracker {
993
1099
  * Start automatic autocapture for pageviews
994
1100
  * @returns void
995
1101
  */
996
- startAutocapture() {
1102
+ startAutoPageviewTracking() {
997
1103
  this.capturePageview();
998
1104
  if (typeof window !== 'undefined') {
999
1105
  // Store original methods for cleanup
@@ -1422,6 +1528,9 @@ class JourniumAnalytics {
1422
1528
  this.unsubscribeOptionsChange = this.client.onOptionsChange((effectiveOptions) => {
1423
1529
  this.handleOptionsChange(effectiveOptions);
1424
1530
  });
1531
+ // Start automatic autocapture immediately if initial options support it
1532
+ // This handles cached remote options or local options with autocapture enabled
1533
+ this.startAutocaptureIfEnabled(initialEffectiveOptions);
1425
1534
  }
1426
1535
  resolveAutocaptureOptions(autocapture) {
1427
1536
  if (autocapture === false) {
@@ -1452,13 +1561,18 @@ class JourniumAnalytics {
1452
1561
  startAutocapture() {
1453
1562
  // Always check effective options (which may include remote options)
1454
1563
  const effectiveOptions = this.client.getEffectiveOptions();
1455
- const autoTrackPageviews = effectiveOptions.autoTrackPageviews !== false;
1456
- const autocaptureEnabled = effectiveOptions.autocapture !== false;
1564
+ // Only enable if effectiveOptions are loaded and autoTrackPageviews is not explicitly false
1565
+ const autoTrackPageviews = effectiveOptions && Object.keys(effectiveOptions).length > 0
1566
+ ? effectiveOptions.autoTrackPageviews !== false
1567
+ : false;
1568
+ const autocaptureEnabled = effectiveOptions && Object.keys(effectiveOptions).length > 0
1569
+ ? effectiveOptions.autocapture !== false
1570
+ : false;
1457
1571
  // Update autocapture tracker options if they've changed
1458
1572
  const autocaptureOptions = this.resolveAutocaptureOptions(effectiveOptions.autocapture);
1459
1573
  this.autocaptureTracker.updateOptions(autocaptureOptions);
1460
1574
  if (autoTrackPageviews) {
1461
- this.pageviewTracker.startAutocapture();
1575
+ this.pageviewTracker.startAutoPageviewTracking();
1462
1576
  }
1463
1577
  if (autocaptureEnabled) {
1464
1578
  this.autocaptureTracker.start();
@@ -1471,30 +1585,60 @@ class JourniumAnalytics {
1471
1585
  this.autocaptureStarted = false;
1472
1586
  }
1473
1587
  /**
1474
- * Handle effective options change (e.g., when remote options are fetched)
1588
+ * Automatically start autocapture if enabled in options
1589
+ * Handles both initial options and empty options during remote-first initialization
1475
1590
  */
1476
- handleOptionsChange(effectiveOptions) {
1477
- // If autocapture was already started, re-evaluate with new options
1591
+ startAutocaptureIfEnabled(effectiveOptions) {
1592
+ // Skip if autocapture was already started manually
1478
1593
  if (this.autocaptureStarted) {
1479
- // Stop current autocapture
1480
- this.pageviewTracker.stopAutocapture();
1481
- this.autocaptureTracker.stop();
1482
- this.autocaptureStarted = false;
1483
- // Re-evaluate if autocapture should be enabled with new options
1594
+ return;
1595
+ }
1596
+ // During remote-first initialization, effective options might be empty initially
1597
+ // Only auto-start if we have actual options loaded, not empty options
1598
+ const hasActualOptions = effectiveOptions && Object.keys(effectiveOptions).length > 0;
1599
+ if (hasActualOptions) {
1600
+ // Use same logic as manual startAutocapture() but only start automatically
1484
1601
  const autoTrackPageviews = effectiveOptions.autoTrackPageviews !== false;
1485
1602
  const autocaptureEnabled = effectiveOptions.autocapture !== false;
1486
1603
  // Update autocapture tracker options
1487
1604
  const autocaptureOptions = this.resolveAutocaptureOptions(effectiveOptions.autocapture);
1488
1605
  this.autocaptureTracker.updateOptions(autocaptureOptions);
1489
- // Restart only if still enabled
1490
1606
  if (autoTrackPageviews) {
1491
- this.pageviewTracker.startAutocapture();
1607
+ this.pageviewTracker.startAutoPageviewTracking();
1492
1608
  }
1493
1609
  if (autocaptureEnabled) {
1494
1610
  this.autocaptureTracker.start();
1495
1611
  }
1496
- this.autocaptureStarted = autoTrackPageviews || autocaptureEnabled;
1612
+ if (autoTrackPageviews || autocaptureEnabled) {
1613
+ this.autocaptureStarted = true;
1614
+ }
1615
+ }
1616
+ // If options are empty (during initialization), wait for options change callback
1617
+ }
1618
+ /**
1619
+ * Handle effective options change (e.g., when remote options are fetched)
1620
+ */
1621
+ handleOptionsChange(effectiveOptions) {
1622
+ // Stop current autocapture if it was already started
1623
+ if (this.autocaptureStarted) {
1624
+ this.pageviewTracker.stopAutocapture();
1625
+ this.autocaptureTracker.stop();
1626
+ this.autocaptureStarted = false;
1627
+ }
1628
+ // Evaluate if autocapture should be enabled with new options
1629
+ const autoTrackPageviews = effectiveOptions.autoTrackPageviews !== false;
1630
+ const autocaptureEnabled = effectiveOptions.autocapture !== false;
1631
+ // Update autocapture tracker options
1632
+ const autocaptureOptions = this.resolveAutocaptureOptions(effectiveOptions.autocapture);
1633
+ this.autocaptureTracker.updateOptions(autocaptureOptions);
1634
+ // Start autocapture based on new options (even if it wasn't started before)
1635
+ if (autoTrackPageviews) {
1636
+ this.pageviewTracker.startAutoPageviewTracking();
1637
+ }
1638
+ if (autocaptureEnabled) {
1639
+ this.autocaptureTracker.start();
1497
1640
  }
1641
+ this.autocaptureStarted = autoTrackPageviews || autocaptureEnabled;
1498
1642
  }
1499
1643
  async flush() {
1500
1644
  return this.client.flush();