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