@journium/react 1.0.5 → 1.0.7

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
@@ -667,14 +667,47 @@ class BrowserIdentityManager {
667
667
  }
668
668
  }
669
669
 
670
+ class Logger {
671
+ static setDebug(enabled) {
672
+ this.isDebugEnabled = enabled;
673
+ }
674
+ static isDebug() {
675
+ return this.isDebugEnabled;
676
+ }
677
+ static log(...args) {
678
+ if (this.isDebugEnabled) {
679
+ console.log(...args);
680
+ }
681
+ }
682
+ static warn(...args) {
683
+ if (this.isDebugEnabled) {
684
+ console.warn(...args);
685
+ }
686
+ }
687
+ static error(...args) {
688
+ if (this.isDebugEnabled) {
689
+ console.error(...args);
690
+ }
691
+ }
692
+ static info(...args) {
693
+ if (this.isDebugEnabled) {
694
+ console.info(...args);
695
+ }
696
+ }
697
+ }
698
+ Logger.isDebugEnabled = false;
699
+
670
700
  class JourniumClient {
671
701
  constructor(config) {
702
+ var _a;
672
703
  this.queue = [];
673
704
  this.flushTimer = null;
674
705
  this.initialized = false;
706
+ this.optionsChangeCallbacks = new Set();
675
707
  // Validate required configuration
676
708
  if (!config.publishableKey) {
677
- console.error('Journium: publishableKey is required but not provided. SDK will not function.');
709
+ Logger.setDebug(true);
710
+ Logger.error('Journium: publishableKey is required but not provided. SDK will not function.');
678
711
  return;
679
712
  }
680
713
  // Set default apiHost if not provided
@@ -696,6 +729,8 @@ class JourniumClient {
696
729
  if (this.config.options) {
697
730
  this.effectiveOptions = mergeOptions(defaultOptions, this.config.options);
698
731
  }
732
+ // Initialize Logger with debug setting
733
+ Logger.setDebug((_a = this.effectiveOptions.debug) !== null && _a !== void 0 ? _a : false);
699
734
  // Initialize identity manager
700
735
  this.identityManager = new BrowserIdentityManager(this.effectiveOptions.sessionTimeout, this.config.publishableKey);
701
736
  // Initialize synchronously with cached config, fetch fresh config in background
@@ -703,7 +738,6 @@ class JourniumClient {
703
738
  this.fetchRemoteOptionsAsync();
704
739
  }
705
740
  loadCachedOptions() {
706
- var _a;
707
741
  if (typeof window === 'undefined' || !window.localStorage) {
708
742
  return null;
709
743
  }
@@ -712,14 +746,11 @@ class JourniumClient {
712
746
  return cached ? JSON.parse(cached) : null;
713
747
  }
714
748
  catch (error) {
715
- if ((_a = this.effectiveOptions) === null || _a === void 0 ? void 0 : _a.debug) {
716
- console.warn('Journium: Failed to load cached config:', error);
717
- }
749
+ Logger.warn('Journium: Failed to load cached config:', error);
718
750
  return null;
719
751
  }
720
752
  }
721
753
  saveCachedOptions(options) {
722
- var _a;
723
754
  if (typeof window === 'undefined' || !window.localStorage) {
724
755
  return;
725
756
  }
@@ -727,30 +758,34 @@ class JourniumClient {
727
758
  window.localStorage.setItem(this.optionsStorageKey, JSON.stringify(options));
728
759
  }
729
760
  catch (error) {
730
- if ((_a = this.effectiveOptions) === null || _a === void 0 ? void 0 : _a.debug) {
731
- console.warn('Journium: Failed to save config to cache:', error);
732
- }
761
+ Logger.warn('Journium: Failed to save config to cache:', error);
733
762
  }
734
763
  }
735
764
  initializeSync() {
736
765
  // Step 1: Load cached remote options from localStorage (synchronous)
737
766
  const cachedRemoteOptions = this.loadCachedOptions();
738
- // Step 2: If no local options provided, use cached remote options
739
- if (!this.config.options && cachedRemoteOptions) {
740
- this.effectiveOptions = cachedRemoteOptions;
741
- if (this.effectiveOptions.debug) {
742
- console.log('Journium: Using cached remote options:', cachedRemoteOptions);
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);
774
+ }
775
+ else {
776
+ // No local options, use cached remote options as-is
777
+ this.effectiveOptions = cachedRemoteOptions;
778
+ Logger.log('Journium: Using cached remote options:', cachedRemoteOptions);
743
779
  }
744
780
  }
781
+ // If no cached options, effectiveOptions already has defaults merged with local options from constructor
745
782
  // Step 3: Mark as initialized immediately - no need to wait for remote fetch
746
783
  this.initialized = true;
747
784
  // Step 4: Start flush timer immediately
748
785
  if (this.effectiveOptions.flushInterval && this.effectiveOptions.flushInterval > 0) {
749
786
  this.startFlushTimer();
750
787
  }
751
- if (this.effectiveOptions.debug) {
752
- console.log('Journium: Client initialized with effective options:', this.effectiveOptions);
753
- }
788
+ Logger.log('Journium: Client initialized with effective options:', this.effectiveOptions);
754
789
  }
755
790
  async fetchRemoteOptionsAsync() {
756
791
  // Fetch fresh config in background
@@ -759,10 +794,9 @@ class JourniumClient {
759
794
  }
760
795
  }
761
796
  async fetchAndCacheRemoteOptions() {
797
+ var _a;
762
798
  try {
763
- if (this.effectiveOptions.debug) {
764
- console.log('Journium: Fetching remote configuration in background...');
765
- }
799
+ Logger.log('Journium: Fetching remote configuration in background...');
766
800
  const remoteOptionsResponse = await fetchRemoteOptions(this.config.apiHost, this.config.publishableKey);
767
801
  if (remoteOptionsResponse && remoteOptionsResponse.success) {
768
802
  // Save remote config to cache for next session
@@ -780,18 +814,38 @@ class JourniumClient {
780
814
  if (this.effectiveOptions.sessionTimeout) {
781
815
  this.identityManager.updateSessionTimeout(this.effectiveOptions.sessionTimeout);
782
816
  }
783
- if (this.effectiveOptions.debug) {
784
- console.log('Journium: Background remote options applied:', remoteOptionsResponse.config);
785
- console.log('Journium: New effective options:', this.effectiveOptions);
786
- }
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();
787
823
  }
788
824
  }
789
825
  catch (error) {
790
- if (this.effectiveOptions.debug) {
791
- console.warn('Journium: Background remote options fetch failed:', error);
792
- }
826
+ Logger.warn('Journium: Background remote options fetch failed:', error);
793
827
  }
794
828
  }
829
+ /**
830
+ * Register a callback to be notified when effective options change (e.g., when remote options are fetched)
831
+ */
832
+ onOptionsChange(callback) {
833
+ this.optionsChangeCallbacks.add(callback);
834
+ // Return unsubscribe function
835
+ return () => {
836
+ this.optionsChangeCallbacks.delete(callback);
837
+ };
838
+ }
839
+ notifyOptionsChange() {
840
+ this.optionsChangeCallbacks.forEach(callback => {
841
+ try {
842
+ callback(this.effectiveOptions);
843
+ }
844
+ catch (error) {
845
+ Logger.warn('Journium: Error in options change callback:', error);
846
+ }
847
+ });
848
+ }
795
849
  startFlushTimer() {
796
850
  if (this.flushTimer) {
797
851
  clearInterval(this.flushTimer);
@@ -818,24 +872,17 @@ class JourniumClient {
818
872
  if (!response.ok) {
819
873
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
820
874
  }
821
- if (this.effectiveOptions.debug) {
822
- console.log('Journium: Successfully sent events', events);
823
- }
875
+ Logger.log('Journium: Successfully sent events', events);
824
876
  }
825
877
  catch (error) {
826
- if (this.effectiveOptions.debug) {
827
- console.error('Journium: Failed to send events', error);
828
- }
878
+ Logger.error('Journium: Failed to send events', error);
829
879
  throw error;
830
880
  }
831
881
  }
832
882
  identify(distinctId, attributes = {}) {
833
- var _a;
834
883
  // Don't identify if SDK is not properly configured
835
884
  if (!this.config || !this.config.publishableKey || !this.initialized) {
836
- if ((_a = this.effectiveOptions) === null || _a === void 0 ? void 0 : _a.debug) {
837
- console.warn('Journium: identify() call rejected - SDK not ready');
838
- }
885
+ Logger.warn('Journium: identify() call rejected - SDK not ready');
839
886
  return;
840
887
  }
841
888
  // Call identify on identity manager to get previous distinct ID
@@ -846,32 +893,22 @@ class JourniumClient {
846
893
  $anon_distinct_id: previousDistinctId,
847
894
  };
848
895
  this.track('$identify', identifyProperties);
849
- if (this.effectiveOptions.debug) {
850
- console.log('Journium: User identified', { distinctId, attributes, previousDistinctId });
851
- }
896
+ Logger.log('Journium: User identified', { distinctId, attributes, previousDistinctId });
852
897
  }
853
898
  reset() {
854
- var _a;
855
899
  // Don't reset if SDK is not properly configured
856
900
  if (!this.config || !this.config.publishableKey || !this.initialized) {
857
- if ((_a = this.effectiveOptions) === null || _a === void 0 ? void 0 : _a.debug) {
858
- console.warn('Journium: reset() call rejected - SDK not ready');
859
- }
901
+ Logger.warn('Journium: reset() call rejected - SDK not ready');
860
902
  return;
861
903
  }
862
904
  // Reset identity in identity manager
863
905
  this.identityManager.reset();
864
- if (this.effectiveOptions.debug) {
865
- console.log('Journium: User identity reset');
866
- }
906
+ Logger.log('Journium: User identity reset');
867
907
  }
868
908
  track(event, properties = {}) {
869
- var _a;
870
909
  // Don't track if SDK is not properly configured
871
910
  if (!this.config || !this.config.publishableKey || !this.initialized) {
872
- if ((_a = this.effectiveOptions) === null || _a === void 0 ? void 0 : _a.debug) {
873
- console.warn('Journium: track() call rejected - SDK not ready');
874
- }
911
+ Logger.warn('Journium: track() call rejected - SDK not ready');
875
912
  return;
876
913
  }
877
914
  const identity = this.identityManager.getIdentity();
@@ -897,9 +934,7 @@ class JourniumClient {
897
934
  properties: eventProperties,
898
935
  };
899
936
  this.queue.push(journiumEvent);
900
- if (this.effectiveOptions.debug) {
901
- console.log('Journium: Event tracked', journiumEvent);
902
- }
937
+ Logger.log('Journium: Event tracked', journiumEvent);
903
938
  if (this.queue.length >= this.effectiveOptions.flushAt) {
904
939
  this.flush();
905
940
  }
@@ -1022,6 +1057,31 @@ class AutocaptureTracker {
1022
1057
  ...options,
1023
1058
  };
1024
1059
  }
1060
+ /**
1061
+ * Update autocapture options and restart if currently active
1062
+ */
1063
+ updateOptions(options) {
1064
+ const wasActive = this.isActive;
1065
+ // Stop if currently active
1066
+ if (wasActive) {
1067
+ this.stop();
1068
+ }
1069
+ // Update options
1070
+ this.options = {
1071
+ captureClicks: true,
1072
+ captureFormSubmits: true,
1073
+ captureFormChanges: true,
1074
+ captureTextSelection: false,
1075
+ ignoreClasses: ['journium-ignore'],
1076
+ ignoreElements: ['script', 'style', 'noscript'],
1077
+ captureContentText: true,
1078
+ ...options,
1079
+ };
1080
+ // Restart if it was active before
1081
+ if (wasActive) {
1082
+ this.start();
1083
+ }
1084
+ }
1025
1085
  start() {
1026
1086
  if (!isBrowser() || this.isActive) {
1027
1087
  return;
@@ -1351,14 +1411,19 @@ class AutocaptureTracker {
1351
1411
 
1352
1412
  class JourniumAnalytics {
1353
1413
  constructor(config) {
1354
- var _a, _b;
1414
+ this.autocaptureStarted = false;
1355
1415
  this.config = config;
1356
1416
  this.client = new JourniumClient(config);
1357
1417
  this.pageviewTracker = new PageviewTracker(this.client);
1358
- const autocaptureOptions = this.resolveAutocaptureOptions((_a = config.options) === null || _a === void 0 ? void 0 : _a.autocapture);
1359
- this.autocaptureTracker = new AutocaptureTracker(this.client, autocaptureOptions);
1360
- // Store resolved autocapture state for startAutocapture method
1361
- this.autocaptureEnabled = ((_b = config.options) === null || _b === void 0 ? void 0 : _b.autocapture) !== false;
1418
+ // Initialize autocapture tracker with effective options (may include cached remote options)
1419
+ // This ensures we use the correct initial state even if cached remote options exist
1420
+ const initialEffectiveOptions = this.client.getEffectiveOptions();
1421
+ const initialAutocaptureOptions = this.resolveAutocaptureOptions(initialEffectiveOptions.autocapture);
1422
+ this.autocaptureTracker = new AutocaptureTracker(this.client, initialAutocaptureOptions);
1423
+ // Listen for options changes (e.g., when fresh remote options are fetched)
1424
+ this.unsubscribeOptionsChange = this.client.onOptionsChange((effectiveOptions) => {
1425
+ this.handleOptionsChange(effectiveOptions);
1426
+ });
1362
1427
  }
1363
1428
  resolveAutocaptureOptions(autocapture) {
1364
1429
  if (autocapture === false) {
@@ -1387,19 +1452,51 @@ class JourniumAnalytics {
1387
1452
  this.pageviewTracker.capturePageview(properties);
1388
1453
  }
1389
1454
  startAutocapture() {
1390
- // Check if automatic pageview tracking is enabled (defaults to true)
1455
+ // Always check effective options (which may include remote options)
1391
1456
  const effectiveOptions = this.client.getEffectiveOptions();
1392
1457
  const autoTrackPageviews = effectiveOptions.autoTrackPageviews !== false;
1458
+ const autocaptureEnabled = effectiveOptions.autocapture !== false;
1459
+ // Update autocapture tracker options if they've changed
1460
+ const autocaptureOptions = this.resolveAutocaptureOptions(effectiveOptions.autocapture);
1461
+ this.autocaptureTracker.updateOptions(autocaptureOptions);
1393
1462
  if (autoTrackPageviews) {
1394
1463
  this.pageviewTracker.startAutocapture();
1395
1464
  }
1396
- if (this.autocaptureEnabled) {
1465
+ if (autocaptureEnabled) {
1397
1466
  this.autocaptureTracker.start();
1398
1467
  }
1468
+ this.autocaptureStarted = true;
1399
1469
  }
1400
1470
  stopAutocapture() {
1401
1471
  this.pageviewTracker.stopAutocapture();
1402
1472
  this.autocaptureTracker.stop();
1473
+ this.autocaptureStarted = false;
1474
+ }
1475
+ /**
1476
+ * Handle effective options change (e.g., when remote options are fetched)
1477
+ */
1478
+ handleOptionsChange(effectiveOptions) {
1479
+ // If autocapture was already started, re-evaluate with new options
1480
+ 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
1486
+ const autoTrackPageviews = effectiveOptions.autoTrackPageviews !== false;
1487
+ const autocaptureEnabled = effectiveOptions.autocapture !== false;
1488
+ // Update autocapture tracker options
1489
+ const autocaptureOptions = this.resolveAutocaptureOptions(effectiveOptions.autocapture);
1490
+ this.autocaptureTracker.updateOptions(autocaptureOptions);
1491
+ // Restart only if still enabled
1492
+ if (autoTrackPageviews) {
1493
+ this.pageviewTracker.startAutocapture();
1494
+ }
1495
+ if (autocaptureEnabled) {
1496
+ this.autocaptureTracker.start();
1497
+ }
1498
+ this.autocaptureStarted = autoTrackPageviews || autocaptureEnabled;
1499
+ }
1403
1500
  }
1404
1501
  async flush() {
1405
1502
  return this.client.flush();
@@ -1407,9 +1504,18 @@ class JourniumAnalytics {
1407
1504
  getEffectiveOptions() {
1408
1505
  return this.client.getEffectiveOptions();
1409
1506
  }
1507
+ /**
1508
+ * Register a callback to be notified when effective options change
1509
+ */
1510
+ onOptionsChange(callback) {
1511
+ return this.client.onOptionsChange(callback);
1512
+ }
1410
1513
  destroy() {
1411
1514
  this.pageviewTracker.stopAutocapture();
1412
1515
  this.autocaptureTracker.stop();
1516
+ if (this.unsubscribeOptionsChange) {
1517
+ this.unsubscribeOptionsChange();
1518
+ }
1413
1519
  this.client.destroy();
1414
1520
  }
1415
1521
  }
@@ -1423,15 +1529,25 @@ const JourniumProvider = ({ children, config, }) => {
1423
1529
  const [effectiveOptions, setEffectiveOptions] = useState(null);
1424
1530
  useEffect(() => {
1425
1531
  const analyticsInstance = new JourniumAnalytics(config);
1426
- // Get effective options and check if autocapture is enabled
1427
- const effective = analyticsInstance.getEffectiveOptions();
1428
- setEffectiveOptions(effective);
1429
- const autocaptureEnabled = effective.autocapture !== false;
1532
+ // Get initial effective options (may include cached remote options)
1533
+ const initialEffective = analyticsInstance.getEffectiveOptions();
1534
+ setEffectiveOptions(initialEffective);
1535
+ // Check if autocapture should be enabled based on initial effective options
1536
+ const autocaptureEnabled = initialEffective.autocapture !== false;
1430
1537
  if (autocaptureEnabled) {
1431
1538
  analyticsInstance.startAutocapture();
1432
1539
  }
1433
1540
  setAnalytics(analyticsInstance);
1541
+ // 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
1544
+ const unsubscribe = analyticsInstance.onOptionsChange((newOptions) => {
1545
+ setEffectiveOptions(newOptions);
1546
+ });
1434
1547
  return () => {
1548
+ if (unsubscribe) {
1549
+ unsubscribe();
1550
+ }
1435
1551
  analyticsInstance.destroy();
1436
1552
  setAnalytics(null);
1437
1553
  setEffectiveOptions(null);
@@ -1500,5 +1616,5 @@ const useAutocapture = () => {
1500
1616
  return { startAutocapture, stopAutocapture };
1501
1617
  };
1502
1618
 
1503
- export { AutocaptureTracker, BrowserIdentityManager, JourniumAnalytics, JourniumClient, JourniumProvider, PageviewTracker, fetchRemoteOptions, generateId, generateUuidv7, getCurrentTimestamp, getCurrentUrl, getPageTitle, getReferrer, init, isBrowser, isNode, mergeOptions, useAutoTrackPageview, useAutocapture, useIdentify, useJournium, useReset, useTrackEvent, useTrackPageview };
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 };
1504
1620
  //# sourceMappingURL=index.mjs.map