@journium/react 1.0.6 → 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,CAiC5D,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,14 +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
- // Validate required configuration
709
- if (!config.publishableKey) {
708
+ this.initializationComplete = false;
709
+ this.initializationFailed = false;
710
+ this.disabled = false;
711
+ this.optionsChangeCallbacks = new Set();
712
+ // Validate required configuration - put in disabled state if invalid
713
+ if (!config.publishableKey || config.publishableKey.trim() === '') {
714
+ this.disabled = true;
710
715
  Logger.setDebug(true);
711
- 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';
712
721
  return;
713
722
  }
714
723
  // Set default apiHost if not provided
@@ -718,25 +727,15 @@ class JourniumClient {
718
727
  };
719
728
  // Generate storage key for options caching
720
729
  this.optionsStorageKey = `jrnm_${config.publishableKey}_options`;
721
- // Generate default values
722
- const defaultOptions = {
723
- debug: false,
724
- flushAt: 20,
725
- flushInterval: 10000,
726
- sessionTimeout: 30 * 60 * 1000, // 30 minutes
727
- };
728
- // Initialize effective options with local options taking precedence over defaults
729
- this.effectiveOptions = { ...defaultOptions };
730
- if (this.config.options) {
731
- this.effectiveOptions = mergeOptions(defaultOptions, this.config.options);
732
- }
733
- // Initialize Logger with debug setting
734
- Logger.setDebug((_a = this.effectiveOptions.debug) !== null && _a !== void 0 ? _a : false);
735
- // Initialize identity manager
736
- this.identityManager = new BrowserIdentityManager(this.effectiveOptions.sessionTimeout, this.config.publishableKey);
737
- // Initialize synchronously with cached config, fetch fresh config in background
738
- this.initializeSync();
739
- 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();
740
739
  }
741
740
  loadCachedOptions() {
742
741
  if (typeof window === 'undefined' || !window.localStorage) {
@@ -762,57 +761,159 @@ class JourniumClient {
762
761
  Logger.warn('Journium: Failed to save config to cache:', error);
763
762
  }
764
763
  }
765
- initializeSync() {
766
- // Step 1: Load cached remote options from localStorage (synchronous)
767
- const cachedRemoteOptions = this.loadCachedOptions();
768
- // Step 2: If no local options provided, use cached remote options
769
- if (!this.config.options && cachedRemoteOptions) {
770
- this.effectiveOptions = cachedRemoteOptions;
771
- Logger.log('Journium: Using cached remote options:', cachedRemoteOptions);
772
- }
773
- // Step 3: Mark as initialized immediately - no need to wait for remote fetch
774
- this.initialized = true;
775
- // Step 4: Start flush timer immediately
776
- if (this.effectiveOptions.flushInterval && this.effectiveOptions.flushInterval > 0) {
777
- this.startFlushTimer();
778
- }
779
- Logger.log('Journium: Client initialized with effective options:', this.effectiveOptions);
780
- }
781
- async fetchRemoteOptionsAsync() {
782
- // Fetch fresh config in background
783
- if (this.config.publishableKey) {
784
- await this.fetchAndCacheRemoteOptions();
785
- }
786
- }
787
- async fetchAndCacheRemoteOptions() {
764
+ async initializeAsync() {
788
765
  var _a;
789
766
  try {
790
- Logger.log('Journium: Fetching remote configuration in background...');
791
- const remoteOptionsResponse = await fetchRemoteOptions(this.config.apiHost, this.config.publishableKey);
792
- if (remoteOptionsResponse && remoteOptionsResponse.success) {
793
- // Save remote config to cache for next session
794
- this.saveCachedOptions(remoteOptionsResponse.config);
795
- // Update effective options: local options (if provided) overrides fresh remote options
796
- if (!this.config.options) {
797
- // No local options provided, use fresh remote options
798
- this.effectiveOptions = remoteOptionsResponse.config;
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);
799
777
  }
800
778
  else {
801
- // Local options provided, merge it over fresh remote options
802
- this.effectiveOptions = mergeOptions(remoteOptionsResponse.config, this.config.options);
779
+ this.effectiveOptions = remoteOptions;
780
+ Logger.log('Journium: Using fresh remote config:', this.effectiveOptions);
803
781
  }
804
- // Update session timeout if provided in fresh effective options
805
- if (this.effectiveOptions.sessionTimeout) {
806
- this.identityManager.updateSessionTimeout(this.effectiveOptions.sessionTimeout);
782
+ }
783
+ else {
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;
807
802
  }
808
- Logger.log('Journium: Background remote options applied:', remoteOptionsResponse.config);
809
- Logger.log('Journium: New effective options:', this.effectiveOptions);
810
- // Update Logger debug setting with new options
811
- Logger.setDebug((_a = this.effectiveOptions.debug) !== null && _a !== void 0 ? _a : false);
812
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();
813
822
  }
814
823
  catch (error) {
815
- Logger.warn('Journium: Background remote options fetch failed:', error);
824
+ Logger.error('Journium: Initialization failed:', error);
825
+ this.initializationFailed = true;
826
+ this.initializationComplete = false;
827
+ }
828
+ }
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;
845
+ }
846
+ else {
847
+ throw new Error('Remote config fetch unsuccessful');
848
+ }
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));
859
+ }
860
+ }
861
+ }
862
+ return null;
863
+ }
864
+ /**
865
+ * Register a callback to be notified when effective options change (e.g., when remote options are fetched)
866
+ */
867
+ onOptionsChange(callback) {
868
+ this.optionsChangeCallbacks.add(callback);
869
+ // Return unsubscribe function
870
+ return () => {
871
+ this.optionsChangeCallbacks.delete(callback);
872
+ };
873
+ }
874
+ notifyOptionsChange() {
875
+ this.optionsChangeCallbacks.forEach(callback => {
876
+ try {
877
+ callback(this.effectiveOptions);
878
+ }
879
+ catch (error) {
880
+ Logger.warn('Journium: Error in options change callback:', error);
881
+ }
882
+ });
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();
816
917
  }
817
918
  }
818
919
  startFlushTimer() {
@@ -821,6 +922,7 @@ class JourniumClient {
821
922
  }
822
923
  // Use universal setInterval (works in both browser and Node.js)
823
924
  this.flushTimer = setInterval(() => {
925
+ // console.log('2 Journium: Flushing events...'+JSON.stringify(this.effectiveOptions));
824
926
  this.flush();
825
927
  }, this.effectiveOptions.flushInterval);
826
928
  }
@@ -849,9 +951,14 @@ class JourniumClient {
849
951
  }
850
952
  }
851
953
  identify(distinctId, attributes = {}) {
852
- // Don't identify if SDK is not properly configured
853
- if (!this.config || !this.config.publishableKey || !this.initialized) {
854
- 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');
855
962
  return;
856
963
  }
857
964
  // Call identify on identity manager to get previous distinct ID
@@ -865,9 +972,14 @@ class JourniumClient {
865
972
  Logger.log('Journium: User identified', { distinctId, attributes, previousDistinctId });
866
973
  }
867
974
  reset() {
868
- // Don't reset if SDK is not properly configured
869
- if (!this.config || !this.config.publishableKey || !this.initialized) {
870
- 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');
871
983
  return;
872
984
  }
873
985
  // Reset identity in identity manager
@@ -875,36 +987,58 @@ class JourniumClient {
875
987
  Logger.log('Journium: User identity reset');
876
988
  }
877
989
  track(event, properties = {}) {
878
- // Don't track if SDK is not properly configured
879
- if (!this.config || !this.config.publishableKey || !this.initialized) {
880
- 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');
881
993
  return;
882
994
  }
883
- const identity = this.identityManager.getIdentity();
884
- const userAgentInfo = this.identityManager.getUserAgentInfo();
885
- // Create standardized event properties
886
- const eventProperties = {
887
- $device_id: identity === null || identity === void 0 ? void 0 : identity.$device_id,
888
- distinct_id: identity === null || identity === void 0 ? void 0 : identity.distinct_id,
889
- $session_id: identity === null || identity === void 0 ? void 0 : identity.$session_id,
890
- $is_identified: (identity === null || identity === void 0 ? void 0 : identity.$user_state) === 'identified',
891
- $current_url: typeof window !== 'undefined' ? window.location.href : '',
892
- $pathname: typeof window !== 'undefined' ? window.location.pathname : '',
893
- ...userAgentInfo,
894
- $lib_version: '0.1.0', // TODO: Get from package.json
895
- $platform: 'web',
896
- ...properties, // User-provided properties override defaults
897
- };
995
+ // Create minimal event without identity properties (will be added later if staging)
898
996
  const journiumEvent = {
899
997
  uuid: generateUuidv7(),
900
998
  ingestion_key: this.config.publishableKey,
901
999
  client_timestamp: getCurrentTimestamp(),
902
1000
  event,
903
- properties: eventProperties,
1001
+ properties: { ...properties }, // Only user properties for now
904
1002
  };
905
- this.queue.push(journiumEvent);
906
- Logger.log('Journium: Event tracked', journiumEvent);
907
- 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));
908
1042
  this.flush();
909
1043
  }
910
1044
  }
@@ -913,6 +1047,11 @@ class JourniumClient {
913
1047
  if (!this.config || !this.config.publishableKey) {
914
1048
  return;
915
1049
  }
1050
+ // Don't flush if initialization failed
1051
+ if (this.initializationFailed) {
1052
+ Logger.warn('Journium: flush() call rejected - initialization failed');
1053
+ return;
1054
+ }
916
1055
  if (this.queue.length === 0)
917
1056
  return;
918
1057
  const events = [...this.queue];
@@ -964,7 +1103,7 @@ class PageviewTracker {
964
1103
  * Start automatic autocapture for pageviews
965
1104
  * @returns void
966
1105
  */
967
- startAutocapture() {
1106
+ startAutoPageviewTracking() {
968
1107
  this.capturePageview();
969
1108
  if (typeof window !== 'undefined') {
970
1109
  // Store original methods for cleanup
@@ -1026,6 +1165,31 @@ class AutocaptureTracker {
1026
1165
  ...options,
1027
1166
  };
1028
1167
  }
1168
+ /**
1169
+ * Update autocapture options and restart if currently active
1170
+ */
1171
+ updateOptions(options) {
1172
+ const wasActive = this.isActive;
1173
+ // Stop if currently active
1174
+ if (wasActive) {
1175
+ this.stop();
1176
+ }
1177
+ // Update options
1178
+ this.options = {
1179
+ captureClicks: true,
1180
+ captureFormSubmits: true,
1181
+ captureFormChanges: true,
1182
+ captureTextSelection: false,
1183
+ ignoreClasses: ['journium-ignore'],
1184
+ ignoreElements: ['script', 'style', 'noscript'],
1185
+ captureContentText: true,
1186
+ ...options,
1187
+ };
1188
+ // Restart if it was active before
1189
+ if (wasActive) {
1190
+ this.start();
1191
+ }
1192
+ }
1029
1193
  start() {
1030
1194
  if (!isBrowser() || this.isActive) {
1031
1195
  return;
@@ -1355,14 +1519,22 @@ class AutocaptureTracker {
1355
1519
 
1356
1520
  class JourniumAnalytics {
1357
1521
  constructor(config) {
1358
- var _a, _b;
1522
+ this.autocaptureStarted = false;
1359
1523
  this.config = config;
1360
1524
  this.client = new JourniumClient(config);
1361
1525
  this.pageviewTracker = new PageviewTracker(this.client);
1362
- const autocaptureOptions = this.resolveAutocaptureOptions((_a = config.options) === null || _a === void 0 ? void 0 : _a.autocapture);
1363
- this.autocaptureTracker = new AutocaptureTracker(this.client, autocaptureOptions);
1364
- // Store resolved autocapture state for startAutocapture method
1365
- this.autocaptureEnabled = ((_b = config.options) === null || _b === void 0 ? void 0 : _b.autocapture) !== false;
1526
+ // Initialize autocapture tracker with effective options (may include cached remote options)
1527
+ // This ensures we use the correct initial state even if cached remote options exist
1528
+ const initialEffectiveOptions = this.client.getEffectiveOptions();
1529
+ const initialAutocaptureOptions = this.resolveAutocaptureOptions(initialEffectiveOptions.autocapture);
1530
+ this.autocaptureTracker = new AutocaptureTracker(this.client, initialAutocaptureOptions);
1531
+ // Listen for options changes (e.g., when fresh remote options are fetched)
1532
+ this.unsubscribeOptionsChange = this.client.onOptionsChange((effectiveOptions) => {
1533
+ this.handleOptionsChange(effectiveOptions);
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);
1366
1538
  }
1367
1539
  resolveAutocaptureOptions(autocapture) {
1368
1540
  if (autocapture === false) {
@@ -1391,19 +1563,86 @@ class JourniumAnalytics {
1391
1563
  this.pageviewTracker.capturePageview(properties);
1392
1564
  }
1393
1565
  startAutocapture() {
1394
- // Check if automatic pageview tracking is enabled (defaults to true)
1566
+ // Always check effective options (which may include remote options)
1395
1567
  const effectiveOptions = this.client.getEffectiveOptions();
1396
- const autoTrackPageviews = effectiveOptions.autoTrackPageviews !== 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;
1575
+ // Update autocapture tracker options if they've changed
1576
+ const autocaptureOptions = this.resolveAutocaptureOptions(effectiveOptions.autocapture);
1577
+ this.autocaptureTracker.updateOptions(autocaptureOptions);
1397
1578
  if (autoTrackPageviews) {
1398
- this.pageviewTracker.startAutocapture();
1579
+ this.pageviewTracker.startAutoPageviewTracking();
1399
1580
  }
1400
- if (this.autocaptureEnabled) {
1581
+ if (autocaptureEnabled) {
1401
1582
  this.autocaptureTracker.start();
1402
1583
  }
1584
+ this.autocaptureStarted = true;
1403
1585
  }
1404
1586
  stopAutocapture() {
1405
1587
  this.pageviewTracker.stopAutocapture();
1406
1588
  this.autocaptureTracker.stop();
1589
+ this.autocaptureStarted = false;
1590
+ }
1591
+ /**
1592
+ * Automatically start autocapture if enabled in options
1593
+ * Handles both initial options and empty options during remote-first initialization
1594
+ */
1595
+ startAutocaptureIfEnabled(effectiveOptions) {
1596
+ // Skip if autocapture was already started manually
1597
+ if (this.autocaptureStarted) {
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
1605
+ const autoTrackPageviews = effectiveOptions.autoTrackPageviews !== false;
1606
+ const autocaptureEnabled = effectiveOptions.autocapture !== false;
1607
+ // Update autocapture tracker options
1608
+ const autocaptureOptions = this.resolveAutocaptureOptions(effectiveOptions.autocapture);
1609
+ this.autocaptureTracker.updateOptions(autocaptureOptions);
1610
+ if (autoTrackPageviews) {
1611
+ this.pageviewTracker.startAutoPageviewTracking();
1612
+ }
1613
+ if (autocaptureEnabled) {
1614
+ this.autocaptureTracker.start();
1615
+ }
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;
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;
1407
1646
  }
1408
1647
  async flush() {
1409
1648
  return this.client.flush();
@@ -1411,9 +1650,18 @@ class JourniumAnalytics {
1411
1650
  getEffectiveOptions() {
1412
1651
  return this.client.getEffectiveOptions();
1413
1652
  }
1653
+ /**
1654
+ * Register a callback to be notified when effective options change
1655
+ */
1656
+ onOptionsChange(callback) {
1657
+ return this.client.onOptionsChange(callback);
1658
+ }
1414
1659
  destroy() {
1415
1660
  this.pageviewTracker.stopAutocapture();
1416
1661
  this.autocaptureTracker.stop();
1662
+ if (this.unsubscribeOptionsChange) {
1663
+ this.unsubscribeOptionsChange();
1664
+ }
1417
1665
  this.client.destroy();
1418
1666
  }
1419
1667
  }
@@ -1421,21 +1669,27 @@ const init = (config) => {
1421
1669
  return new JourniumAnalytics(config);
1422
1670
  };
1423
1671
 
1424
- const JourniumContext = React.createContext({ analytics: null, config: null, effectiveOptions: null });
1672
+ const JourniumContext = React.createContext(undefined);
1425
1673
  const JourniumProvider = ({ children, config, }) => {
1426
1674
  const [analytics, setAnalytics] = React.useState(null);
1427
1675
  const [effectiveOptions, setEffectiveOptions] = React.useState(null);
1428
1676
  React.useEffect(() => {
1429
1677
  const analyticsInstance = new JourniumAnalytics(config);
1430
- // Get effective options and check if autocapture is enabled
1431
- const effective = analyticsInstance.getEffectiveOptions();
1432
- setEffectiveOptions(effective);
1433
- const autocaptureEnabled = effective.autocapture !== false;
1434
- if (autocaptureEnabled) {
1435
- analyticsInstance.startAutocapture();
1436
- }
1678
+ // Get initial effective options (may be empty during remote-first initialization)
1679
+ const initialEffective = analyticsInstance.getEffectiveOptions();
1680
+ setEffectiveOptions(initialEffective);
1681
+ // Don't start autocapture immediately with potentially empty options
1682
+ // Let the analytics instance handle autocapture after initialization completes
1437
1683
  setAnalytics(analyticsInstance);
1684
+ // Listen for options changes (when remote options are fetched)
1685
+ // The JourniumAnalytics will automatically start autocapture when initialization completes
1686
+ const unsubscribe = analyticsInstance.onOptionsChange((newOptions) => {
1687
+ setEffectiveOptions(newOptions);
1688
+ });
1438
1689
  return () => {
1690
+ if (unsubscribe) {
1691
+ unsubscribe();
1692
+ }
1439
1693
  analyticsInstance.destroy();
1440
1694
  setAnalytics(null);
1441
1695
  setEffectiveOptions(null);