@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/context.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import React, { ReactNode } from 'react';
2
- import { JourniumAnalytics } from '@journium/js';
2
+ import { init } from '@journium/js';
3
3
  import { JourniumConfig, JourniumLocalOptions } from '@journium/core';
4
+ type JourniumAnalyticsInstance = ReturnType<typeof init>;
4
5
  interface JourniumContextValue {
5
- analytics: JourniumAnalytics | null;
6
+ analytics: JourniumAnalyticsInstance | null;
6
7
  config: JourniumConfig | null;
7
8
  effectiveOptions: JourniumLocalOptions | null;
8
9
  }
@@ -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,IAAI,EAAE,MAAM,cAAc,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtE,KAAK,yBAAyB,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC;AAEzD,UAAU,oBAAoB;IAC5B,SAAS,EAAE,yBAAyB,GAAG,IAAI,CAAC;IAC5C,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
@@ -450,9 +450,9 @@ const fetchRemoteOptions = async (apiHost, publishableKey, fetchFn) => {
450
450
  'Content-Type': 'application/json',
451
451
  },
452
452
  });
453
- if (!response.ok) {
454
- throw new Error(`Options fetch failed: ${response.status} ${response.statusText}`);
455
- }
453
+ // if (!response.ok) {
454
+ // throw new Error(`Options fetch failed: ${response.status} ${response.statusText}`);
455
+ // }
456
456
  const data = await response.json();
457
457
  return data;
458
458
  }
@@ -701,16 +701,20 @@ 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;
708
710
  this.optionsChangeCallbacks = new Set();
709
711
  // Validate required configuration
710
- if (!config.publishableKey) {
712
+ if (!config.publishableKey || config.publishableKey.trim() === '') {
713
+ // Reject initialization with clear error
714
+ const errorMsg = 'Journium: publishableKey is required but not provided or is empty. SDK cannot be initialized.';
711
715
  Logger.setDebug(true);
712
- Logger.error('Journium: publishableKey is required but not provided. SDK will not function.');
713
- return;
716
+ Logger.error(errorMsg);
717
+ throw new Error(errorMsg);
714
718
  }
715
719
  // Set default apiHost if not provided
716
720
  this.config = {
@@ -719,25 +723,15 @@ class JourniumClient {
719
723
  };
720
724
  // Generate storage key for options caching
721
725
  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();
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();
741
735
  }
742
736
  loadCachedOptions() {
743
737
  if (typeof window === 'undefined' || !window.localStorage) {
@@ -763,70 +757,108 @@ class JourniumClient {
763
757
  Logger.warn('Journium: Failed to save config to cache:', error);
764
758
  }
765
759
  }
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);
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
+ }
776
778
  }
777
779
  else {
778
- // No local options, use cached remote options as-is
779
- this.effectiveOptions = cachedRemoteOptions;
780
- 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
+
783
+ if (cachedRemoteOptions) {
784
+ if (this.config.options) {
785
+ this.effectiveOptions = mergeOptions(this.config.options, cachedRemoteOptions);
786
+ Logger.log('Journium: Fresh config failed, using cached remote config merged with local options:', this.effectiveOptions);
787
+ } else {
788
+ this.effectiveOptions = cachedRemoteOptions;
789
+ Logger.log('Journium: Fresh config failed, using cached remote config:', this.effectiveOptions);
790
+ }
791
+ } else {
792
+ // Step 5: No remote config and no cached config - initialization fails
793
+ Logger.error('Journium: Initialization failed - no remote config available and no cached config found');
794
+ this.initializationFailed = true;
795
+ this.initializationComplete = false;
796
+ return;
797
+ } */
781
798
  }
799
+ // Step 6: Update identity manager session timeout if provided
800
+ if (this.effectiveOptions.sessionTimeout) {
801
+ this.identityManager.updateSessionTimeout(this.effectiveOptions.sessionTimeout);
802
+ }
803
+ // Step 7: Update Logger debug setting
804
+ Logger.setDebug((_a = this.effectiveOptions.debug) !== null && _a !== void 0 ? _a : false);
805
+ // Step 8: Mark initialization as complete
806
+ this.initializationComplete = true;
807
+ this.initializationFailed = false;
808
+ // Step 9: Process any staged events
809
+ this.processStagedEvents();
810
+ // Step 10: Start flush timer
811
+ if (this.effectiveOptions.flushInterval && this.effectiveOptions.flushInterval > 0) {
812
+ this.startFlushTimer();
813
+ }
814
+ Logger.log('Journium: Initialization complete with options:', this.effectiveOptions);
815
+ // Step 11: Notify callbacks about options
816
+ this.notifyOptionsChange();
782
817
  }
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();
818
+ catch (error) {
819
+ Logger.error('Journium: Initialization failed:', error);
820
+ this.initializationFailed = true;
821
+ this.initializationComplete = false;
796
822
  }
797
823
  }
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;
824
+ async fetchRemoteOptionsWithRetry() {
825
+ const maxRetries = 2;
826
+ const timeoutMs = 15000; // 15 seconds
827
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
828
+ try {
829
+ Logger.log(`Journium: Fetching remote config (attempt ${attempt}/${maxRetries})...`);
830
+ // Create timeout promise
831
+ const timeoutPromise = new Promise((_, reject) => {
832
+ setTimeout(() => reject(new Error('Timeout')), timeoutMs);
833
+ });
834
+ // Race fetch against timeout
835
+ const fetchPromise = fetchRemoteOptions(this.config.apiHost, this.config.publishableKey);
836
+ const remoteOptionsResponse = await Promise.race([fetchPromise, timeoutPromise]);
837
+ if (remoteOptionsResponse && remoteOptionsResponse.status === 'success') {
838
+ Logger.log('Journium: Successfully fetched fresh remote config:', remoteOptionsResponse.config);
839
+ return remoteOptionsResponse.config || null;
810
840
  }
811
- else {
812
- // Local options provided, merge it over fresh remote options
813
- this.effectiveOptions = mergeOptions(remoteOptionsResponse.config, this.config.options);
841
+ else if (remoteOptionsResponse && remoteOptionsResponse.status === 'error' && remoteOptionsResponse.errorCode === 'J_ERR_TENANT_NOT_FOUND') {
842
+ Logger.error('Journium: Invalid publishableKey is being used.');
843
+ return null;
814
844
  }
815
- // Update session timeout if provided in fresh effective options
816
- if (this.effectiveOptions.sessionTimeout) {
817
- this.identityManager.updateSessionTimeout(this.effectiveOptions.sessionTimeout);
845
+ {
846
+ throw new Error('Remote config fetch unsuccessful');
847
+ }
848
+ }
849
+ catch (error) {
850
+ Logger.warn(`Journium: Remote config fetch attempt ${attempt} failed:`, error);
851
+ if (attempt === maxRetries) {
852
+ Logger.warn('Journium: All remote config fetch attempts failed, falling back to cached config');
853
+ return null;
854
+ }
855
+ // Wait 1 second before retry (except on last attempt)
856
+ if (attempt < maxRetries) {
857
+ await new Promise(resolve => setTimeout(resolve, 1000));
818
858
  }
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
859
  }
826
860
  }
827
- catch (error) {
828
- Logger.warn('Journium: Background remote options fetch failed:', error);
829
- }
861
+ return null;
830
862
  }
831
863
  /**
832
864
  * Register a callback to be notified when effective options change (e.g., when remote options are fetched)
@@ -848,12 +880,48 @@ class JourniumClient {
848
880
  }
849
881
  });
850
882
  }
883
+ processStagedEvents() {
884
+ if (this.stagedEvents.length === 0)
885
+ return;
886
+ Logger.log(`Journium: Processing ${this.stagedEvents.length} staged events`);
887
+ // Move staged events to main queue, adding identity properties now
888
+ const identity = this.identityManager.getIdentity();
889
+ const userAgentInfo = this.identityManager.getUserAgentInfo();
890
+ for (const stagedEvent of this.stagedEvents) {
891
+ // Add identity properties that weren't available during staging
892
+ const eventWithIdentity = {
893
+ ...stagedEvent,
894
+ properties: {
895
+ $device_id: identity === null || identity === void 0 ? void 0 : identity.$device_id,
896
+ distinct_id: identity === null || identity === void 0 ? void 0 : identity.distinct_id,
897
+ $session_id: identity === null || identity === void 0 ? void 0 : identity.$session_id,
898
+ $is_identified: (identity === null || identity === void 0 ? void 0 : identity.$user_state) === 'identified',
899
+ $current_url: typeof window !== 'undefined' ? window.location.href : '',
900
+ $pathname: typeof window !== 'undefined' ? window.location.pathname : '',
901
+ ...userAgentInfo,
902
+ $lib_version: '0.1.0', // TODO: Get from package.json
903
+ $platform: 'web',
904
+ ...stagedEvent.properties, // Original properties override system properties
905
+ },
906
+ };
907
+ this.queue.push(eventWithIdentity);
908
+ }
909
+ // Clear staged events
910
+ this.stagedEvents = [];
911
+ Logger.log('Journium: Staged events processed and moved to main queue');
912
+ // Check if we should flush immediately
913
+ if (this.queue.length >= this.effectiveOptions.flushAt) {
914
+ // console.log('1 Journium: Flushing events...'+JSON.stringify(this.effectiveOptions));
915
+ this.flush();
916
+ }
917
+ }
851
918
  startFlushTimer() {
852
919
  if (this.flushTimer) {
853
920
  clearInterval(this.flushTimer);
854
921
  }
855
922
  // Use universal setInterval (works in both browser and Node.js)
856
923
  this.flushTimer = setInterval(() => {
924
+ // console.log('2 Journium: Flushing events...'+JSON.stringify(this.effectiveOptions));
857
925
  this.flush();
858
926
  }, this.effectiveOptions.flushInterval);
859
927
  }
@@ -882,9 +950,9 @@ class JourniumClient {
882
950
  }
883
951
  }
884
952
  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');
953
+ // Don't identify if initialization failed
954
+ if (this.initializationFailed) {
955
+ Logger.warn('Journium: identify() call rejected - initialization failed');
888
956
  return;
889
957
  }
890
958
  // Call identify on identity manager to get previous distinct ID
@@ -898,9 +966,9 @@ class JourniumClient {
898
966
  Logger.log('Journium: User identified', { distinctId, attributes, previousDistinctId });
899
967
  }
900
968
  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');
969
+ // Don't reset if initialization failed
970
+ if (this.initializationFailed) {
971
+ Logger.warn('Journium: reset() call rejected - initialization failed');
904
972
  return;
905
973
  }
906
974
  // Reset identity in identity manager
@@ -908,42 +976,60 @@ class JourniumClient {
908
976
  Logger.log('Journium: User identity reset');
909
977
  }
910
978
  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');
914
- return;
915
- }
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
- };
979
+ // Create minimal event without identity properties (will be added later if staging)
931
980
  const journiumEvent = {
932
981
  uuid: generateUuidv7(),
933
982
  ingestion_key: this.config.publishableKey,
934
983
  client_timestamp: getCurrentTimestamp(),
935
984
  event,
936
- properties: eventProperties,
985
+ properties: { ...properties }, // Only user properties for now
937
986
  };
938
- this.queue.push(journiumEvent);
939
- Logger.log('Journium: Event tracked', journiumEvent);
940
- if (this.queue.length >= this.effectiveOptions.flushAt) {
987
+ // Stage events during initialization, add to queue after initialization
988
+ if (!this.initializationComplete) {
989
+ // If initialization failed, reject events
990
+ if (this.initializationFailed) {
991
+ Logger.warn('Journium: track() call rejected - initialization failed');
992
+ return;
993
+ }
994
+ this.stagedEvents.push(journiumEvent);
995
+ Logger.log('Journium: Event staged during initialization', journiumEvent);
996
+ return;
997
+ }
998
+ // If initialization failed, reject events
999
+ if (this.initializationFailed) {
1000
+ Logger.warn('Journium: track() call rejected - initialization failed');
1001
+ return;
1002
+ }
1003
+ // Add identity properties for immediate events (after initialization)
1004
+ const identity = this.identityManager.getIdentity();
1005
+ const userAgentInfo = this.identityManager.getUserAgentInfo();
1006
+ const eventWithIdentity = {
1007
+ ...journiumEvent,
1008
+ properties: {
1009
+ $device_id: identity === null || identity === void 0 ? void 0 : identity.$device_id,
1010
+ distinct_id: identity === null || identity === void 0 ? void 0 : identity.distinct_id,
1011
+ $session_id: identity === null || identity === void 0 ? void 0 : identity.$session_id,
1012
+ $is_identified: (identity === null || identity === void 0 ? void 0 : identity.$user_state) === 'identified',
1013
+ $current_url: typeof window !== 'undefined' ? window.location.href : '',
1014
+ $pathname: typeof window !== 'undefined' ? window.location.pathname : '',
1015
+ ...userAgentInfo,
1016
+ $lib_version: '0.1.0', // TODO: Get from package.json
1017
+ $platform: 'web',
1018
+ ...properties, // User-provided properties override system properties
1019
+ },
1020
+ };
1021
+ this.queue.push(eventWithIdentity);
1022
+ Logger.log('Journium: Event tracked', eventWithIdentity);
1023
+ // Only flush if we have effective options (after initialization)
1024
+ if (this.effectiveOptions.flushAt && this.queue.length >= this.effectiveOptions.flushAt) {
1025
+ // console.log('3 Journium: Flushing events...'+JSON.stringify(this.effectiveOptions));
941
1026
  this.flush();
942
1027
  }
943
1028
  }
944
1029
  async flush() {
945
- // Don't flush if SDK is not properly configured
946
- if (!this.config || !this.config.publishableKey) {
1030
+ // Don't flush if initialization failed
1031
+ if (this.initializationFailed) {
1032
+ Logger.warn('Journium: flush() call rejected - initialization failed');
947
1033
  return;
948
1034
  }
949
1035
  if (this.queue.length === 0)
@@ -997,7 +1083,7 @@ class PageviewTracker {
997
1083
  * Start automatic autocapture for pageviews
998
1084
  * @returns void
999
1085
  */
1000
- startAutocapture() {
1086
+ startAutoPageviewTracking() {
1001
1087
  this.capturePageview();
1002
1088
  if (typeof window !== 'undefined') {
1003
1089
  // Store original methods for cleanup
@@ -1426,6 +1512,9 @@ class JourniumAnalytics {
1426
1512
  this.unsubscribeOptionsChange = this.client.onOptionsChange((effectiveOptions) => {
1427
1513
  this.handleOptionsChange(effectiveOptions);
1428
1514
  });
1515
+ // Start automatic autocapture immediately if initial options support it
1516
+ // This handles cached remote options or local options with autocapture enabled
1517
+ this.startAutocaptureIfEnabled(initialEffectiveOptions);
1429
1518
  }
1430
1519
  resolveAutocaptureOptions(autocapture) {
1431
1520
  if (autocapture === false) {
@@ -1456,13 +1545,18 @@ class JourniumAnalytics {
1456
1545
  startAutocapture() {
1457
1546
  // Always check effective options (which may include remote options)
1458
1547
  const effectiveOptions = this.client.getEffectiveOptions();
1459
- const autoTrackPageviews = effectiveOptions.autoTrackPageviews !== false;
1460
- const autocaptureEnabled = effectiveOptions.autocapture !== false;
1548
+ // Only enable if effectiveOptions are loaded and autoTrackPageviews is not explicitly false
1549
+ const autoTrackPageviews = effectiveOptions && Object.keys(effectiveOptions).length > 0
1550
+ ? effectiveOptions.autoTrackPageviews !== false
1551
+ : false;
1552
+ const autocaptureEnabled = effectiveOptions && Object.keys(effectiveOptions).length > 0
1553
+ ? effectiveOptions.autocapture !== false
1554
+ : false;
1461
1555
  // Update autocapture tracker options if they've changed
1462
1556
  const autocaptureOptions = this.resolveAutocaptureOptions(effectiveOptions.autocapture);
1463
1557
  this.autocaptureTracker.updateOptions(autocaptureOptions);
1464
1558
  if (autoTrackPageviews) {
1465
- this.pageviewTracker.startAutocapture();
1559
+ this.pageviewTracker.startAutoPageviewTracking();
1466
1560
  }
1467
1561
  if (autocaptureEnabled) {
1468
1562
  this.autocaptureTracker.start();
@@ -1475,30 +1569,60 @@ class JourniumAnalytics {
1475
1569
  this.autocaptureStarted = false;
1476
1570
  }
1477
1571
  /**
1478
- * Handle effective options change (e.g., when remote options are fetched)
1572
+ * Automatically start autocapture if enabled in options
1573
+ * Handles both initial options and empty options during remote-first initialization
1479
1574
  */
1480
- handleOptionsChange(effectiveOptions) {
1481
- // If autocapture was already started, re-evaluate with new options
1575
+ startAutocaptureIfEnabled(effectiveOptions) {
1576
+ // Skip if autocapture was already started manually
1482
1577
  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
1578
+ return;
1579
+ }
1580
+ // During remote-first initialization, effective options might be empty initially
1581
+ // Only auto-start if we have actual options loaded, not empty options
1582
+ const hasActualOptions = effectiveOptions && Object.keys(effectiveOptions).length > 0;
1583
+ if (hasActualOptions) {
1584
+ // Use same logic as manual startAutocapture() but only start automatically
1488
1585
  const autoTrackPageviews = effectiveOptions.autoTrackPageviews !== false;
1489
1586
  const autocaptureEnabled = effectiveOptions.autocapture !== false;
1490
1587
  // Update autocapture tracker options
1491
1588
  const autocaptureOptions = this.resolveAutocaptureOptions(effectiveOptions.autocapture);
1492
1589
  this.autocaptureTracker.updateOptions(autocaptureOptions);
1493
- // Restart only if still enabled
1494
1590
  if (autoTrackPageviews) {
1495
- this.pageviewTracker.startAutocapture();
1591
+ this.pageviewTracker.startAutoPageviewTracking();
1496
1592
  }
1497
1593
  if (autocaptureEnabled) {
1498
1594
  this.autocaptureTracker.start();
1499
1595
  }
1500
- this.autocaptureStarted = autoTrackPageviews || autocaptureEnabled;
1596
+ if (autoTrackPageviews || autocaptureEnabled) {
1597
+ this.autocaptureStarted = true;
1598
+ }
1501
1599
  }
1600
+ // If options are empty (during initialization), wait for options change callback
1601
+ }
1602
+ /**
1603
+ * Handle effective options change (e.g., when remote options are fetched)
1604
+ */
1605
+ handleOptionsChange(effectiveOptions) {
1606
+ // Stop current autocapture if it was already started
1607
+ if (this.autocaptureStarted) {
1608
+ this.pageviewTracker.stopAutocapture();
1609
+ this.autocaptureTracker.stop();
1610
+ this.autocaptureStarted = false;
1611
+ }
1612
+ // Evaluate if autocapture should be enabled with new options
1613
+ const autoTrackPageviews = effectiveOptions.autoTrackPageviews !== false;
1614
+ const autocaptureEnabled = effectiveOptions.autocapture !== false;
1615
+ // Update autocapture tracker options
1616
+ const autocaptureOptions = this.resolveAutocaptureOptions(effectiveOptions.autocapture);
1617
+ this.autocaptureTracker.updateOptions(autocaptureOptions);
1618
+ // Start autocapture based on new options (even if it wasn't started before)
1619
+ if (autoTrackPageviews) {
1620
+ this.pageviewTracker.startAutoPageviewTracking();
1621
+ }
1622
+ if (autocaptureEnabled) {
1623
+ this.autocaptureTracker.start();
1624
+ }
1625
+ this.autocaptureStarted = autoTrackPageviews || autocaptureEnabled;
1502
1626
  }
1503
1627
  async flush() {
1504
1628
  return this.client.flush();
@@ -1525,24 +1649,20 @@ const init = (config) => {
1525
1649
  return new JourniumAnalytics(config);
1526
1650
  };
1527
1651
 
1528
- const JourniumContext = React.createContext({ analytics: null, config: null, effectiveOptions: null });
1652
+ const JourniumContext = React.createContext(undefined);
1529
1653
  const JourniumProvider = ({ children, config, }) => {
1530
1654
  const [analytics, setAnalytics] = React.useState(null);
1531
1655
  const [effectiveOptions, setEffectiveOptions] = React.useState(null);
1532
1656
  React.useEffect(() => {
1533
- const analyticsInstance = new JourniumAnalytics(config);
1534
- // Get initial effective options (may include cached remote options)
1657
+ const analyticsInstance = init(config);
1658
+ // Get initial effective options (may be empty during remote-first initialization)
1535
1659
  const initialEffective = analyticsInstance.getEffectiveOptions();
1536
1660
  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
- }
1661
+ // Don't start autocapture immediately with potentially empty options
1662
+ // Let the analytics instance handle autocapture after initialization completes
1542
1663
  setAnalytics(analyticsInstance);
1543
1664
  // 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
1665
+ // The JourniumAnalytics will automatically start autocapture when initialization completes
1546
1666
  const unsubscribe = analyticsInstance.onOptionsChange((newOptions) => {
1547
1667
  setEffectiveOptions(newOptions);
1548
1668
  });
@@ -1620,7 +1740,6 @@ const useAutocapture = () => {
1620
1740
 
1621
1741
  exports.AutocaptureTracker = AutocaptureTracker;
1622
1742
  exports.BrowserIdentityManager = BrowserIdentityManager;
1623
- exports.JourniumAnalytics = JourniumAnalytics;
1624
1743
  exports.JourniumClient = JourniumClient;
1625
1744
  exports.JourniumProvider = JourniumProvider;
1626
1745
  exports.Logger = Logger;