@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.
- package/CHANGELOG.md +12 -0
- package/dist/npm/legacy/bundled/cjs/index.cjs +44 -23
- package/dist/npm/legacy/bundled/esm/index.mjs +44 -23
- package/dist/npm/legacy/bundled/umd/index.js +44 -23
- package/dist/npm/legacy/cjs/index.cjs +44 -23
- package/dist/npm/legacy/content-script/cjs/index.cjs +44 -23
- package/dist/npm/legacy/content-script/esm/index.mjs +44 -23
- package/dist/npm/legacy/content-script/umd/index.js +44 -23
- package/dist/npm/legacy/esm/index.mjs +44 -23
- package/dist/npm/legacy/umd/index.js +44 -23
- package/dist/npm/modern/bundled/cjs/index.cjs +43 -22
- package/dist/npm/modern/bundled/esm/index.mjs +43 -22
- package/dist/npm/modern/bundled/umd/index.js +43 -22
- package/dist/npm/modern/cjs/index.cjs +43 -22
- package/dist/npm/modern/content-script/cjs/index.cjs +43 -22
- package/dist/npm/modern/content-script/esm/index.mjs +43 -22
- package/dist/npm/modern/content-script/umd/index.js +43 -22
- package/dist/npm/modern/esm/index.mjs +43 -22
- package/dist/npm/modern/umd/index.js +43 -22
- package/package.json +1 -1
|
@@ -519,7 +519,7 @@ error.stack=`${stack}\n${MANUAL_ERROR_IDENTIFIER}`;break;case stacktrace:// esli
|
|
|
519
519
|
error.stacktrace=`${stacktrace}\n${MANUAL_ERROR_IDENTIFIER}`;break;case operaSourceloc:default:// eslint-disable-next-line no-param-reassign
|
|
520
520
|
error['opera#sourceloc']=`${operaSourceloc}\n${MANUAL_ERROR_IDENTIFIER}`;break;}}}globalThis.dispatchEvent(new ErrorEvent('error',{error,bubbles:true,cancelable:true,composed:true}));};
|
|
521
521
|
|
|
522
|
-
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.
|
|
522
|
+
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';
|
|
523
523
|
|
|
524
524
|
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';
|
|
525
525
|
|
|
@@ -547,8 +547,9 @@ data[dataKey]=params.get(key);}});return data;};/**
|
|
|
547
547
|
* Parse query string into preload buffer events & push into existing array before any other events
|
|
548
548
|
*/const retrieveEventsFromQueryString=(argumentsArray=[])=>{// Mapping for trait and properties values based on key prefix
|
|
549
549
|
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
|
|
550
|
-
if(queryObject.get(QUERY_PARAM_TRACK_EVENT_NAME_KEY)){argumentsArray.unshift(['track',queryObject.get(QUERY_PARAM_TRACK_EVENT_NAME_KEY),getEventDataFromQueryString(queryObject,eventArgumentToQueryParamMap.properties)]);}//
|
|
551
|
-
|
|
550
|
+
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
|
|
551
|
+
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
|
|
552
|
+
const identifyApiArgs=[...(userId?[userId]:[]),userTraits];argumentsArray.unshift(['identify',...identifyApiArgs]);}// Set anonymousID
|
|
552
553
|
if(queryObject.get(QUERY_PARAM_ANONYMOUS_ID_KEY)){argumentsArray.unshift(['setAnonymousId',queryObject.get(QUERY_PARAM_ANONYMOUS_ID_KEY)]);}};/**
|
|
553
554
|
* Retrieve an existing buffered load method call and remove from the existing array
|
|
554
555
|
*/const getPreloadedLoadEvent=preloadedEventsArray=>{const loadMethodName='load';let loadEvent=[];/**
|
|
@@ -3293,7 +3294,9 @@ enrichedEvent.userId=to??enrichedEvent.userId;return enrichedEvent;}/**
|
|
|
3293
3294
|
* @param event Incoming event data
|
|
3294
3295
|
*/addEvent(event){this.userSessionManager.refreshSession();const rudderEvent=this.eventFactory.create(event);this.eventRepository.enqueue(rudderEvent,event.callback);}}
|
|
3295
3296
|
|
|
3296
|
-
class UserSessionManager{
|
|
3297
|
+
class UserSessionManager{/**
|
|
3298
|
+
* Tracks whether a server-side cookie setting request is in progress or not.
|
|
3299
|
+
*/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={};}/**
|
|
3297
3300
|
* Initialize User session with values from storage
|
|
3298
3301
|
*/init(){this.syncStorageDataToState();// Register the effect to sync with storage
|
|
3299
3302
|
this.registerEffects();}syncStorageDataToState(){this.migrateStorageIfNeeded();this.migrateDataFromPreviousStorage();// get the values from storage and set it again
|
|
@@ -3323,20 +3326,31 @@ cutOffDuration=DEFAULT_SESSION_CUT_OFF_DURATION_MS;}else if(cutOffDuration<sessi
|
|
|
3323
3326
|
* @param callback
|
|
3324
3327
|
*/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});}/**
|
|
3325
3328
|
* A function to make an external request to set the cookie from server side
|
|
3326
|
-
* @param key
|
|
3327
|
-
* @param
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3329
|
+
* @param sessionToCookiesMap map of session key to cookie name
|
|
3330
|
+
* @param cb callback function to be called when the cookie is set
|
|
3331
|
+
* @param store store to be used to get the cookie value
|
|
3332
|
+
*/setServerSideCookies(sessionToCookiesMap,cb,store){// Retrieve the cookie value from the state
|
|
3333
|
+
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
|
|
3334
|
+
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
|
|
3335
|
+
const encryptedCookieData=this.getEncryptedCookieData(getCurrentCookieValuesFromState(),store);if(encryptedCookieData.length>0){// make request to data service to set the cookie from server side
|
|
3336
|
+
this.makeRequestToSetCookie(encryptedCookieData,(res,details)=>{// Mark the cookie req status as done
|
|
3337
|
+
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.
|
|
3338
|
+
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
|
|
3339
|
+
// or other cookie requests might have updated the cookie value.
|
|
3340
|
+
// Log an error only when cookie didn't exist previously and currently also doesn't exist.
|
|
3341
|
+
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
|
|
3342
|
+
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
|
|
3343
|
+
clearInProgressFlags();}}/**
|
|
3331
3344
|
* A function to sync values in storage
|
|
3332
3345
|
* @param sessionKey
|
|
3333
|
-
|
|
3334
|
-
*/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
|
|
3346
|
+
*/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
|
|
3335
3347
|
// set the cookie from server side
|
|
3336
|
-
if(state.serverCookies.isEnabledServerSideCookies.value&&storageType===COOKIE_STORAGE){
|
|
3348
|
+
if(state.serverCookies.isEnabledServerSideCookies.value&&storageType===COOKIE_STORAGE){// Mark the requests as in progress.
|
|
3349
|
+
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
|
|
3350
|
+
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);}}}/**
|
|
3337
3351
|
* Function to update storage whenever state value changes
|
|
3338
3352
|
*/registerEffects(){// This will work as long as the user session entry key names are same as the state keys
|
|
3339
|
-
USER_SESSION_KEYS.forEach(sessionKey=>{E(()=>{this.syncValueToStorage(sessionKey
|
|
3353
|
+
USER_SESSION_KEYS.forEach(sessionKey=>{E(()=>{this.syncValueToStorage(sessionKey);});});}/**
|
|
3340
3354
|
* Sets anonymous id in the following precedence:
|
|
3341
3355
|
*
|
|
3342
3356
|
* 1. anonymousId: Id directly provided to the function.
|
|
@@ -3353,30 +3367,37 @@ const autoCapturedAnonymousId=this.pluginsManager?.invokeSingle('storage.getAnon
|
|
|
3353
3367
|
// This is needed for entries that are fetched from the storage
|
|
3354
3368
|
// during the current session (for example, session info)
|
|
3355
3369
|
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;}/**
|
|
3370
|
+
* Fetches the value for a session key. Preferably from storage, if the server-side
|
|
3371
|
+
* cookies request is not in progress. Otherwise, from the state.
|
|
3372
|
+
* @param sessionKey - The session key to fetch the value for
|
|
3373
|
+
* @returns - The value for the session key
|
|
3374
|
+
*/getUserSessionValue(sessionKey){// If the server-side cookies request is in progress, fetch the value from the state.
|
|
3375
|
+
if(this.serverSideCookiesRequestInProgress[sessionKey]){return state.session[sessionKey].value;}// Otherwise, fetch the value from storage.
|
|
3376
|
+
return this.getEntryValue(sessionKey);}/**
|
|
3356
3377
|
* Fetches User Id
|
|
3357
3378
|
* @returns
|
|
3358
|
-
*/getUserId(){return this.
|
|
3379
|
+
*/getUserId(){return this.getUserSessionValue('userId');}/**
|
|
3359
3380
|
* Fetches User Traits
|
|
3360
3381
|
* @returns
|
|
3361
|
-
*/getUserTraits(){return this.
|
|
3382
|
+
*/getUserTraits(){return this.getUserSessionValue('userTraits');}/**
|
|
3362
3383
|
* Fetches Group Id
|
|
3363
3384
|
* @returns
|
|
3364
|
-
*/getGroupId(){return this.
|
|
3385
|
+
*/getGroupId(){return this.getUserSessionValue('groupId');}/**
|
|
3365
3386
|
* Fetches Group Traits
|
|
3366
3387
|
* @returns
|
|
3367
|
-
*/getGroupTraits(){return this.
|
|
3388
|
+
*/getGroupTraits(){return this.getUserSessionValue('groupTraits');}/**
|
|
3368
3389
|
* Fetches Initial Referrer
|
|
3369
3390
|
* @returns
|
|
3370
|
-
*/getInitialReferrer(){return this.
|
|
3391
|
+
*/getInitialReferrer(){return this.getUserSessionValue('initialReferrer');}/**
|
|
3371
3392
|
* Fetches Initial Referring domain
|
|
3372
3393
|
* @returns
|
|
3373
|
-
*/getInitialReferringDomain(){return this.
|
|
3394
|
+
*/getInitialReferringDomain(){return this.getUserSessionValue('initialReferringDomain');}/**
|
|
3374
3395
|
* Fetches session tracking information from storage
|
|
3375
3396
|
* @returns
|
|
3376
|
-
*/getSessionInfo(){return this.
|
|
3397
|
+
*/getSessionInfo(){return this.getUserSessionValue('sessionInfo');}/**
|
|
3377
3398
|
* Fetches auth token from storage
|
|
3378
3399
|
* @returns
|
|
3379
|
-
*/getAuthToken(){return this.
|
|
3400
|
+
*/getAuthToken(){return this.getUserSessionValue('authToken');}/**
|
|
3380
3401
|
* If session is active it returns the sessionId
|
|
3381
3402
|
* @returns
|
|
3382
3403
|
*/getSessionId(){const sessionInfo=this.getSessionInfo()??DEFAULT_USER_SESSION_VALUES.sessionInfo;if(sessionInfo.autoTrack&&!hasSessionExpired(sessionInfo)||sessionInfo.manualTrack){return sessionInfo.id??null;}return null;}/**
|
|
@@ -3392,7 +3413,7 @@ this.migrateStorageIfNeeded([store],[sessionKey]);const storageKey=entries[sessi
|
|
|
3392
3413
|
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.
|
|
3393
3414
|
state.session.sessionInfo.value=sessionInfo;if(state.lifecycle.status.value!=='readyExecuted'){// Force update the storage as the 'effect' blocks are not getting triggered
|
|
3394
3415
|
// when processing preload buffered requests
|
|
3395
|
-
this.syncValueToStorage('sessionInfo'
|
|
3416
|
+
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();}}/**
|
|
3396
3417
|
* Reset state values
|
|
3397
3418
|
* @param options options for reset
|
|
3398
3419
|
* @returns
|
|
@@ -515,7 +515,7 @@ error.stack=`${stack}\n${MANUAL_ERROR_IDENTIFIER}`;break;case stacktrace:// esli
|
|
|
515
515
|
error.stacktrace=`${stacktrace}\n${MANUAL_ERROR_IDENTIFIER}`;break;case operaSourceloc:default:// eslint-disable-next-line no-param-reassign
|
|
516
516
|
error['opera#sourceloc']=`${operaSourceloc}\n${MANUAL_ERROR_IDENTIFIER}`;break;}}}globalThis.dispatchEvent(new ErrorEvent('error',{error,bubbles:true,cancelable:true,composed:true}));};
|
|
517
517
|
|
|
518
|
-
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.
|
|
518
|
+
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';
|
|
519
519
|
|
|
520
520
|
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';
|
|
521
521
|
|
|
@@ -543,8 +543,9 @@ data[dataKey]=params.get(key);}});return data;};/**
|
|
|
543
543
|
* Parse query string into preload buffer events & push into existing array before any other events
|
|
544
544
|
*/const retrieveEventsFromQueryString=(argumentsArray=[])=>{// Mapping for trait and properties values based on key prefix
|
|
545
545
|
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
|
|
546
|
-
if(queryObject.get(QUERY_PARAM_TRACK_EVENT_NAME_KEY)){argumentsArray.unshift(['track',queryObject.get(QUERY_PARAM_TRACK_EVENT_NAME_KEY),getEventDataFromQueryString(queryObject,eventArgumentToQueryParamMap.properties)]);}//
|
|
547
|
-
|
|
546
|
+
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
|
|
547
|
+
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
|
|
548
|
+
const identifyApiArgs=[...(userId?[userId]:[]),userTraits];argumentsArray.unshift(['identify',...identifyApiArgs]);}// Set anonymousID
|
|
548
549
|
if(queryObject.get(QUERY_PARAM_ANONYMOUS_ID_KEY)){argumentsArray.unshift(['setAnonymousId',queryObject.get(QUERY_PARAM_ANONYMOUS_ID_KEY)]);}};/**
|
|
549
550
|
* Retrieve an existing buffered load method call and remove from the existing array
|
|
550
551
|
*/const getPreloadedLoadEvent=preloadedEventsArray=>{const loadMethodName='load';let loadEvent=[];/**
|
|
@@ -3289,7 +3290,9 @@ enrichedEvent.userId=to??enrichedEvent.userId;return enrichedEvent;}/**
|
|
|
3289
3290
|
* @param event Incoming event data
|
|
3290
3291
|
*/addEvent(event){this.userSessionManager.refreshSession();const rudderEvent=this.eventFactory.create(event);this.eventRepository.enqueue(rudderEvent,event.callback);}}
|
|
3291
3292
|
|
|
3292
|
-
class UserSessionManager{
|
|
3293
|
+
class UserSessionManager{/**
|
|
3294
|
+
* Tracks whether a server-side cookie setting request is in progress or not.
|
|
3295
|
+
*/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={};}/**
|
|
3293
3296
|
* Initialize User session with values from storage
|
|
3294
3297
|
*/init(){this.syncStorageDataToState();// Register the effect to sync with storage
|
|
3295
3298
|
this.registerEffects();}syncStorageDataToState(){this.migrateStorageIfNeeded();this.migrateDataFromPreviousStorage();// get the values from storage and set it again
|
|
@@ -3319,20 +3322,31 @@ cutOffDuration=DEFAULT_SESSION_CUT_OFF_DURATION_MS;}else if(cutOffDuration<sessi
|
|
|
3319
3322
|
* @param callback
|
|
3320
3323
|
*/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});}/**
|
|
3321
3324
|
* A function to make an external request to set the cookie from server side
|
|
3322
|
-
* @param key
|
|
3323
|
-
* @param
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3325
|
+
* @param sessionToCookiesMap map of session key to cookie name
|
|
3326
|
+
* @param cb callback function to be called when the cookie is set
|
|
3327
|
+
* @param store store to be used to get the cookie value
|
|
3328
|
+
*/setServerSideCookies(sessionToCookiesMap,cb,store){// Retrieve the cookie value from the state
|
|
3329
|
+
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
|
|
3330
|
+
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
|
|
3331
|
+
const encryptedCookieData=this.getEncryptedCookieData(getCurrentCookieValuesFromState(),store);if(encryptedCookieData.length>0){// make request to data service to set the cookie from server side
|
|
3332
|
+
this.makeRequestToSetCookie(encryptedCookieData,(res,details)=>{// Mark the cookie req status as done
|
|
3333
|
+
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.
|
|
3334
|
+
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
|
|
3335
|
+
// or other cookie requests might have updated the cookie value.
|
|
3336
|
+
// Log an error only when cookie didn't exist previously and currently also doesn't exist.
|
|
3337
|
+
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
|
|
3338
|
+
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
|
|
3339
|
+
clearInProgressFlags();}}/**
|
|
3327
3340
|
* A function to sync values in storage
|
|
3328
3341
|
* @param sessionKey
|
|
3329
|
-
|
|
3330
|
-
*/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
|
|
3342
|
+
*/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
|
|
3331
3343
|
// set the cookie from server side
|
|
3332
|
-
if(state.serverCookies.isEnabledServerSideCookies.value&&storageType===COOKIE_STORAGE){
|
|
3344
|
+
if(state.serverCookies.isEnabledServerSideCookies.value&&storageType===COOKIE_STORAGE){// Mark the requests as in progress.
|
|
3345
|
+
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
|
|
3346
|
+
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);}}}/**
|
|
3333
3347
|
* Function to update storage whenever state value changes
|
|
3334
3348
|
*/registerEffects(){// This will work as long as the user session entry key names are same as the state keys
|
|
3335
|
-
USER_SESSION_KEYS.forEach(sessionKey=>{E(()=>{this.syncValueToStorage(sessionKey
|
|
3349
|
+
USER_SESSION_KEYS.forEach(sessionKey=>{E(()=>{this.syncValueToStorage(sessionKey);});});}/**
|
|
3336
3350
|
* Sets anonymous id in the following precedence:
|
|
3337
3351
|
*
|
|
3338
3352
|
* 1. anonymousId: Id directly provided to the function.
|
|
@@ -3349,30 +3363,37 @@ const autoCapturedAnonymousId=this.pluginsManager?.invokeSingle('storage.getAnon
|
|
|
3349
3363
|
// This is needed for entries that are fetched from the storage
|
|
3350
3364
|
// during the current session (for example, session info)
|
|
3351
3365
|
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;}/**
|
|
3366
|
+
* Fetches the value for a session key. Preferably from storage, if the server-side
|
|
3367
|
+
* cookies request is not in progress. Otherwise, from the state.
|
|
3368
|
+
* @param sessionKey - The session key to fetch the value for
|
|
3369
|
+
* @returns - The value for the session key
|
|
3370
|
+
*/getUserSessionValue(sessionKey){// If the server-side cookies request is in progress, fetch the value from the state.
|
|
3371
|
+
if(this.serverSideCookiesRequestInProgress[sessionKey]){return state.session[sessionKey].value;}// Otherwise, fetch the value from storage.
|
|
3372
|
+
return this.getEntryValue(sessionKey);}/**
|
|
3352
3373
|
* Fetches User Id
|
|
3353
3374
|
* @returns
|
|
3354
|
-
*/getUserId(){return this.
|
|
3375
|
+
*/getUserId(){return this.getUserSessionValue('userId');}/**
|
|
3355
3376
|
* Fetches User Traits
|
|
3356
3377
|
* @returns
|
|
3357
|
-
*/getUserTraits(){return this.
|
|
3378
|
+
*/getUserTraits(){return this.getUserSessionValue('userTraits');}/**
|
|
3358
3379
|
* Fetches Group Id
|
|
3359
3380
|
* @returns
|
|
3360
|
-
*/getGroupId(){return this.
|
|
3381
|
+
*/getGroupId(){return this.getUserSessionValue('groupId');}/**
|
|
3361
3382
|
* Fetches Group Traits
|
|
3362
3383
|
* @returns
|
|
3363
|
-
*/getGroupTraits(){return this.
|
|
3384
|
+
*/getGroupTraits(){return this.getUserSessionValue('groupTraits');}/**
|
|
3364
3385
|
* Fetches Initial Referrer
|
|
3365
3386
|
* @returns
|
|
3366
|
-
*/getInitialReferrer(){return this.
|
|
3387
|
+
*/getInitialReferrer(){return this.getUserSessionValue('initialReferrer');}/**
|
|
3367
3388
|
* Fetches Initial Referring domain
|
|
3368
3389
|
* @returns
|
|
3369
|
-
*/getInitialReferringDomain(){return this.
|
|
3390
|
+
*/getInitialReferringDomain(){return this.getUserSessionValue('initialReferringDomain');}/**
|
|
3370
3391
|
* Fetches session tracking information from storage
|
|
3371
3392
|
* @returns
|
|
3372
|
-
*/getSessionInfo(){return this.
|
|
3393
|
+
*/getSessionInfo(){return this.getUserSessionValue('sessionInfo');}/**
|
|
3373
3394
|
* Fetches auth token from storage
|
|
3374
3395
|
* @returns
|
|
3375
|
-
*/getAuthToken(){return this.
|
|
3396
|
+
*/getAuthToken(){return this.getUserSessionValue('authToken');}/**
|
|
3376
3397
|
* If session is active it returns the sessionId
|
|
3377
3398
|
* @returns
|
|
3378
3399
|
*/getSessionId(){const sessionInfo=this.getSessionInfo()??DEFAULT_USER_SESSION_VALUES.sessionInfo;if(sessionInfo.autoTrack&&!hasSessionExpired(sessionInfo)||sessionInfo.manualTrack){return sessionInfo.id??null;}return null;}/**
|
|
@@ -3388,7 +3409,7 @@ this.migrateStorageIfNeeded([store],[sessionKey]);const storageKey=entries[sessi
|
|
|
3388
3409
|
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.
|
|
3389
3410
|
state.session.sessionInfo.value=sessionInfo;if(state.lifecycle.status.value!=='readyExecuted'){// Force update the storage as the 'effect' blocks are not getting triggered
|
|
3390
3411
|
// when processing preload buffered requests
|
|
3391
|
-
this.syncValueToStorage('sessionInfo'
|
|
3412
|
+
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();}}/**
|
|
3392
3413
|
* Reset state values
|
|
3393
3414
|
* @param options options for reset
|
|
3394
3415
|
* @returns
|
|
@@ -521,7 +521,7 @@
|
|
|
521
521
|
error.stacktrace=`${stacktrace}\n${MANUAL_ERROR_IDENTIFIER}`;break;case operaSourceloc:default:// eslint-disable-next-line no-param-reassign
|
|
522
522
|
error['opera#sourceloc']=`${operaSourceloc}\n${MANUAL_ERROR_IDENTIFIER}`;break;}}}globalThis.dispatchEvent(new ErrorEvent('error',{error,bubbles:true,cancelable:true,composed:true}));};
|
|
523
523
|
|
|
524
|
-
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.
|
|
524
|
+
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';
|
|
525
525
|
|
|
526
526
|
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';
|
|
527
527
|
|
|
@@ -549,8 +549,9 @@
|
|
|
549
549
|
* Parse query string into preload buffer events & push into existing array before any other events
|
|
550
550
|
*/const retrieveEventsFromQueryString=(argumentsArray=[])=>{// Mapping for trait and properties values based on key prefix
|
|
551
551
|
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
|
|
552
|
-
if(queryObject.get(QUERY_PARAM_TRACK_EVENT_NAME_KEY)){argumentsArray.unshift(['track',queryObject.get(QUERY_PARAM_TRACK_EVENT_NAME_KEY),getEventDataFromQueryString(queryObject,eventArgumentToQueryParamMap.properties)]);}//
|
|
553
|
-
|
|
552
|
+
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
|
|
553
|
+
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
|
|
554
|
+
const identifyApiArgs=[...(userId?[userId]:[]),userTraits];argumentsArray.unshift(['identify',...identifyApiArgs]);}// Set anonymousID
|
|
554
555
|
if(queryObject.get(QUERY_PARAM_ANONYMOUS_ID_KEY)){argumentsArray.unshift(['setAnonymousId',queryObject.get(QUERY_PARAM_ANONYMOUS_ID_KEY)]);}};/**
|
|
555
556
|
* Retrieve an existing buffered load method call and remove from the existing array
|
|
556
557
|
*/const getPreloadedLoadEvent=preloadedEventsArray=>{const loadMethodName='load';let loadEvent=[];/**
|
|
@@ -3295,7 +3296,9 @@
|
|
|
3295
3296
|
* @param event Incoming event data
|
|
3296
3297
|
*/addEvent(event){this.userSessionManager.refreshSession();const rudderEvent=this.eventFactory.create(event);this.eventRepository.enqueue(rudderEvent,event.callback);}}
|
|
3297
3298
|
|
|
3298
|
-
class UserSessionManager{
|
|
3299
|
+
class UserSessionManager{/**
|
|
3300
|
+
* Tracks whether a server-side cookie setting request is in progress or not.
|
|
3301
|
+
*/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={};}/**
|
|
3299
3302
|
* Initialize User session with values from storage
|
|
3300
3303
|
*/init(){this.syncStorageDataToState();// Register the effect to sync with storage
|
|
3301
3304
|
this.registerEffects();}syncStorageDataToState(){this.migrateStorageIfNeeded();this.migrateDataFromPreviousStorage();// get the values from storage and set it again
|
|
@@ -3325,20 +3328,31 @@
|
|
|
3325
3328
|
* @param callback
|
|
3326
3329
|
*/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});}/**
|
|
3327
3330
|
* A function to make an external request to set the cookie from server side
|
|
3328
|
-
* @param key
|
|
3329
|
-
* @param
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3331
|
+
* @param sessionToCookiesMap map of session key to cookie name
|
|
3332
|
+
* @param cb callback function to be called when the cookie is set
|
|
3333
|
+
* @param store store to be used to get the cookie value
|
|
3334
|
+
*/setServerSideCookies(sessionToCookiesMap,cb,store){// Retrieve the cookie value from the state
|
|
3335
|
+
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
|
|
3336
|
+
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
|
|
3337
|
+
const encryptedCookieData=this.getEncryptedCookieData(getCurrentCookieValuesFromState(),store);if(encryptedCookieData.length>0){// make request to data service to set the cookie from server side
|
|
3338
|
+
this.makeRequestToSetCookie(encryptedCookieData,(res,details)=>{// Mark the cookie req status as done
|
|
3339
|
+
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.
|
|
3340
|
+
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
|
|
3341
|
+
// or other cookie requests might have updated the cookie value.
|
|
3342
|
+
// Log an error only when cookie didn't exist previously and currently also doesn't exist.
|
|
3343
|
+
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
|
|
3344
|
+
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
|
|
3345
|
+
clearInProgressFlags();}}/**
|
|
3333
3346
|
* A function to sync values in storage
|
|
3334
3347
|
* @param sessionKey
|
|
3335
|
-
|
|
3336
|
-
*/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
|
|
3348
|
+
*/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
|
|
3337
3349
|
// set the cookie from server side
|
|
3338
|
-
if(state.serverCookies.isEnabledServerSideCookies.value&&storageType===COOKIE_STORAGE){
|
|
3350
|
+
if(state.serverCookies.isEnabledServerSideCookies.value&&storageType===COOKIE_STORAGE){// Mark the requests as in progress.
|
|
3351
|
+
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
|
|
3352
|
+
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);}}}/**
|
|
3339
3353
|
* Function to update storage whenever state value changes
|
|
3340
3354
|
*/registerEffects(){// This will work as long as the user session entry key names are same as the state keys
|
|
3341
|
-
USER_SESSION_KEYS.forEach(sessionKey=>{E(()=>{this.syncValueToStorage(sessionKey
|
|
3355
|
+
USER_SESSION_KEYS.forEach(sessionKey=>{E(()=>{this.syncValueToStorage(sessionKey);});});}/**
|
|
3342
3356
|
* Sets anonymous id in the following precedence:
|
|
3343
3357
|
*
|
|
3344
3358
|
* 1. anonymousId: Id directly provided to the function.
|
|
@@ -3355,30 +3369,37 @@
|
|
|
3355
3369
|
// This is needed for entries that are fetched from the storage
|
|
3356
3370
|
// during the current session (for example, session info)
|
|
3357
3371
|
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;}/**
|
|
3372
|
+
* Fetches the value for a session key. Preferably from storage, if the server-side
|
|
3373
|
+
* cookies request is not in progress. Otherwise, from the state.
|
|
3374
|
+
* @param sessionKey - The session key to fetch the value for
|
|
3375
|
+
* @returns - The value for the session key
|
|
3376
|
+
*/getUserSessionValue(sessionKey){// If the server-side cookies request is in progress, fetch the value from the state.
|
|
3377
|
+
if(this.serverSideCookiesRequestInProgress[sessionKey]){return state.session[sessionKey].value;}// Otherwise, fetch the value from storage.
|
|
3378
|
+
return this.getEntryValue(sessionKey);}/**
|
|
3358
3379
|
* Fetches User Id
|
|
3359
3380
|
* @returns
|
|
3360
|
-
*/getUserId(){return this.
|
|
3381
|
+
*/getUserId(){return this.getUserSessionValue('userId');}/**
|
|
3361
3382
|
* Fetches User Traits
|
|
3362
3383
|
* @returns
|
|
3363
|
-
*/getUserTraits(){return this.
|
|
3384
|
+
*/getUserTraits(){return this.getUserSessionValue('userTraits');}/**
|
|
3364
3385
|
* Fetches Group Id
|
|
3365
3386
|
* @returns
|
|
3366
|
-
*/getGroupId(){return this.
|
|
3387
|
+
*/getGroupId(){return this.getUserSessionValue('groupId');}/**
|
|
3367
3388
|
* Fetches Group Traits
|
|
3368
3389
|
* @returns
|
|
3369
|
-
*/getGroupTraits(){return this.
|
|
3390
|
+
*/getGroupTraits(){return this.getUserSessionValue('groupTraits');}/**
|
|
3370
3391
|
* Fetches Initial Referrer
|
|
3371
3392
|
* @returns
|
|
3372
|
-
*/getInitialReferrer(){return this.
|
|
3393
|
+
*/getInitialReferrer(){return this.getUserSessionValue('initialReferrer');}/**
|
|
3373
3394
|
* Fetches Initial Referring domain
|
|
3374
3395
|
* @returns
|
|
3375
|
-
*/getInitialReferringDomain(){return this.
|
|
3396
|
+
*/getInitialReferringDomain(){return this.getUserSessionValue('initialReferringDomain');}/**
|
|
3376
3397
|
* Fetches session tracking information from storage
|
|
3377
3398
|
* @returns
|
|
3378
|
-
*/getSessionInfo(){return this.
|
|
3399
|
+
*/getSessionInfo(){return this.getUserSessionValue('sessionInfo');}/**
|
|
3379
3400
|
* Fetches auth token from storage
|
|
3380
3401
|
* @returns
|
|
3381
|
-
*/getAuthToken(){return this.
|
|
3402
|
+
*/getAuthToken(){return this.getUserSessionValue('authToken');}/**
|
|
3382
3403
|
* If session is active it returns the sessionId
|
|
3383
3404
|
* @returns
|
|
3384
3405
|
*/getSessionId(){const sessionInfo=this.getSessionInfo()??DEFAULT_USER_SESSION_VALUES.sessionInfo;if(sessionInfo.autoTrack&&!hasSessionExpired(sessionInfo)||sessionInfo.manualTrack){return sessionInfo.id??null;}return null;}/**
|
|
@@ -3394,7 +3415,7 @@
|
|
|
3394
3415
|
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.
|
|
3395
3416
|
state.session.sessionInfo.value=sessionInfo;if(state.lifecycle.status.value!=='readyExecuted'){// Force update the storage as the 'effect' blocks are not getting triggered
|
|
3396
3417
|
// when processing preload buffered requests
|
|
3397
|
-
this.syncValueToStorage('sessionInfo'
|
|
3418
|
+
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();}}/**
|
|
3398
3419
|
* Reset state values
|
|
3399
3420
|
* @param options options for reset
|
|
3400
3421
|
* @returns
|
|
@@ -510,7 +510,7 @@ error.stack=`${stack}\n${MANUAL_ERROR_IDENTIFIER}`;break;case stacktrace:// esli
|
|
|
510
510
|
error.stacktrace=`${stacktrace}\n${MANUAL_ERROR_IDENTIFIER}`;break;case operaSourceloc:default:// eslint-disable-next-line no-param-reassign
|
|
511
511
|
error['opera#sourceloc']=`${operaSourceloc}\n${MANUAL_ERROR_IDENTIFIER}`;break;}}}globalThis.dispatchEvent(new ErrorEvent('error',{error,bubbles:true,cancelable:true,composed:true}));};
|
|
512
512
|
|
|
513
|
-
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.
|
|
513
|
+
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';
|
|
514
514
|
|
|
515
515
|
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';
|
|
516
516
|
|
|
@@ -538,8 +538,9 @@ data[dataKey]=params.get(key);}});return data;};/**
|
|
|
538
538
|
* Parse query string into preload buffer events & push into existing array before any other events
|
|
539
539
|
*/const retrieveEventsFromQueryString=(argumentsArray=[])=>{// Mapping for trait and properties values based on key prefix
|
|
540
540
|
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
|
|
541
|
-
if(queryObject.get(QUERY_PARAM_TRACK_EVENT_NAME_KEY)){argumentsArray.unshift(['track',queryObject.get(QUERY_PARAM_TRACK_EVENT_NAME_KEY),getEventDataFromQueryString(queryObject,eventArgumentToQueryParamMap.properties)]);}//
|
|
542
|
-
|
|
541
|
+
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
|
|
542
|
+
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
|
|
543
|
+
const identifyApiArgs=[...(userId?[userId]:[]),userTraits];argumentsArray.unshift(['identify',...identifyApiArgs]);}// Set anonymousID
|
|
543
544
|
if(queryObject.get(QUERY_PARAM_ANONYMOUS_ID_KEY)){argumentsArray.unshift(['setAnonymousId',queryObject.get(QUERY_PARAM_ANONYMOUS_ID_KEY)]);}};/**
|
|
544
545
|
* Retrieve an existing buffered load method call and remove from the existing array
|
|
545
546
|
*/const getPreloadedLoadEvent=preloadedEventsArray=>{const loadMethodName='load';let loadEvent=[];/**
|
|
@@ -1585,7 +1586,9 @@ enrichedEvent.userId=to??enrichedEvent.userId;return enrichedEvent;}/**
|
|
|
1585
1586
|
* @param event Incoming event data
|
|
1586
1587
|
*/addEvent(event){this.userSessionManager.refreshSession();const rudderEvent=this.eventFactory.create(event);this.eventRepository.enqueue(rudderEvent,event.callback);}}
|
|
1587
1588
|
|
|
1588
|
-
class UserSessionManager{
|
|
1589
|
+
class UserSessionManager{/**
|
|
1590
|
+
* Tracks whether a server-side cookie setting request is in progress or not.
|
|
1591
|
+
*/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={};}/**
|
|
1589
1592
|
* Initialize User session with values from storage
|
|
1590
1593
|
*/init(){this.syncStorageDataToState();// Register the effect to sync with storage
|
|
1591
1594
|
this.registerEffects();}syncStorageDataToState(){this.migrateStorageIfNeeded();this.migrateDataFromPreviousStorage();// get the values from storage and set it again
|
|
@@ -1615,20 +1618,31 @@ cutOffDuration=DEFAULT_SESSION_CUT_OFF_DURATION_MS;}else if(cutOffDuration<sessi
|
|
|
1615
1618
|
* @param callback
|
|
1616
1619
|
*/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});}/**
|
|
1617
1620
|
* A function to make an external request to set the cookie from server side
|
|
1618
|
-
* @param key
|
|
1619
|
-
* @param
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1621
|
+
* @param sessionToCookiesMap map of session key to cookie name
|
|
1622
|
+
* @param cb callback function to be called when the cookie is set
|
|
1623
|
+
* @param store store to be used to get the cookie value
|
|
1624
|
+
*/setServerSideCookies(sessionToCookiesMap,cb,store){// Retrieve the cookie value from the state
|
|
1625
|
+
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
|
|
1626
|
+
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
|
|
1627
|
+
const encryptedCookieData=this.getEncryptedCookieData(getCurrentCookieValuesFromState(),store);if(encryptedCookieData.length>0){// make request to data service to set the cookie from server side
|
|
1628
|
+
this.makeRequestToSetCookie(encryptedCookieData,(res,details)=>{// Mark the cookie req status as done
|
|
1629
|
+
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.
|
|
1630
|
+
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
|
|
1631
|
+
// or other cookie requests might have updated the cookie value.
|
|
1632
|
+
// Log an error only when cookie didn't exist previously and currently also doesn't exist.
|
|
1633
|
+
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
|
|
1634
|
+
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
|
|
1635
|
+
clearInProgressFlags();}}/**
|
|
1623
1636
|
* A function to sync values in storage
|
|
1624
1637
|
* @param sessionKey
|
|
1625
|
-
|
|
1626
|
-
*/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
|
|
1638
|
+
*/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
|
|
1627
1639
|
// set the cookie from server side
|
|
1628
|
-
if(state.serverCookies.isEnabledServerSideCookies.value&&storageType===COOKIE_STORAGE){
|
|
1640
|
+
if(state.serverCookies.isEnabledServerSideCookies.value&&storageType===COOKIE_STORAGE){// Mark the requests as in progress.
|
|
1641
|
+
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
|
|
1642
|
+
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);}}}/**
|
|
1629
1643
|
* Function to update storage whenever state value changes
|
|
1630
1644
|
*/registerEffects(){// This will work as long as the user session entry key names are same as the state keys
|
|
1631
|
-
USER_SESSION_KEYS.forEach(sessionKey=>{E(()=>{this.syncValueToStorage(sessionKey
|
|
1645
|
+
USER_SESSION_KEYS.forEach(sessionKey=>{E(()=>{this.syncValueToStorage(sessionKey);});});}/**
|
|
1632
1646
|
* Sets anonymous id in the following precedence:
|
|
1633
1647
|
*
|
|
1634
1648
|
* 1. anonymousId: Id directly provided to the function.
|
|
@@ -1645,30 +1659,37 @@ const autoCapturedAnonymousId=this.pluginsManager?.invokeSingle('storage.getAnon
|
|
|
1645
1659
|
// This is needed for entries that are fetched from the storage
|
|
1646
1660
|
// during the current session (for example, session info)
|
|
1647
1661
|
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;}/**
|
|
1662
|
+
* Fetches the value for a session key. Preferably from storage, if the server-side
|
|
1663
|
+
* cookies request is not in progress. Otherwise, from the state.
|
|
1664
|
+
* @param sessionKey - The session key to fetch the value for
|
|
1665
|
+
* @returns - The value for the session key
|
|
1666
|
+
*/getUserSessionValue(sessionKey){// If the server-side cookies request is in progress, fetch the value from the state.
|
|
1667
|
+
if(this.serverSideCookiesRequestInProgress[sessionKey]){return state.session[sessionKey].value;}// Otherwise, fetch the value from storage.
|
|
1668
|
+
return this.getEntryValue(sessionKey);}/**
|
|
1648
1669
|
* Fetches User Id
|
|
1649
1670
|
* @returns
|
|
1650
|
-
*/getUserId(){return this.
|
|
1671
|
+
*/getUserId(){return this.getUserSessionValue('userId');}/**
|
|
1651
1672
|
* Fetches User Traits
|
|
1652
1673
|
* @returns
|
|
1653
|
-
*/getUserTraits(){return this.
|
|
1674
|
+
*/getUserTraits(){return this.getUserSessionValue('userTraits');}/**
|
|
1654
1675
|
* Fetches Group Id
|
|
1655
1676
|
* @returns
|
|
1656
|
-
*/getGroupId(){return this.
|
|
1677
|
+
*/getGroupId(){return this.getUserSessionValue('groupId');}/**
|
|
1657
1678
|
* Fetches Group Traits
|
|
1658
1679
|
* @returns
|
|
1659
|
-
*/getGroupTraits(){return this.
|
|
1680
|
+
*/getGroupTraits(){return this.getUserSessionValue('groupTraits');}/**
|
|
1660
1681
|
* Fetches Initial Referrer
|
|
1661
1682
|
* @returns
|
|
1662
|
-
*/getInitialReferrer(){return this.
|
|
1683
|
+
*/getInitialReferrer(){return this.getUserSessionValue('initialReferrer');}/**
|
|
1663
1684
|
* Fetches Initial Referring domain
|
|
1664
1685
|
* @returns
|
|
1665
|
-
*/getInitialReferringDomain(){return this.
|
|
1686
|
+
*/getInitialReferringDomain(){return this.getUserSessionValue('initialReferringDomain');}/**
|
|
1666
1687
|
* Fetches session tracking information from storage
|
|
1667
1688
|
* @returns
|
|
1668
|
-
*/getSessionInfo(){return this.
|
|
1689
|
+
*/getSessionInfo(){return this.getUserSessionValue('sessionInfo');}/**
|
|
1669
1690
|
* Fetches auth token from storage
|
|
1670
1691
|
* @returns
|
|
1671
|
-
*/getAuthToken(){return this.
|
|
1692
|
+
*/getAuthToken(){return this.getUserSessionValue('authToken');}/**
|
|
1672
1693
|
* If session is active it returns the sessionId
|
|
1673
1694
|
* @returns
|
|
1674
1695
|
*/getSessionId(){const sessionInfo=this.getSessionInfo()??DEFAULT_USER_SESSION_VALUES.sessionInfo;if(sessionInfo.autoTrack&&!hasSessionExpired(sessionInfo)||sessionInfo.manualTrack){return sessionInfo.id??null;}return null;}/**
|
|
@@ -1684,7 +1705,7 @@ this.migrateStorageIfNeeded([store],[sessionKey]);const storageKey=entries[sessi
|
|
|
1684
1705
|
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.
|
|
1685
1706
|
state.session.sessionInfo.value=sessionInfo;if(state.lifecycle.status.value!=='readyExecuted'){// Force update the storage as the 'effect' blocks are not getting triggered
|
|
1686
1707
|
// when processing preload buffered requests
|
|
1687
|
-
this.syncValueToStorage('sessionInfo'
|
|
1708
|
+
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();}}/**
|
|
1688
1709
|
* Reset state values
|
|
1689
1710
|
* @param options options for reset
|
|
1690
1711
|
* @returns
|