@rudderstack/analytics-js 3.25.1 → 3.26.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.
@@ -512,7 +512,7 @@
512
512
  error.stacktrace=`${stacktrace}\n${MANUAL_ERROR_IDENTIFIER}`;break;case operaSourceloc:default:// eslint-disable-next-line no-param-reassign
513
513
  error['opera#sourceloc']=`${operaSourceloc}\n${MANUAL_ERROR_IDENTIFIER}`;break;}}}globalThis.dispatchEvent(new ErrorEvent('error',{error,bubbles:true,cancelable:true,composed:true}));};
514
514
 
515
- const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.25.1';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
515
+ const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.26.0';const APP_NAMESPACE='com.rudderlabs.javascript';const MODULE_TYPE='npm';const ADBLOCK_PAGE_CATEGORY='RudderJS-Initiated';const ADBLOCK_PAGE_NAME='ad-block page request';const ADBLOCK_PAGE_PATH='/ad-blocked';const GLOBAL_PRELOAD_BUFFER='preloadedEventsBuffer';const CONSENT_TRACK_EVENT_NAME='Consent Management Interaction';
516
516
 
517
517
  const QUERY_PARAM_TRAIT_PREFIX='ajs_trait_';const QUERY_PARAM_PROPERTY_PREFIX='ajs_prop_';const QUERY_PARAM_ANONYMOUS_ID_KEY='ajs_aid';const QUERY_PARAM_USER_ID_KEY='ajs_uid';const QUERY_PARAM_TRACK_EVENT_NAME_KEY='ajs_event';
518
518
 
@@ -540,8 +540,9 @@
540
540
  * Parse query string into preload buffer events & push into existing array before any other events
541
541
  */const retrieveEventsFromQueryString=(argumentsArray=[])=>{// Mapping for trait and properties values based on key prefix
542
542
  const eventArgumentToQueryParamMap={trait:QUERY_PARAM_TRAIT_PREFIX,properties:QUERY_PARAM_PROPERTY_PREFIX};const queryObject=new URLSearchParams(globalThis.location.search);// Add track events with name and properties
543
- if(queryObject.get(QUERY_PARAM_TRACK_EVENT_NAME_KEY)){argumentsArray.unshift(['track',queryObject.get(QUERY_PARAM_TRACK_EVENT_NAME_KEY),getEventDataFromQueryString(queryObject,eventArgumentToQueryParamMap.properties)]);}// Set userId and user traits
544
- if(queryObject.get(QUERY_PARAM_USER_ID_KEY)){argumentsArray.unshift(['identify',queryObject.get(QUERY_PARAM_USER_ID_KEY),getEventDataFromQueryString(queryObject,eventArgumentToQueryParamMap.trait)]);}// Set anonymousID
543
+ if(queryObject.get(QUERY_PARAM_TRACK_EVENT_NAME_KEY)){argumentsArray.unshift(['track',queryObject.get(QUERY_PARAM_TRACK_EVENT_NAME_KEY),getEventDataFromQueryString(queryObject,eventArgumentToQueryParamMap.properties)]);}// Send identify event
544
+ const userId=queryObject.get(QUERY_PARAM_USER_ID_KEY);const userTraits=getEventDataFromQueryString(queryObject,eventArgumentToQueryParamMap.trait);if(userId||isNonEmptyObject(userTraits)){// In identify API, user ID is optional
545
+ const identifyApiArgs=[...(userId?[userId]:[]),userTraits];argumentsArray.unshift(['identify',...identifyApiArgs]);}// Set anonymousID
545
546
  if(queryObject.get(QUERY_PARAM_ANONYMOUS_ID_KEY)){argumentsArray.unshift(['setAnonymousId',queryObject.get(QUERY_PARAM_ANONYMOUS_ID_KEY)]);}};/**
546
547
  * Retrieve an existing buffered load method call and remove from the existing array
547
548
  */const getPreloadedLoadEvent=preloadedEventsArray=>{const loadMethodName='load';let loadEvent=[];/**
@@ -1587,7 +1588,9 @@
1587
1588
  * @param event Incoming event data
1588
1589
  */addEvent(event){this.userSessionManager.refreshSession();const rudderEvent=this.eventFactory.create(event);this.eventRepository.enqueue(rudderEvent,event.callback);}}
1589
1590
 
1590
- class UserSessionManager{constructor(pluginsManager,storeManager,httpClient,errorHandler,logger){this.storeManager=storeManager;this.pluginsManager=pluginsManager;this.logger=logger;this.errorHandler=errorHandler;this.httpClient=httpClient;this.onError=this.onError.bind(this);this.serverSideCookieDebounceFuncs={};}/**
1591
+ class UserSessionManager{/**
1592
+ * Tracks whether a server-side cookie setting request is in progress or not.
1593
+ */constructor(pluginsManager,storeManager,httpClient,errorHandler,logger){this.storeManager=storeManager;this.pluginsManager=pluginsManager;this.logger=logger;this.errorHandler=errorHandler;this.httpClient=httpClient;this.onError=this.onError.bind(this);this.serverSideCookieDebounceFuncs={};this.serverSideCookiesRequestInProgress={};}/**
1591
1594
  * Initialize User session with values from storage
1592
1595
  */init(){this.syncStorageDataToState();// Register the effect to sync with storage
1593
1596
  this.registerEffects();}syncStorageDataToState(){this.migrateStorageIfNeeded();this.migrateDataFromPreviousStorage();// get the values from storage and set it again
@@ -1617,20 +1620,31 @@
1617
1620
  * @param callback
1618
1621
  */makeRequestToSetCookie(encryptedCookieData,callback){this.httpClient?.getAsyncData({url:state.serverCookies.dataServiceUrl.value,options:{method:'POST',data:stringifyWithoutCircular({reqType:'setCookies',workspaceId:state.source.value?.workspaceId,data:{options:{maxAge:state.storage.cookie.value?.maxage,path:state.storage.cookie.value?.path,domain:state.storage.cookie.value?.domain,sameSite:state.storage.cookie.value?.samesite,secure:state.storage.cookie.value?.secure,expires:state.storage.cookie.value?.expires},cookies:encryptedCookieData}}),sendRawData:true,withCredentials:true},isRawResponse:true,callback});}/**
1619
1622
  * A function to make an external request to set the cookie from server side
1620
- * @param key cookie name
1621
- * @param value encrypted cookie value
1622
- */setServerSideCookies(cookiesData,cb,store){try{// encrypt cookies values
1623
- const encryptedCookieData=this.getEncryptedCookieData(cookiesData,store);if(encryptedCookieData.length>0){// make request to data service to set the cookie from server side
1624
- this.makeRequestToSetCookie(encryptedCookieData,(res,details)=>{if(details?.xhr?.status===200){cookiesData.forEach(cData=>{const cookieValue=store?.get(cData.name);const before=stringifyWithoutCircular(cData.value,false,[]);const after=stringifyWithoutCircular(cookieValue,false,[]);if(after!==before){this.logger.error(FAILED_SETTING_COOKIE_FROM_SERVER_ERROR(cData.name));if(cb){cb(cData.name,cData.value);}}});}else {this.logger.error(DATA_SERVER_REQUEST_FAIL_ERROR(details?.xhr?.status));cookiesData.forEach(each=>{if(cb){cb(each.name,each.value);}});}});}}catch(e){this.onError(e,FAILED_SETTING_COOKIE_FROM_SERVER_GLOBAL_ERROR,FAILED_SETTING_COOKIE_FROM_SERVER_GLOBAL_ERROR);cookiesData.forEach(each=>{if(cb){cb(each.name,each.value);}});}}/**
1623
+ * @param sessionToCookiesMap map of session key to cookie name
1624
+ * @param cb callback function to be called when the cookie is set
1625
+ * @param store store to be used to get the cookie value
1626
+ */setServerSideCookies(sessionToCookiesMap,cb,store){// Retrieve the cookie value from the state
1627
+ const sessionKeys=Object.keys(sessionToCookiesMap);const getCurrentCookieValuesFromState=()=>{return sessionKeys.map(sessionKey=>{return {name:sessionToCookiesMap[sessionKey].name,value:state.session[sessionKey].value};});};// Preserve the current cookie values
1628
+ const originalCookieValues={};sessionKeys.forEach(sessionKey=>{originalCookieValues[sessionToCookiesMap[sessionKey].name]=store?.get(sessionToCookiesMap[sessionKey].name);});const clearInProgressFlags=()=>{sessionKeys.forEach(sessionKey=>{this.serverSideCookiesRequestInProgress[sessionKey]=false;});};const setCookiesClientSide=()=>{getCurrentCookieValuesFromState().forEach(each=>{if(cb){cb(each.name,each.value);}});};try{const expectedCookieValues={};sessionKeys.forEach(sessionKey=>{expectedCookieValues[sessionToCookiesMap[sessionKey].name]=state.session[sessionKey].value;});// encrypt cookies values
1629
+ const encryptedCookieData=this.getEncryptedCookieData(getCurrentCookieValuesFromState(),store);if(encryptedCookieData.length>0){// make request to data service to set the cookie from server side
1630
+ this.makeRequestToSetCookie(encryptedCookieData,(res,details)=>{// Mark the cookie req status as done
1631
+ clearInProgressFlags();if(details?.xhr?.status===200){getCurrentCookieValuesFromState().forEach(cData=>{const originalCookieVal=originalCookieValues[cData.name];const currentCookieVal=store?.get(cData.name);// Check if the expected cookie values are set.
1632
+ if(stringifyWithoutCircular(expectedCookieValues[cData.name],false,[])!==stringifyWithoutCircular(currentCookieVal,false,[])){// It's fine if the values don't match as other active SDK sessions might have updated the cookie values
1633
+ // or other cookie requests might have updated the cookie value.
1634
+ // Log an error only when cookie didn't exist previously and currently also doesn't exist.
1635
+ if(isNull(originalCookieVal)&&isNull(currentCookieVal)){this.logger.error(FAILED_SETTING_COOKIE_FROM_SERVER_ERROR(cData.name));}if(cb){cb(cData.name,cData.value);}}});}else {this.logger.error(DATA_SERVER_REQUEST_FAIL_ERROR(details?.xhr?.status));setCookiesClientSide();}});}else {setCookiesClientSide();// Mark the cookie req status as done
1636
+ clearInProgressFlags();}}catch(e){this.onError(e,FAILED_SETTING_COOKIE_FROM_SERVER_GLOBAL_ERROR,FAILED_SETTING_COOKIE_FROM_SERVER_GLOBAL_ERROR);setCookiesClientSide();// Mark the cookie req status as done
1637
+ clearInProgressFlags();}}/**
1625
1638
  * A function to sync values in storage
1626
1639
  * @param sessionKey
1627
- * @param value
1628
- */syncValueToStorage(sessionKey,value){const entries=state.storage.entries.value;const storageType=entries[sessionKey]?.type;if(isStorageTypeValidForStoringData(storageType)){const curStore=this.storeManager?.getStore(storageClientDataStoreNameMap[storageType]);const key=entries[sessionKey]?.key;if(value&&(isString(value)||isNonEmptyObject(value))){// if useServerSideCookies load option is set to true
1640
+ */syncValueToStorage(sessionKey){const entries=state.storage.entries.value;const storageType=entries[sessionKey]?.type;if(isStorageTypeValidForStoringData(storageType)){const curStore=this.storeManager.getStore(storageClientDataStoreNameMap[storageType]);const cookieName=entries[sessionKey]?.key;const cookieValue=state.session[sessionKey].value;if(cookieValue&&(isString(cookieValue)||isNonEmptyObject(cookieValue))){// if useServerSideCookies load option is set to true
1629
1641
  // set the cookie from server side
1630
- if(state.serverCookies.isEnabledServerSideCookies.value&&storageType===COOKIE_STORAGE){if(this.serverSideCookieDebounceFuncs[sessionKey]){globalThis.clearTimeout(this.serverSideCookieDebounceFuncs[sessionKey]);}this.serverSideCookieDebounceFuncs[sessionKey]=globalThis.setTimeout(()=>{this.setServerSideCookies([{name:key,value}],(cookieName,cookieValue)=>{curStore?.set(cookieName,cookieValue);},curStore);},SERVER_SIDE_COOKIES_DEBOUNCE_TIME);}else {curStore?.set(key,value);}}else {curStore?.remove(key);}}}/**
1642
+ if(state.serverCookies.isEnabledServerSideCookies.value&&storageType===COOKIE_STORAGE){// Mark the requests as in progress.
1643
+ this.serverSideCookiesRequestInProgress[sessionKey]=true;if(this.serverSideCookieDebounceFuncs[sessionKey]){globalThis.clearTimeout(this.serverSideCookieDebounceFuncs[sessionKey]);}this.serverSideCookieDebounceFuncs[sessionKey]=globalThis.setTimeout(()=>{// Create a map of session key to cookie name
1644
+ const sessionToCookiesMap={[sessionKey]:{name:cookieName}};this.setServerSideCookies(sessionToCookiesMap,(cookieName,cookieValue)=>{curStore?.set(cookieName,cookieValue);},curStore);},SERVER_SIDE_COOKIES_DEBOUNCE_TIME);}else {curStore?.set(cookieName,cookieValue);}}else {curStore?.remove(cookieName);}}}/**
1631
1645
  * Function to update storage whenever state value changes
1632
1646
  */registerEffects(){// This will work as long as the user session entry key names are same as the state keys
1633
- USER_SESSION_KEYS.forEach(sessionKey=>{E(()=>{this.syncValueToStorage(sessionKey,state.session[sessionKey].value);});});}/**
1647
+ USER_SESSION_KEYS.forEach(sessionKey=>{E(()=>{this.syncValueToStorage(sessionKey);});});}/**
1634
1648
  * Sets anonymous id in the following precedence:
1635
1649
  *
1636
1650
  * 1. anonymousId: Id directly provided to the function.
@@ -1647,30 +1661,37 @@
1647
1661
  // This is needed for entries that are fetched from the storage
1648
1662
  // during the current session (for example, session info)
1649
1663
  this.migrateStorageIfNeeded([store],[sessionKey]);const storageKey=entries[sessionKey]?.key;return store?.get(storageKey)??null;}return null;}getExternalAnonymousIdByCookieName(key){const storageEngine=getStorageEngine(COOKIE_STORAGE);if(storageEngine?.isEnabled){return storageEngine.getItem(key)??null;}return null;}/**
1664
+ * Fetches the value for a session key. Preferably from storage, if the server-side
1665
+ * cookies request is not in progress. Otherwise, from the state.
1666
+ * @param sessionKey - The session key to fetch the value for
1667
+ * @returns - The value for the session key
1668
+ */getUserSessionValue(sessionKey){// If the server-side cookies request is in progress, fetch the value from the state.
1669
+ if(this.serverSideCookiesRequestInProgress[sessionKey]){return state.session[sessionKey].value;}// Otherwise, fetch the value from storage.
1670
+ return this.getEntryValue(sessionKey);}/**
1650
1671
  * Fetches User Id
1651
1672
  * @returns
1652
- */getUserId(){return this.getEntryValue('userId');}/**
1673
+ */getUserId(){return this.getUserSessionValue('userId');}/**
1653
1674
  * Fetches User Traits
1654
1675
  * @returns
1655
- */getUserTraits(){return this.getEntryValue('userTraits');}/**
1676
+ */getUserTraits(){return this.getUserSessionValue('userTraits');}/**
1656
1677
  * Fetches Group Id
1657
1678
  * @returns
1658
- */getGroupId(){return this.getEntryValue('groupId');}/**
1679
+ */getGroupId(){return this.getUserSessionValue('groupId');}/**
1659
1680
  * Fetches Group Traits
1660
1681
  * @returns
1661
- */getGroupTraits(){return this.getEntryValue('groupTraits');}/**
1682
+ */getGroupTraits(){return this.getUserSessionValue('groupTraits');}/**
1662
1683
  * Fetches Initial Referrer
1663
1684
  * @returns
1664
- */getInitialReferrer(){return this.getEntryValue('initialReferrer');}/**
1685
+ */getInitialReferrer(){return this.getUserSessionValue('initialReferrer');}/**
1665
1686
  * Fetches Initial Referring domain
1666
1687
  * @returns
1667
- */getInitialReferringDomain(){return this.getEntryValue('initialReferringDomain');}/**
1688
+ */getInitialReferringDomain(){return this.getUserSessionValue('initialReferringDomain');}/**
1668
1689
  * Fetches session tracking information from storage
1669
1690
  * @returns
1670
- */getSessionInfo(){return this.getEntryValue('sessionInfo');}/**
1691
+ */getSessionInfo(){return this.getUserSessionValue('sessionInfo');}/**
1671
1692
  * Fetches auth token from storage
1672
1693
  * @returns
1673
- */getAuthToken(){return this.getEntryValue('authToken');}/**
1694
+ */getAuthToken(){return this.getUserSessionValue('authToken');}/**
1674
1695
  * If session is active it returns the sessionId
1675
1696
  * @returns
1676
1697
  */getSessionId(){const sessionInfo=this.getSessionInfo()??DEFAULT_USER_SESSION_VALUES.sessionInfo;if(sessionInfo.autoTrack&&!hasSessionExpired(sessionInfo)||sessionInfo.manualTrack){return sessionInfo.id??null;}return null;}/**
@@ -1686,7 +1707,7 @@
1686
1707
  if(sessionInfo.sessionStart===undefined){sessionInfo={...sessionInfo,sessionStart:true};}else if(sessionInfo.sessionStart){sessionInfo={...sessionInfo,sessionStart:false};}}// Always write to state (in-turn to storage) to keep the session info up to date.
1687
1708
  state.session.sessionInfo.value=sessionInfo;if(state.lifecycle.status.value!=='readyExecuted'){// Force update the storage as the 'effect' blocks are not getting triggered
1688
1709
  // when processing preload buffered requests
1689
- this.syncValueToStorage('sessionInfo',sessionInfo);}}resetAndStartNewSession(){const session=state.session;const{manualTrack,autoTrack,timeout,cutOff}=session.sessionInfo.value;if(autoTrack){const sessionInfo={...DEFAULT_USER_SESSION_VALUES.sessionInfo,timeout};if(cutOff){sessionInfo.cutOff={enabled:cutOff.enabled,duration:cutOff.duration};}session.sessionInfo.value=sessionInfo;this.startOrRenewAutoTracking(session.sessionInfo.value);}else if(manualTrack){this.startManualTrackingInternal();}}/**
1710
+ this.syncValueToStorage('sessionInfo');}}resetAndStartNewSession(){const session=state.session;const{manualTrack,autoTrack,timeout,cutOff}=session.sessionInfo.value;if(autoTrack){const sessionInfo={...DEFAULT_USER_SESSION_VALUES.sessionInfo,timeout};if(cutOff){sessionInfo.cutOff={enabled:cutOff.enabled,duration:cutOff.duration};}session.sessionInfo.value=sessionInfo;this.startOrRenewAutoTracking(session.sessionInfo.value);}else if(manualTrack){this.startManualTrackingInternal();}}/**
1690
1711
  * Reset state values
1691
1712
  * @param options options for reset
1692
1713
  * @returns
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rudderstack/analytics-js",
3
- "version": "3.25.1",
3
+ "version": "3.26.0",
4
4
  "description": "RudderStack JavaScript SDK",
5
5
  "main": "dist/npm/modern/cjs/index.cjs",
6
6
  "module": "dist/npm/modern/esm/index.mjs",