@journium/react 1.0.7 → 1.1.1

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
@@ -448,9 +448,9 @@ const fetchRemoteOptions = async (apiHost, publishableKey, fetchFn) => {
448
448
  'Content-Type': 'application/json',
449
449
  },
450
450
  });
451
- if (!response.ok) {
452
- throw new Error(`Options fetch failed: ${response.status} ${response.statusText}`);
453
- }
451
+ // if (!response.ok) {
452
+ // throw new Error(`Options fetch failed: ${response.status} ${response.statusText}`);
453
+ // }
454
454
  const data = await response.json();
455
455
  return data;
456
456
  }
@@ -699,16 +699,20 @@ Logger.isDebugEnabled = false;
699
699
 
700
700
  class JourniumClient {
701
701
  constructor(config) {
702
- var _a;
702
+ var _a, _b, _c, _d;
703
703
  this.queue = [];
704
+ this.stagedEvents = [];
704
705
  this.flushTimer = null;
705
- this.initialized = false;
706
+ this.initializationComplete = false;
707
+ this.initializationFailed = false;
706
708
  this.optionsChangeCallbacks = new Set();
707
709
  // Validate required configuration
708
- if (!config.publishableKey) {
710
+ if (!config.publishableKey || config.publishableKey.trim() === '') {
711
+ // Reject initialization with clear error
712
+ const errorMsg = 'Journium: publishableKey is required but not provided or is empty. SDK cannot be initialized.';
709
713
  Logger.setDebug(true);
710
- Logger.error('Journium: publishableKey is required but not provided. SDK will not function.');
711
- return;
714
+ Logger.error(errorMsg);
715
+ throw new Error(errorMsg);
712
716
  }
713
717
  // Set default apiHost if not provided
714
718
  this.config = {
@@ -717,25 +721,15 @@ class JourniumClient {
717
721
  };
718
722
  // Generate storage key for options caching
719
723
  this.optionsStorageKey = `jrnm_${config.publishableKey}_options`;
720
- // Generate default values
721
- const defaultOptions = {
722
- debug: false,
723
- flushAt: 20,
724
- flushInterval: 10000,
725
- sessionTimeout: 30 * 60 * 1000, // 30 minutes
726
- };
727
- // Initialize effective options with local options taking precedence over defaults
728
- this.effectiveOptions = { ...defaultOptions };
729
- if (this.config.options) {
730
- this.effectiveOptions = mergeOptions(defaultOptions, this.config.options);
731
- }
732
- // Initialize Logger with debug setting
733
- Logger.setDebug((_a = this.effectiveOptions.debug) !== null && _a !== void 0 ? _a : false);
734
- // Initialize identity manager
735
- this.identityManager = new BrowserIdentityManager(this.effectiveOptions.sessionTimeout, this.config.publishableKey);
736
- // Initialize synchronously with cached config, fetch fresh config in background
737
- this.initializeSync();
738
- this.fetchRemoteOptionsAsync();
724
+ // Initialize with minimal defaults for identity manager
725
+ const fallbackSessionTimeout = 30 * 60 * 1000; // 30 minutes
726
+ this.effectiveOptions = {}; // Will be set after remote config
727
+ // Initialize Logger with local debug setting or false
728
+ Logger.setDebug((_b = (_a = this.config.options) === null || _a === void 0 ? void 0 : _a.debug) !== null && _b !== void 0 ? _b : false);
729
+ // Initialize identity manager with fallback timeout
730
+ 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);
731
+ // Initialize asynchronously - wait for remote config first
732
+ this.initializeAsync();
739
733
  }
740
734
  loadCachedOptions() {
741
735
  if (typeof window === 'undefined' || !window.localStorage) {
@@ -761,70 +755,108 @@ class JourniumClient {
761
755
  Logger.warn('Journium: Failed to save config to cache:', error);
762
756
  }
763
757
  }
764
- initializeSync() {
765
- // Step 1: Load cached remote options from localStorage (synchronous)
766
- const cachedRemoteOptions = this.loadCachedOptions();
767
- // Step 2: Merge cached remote options with local options (if cached options exist)
768
- // Local options take precedence over cached remote options
769
- if (cachedRemoteOptions) {
770
- if (this.config.options) {
771
- // Merge: local options override cached remote options
772
- this.effectiveOptions = mergeOptions(cachedRemoteOptions, this.config.options);
773
- Logger.log('Journium: Using cached remote options merged with local options:', this.effectiveOptions);
758
+ async initializeAsync() {
759
+ var _a;
760
+ try {
761
+ Logger.log('Journium: Starting initialization - fetching fresh remote config...');
762
+ // Step 1: Try to fetch fresh remote config with timeout and retry
763
+ const remoteOptions = await this.fetchRemoteOptionsWithRetry();
764
+ if (remoteOptions) {
765
+ // Step 2: Cache the fresh remote config
766
+ this.saveCachedOptions(remoteOptions);
767
+ // Step 3: Merge local options over remote config (local overrides remote)
768
+ if (this.config.options) {
769
+ this.effectiveOptions = mergeOptions(this.config.options, remoteOptions);
770
+ Logger.log('Journium: Using fresh remote config merged with local options:', this.effectiveOptions);
771
+ }
772
+ else {
773
+ this.effectiveOptions = remoteOptions;
774
+ Logger.log('Journium: Using fresh remote config:', this.effectiveOptions);
775
+ }
774
776
  }
775
777
  else {
776
- // No local options, use cached remote options as-is
777
- this.effectiveOptions = cachedRemoteOptions;
778
- Logger.log('Journium: Using cached remote options:', cachedRemoteOptions);
778
+ // Step 4: Fallback to cached config if fresh fetch failed
779
+ /* const cachedRemoteOptions = this.loadCachedOptions();
780
+
781
+ if (cachedRemoteOptions) {
782
+ if (this.config.options) {
783
+ this.effectiveOptions = mergeOptions(this.config.options, cachedRemoteOptions);
784
+ Logger.log('Journium: Fresh config failed, using cached remote config merged with local options:', this.effectiveOptions);
785
+ } else {
786
+ this.effectiveOptions = cachedRemoteOptions;
787
+ Logger.log('Journium: Fresh config failed, using cached remote config:', this.effectiveOptions);
788
+ }
789
+ } else {
790
+ // Step 5: No remote config and no cached config - initialization fails
791
+ Logger.error('Journium: Initialization failed - no remote config available and no cached config found');
792
+ this.initializationFailed = true;
793
+ this.initializationComplete = false;
794
+ return;
795
+ } */
779
796
  }
797
+ // Step 6: Update identity manager session timeout if provided
798
+ if (this.effectiveOptions.sessionTimeout) {
799
+ this.identityManager.updateSessionTimeout(this.effectiveOptions.sessionTimeout);
800
+ }
801
+ // Step 7: Update Logger debug setting
802
+ Logger.setDebug((_a = this.effectiveOptions.debug) !== null && _a !== void 0 ? _a : false);
803
+ // Step 8: Mark initialization as complete
804
+ this.initializationComplete = true;
805
+ this.initializationFailed = false;
806
+ // Step 9: Process any staged events
807
+ this.processStagedEvents();
808
+ // Step 10: Start flush timer
809
+ if (this.effectiveOptions.flushInterval && this.effectiveOptions.flushInterval > 0) {
810
+ this.startFlushTimer();
811
+ }
812
+ Logger.log('Journium: Initialization complete with options:', this.effectiveOptions);
813
+ // Step 11: Notify callbacks about options
814
+ this.notifyOptionsChange();
780
815
  }
781
- // If no cached options, effectiveOptions already has defaults merged with local options from constructor
782
- // Step 3: Mark as initialized immediately - no need to wait for remote fetch
783
- this.initialized = true;
784
- // Step 4: Start flush timer immediately
785
- if (this.effectiveOptions.flushInterval && this.effectiveOptions.flushInterval > 0) {
786
- this.startFlushTimer();
787
- }
788
- Logger.log('Journium: Client initialized with effective options:', this.effectiveOptions);
789
- }
790
- async fetchRemoteOptionsAsync() {
791
- // Fetch fresh config in background
792
- if (this.config.publishableKey) {
793
- await this.fetchAndCacheRemoteOptions();
816
+ catch (error) {
817
+ Logger.error('Journium: Initialization failed:', error);
818
+ this.initializationFailed = true;
819
+ this.initializationComplete = false;
794
820
  }
795
821
  }
796
- async fetchAndCacheRemoteOptions() {
797
- var _a;
798
- try {
799
- Logger.log('Journium: Fetching remote configuration in background...');
800
- const remoteOptionsResponse = await fetchRemoteOptions(this.config.apiHost, this.config.publishableKey);
801
- if (remoteOptionsResponse && remoteOptionsResponse.success) {
802
- // Save remote config to cache for next session
803
- this.saveCachedOptions(remoteOptionsResponse.config);
804
- // Update effective options: local options (if provided) overrides fresh remote options
805
- if (!this.config.options) {
806
- // No local options provided, use fresh remote options
807
- this.effectiveOptions = remoteOptionsResponse.config;
822
+ async fetchRemoteOptionsWithRetry() {
823
+ const maxRetries = 2;
824
+ const timeoutMs = 15000; // 15 seconds
825
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
826
+ try {
827
+ Logger.log(`Journium: Fetching remote config (attempt ${attempt}/${maxRetries})...`);
828
+ // Create timeout promise
829
+ const timeoutPromise = new Promise((_, reject) => {
830
+ setTimeout(() => reject(new Error('Timeout')), timeoutMs);
831
+ });
832
+ // Race fetch against timeout
833
+ const fetchPromise = fetchRemoteOptions(this.config.apiHost, this.config.publishableKey);
834
+ const remoteOptionsResponse = await Promise.race([fetchPromise, timeoutPromise]);
835
+ if (remoteOptionsResponse && remoteOptionsResponse.status === 'success') {
836
+ Logger.log('Journium: Successfully fetched fresh remote config:', remoteOptionsResponse.config);
837
+ return remoteOptionsResponse.config || null;
808
838
  }
809
- else {
810
- // Local options provided, merge it over fresh remote options
811
- this.effectiveOptions = mergeOptions(remoteOptionsResponse.config, this.config.options);
839
+ else if (remoteOptionsResponse && remoteOptionsResponse.status === 'error' && remoteOptionsResponse.errorCode === 'J_ERR_TENANT_NOT_FOUND') {
840
+ Logger.error('Journium: Invalid publishableKey is being used.');
841
+ return null;
812
842
  }
813
- // Update session timeout if provided in fresh effective options
814
- if (this.effectiveOptions.sessionTimeout) {
815
- this.identityManager.updateSessionTimeout(this.effectiveOptions.sessionTimeout);
843
+ {
844
+ throw new Error('Remote config fetch unsuccessful');
845
+ }
846
+ }
847
+ catch (error) {
848
+ Logger.warn(`Journium: Remote config fetch attempt ${attempt} failed:`, error);
849
+ if (attempt === maxRetries) {
850
+ Logger.warn('Journium: All remote config fetch attempts failed, falling back to cached config');
851
+ return null;
852
+ }
853
+ // Wait 1 second before retry (except on last attempt)
854
+ if (attempt < maxRetries) {
855
+ await new Promise(resolve => setTimeout(resolve, 1000));
816
856
  }
817
- Logger.log('Journium: Background remote options applied:', remoteOptionsResponse.config);
818
- Logger.log('Journium: New effective options:', this.effectiveOptions);
819
- // Update Logger debug setting with new options
820
- Logger.setDebug((_a = this.effectiveOptions.debug) !== null && _a !== void 0 ? _a : false);
821
- // Notify all registered callbacks about the options change
822
- this.notifyOptionsChange();
823
857
  }
824
858
  }
825
- catch (error) {
826
- Logger.warn('Journium: Background remote options fetch failed:', error);
827
- }
859
+ return null;
828
860
  }
829
861
  /**
830
862
  * Register a callback to be notified when effective options change (e.g., when remote options are fetched)
@@ -846,12 +878,48 @@ class JourniumClient {
846
878
  }
847
879
  });
848
880
  }
881
+ processStagedEvents() {
882
+ if (this.stagedEvents.length === 0)
883
+ return;
884
+ Logger.log(`Journium: Processing ${this.stagedEvents.length} staged events`);
885
+ // Move staged events to main queue, adding identity properties now
886
+ const identity = this.identityManager.getIdentity();
887
+ const userAgentInfo = this.identityManager.getUserAgentInfo();
888
+ for (const stagedEvent of this.stagedEvents) {
889
+ // Add identity properties that weren't available during staging
890
+ const eventWithIdentity = {
891
+ ...stagedEvent,
892
+ properties: {
893
+ $device_id: identity === null || identity === void 0 ? void 0 : identity.$device_id,
894
+ distinct_id: identity === null || identity === void 0 ? void 0 : identity.distinct_id,
895
+ $session_id: identity === null || identity === void 0 ? void 0 : identity.$session_id,
896
+ $is_identified: (identity === null || identity === void 0 ? void 0 : identity.$user_state) === 'identified',
897
+ $current_url: typeof window !== 'undefined' ? window.location.href : '',
898
+ $pathname: typeof window !== 'undefined' ? window.location.pathname : '',
899
+ ...userAgentInfo,
900
+ $lib_version: '0.1.0', // TODO: Get from package.json
901
+ $platform: 'web',
902
+ ...stagedEvent.properties, // Original properties override system properties
903
+ },
904
+ };
905
+ this.queue.push(eventWithIdentity);
906
+ }
907
+ // Clear staged events
908
+ this.stagedEvents = [];
909
+ Logger.log('Journium: Staged events processed and moved to main queue');
910
+ // Check if we should flush immediately
911
+ if (this.queue.length >= this.effectiveOptions.flushAt) {
912
+ // console.log('1 Journium: Flushing events...'+JSON.stringify(this.effectiveOptions));
913
+ this.flush();
914
+ }
915
+ }
849
916
  startFlushTimer() {
850
917
  if (this.flushTimer) {
851
918
  clearInterval(this.flushTimer);
852
919
  }
853
920
  // Use universal setInterval (works in both browser and Node.js)
854
921
  this.flushTimer = setInterval(() => {
922
+ // console.log('2 Journium: Flushing events...'+JSON.stringify(this.effectiveOptions));
855
923
  this.flush();
856
924
  }, this.effectiveOptions.flushInterval);
857
925
  }
@@ -880,9 +948,9 @@ class JourniumClient {
880
948
  }
881
949
  }
882
950
  identify(distinctId, attributes = {}) {
883
- // Don't identify if SDK is not properly configured
884
- if (!this.config || !this.config.publishableKey || !this.initialized) {
885
- Logger.warn('Journium: identify() call rejected - SDK not ready');
951
+ // Don't identify if initialization failed
952
+ if (this.initializationFailed) {
953
+ Logger.warn('Journium: identify() call rejected - initialization failed');
886
954
  return;
887
955
  }
888
956
  // Call identify on identity manager to get previous distinct ID
@@ -896,9 +964,9 @@ class JourniumClient {
896
964
  Logger.log('Journium: User identified', { distinctId, attributes, previousDistinctId });
897
965
  }
898
966
  reset() {
899
- // Don't reset if SDK is not properly configured
900
- if (!this.config || !this.config.publishableKey || !this.initialized) {
901
- Logger.warn('Journium: reset() call rejected - SDK not ready');
967
+ // Don't reset if initialization failed
968
+ if (this.initializationFailed) {
969
+ Logger.warn('Journium: reset() call rejected - initialization failed');
902
970
  return;
903
971
  }
904
972
  // Reset identity in identity manager
@@ -906,42 +974,60 @@ class JourniumClient {
906
974
  Logger.log('Journium: User identity reset');
907
975
  }
908
976
  track(event, properties = {}) {
909
- // Don't track if SDK is not properly configured
910
- if (!this.config || !this.config.publishableKey || !this.initialized) {
911
- Logger.warn('Journium: track() call rejected - SDK not ready');
912
- return;
913
- }
914
- const identity = this.identityManager.getIdentity();
915
- const userAgentInfo = this.identityManager.getUserAgentInfo();
916
- // Create standardized event properties
917
- const eventProperties = {
918
- $device_id: identity === null || identity === void 0 ? void 0 : identity.$device_id,
919
- distinct_id: identity === null || identity === void 0 ? void 0 : identity.distinct_id,
920
- $session_id: identity === null || identity === void 0 ? void 0 : identity.$session_id,
921
- $is_identified: (identity === null || identity === void 0 ? void 0 : identity.$user_state) === 'identified',
922
- $current_url: typeof window !== 'undefined' ? window.location.href : '',
923
- $pathname: typeof window !== 'undefined' ? window.location.pathname : '',
924
- ...userAgentInfo,
925
- $lib_version: '0.1.0', // TODO: Get from package.json
926
- $platform: 'web',
927
- ...properties, // User-provided properties override defaults
928
- };
977
+ // Create minimal event without identity properties (will be added later if staging)
929
978
  const journiumEvent = {
930
979
  uuid: generateUuidv7(),
931
980
  ingestion_key: this.config.publishableKey,
932
981
  client_timestamp: getCurrentTimestamp(),
933
982
  event,
934
- properties: eventProperties,
983
+ properties: { ...properties }, // Only user properties for now
935
984
  };
936
- this.queue.push(journiumEvent);
937
- Logger.log('Journium: Event tracked', journiumEvent);
938
- if (this.queue.length >= this.effectiveOptions.flushAt) {
985
+ // Stage events during initialization, add to queue after initialization
986
+ if (!this.initializationComplete) {
987
+ // If initialization failed, reject events
988
+ if (this.initializationFailed) {
989
+ Logger.warn('Journium: track() call rejected - initialization failed');
990
+ return;
991
+ }
992
+ this.stagedEvents.push(journiumEvent);
993
+ Logger.log('Journium: Event staged during initialization', journiumEvent);
994
+ return;
995
+ }
996
+ // If initialization failed, reject events
997
+ if (this.initializationFailed) {
998
+ Logger.warn('Journium: track() call rejected - initialization failed');
999
+ return;
1000
+ }
1001
+ // Add identity properties for immediate events (after initialization)
1002
+ const identity = this.identityManager.getIdentity();
1003
+ const userAgentInfo = this.identityManager.getUserAgentInfo();
1004
+ const eventWithIdentity = {
1005
+ ...journiumEvent,
1006
+ properties: {
1007
+ $device_id: identity === null || identity === void 0 ? void 0 : identity.$device_id,
1008
+ distinct_id: identity === null || identity === void 0 ? void 0 : identity.distinct_id,
1009
+ $session_id: identity === null || identity === void 0 ? void 0 : identity.$session_id,
1010
+ $is_identified: (identity === null || identity === void 0 ? void 0 : identity.$user_state) === 'identified',
1011
+ $current_url: typeof window !== 'undefined' ? window.location.href : '',
1012
+ $pathname: typeof window !== 'undefined' ? window.location.pathname : '',
1013
+ ...userAgentInfo,
1014
+ $lib_version: '0.1.0', // TODO: Get from package.json
1015
+ $platform: 'web',
1016
+ ...properties, // User-provided properties override system properties
1017
+ },
1018
+ };
1019
+ this.queue.push(eventWithIdentity);
1020
+ Logger.log('Journium: Event tracked', eventWithIdentity);
1021
+ // Only flush if we have effective options (after initialization)
1022
+ if (this.effectiveOptions.flushAt && this.queue.length >= this.effectiveOptions.flushAt) {
1023
+ // console.log('3 Journium: Flushing events...'+JSON.stringify(this.effectiveOptions));
939
1024
  this.flush();
940
1025
  }
941
1026
  }
942
1027
  async flush() {
943
- // Don't flush if SDK is not properly configured
944
- if (!this.config || !this.config.publishableKey) {
1028
+ // Don't flush if initialization failed
1029
+ if (this.initializationFailed) {
1030
+ Logger.warn('Journium: flush() call rejected - initialization failed');
945
1031
  return;
946
1032
  }
947
1033
  if (this.queue.length === 0)
@@ -995,7 +1081,7 @@ class PageviewTracker {
995
1081
  * Start automatic autocapture for pageviews
996
1082
  * @returns void
997
1083
  */
998
- startAutocapture() {
1084
+ startAutoPageviewTracking() {
999
1085
  this.capturePageview();
1000
1086
  if (typeof window !== 'undefined') {
1001
1087
  // Store original methods for cleanup
@@ -1424,6 +1510,9 @@ class JourniumAnalytics {
1424
1510
  this.unsubscribeOptionsChange = this.client.onOptionsChange((effectiveOptions) => {
1425
1511
  this.handleOptionsChange(effectiveOptions);
1426
1512
  });
1513
+ // Start automatic autocapture immediately if initial options support it
1514
+ // This handles cached remote options or local options with autocapture enabled
1515
+ this.startAutocaptureIfEnabled(initialEffectiveOptions);
1427
1516
  }
1428
1517
  resolveAutocaptureOptions(autocapture) {
1429
1518
  if (autocapture === false) {
@@ -1454,13 +1543,18 @@ class JourniumAnalytics {
1454
1543
  startAutocapture() {
1455
1544
  // Always check effective options (which may include remote options)
1456
1545
  const effectiveOptions = this.client.getEffectiveOptions();
1457
- const autoTrackPageviews = effectiveOptions.autoTrackPageviews !== false;
1458
- const autocaptureEnabled = effectiveOptions.autocapture !== false;
1546
+ // Only enable if effectiveOptions are loaded and autoTrackPageviews is not explicitly false
1547
+ const autoTrackPageviews = effectiveOptions && Object.keys(effectiveOptions).length > 0
1548
+ ? effectiveOptions.autoTrackPageviews !== false
1549
+ : false;
1550
+ const autocaptureEnabled = effectiveOptions && Object.keys(effectiveOptions).length > 0
1551
+ ? effectiveOptions.autocapture !== false
1552
+ : false;
1459
1553
  // Update autocapture tracker options if they've changed
1460
1554
  const autocaptureOptions = this.resolveAutocaptureOptions(effectiveOptions.autocapture);
1461
1555
  this.autocaptureTracker.updateOptions(autocaptureOptions);
1462
1556
  if (autoTrackPageviews) {
1463
- this.pageviewTracker.startAutocapture();
1557
+ this.pageviewTracker.startAutoPageviewTracking();
1464
1558
  }
1465
1559
  if (autocaptureEnabled) {
1466
1560
  this.autocaptureTracker.start();
@@ -1473,30 +1567,60 @@ class JourniumAnalytics {
1473
1567
  this.autocaptureStarted = false;
1474
1568
  }
1475
1569
  /**
1476
- * Handle effective options change (e.g., when remote options are fetched)
1570
+ * Automatically start autocapture if enabled in options
1571
+ * Handles both initial options and empty options during remote-first initialization
1477
1572
  */
1478
- handleOptionsChange(effectiveOptions) {
1479
- // If autocapture was already started, re-evaluate with new options
1573
+ startAutocaptureIfEnabled(effectiveOptions) {
1574
+ // Skip if autocapture was already started manually
1480
1575
  if (this.autocaptureStarted) {
1481
- // Stop current autocapture
1482
- this.pageviewTracker.stopAutocapture();
1483
- this.autocaptureTracker.stop();
1484
- this.autocaptureStarted = false;
1485
- // Re-evaluate if autocapture should be enabled with new options
1576
+ return;
1577
+ }
1578
+ // During remote-first initialization, effective options might be empty initially
1579
+ // Only auto-start if we have actual options loaded, not empty options
1580
+ const hasActualOptions = effectiveOptions && Object.keys(effectiveOptions).length > 0;
1581
+ if (hasActualOptions) {
1582
+ // Use same logic as manual startAutocapture() but only start automatically
1486
1583
  const autoTrackPageviews = effectiveOptions.autoTrackPageviews !== false;
1487
1584
  const autocaptureEnabled = effectiveOptions.autocapture !== false;
1488
1585
  // Update autocapture tracker options
1489
1586
  const autocaptureOptions = this.resolveAutocaptureOptions(effectiveOptions.autocapture);
1490
1587
  this.autocaptureTracker.updateOptions(autocaptureOptions);
1491
- // Restart only if still enabled
1492
1588
  if (autoTrackPageviews) {
1493
- this.pageviewTracker.startAutocapture();
1589
+ this.pageviewTracker.startAutoPageviewTracking();
1494
1590
  }
1495
1591
  if (autocaptureEnabled) {
1496
1592
  this.autocaptureTracker.start();
1497
1593
  }
1498
- this.autocaptureStarted = autoTrackPageviews || autocaptureEnabled;
1594
+ if (autoTrackPageviews || autocaptureEnabled) {
1595
+ this.autocaptureStarted = true;
1596
+ }
1499
1597
  }
1598
+ // If options are empty (during initialization), wait for options change callback
1599
+ }
1600
+ /**
1601
+ * Handle effective options change (e.g., when remote options are fetched)
1602
+ */
1603
+ handleOptionsChange(effectiveOptions) {
1604
+ // Stop current autocapture if it was already started
1605
+ if (this.autocaptureStarted) {
1606
+ this.pageviewTracker.stopAutocapture();
1607
+ this.autocaptureTracker.stop();
1608
+ this.autocaptureStarted = false;
1609
+ }
1610
+ // Evaluate if autocapture should be enabled with new options
1611
+ const autoTrackPageviews = effectiveOptions.autoTrackPageviews !== false;
1612
+ const autocaptureEnabled = effectiveOptions.autocapture !== false;
1613
+ // Update autocapture tracker options
1614
+ const autocaptureOptions = this.resolveAutocaptureOptions(effectiveOptions.autocapture);
1615
+ this.autocaptureTracker.updateOptions(autocaptureOptions);
1616
+ // Start autocapture based on new options (even if it wasn't started before)
1617
+ if (autoTrackPageviews) {
1618
+ this.pageviewTracker.startAutoPageviewTracking();
1619
+ }
1620
+ if (autocaptureEnabled) {
1621
+ this.autocaptureTracker.start();
1622
+ }
1623
+ this.autocaptureStarted = autoTrackPageviews || autocaptureEnabled;
1500
1624
  }
1501
1625
  async flush() {
1502
1626
  return this.client.flush();
@@ -1523,24 +1647,20 @@ const init = (config) => {
1523
1647
  return new JourniumAnalytics(config);
1524
1648
  };
1525
1649
 
1526
- const JourniumContext = createContext({ analytics: null, config: null, effectiveOptions: null });
1650
+ const JourniumContext = createContext(undefined);
1527
1651
  const JourniumProvider = ({ children, config, }) => {
1528
1652
  const [analytics, setAnalytics] = useState(null);
1529
1653
  const [effectiveOptions, setEffectiveOptions] = useState(null);
1530
1654
  useEffect(() => {
1531
- const analyticsInstance = new JourniumAnalytics(config);
1532
- // Get initial effective options (may include cached remote options)
1655
+ const analyticsInstance = init(config);
1656
+ // Get initial effective options (may be empty during remote-first initialization)
1533
1657
  const initialEffective = analyticsInstance.getEffectiveOptions();
1534
1658
  setEffectiveOptions(initialEffective);
1535
- // Check if autocapture should be enabled based on initial effective options
1536
- const autocaptureEnabled = initialEffective.autocapture !== false;
1537
- if (autocaptureEnabled) {
1538
- analyticsInstance.startAutocapture();
1539
- }
1659
+ // Don't start autocapture immediately with potentially empty options
1660
+ // Let the analytics instance handle autocapture after initialization completes
1540
1661
  setAnalytics(analyticsInstance);
1541
1662
  // Listen for options changes (when remote options are fetched)
1542
- // Note: JourniumAnalytics already handles restarting autocapture when options change
1543
- // We just need to update the effectiveOptions state for consumers
1663
+ // The JourniumAnalytics will automatically start autocapture when initialization completes
1544
1664
  const unsubscribe = analyticsInstance.onOptionsChange((newOptions) => {
1545
1665
  setEffectiveOptions(newOptions);
1546
1666
  });
@@ -1616,5 +1736,5 @@ const useAutocapture = () => {
1616
1736
  return { startAutocapture, stopAutocapture };
1617
1737
  };
1618
1738
 
1619
- export { AutocaptureTracker, BrowserIdentityManager, JourniumAnalytics, JourniumClient, JourniumProvider, Logger, PageviewTracker, fetchRemoteOptions, generateId, generateUuidv7, getCurrentTimestamp, getCurrentUrl, getPageTitle, getReferrer, init, isBrowser, isNode, mergeOptions, useAutoTrackPageview, useAutocapture, useIdentify, useJournium, useReset, useTrackEvent, useTrackPageview };
1739
+ export { AutocaptureTracker, BrowserIdentityManager, JourniumClient, JourniumProvider, Logger, PageviewTracker, fetchRemoteOptions, generateId, generateUuidv7, getCurrentTimestamp, getCurrentUrl, getPageTitle, getReferrer, init, isBrowser, isNode, mergeOptions, useAutoTrackPageview, useAutocapture, useIdentify, useJournium, useReset, useTrackEvent, useTrackPageview };
1620
1740
  //# sourceMappingURL=index.mjs.map