@journium/react 1.1.1 → 1.1.2
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.cjs +120 -99
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +120 -99
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -705,6 +705,9 @@ class JourniumClient {
|
|
|
705
705
|
this.queue = [];
|
|
706
706
|
this.stagedEvents = [];
|
|
707
707
|
this.flushTimer = null;
|
|
708
|
+
this.remoteOptionsRefreshTimer = null;
|
|
709
|
+
this.isRefreshing = false;
|
|
710
|
+
this.lastRemoteOptions = null;
|
|
708
711
|
this.initializationComplete = false;
|
|
709
712
|
this.initializationFailed = false;
|
|
710
713
|
this.optionsChangeCallbacks = new Set();
|
|
@@ -758,61 +761,24 @@ class JourniumClient {
|
|
|
758
761
|
}
|
|
759
762
|
}
|
|
760
763
|
async initializeAsync() {
|
|
761
|
-
var _a;
|
|
762
764
|
try {
|
|
763
765
|
Logger.log('Journium: Starting initialization - fetching fresh remote config...');
|
|
764
|
-
// Step 1: Try to fetch fresh remote config with timeout and retry
|
|
765
766
|
const remoteOptions = await this.fetchRemoteOptionsWithRetry();
|
|
766
|
-
if (remoteOptions) {
|
|
767
|
-
|
|
768
|
-
this.
|
|
769
|
-
|
|
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
|
-
}
|
|
778
|
-
}
|
|
779
|
-
else {
|
|
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
|
-
} */
|
|
798
|
-
}
|
|
799
|
-
// Step 6: Update identity manager session timeout if provided
|
|
800
|
-
if (this.effectiveOptions.sessionTimeout) {
|
|
801
|
-
this.identityManager.updateSessionTimeout(this.effectiveOptions.sessionTimeout);
|
|
767
|
+
if (!remoteOptions) {
|
|
768
|
+
Logger.error('Journium: Initialization failed - no remote config available');
|
|
769
|
+
this.initializationFailed = true;
|
|
770
|
+
return;
|
|
802
771
|
}
|
|
803
|
-
|
|
804
|
-
Logger.
|
|
805
|
-
// Step 8: Mark initialization as complete
|
|
772
|
+
this.applyRemoteOptions(remoteOptions);
|
|
773
|
+
Logger.log('Journium: Effective options after init:', this.effectiveOptions);
|
|
806
774
|
this.initializationComplete = true;
|
|
807
775
|
this.initializationFailed = false;
|
|
808
|
-
// Step 9: Process any staged events
|
|
809
776
|
this.processStagedEvents();
|
|
810
|
-
// Step 10: Start flush timer
|
|
811
777
|
if (this.effectiveOptions.flushInterval && this.effectiveOptions.flushInterval > 0) {
|
|
812
778
|
this.startFlushTimer();
|
|
813
779
|
}
|
|
814
|
-
|
|
815
|
-
|
|
780
|
+
this.startRemoteOptionsRefreshTimer();
|
|
781
|
+
Logger.log('Journium: Initialization complete');
|
|
816
782
|
this.notifyOptionsChange();
|
|
817
783
|
}
|
|
818
784
|
catch (error) {
|
|
@@ -883,35 +849,21 @@ class JourniumClient {
|
|
|
883
849
|
processStagedEvents() {
|
|
884
850
|
if (this.stagedEvents.length === 0)
|
|
885
851
|
return;
|
|
852
|
+
if (this.ingestionPaused) {
|
|
853
|
+
Logger.warn(`Journium: Ingestion is paused — discarding ${this.stagedEvents.length} staged events`);
|
|
854
|
+
this.stagedEvents = [];
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
886
857
|
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
858
|
for (const stagedEvent of this.stagedEvents) {
|
|
891
|
-
|
|
892
|
-
const eventWithIdentity = {
|
|
859
|
+
this.queue.push({
|
|
893
860
|
...stagedEvent,
|
|
894
|
-
properties:
|
|
895
|
-
|
|
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);
|
|
861
|
+
properties: this.buildIdentityProperties(stagedEvent.properties),
|
|
862
|
+
});
|
|
908
863
|
}
|
|
909
|
-
// Clear staged events
|
|
910
864
|
this.stagedEvents = [];
|
|
911
865
|
Logger.log('Journium: Staged events processed and moved to main queue');
|
|
912
|
-
// Check if we should flush immediately
|
|
913
866
|
if (this.queue.length >= this.effectiveOptions.flushAt) {
|
|
914
|
-
// console.log('1 Journium: Flushing events...'+JSON.stringify(this.effectiveOptions));
|
|
915
867
|
this.flush();
|
|
916
868
|
}
|
|
917
869
|
}
|
|
@@ -919,12 +871,88 @@ class JourniumClient {
|
|
|
919
871
|
if (this.flushTimer) {
|
|
920
872
|
clearInterval(this.flushTimer);
|
|
921
873
|
}
|
|
922
|
-
// Use universal setInterval (works in both browser and Node.js)
|
|
923
874
|
this.flushTimer = setInterval(() => {
|
|
924
|
-
// console.log('2 Journium: Flushing events...'+JSON.stringify(this.effectiveOptions));
|
|
925
875
|
this.flush();
|
|
926
876
|
}, this.effectiveOptions.flushInterval);
|
|
927
877
|
}
|
|
878
|
+
startRemoteOptionsRefreshTimer() {
|
|
879
|
+
// Clear any existing timer to prevent duplicate intervals
|
|
880
|
+
if (this.remoteOptionsRefreshTimer) {
|
|
881
|
+
clearInterval(this.remoteOptionsRefreshTimer);
|
|
882
|
+
this.remoteOptionsRefreshTimer = null;
|
|
883
|
+
}
|
|
884
|
+
this.remoteOptionsRefreshTimer = setInterval(() => {
|
|
885
|
+
this.refreshRemoteOptions();
|
|
886
|
+
}, JourniumClient.REMOTE_OPTIONS_REFRESH_INTERVAL);
|
|
887
|
+
Logger.log(`Journium: Scheduling remote options refresh every ${JourniumClient.REMOTE_OPTIONS_REFRESH_INTERVAL}ms`);
|
|
888
|
+
}
|
|
889
|
+
async refreshRemoteOptions() {
|
|
890
|
+
if (this.isRefreshing) {
|
|
891
|
+
Logger.log('Journium: Remote options refresh already in progress, skipping');
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
this.isRefreshing = true;
|
|
895
|
+
Logger.log('Journium: Periodic remote options refresh triggered');
|
|
896
|
+
try {
|
|
897
|
+
const remoteOptions = await this.fetchRemoteOptionsWithRetry();
|
|
898
|
+
if (!remoteOptions) {
|
|
899
|
+
Logger.warn('Journium: Periodic remote options refresh failed, keeping current options');
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
const prevRemoteSnapshot = JSON.stringify(this.lastRemoteOptions);
|
|
903
|
+
const prevFlushInterval = this.effectiveOptions.flushInterval;
|
|
904
|
+
this.applyRemoteOptions(remoteOptions);
|
|
905
|
+
if (prevRemoteSnapshot === JSON.stringify(this.lastRemoteOptions)) {
|
|
906
|
+
Logger.log('Journium: Remote options unchanged after refresh, no update needed');
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
Logger.log('Journium: Remote options updated after refresh:', this.effectiveOptions);
|
|
910
|
+
if (this.effectiveOptions.flushInterval !== prevFlushInterval) {
|
|
911
|
+
if (this.effectiveOptions.flushInterval && this.effectiveOptions.flushInterval > 0) {
|
|
912
|
+
this.startFlushTimer();
|
|
913
|
+
}
|
|
914
|
+
else if (this.flushTimer) {
|
|
915
|
+
clearInterval(this.flushTimer);
|
|
916
|
+
this.flushTimer = null;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
this.notifyOptionsChange();
|
|
920
|
+
}
|
|
921
|
+
catch (error) {
|
|
922
|
+
Logger.error('Journium: Periodic remote options refresh encountered an error:', error);
|
|
923
|
+
}
|
|
924
|
+
finally {
|
|
925
|
+
this.isRefreshing = false;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
applyRemoteOptions(remoteOptions) {
|
|
929
|
+
var _a;
|
|
930
|
+
this.lastRemoteOptions = remoteOptions;
|
|
931
|
+
this.effectiveOptions = this.config.options
|
|
932
|
+
? mergeOptions(this.config.options, remoteOptions)
|
|
933
|
+
: remoteOptions;
|
|
934
|
+
this.saveCachedOptions(remoteOptions);
|
|
935
|
+
if (this.effectiveOptions.sessionTimeout) {
|
|
936
|
+
this.identityManager.updateSessionTimeout(this.effectiveOptions.sessionTimeout);
|
|
937
|
+
}
|
|
938
|
+
Logger.setDebug((_a = this.effectiveOptions.debug) !== null && _a !== void 0 ? _a : false);
|
|
939
|
+
}
|
|
940
|
+
buildIdentityProperties(userProperties = {}) {
|
|
941
|
+
const identity = this.identityManager.getIdentity();
|
|
942
|
+
const userAgentInfo = this.identityManager.getUserAgentInfo();
|
|
943
|
+
return {
|
|
944
|
+
$device_id: identity === null || identity === void 0 ? void 0 : identity.$device_id,
|
|
945
|
+
distinct_id: identity === null || identity === void 0 ? void 0 : identity.distinct_id,
|
|
946
|
+
$session_id: identity === null || identity === void 0 ? void 0 : identity.$session_id,
|
|
947
|
+
$is_identified: (identity === null || identity === void 0 ? void 0 : identity.$user_state) === 'identified',
|
|
948
|
+
$current_url: typeof window !== 'undefined' ? window.location.href : '',
|
|
949
|
+
$pathname: typeof window !== 'undefined' ? window.location.pathname : '',
|
|
950
|
+
...userAgentInfo,
|
|
951
|
+
$lib_version: '0.1.0', // TODO: Get from package.json
|
|
952
|
+
$platform: 'web',
|
|
953
|
+
...userProperties,
|
|
954
|
+
};
|
|
955
|
+
}
|
|
928
956
|
async sendEvents(events) {
|
|
929
957
|
if (!events.length)
|
|
930
958
|
return;
|
|
@@ -984,9 +1012,7 @@ class JourniumClient {
|
|
|
984
1012
|
event,
|
|
985
1013
|
properties: { ...properties }, // Only user properties for now
|
|
986
1014
|
};
|
|
987
|
-
// Stage events during initialization, add to queue after initialization
|
|
988
1015
|
if (!this.initializationComplete) {
|
|
989
|
-
// If initialization failed, reject events
|
|
990
1016
|
if (this.initializationFailed) {
|
|
991
1017
|
Logger.warn('Journium: track() call rejected - initialization failed');
|
|
992
1018
|
return;
|
|
@@ -995,34 +1021,17 @@ class JourniumClient {
|
|
|
995
1021
|
Logger.log('Journium: Event staged during initialization', journiumEvent);
|
|
996
1022
|
return;
|
|
997
1023
|
}
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
Logger.warn('Journium: track() call rejected - initialization failed');
|
|
1024
|
+
if (this.ingestionPaused) {
|
|
1025
|
+
Logger.warn('Journium: Ingestion is paused — event dropped:', journiumEvent.event);
|
|
1001
1026
|
return;
|
|
1002
1027
|
}
|
|
1003
|
-
// Add identity properties for immediate events (after initialization)
|
|
1004
|
-
const identity = this.identityManager.getIdentity();
|
|
1005
|
-
const userAgentInfo = this.identityManager.getUserAgentInfo();
|
|
1006
1028
|
const eventWithIdentity = {
|
|
1007
1029
|
...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
|
-
},
|
|
1030
|
+
properties: this.buildIdentityProperties(properties),
|
|
1020
1031
|
};
|
|
1021
1032
|
this.queue.push(eventWithIdentity);
|
|
1022
1033
|
Logger.log('Journium: Event tracked', eventWithIdentity);
|
|
1023
|
-
// Only flush if we have effective options (after initialization)
|
|
1024
1034
|
if (this.effectiveOptions.flushAt && this.queue.length >= this.effectiveOptions.flushAt) {
|
|
1025
|
-
// console.log('3 Journium: Flushing events...'+JSON.stringify(this.effectiveOptions));
|
|
1026
1035
|
this.flush();
|
|
1027
1036
|
}
|
|
1028
1037
|
}
|
|
@@ -1049,12 +1058,20 @@ class JourniumClient {
|
|
|
1049
1058
|
clearInterval(this.flushTimer);
|
|
1050
1059
|
this.flushTimer = null;
|
|
1051
1060
|
}
|
|
1061
|
+
if (this.remoteOptionsRefreshTimer) {
|
|
1062
|
+
clearInterval(this.remoteOptionsRefreshTimer);
|
|
1063
|
+
this.remoteOptionsRefreshTimer = null;
|
|
1064
|
+
}
|
|
1052
1065
|
this.flush();
|
|
1053
1066
|
}
|
|
1054
1067
|
getEffectiveOptions() {
|
|
1055
1068
|
return this.effectiveOptions;
|
|
1056
1069
|
}
|
|
1070
|
+
get ingestionPaused() {
|
|
1071
|
+
return this.effectiveOptions['ingestionPaused'] === true;
|
|
1072
|
+
}
|
|
1057
1073
|
}
|
|
1074
|
+
JourniumClient.REMOTE_OPTIONS_REFRESH_INTERVAL = 15 * 60 * 1000; // 15 minutes
|
|
1058
1075
|
|
|
1059
1076
|
class PageviewTracker {
|
|
1060
1077
|
constructor(client) {
|
|
@@ -1081,10 +1098,13 @@ class PageviewTracker {
|
|
|
1081
1098
|
}
|
|
1082
1099
|
/**
|
|
1083
1100
|
* Start automatic autocapture for pageviews
|
|
1084
|
-
* @
|
|
1101
|
+
* @param captureInitialPageview - whether to fire a $pageview immediately on start (default: true).
|
|
1102
|
+
* Pass false when restarting after a remote options update to avoid a spurious pageview.
|
|
1085
1103
|
*/
|
|
1086
|
-
startAutoPageviewTracking() {
|
|
1087
|
-
|
|
1104
|
+
startAutoPageviewTracking(captureInitialPageview = true) {
|
|
1105
|
+
if (captureInitialPageview) {
|
|
1106
|
+
this.capturePageview();
|
|
1107
|
+
}
|
|
1088
1108
|
if (typeof window !== 'undefined') {
|
|
1089
1109
|
// Store original methods for cleanup
|
|
1090
1110
|
this.originalPushState = window.history.pushState;
|
|
@@ -1603,21 +1623,22 @@ class JourniumAnalytics {
|
|
|
1603
1623
|
* Handle effective options change (e.g., when remote options are fetched)
|
|
1604
1624
|
*/
|
|
1605
1625
|
handleOptionsChange(effectiveOptions) {
|
|
1606
|
-
//
|
|
1626
|
+
// If autocapture was never started before, this is the initial options application
|
|
1627
|
+
// (async init completed) — treat it like a page load and capture a pageview.
|
|
1628
|
+
// If it was already started, this is a periodic remote options update — only
|
|
1629
|
+
// re-register listeners without emitting a spurious pageview.
|
|
1630
|
+
const isFirstStart = !this.autocaptureStarted;
|
|
1607
1631
|
if (this.autocaptureStarted) {
|
|
1608
1632
|
this.pageviewTracker.stopAutocapture();
|
|
1609
1633
|
this.autocaptureTracker.stop();
|
|
1610
1634
|
this.autocaptureStarted = false;
|
|
1611
1635
|
}
|
|
1612
|
-
// Evaluate if autocapture should be enabled with new options
|
|
1613
1636
|
const autoTrackPageviews = effectiveOptions.autoTrackPageviews !== false;
|
|
1614
1637
|
const autocaptureEnabled = effectiveOptions.autocapture !== false;
|
|
1615
|
-
// Update autocapture tracker options
|
|
1616
1638
|
const autocaptureOptions = this.resolveAutocaptureOptions(effectiveOptions.autocapture);
|
|
1617
1639
|
this.autocaptureTracker.updateOptions(autocaptureOptions);
|
|
1618
|
-
// Start autocapture based on new options (even if it wasn't started before)
|
|
1619
1640
|
if (autoTrackPageviews) {
|
|
1620
|
-
this.pageviewTracker.startAutoPageviewTracking();
|
|
1641
|
+
this.pageviewTracker.startAutoPageviewTracking(isFirstStart);
|
|
1621
1642
|
}
|
|
1622
1643
|
if (autocaptureEnabled) {
|
|
1623
1644
|
this.autocaptureTracker.start();
|