@rudderstack/analytics-js 3.26.0 → 3.28.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 +19 -0
- package/dist/npm/legacy/bundled/cjs/index.cjs +24 -19
- package/dist/npm/legacy/bundled/esm/index.mjs +24 -19
- package/dist/npm/legacy/bundled/umd/index.js +24 -19
- package/dist/npm/legacy/cjs/index.cjs +24 -19
- package/dist/npm/legacy/content-script/cjs/index.cjs +24 -19
- package/dist/npm/legacy/content-script/esm/index.mjs +24 -19
- package/dist/npm/legacy/content-script/umd/index.js +24 -19
- package/dist/npm/legacy/esm/index.mjs +24 -19
- package/dist/npm/legacy/umd/index.js +24 -19
- package/dist/npm/modern/bundled/cjs/index.cjs +23 -18
- package/dist/npm/modern/bundled/esm/index.mjs +23 -18
- package/dist/npm/modern/bundled/umd/index.js +23 -18
- package/dist/npm/modern/cjs/index.cjs +22 -17
- package/dist/npm/modern/content-script/cjs/index.cjs +23 -18
- package/dist/npm/modern/content-script/esm/index.mjs +23 -18
- package/dist/npm/modern/content-script/umd/index.js +23 -18
- package/dist/npm/modern/esm/index.mjs +22 -17
- package/dist/npm/modern/umd/index.js +22 -17
- package/package.json +1 -1
|
@@ -506,7 +506,7 @@ error.stack=`${stack}\n${MANUAL_ERROR_IDENTIFIER}`;break;case stacktrace:// esli
|
|
|
506
506
|
error.stacktrace=`${stacktrace}\n${MANUAL_ERROR_IDENTIFIER}`;break;case operaSourceloc:default:// eslint-disable-next-line no-param-reassign
|
|
507
507
|
error['opera#sourceloc']=`${operaSourceloc}\n${MANUAL_ERROR_IDENTIFIER}`;break;}}}globalThis.dispatchEvent(new ErrorEvent('error',{error,bubbles:true,cancelable:true,composed:true}));};
|
|
508
508
|
|
|
509
|
-
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.
|
|
509
|
+
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.28.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';
|
|
510
510
|
|
|
511
511
|
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';
|
|
512
512
|
|
|
@@ -729,7 +729,7 @@ try{if(JSON.stringify(f)==='{}')return accum;return accum.concat(f);}catch{retur
|
|
|
729
729
|
* Utility to parse XHR JSON response
|
|
730
730
|
*/const responseTextToJson=(responseText,onError)=>{try{return JSON.parse(responseText||'');}catch(err){const error=getMutatedError(err,'Failed to parse response data');onError(error);}return undefined;};
|
|
731
731
|
|
|
732
|
-
const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const PLUGINS_LOAD_FAILURE_MESSAGES=[/Failed to fetch dynamically imported module: .*/];const INTEGRATIONS_LOAD_FAILURE_MESSAGES=[/Unable to load \(.*\) the script with the id .*/,/A timeout of \d+ ms occurred while trying to load the script with id .*/];const ERROR_MESSAGES_TO_BE_FILTERED=[new RegExp(`${FAILED_REQUEST_ERR_MSG_PREFIX}.*`),/A script with the id .* is already loaded\./];const SCRIPT_LOAD_FAILURE_MESSAGES=[...PLUGINS_LOAD_FAILURE_MESSAGES,...INTEGRATIONS_LOAD_FAILURE_MESSAGES];
|
|
732
|
+
const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const PLUGINS_LOAD_FAILURE_MESSAGES=[/Failed to fetch dynamically imported module: .*/];const INTEGRATIONS_LOAD_FAILURE_MESSAGES=[/Unable to load \(.*\) the script with the id .*/,/A timeout of \d+ ms occurred while trying to load the script with id .*/];const ERROR_MESSAGES_TO_BE_FILTERED=[new RegExp(`${FAILED_REQUEST_ERR_MSG_PREFIX}.*`),/A script with the id .* is already loaded\./];const SCRIPT_LOAD_FAILURE_MESSAGES=[...PLUGINS_LOAD_FAILURE_MESSAGES,...INTEGRATIONS_LOAD_FAILURE_MESSAGES];const INTEGRATIONS_ERROR_CATEGORY='integrations';const SDK_ERROR_CATEGORY='sdk';const DEFAULT_ERROR_CATEGORY=SDK_ERROR_CATEGORY;
|
|
733
733
|
|
|
734
734
|
const DEFAULT_XHR_REQUEST_OPTIONS={headers:{Accept:'application/json','Content-Type':'application/json;charset=UTF-8'},method:'GET'};/**
|
|
735
735
|
* Utility to create request configuration based on default options
|
|
@@ -765,7 +765,7 @@ const SDK_FILE_NAME_PREFIXES=()=>['rsa'// Prefix for all the SDK scripts includi
|
|
|
765
765
|
// Potential PII or sensitive data
|
|
766
766
|
const APP_STATE_EXCLUDE_KEYS=['userId','userTraits','groupId','groupTraits','anonymousId','config','integration',// integration instance objects
|
|
767
767
|
'eventBuffer',// pre-load event buffer (may contain PII)
|
|
768
|
-
'traits','authToken'];const NOTIFIER_NAME='RudderStack JavaScript SDK';const SDK_GITHUB_URL='git+https://github.com/rudderlabs/rudder-sdk-js.git';const SOURCE_NAME='js';
|
|
768
|
+
'traits','authToken'];const NOTIFIER_NAME='RudderStack JavaScript SDK';const SDK_GITHUB_URL='git+https://github.com/rudderlabs/rudder-sdk-js.git';const SOURCE_NAME='js';
|
|
769
769
|
|
|
770
770
|
const detectAdBlockers=httpClient=>{state.capabilities.isAdBlockerDetectionInProgress.value=true;try{// Apparently, '?view=ad' is a query param that is blocked by majority of adblockers
|
|
771
771
|
// Use source config URL here as it is very unlikely to be blocked by adblockers
|
|
@@ -811,23 +811,27 @@ if(state.capabilities.cspBlockedURLs.value.includes(extractedURL)){resolve(false
|
|
|
811
811
|
checkIfAdBlockersAreActive(state,httpClient,resolve);}}else {// Filter out errors that are not from the RS CDN.
|
|
812
812
|
resolve(false);}}else {// Allow the error to be notified if no URL could be extracted from the error message
|
|
813
813
|
resolve(true);}}else {resolve(!ERROR_MESSAGES_TO_BE_FILTERED.some(e=>e.test(errMsg)));}});};/**
|
|
814
|
+
* A function to get the directory name from a file path.
|
|
815
|
+
* @param {string} filePath The file path
|
|
816
|
+
* @returns The directory name or undefined if the file path is invalid
|
|
817
|
+
*/const getDirectoryName=filePath=>{if(!filePath){return undefined;}const paths=filePath.split('/');return paths.at(-2);};/**
|
|
818
|
+
* A function to get the top stack path from the exception.
|
|
819
|
+
* @param {Exception} exception The exception object
|
|
820
|
+
* @returns The top stack path or undefined if the exception is invalid
|
|
821
|
+
*/const getTopStackPath=exception=>{const errorOrigin=exception.stacktrace[0]?.file;if(!errorOrigin||typeof errorOrigin!=='string'){return undefined;}return errorOrigin;};/**
|
|
814
822
|
* A function to determine if the error is from Rudder SDK
|
|
815
823
|
* @param {Error} exception
|
|
816
824
|
* @returns
|
|
817
|
-
*/const isSDKError=exception=>{const errorOrigin=exception
|
|
818
|
-
// Ex: parentFolderName will be 'sample' for url: https://example.com/sample/file.min.js
|
|
819
|
-
const parentFolderName=paths[paths.length-2];return parentFolderName===CDN_INT_DIR||SDK_FILE_NAME_PREFIXES().some(prefix=>srcFileName.startsWith(prefix)&&srcFileName.endsWith('.js'));};const getErrorDeliveryPayload=(payload,state,category)=>{const data={version:METRICS_PAYLOAD_VERSION,message_id:generateUUID(),source:{name:SOURCE_NAME,sdk_version:state.context.app.value.version,write_key:state.lifecycle.writeKey.value,install_type:state.context.app.value.installType,category:category??DEFAULT_ERROR_CATEGORY},errors:payload};return stringifyWithoutCircular(data);};/**
|
|
825
|
+
*/const isSDKError=exception=>{const errorOrigin=getTopStackPath(exception);if(!errorOrigin){return false;}const srcFileName=errorOrigin.substring(errorOrigin.lastIndexOf('/')+1);const parentFolderName=getDirectoryName(errorOrigin);return parentFolderName===CDN_INT_DIR||SDK_FILE_NAME_PREFIXES().some(prefix=>srcFileName.startsWith(prefix)&&srcFileName.endsWith('.js'));};const getErrorCategory=(exception,category)=>{if(category){return category;}const errorOrigin=getTopStackPath(exception);const directoryName=getDirectoryName(errorOrigin);if(directoryName===CDN_INT_DIR){return INTEGRATIONS_ERROR_CATEGORY;}return DEFAULT_ERROR_CATEGORY;};const getErrorDeliveryPayload=(payload,state,category)=>{const data={version:METRICS_PAYLOAD_VERSION,message_id:generateUUID(),source:{name:SOURCE_NAME,sdk_version:state.context.app.value.version,write_key:state.lifecycle.writeKey.value,install_type:state.context.app.value.installType,category},errors:payload};return stringifyWithoutCircular(data);};/**
|
|
820
826
|
* A function to get the grouping hash value to be used for the error event.
|
|
821
|
-
* Grouping hash is suppressed for non-cdn installs.
|
|
822
827
|
* If the grouping hash is an error instance, the normalized error message is used as the grouping hash.
|
|
823
828
|
* If the grouping hash is an empty string or not specified, the default grouping hash is used.
|
|
824
829
|
* If the grouping hash is a string, it is used as is.
|
|
825
830
|
* @param curErrGroupingHash The grouping hash value part of the error event
|
|
826
831
|
* @param defaultGroupingHash The default grouping hash value. It is the error message.
|
|
827
|
-
* @param state The application state
|
|
828
832
|
* @param logger The logger instance
|
|
829
833
|
* @returns The final grouping hash value to be used for the error event
|
|
830
|
-
*/const getErrorGroupingHash=(curErrGroupingHash,defaultGroupingHash,
|
|
834
|
+
*/const getErrorGroupingHash=(curErrGroupingHash,defaultGroupingHash,logger)=>{let normalizedGroupingHash;if(!isDefined(curErrGroupingHash)){normalizedGroupingHash=defaultGroupingHash;}else if(isString(curErrGroupingHash)){normalizedGroupingHash=curErrGroupingHash;}else {const normalizedErrorInstance=normalizeError(curErrGroupingHash,logger);if(isDefined(normalizedErrorInstance)){normalizedGroupingHash=normalizedErrorInstance.message;}else {normalizedGroupingHash=defaultGroupingHash;}}return normalizedGroupingHash;};
|
|
831
835
|
|
|
832
836
|
/**
|
|
833
837
|
* A service to handle errors
|
|
@@ -851,15 +855,15 @@ document.addEventListener('securitypolicyviolation',event=>{const blockedURL=isS
|
|
|
851
855
|
*/async onError(errorInfo){try{const{error,context,customMessage,groupingHash,category}=errorInfo;const errorType=errorInfo.errorType??ErrorType.HANDLEDEXCEPTION;const errInstance=getErrInstance(error,errorType);const normalizedError=normalizeError(errInstance,this.logger);if(isUndefined(normalizedError)){return;}const customMsgVal=customMessage?`${customMessage} - `:'';const errorMsgPrefix=`${context}${LOG_CONTEXT_SEPARATOR}${customMsgVal}`;const bsException=createBugsnagException(normalizedError,errorMsgPrefix);const stacktrace=getStacktrace(normalizedError);const isSdkDispatched=stacktrace.includes(MANUAL_ERROR_IDENTIFIER);// Filter errors that are not originated in the SDK.
|
|
852
856
|
// In case of NPM installations, the unhandled errors from the SDK cannot be identified
|
|
853
857
|
// and will NOT be reported unless they occur in plugins or integrations.
|
|
854
|
-
if(!isSdkDispatched&&!isSDKError(bsException)&&errorType!==ErrorType.HANDLEDEXCEPTION){return;}if(state.reporting.isErrorReportingEnabled.value){const isAllowed=await checkIfAllowedToBeNotified(bsException,state,this.httpClient);if(isAllowed){const errorState={severity:'error',unhandled:errorType!==ErrorType.HANDLEDEXCEPTION,severityReason:{type:errorType}};//
|
|
855
|
-
//
|
|
856
|
-
//
|
|
858
|
+
if(!isSdkDispatched&&!isSDKError(bsException)&&errorType!==ErrorType.HANDLEDEXCEPTION){return;}if(state.reporting.isErrorReportingEnabled.value){const isAllowed=await checkIfAllowedToBeNotified(bsException,state,this.httpClient);if(isAllowed){const errorState={severity:'error',unhandled:errorType!==ErrorType.HANDLEDEXCEPTION,severityReason:{type:errorType}};// This will allow custom grouping of errors.
|
|
859
|
+
// In case of NPM installations, the default grouping by surrounding code
|
|
860
|
+
// does not make sense as each user application is different and will create a lot of noise in the alerts.
|
|
857
861
|
// References:
|
|
858
862
|
// https://docs.bugsnag.com/platforms/javascript/customizing-error-reports/#groupinghash
|
|
859
863
|
// https://docs.bugsnag.com/product/error-grouping/#user_defined
|
|
860
|
-
const normalizedGroupingHash=getErrorGroupingHash(groupingHash,bsException.message,
|
|
861
|
-
const bugsnagPayload=getBugsnagErrorEvent(bsException,errorState,state,normalizedGroupingHash);// send it to metrics service
|
|
862
|
-
this.httpClient.getAsyncData({url:state.metrics.metricsServiceUrl.value,options:{method:'POST',data:getErrorDeliveryPayload(bugsnagPayload,state,
|
|
864
|
+
const normalizedGroupingHash=getErrorGroupingHash(groupingHash,bsException.message,this.logger);// Get the final payload to be sent to the metrics service
|
|
865
|
+
const bugsnagPayload=getBugsnagErrorEvent(bsException,errorState,state,normalizedGroupingHash);const errorCategory=getErrorCategory(bsException,category);// send it to metrics service
|
|
866
|
+
this.httpClient.getAsyncData({url:state.metrics.metricsServiceUrl.value,options:{method:'POST',data:getErrorDeliveryPayload(bugsnagPayload,state,errorCategory),sendRawData:true},isRawResponse:true});}}// Log handled errors and errors dispatched by the SDK
|
|
863
867
|
if(errorType===ErrorType.HANDLEDEXCEPTION||isSdkDispatched){this.logger.error(bsException.message);}}catch(err){// If an error occurs while handling an error, log it
|
|
864
868
|
this.logger.error(HANDLE_ERROR_FAILURE(ERROR_HANDLER),err);}}/**
|
|
865
869
|
* Add breadcrumbs to add insight of a user's journey before an error
|
|
@@ -1152,7 +1156,8 @@ this.swapQueueStoreToInMemoryEngine();// and save it there
|
|
|
1152
1156
|
this.set(key,value);}else {const customMessage=STORE_DATA_SAVE_ERROR(key);this.onError(err,customMessage,customMessage);}}}/**
|
|
1153
1157
|
* Get by Key.
|
|
1154
1158
|
*/get(key){const validKey=this.createValidKey(key);let decryptedValue;try{if(!validKey){return null;}decryptedValue=this.decrypt(this.engine.getItem(validKey));if(isNullOrUndefined(decryptedValue)||decryptedValue===''){return null;}// storejs that is used in localstorage engine already deserializes json strings but swallows errors
|
|
1155
|
-
return JSON.parse(decryptedValue);}catch(err){const
|
|
1159
|
+
return JSON.parse(decryptedValue);}catch(err){const encryptionPluginName=state.storage.encryptionPluginName.value;// Skip error reporting only when the encryption plugin is configured but failed to load
|
|
1160
|
+
const shouldReportError=!encryptionPluginName||!state.plugins.failedPlugins.value.includes(encryptionPluginName);if(shouldReportError){const customMessage=STORE_DATA_FETCH_ERROR(key);this.onError(err,customMessage,customMessage);}return null;}}/**
|
|
1156
1161
|
* Remove by Key.
|
|
1157
1162
|
*/remove(key){const validKey=this.createValidKey(key);if(validKey){this.engine.removeItem(validKey);}}/**
|
|
1158
1163
|
* Get original engine
|
|
@@ -1855,7 +1860,7 @@ E(()=>{const areAllDestinationsReady=totalDestinationsToLoad===0||state.nativeDe
|
|
|
1855
1860
|
*/// eslint-disable-next-line class-methods-use-this
|
|
1856
1861
|
onDestinationsReady(){// May be do any destination specific actions here
|
|
1857
1862
|
// Mark the ready status if not already done
|
|
1858
|
-
if(state.lifecycle.status.value!=='ready'){state.lifecycle.status.value='ready';}}// End lifecycle methods
|
|
1863
|
+
if(state.lifecycle.status.value!=='ready'){r(()=>{state.lifecycle.status.value='ready';});}}// End lifecycle methods
|
|
1859
1864
|
// Start consumer exposed methods
|
|
1860
1865
|
ready(callback,isBufferedInvocation=false){const type='ready';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,callback]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);if(!isFunction(callback)){this.logger.error(INVALID_CALLBACK_FN_ERROR(READY_API));return;}/**
|
|
1861
1866
|
* If destinations are loaded or no integration is available for loading
|
|
@@ -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.
|
|
515
|
+
const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.28.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
|
|
|
@@ -735,7 +735,7 @@
|
|
|
735
735
|
* Utility to parse XHR JSON response
|
|
736
736
|
*/const responseTextToJson=(responseText,onError)=>{try{return JSON.parse(responseText||'');}catch(err){const error=getMutatedError(err,'Failed to parse response data');onError(error);}return undefined;};
|
|
737
737
|
|
|
738
|
-
const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const PLUGINS_LOAD_FAILURE_MESSAGES=[/Failed to fetch dynamically imported module: .*/];const INTEGRATIONS_LOAD_FAILURE_MESSAGES=[/Unable to load \(.*\) the script with the id .*/,/A timeout of \d+ ms occurred while trying to load the script with id .*/];const ERROR_MESSAGES_TO_BE_FILTERED=[new RegExp(`${FAILED_REQUEST_ERR_MSG_PREFIX}.*`),/A script with the id .* is already loaded\./];const SCRIPT_LOAD_FAILURE_MESSAGES=[...PLUGINS_LOAD_FAILURE_MESSAGES,...INTEGRATIONS_LOAD_FAILURE_MESSAGES];
|
|
738
|
+
const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const PLUGINS_LOAD_FAILURE_MESSAGES=[/Failed to fetch dynamically imported module: .*/];const INTEGRATIONS_LOAD_FAILURE_MESSAGES=[/Unable to load \(.*\) the script with the id .*/,/A timeout of \d+ ms occurred while trying to load the script with id .*/];const ERROR_MESSAGES_TO_BE_FILTERED=[new RegExp(`${FAILED_REQUEST_ERR_MSG_PREFIX}.*`),/A script with the id .* is already loaded\./];const SCRIPT_LOAD_FAILURE_MESSAGES=[...PLUGINS_LOAD_FAILURE_MESSAGES,...INTEGRATIONS_LOAD_FAILURE_MESSAGES];const INTEGRATIONS_ERROR_CATEGORY='integrations';const SDK_ERROR_CATEGORY='sdk';const DEFAULT_ERROR_CATEGORY=SDK_ERROR_CATEGORY;
|
|
739
739
|
|
|
740
740
|
const DEFAULT_XHR_REQUEST_OPTIONS={headers:{Accept:'application/json','Content-Type':'application/json;charset=UTF-8'},method:'GET'};/**
|
|
741
741
|
* Utility to create request configuration based on default options
|
|
@@ -771,7 +771,7 @@
|
|
|
771
771
|
// Potential PII or sensitive data
|
|
772
772
|
const APP_STATE_EXCLUDE_KEYS=['userId','userTraits','groupId','groupTraits','anonymousId','config','integration',// integration instance objects
|
|
773
773
|
'eventBuffer',// pre-load event buffer (may contain PII)
|
|
774
|
-
'traits','authToken'];const NOTIFIER_NAME='RudderStack JavaScript SDK';const SDK_GITHUB_URL='git+https://github.com/rudderlabs/rudder-sdk-js.git';const SOURCE_NAME='js';
|
|
774
|
+
'traits','authToken'];const NOTIFIER_NAME='RudderStack JavaScript SDK';const SDK_GITHUB_URL='git+https://github.com/rudderlabs/rudder-sdk-js.git';const SOURCE_NAME='js';
|
|
775
775
|
|
|
776
776
|
const detectAdBlockers=httpClient=>{state.capabilities.isAdBlockerDetectionInProgress.value=true;try{// Apparently, '?view=ad' is a query param that is blocked by majority of adblockers
|
|
777
777
|
// Use source config URL here as it is very unlikely to be blocked by adblockers
|
|
@@ -817,23 +817,27 @@
|
|
|
817
817
|
checkIfAdBlockersAreActive(state,httpClient,resolve);}}else {// Filter out errors that are not from the RS CDN.
|
|
818
818
|
resolve(false);}}else {// Allow the error to be notified if no URL could be extracted from the error message
|
|
819
819
|
resolve(true);}}else {resolve(!ERROR_MESSAGES_TO_BE_FILTERED.some(e=>e.test(errMsg)));}});};/**
|
|
820
|
+
* A function to get the directory name from a file path.
|
|
821
|
+
* @param {string} filePath The file path
|
|
822
|
+
* @returns The directory name or undefined if the file path is invalid
|
|
823
|
+
*/const getDirectoryName=filePath=>{if(!filePath){return undefined;}const paths=filePath.split('/');return paths.at(-2);};/**
|
|
824
|
+
* A function to get the top stack path from the exception.
|
|
825
|
+
* @param {Exception} exception The exception object
|
|
826
|
+
* @returns The top stack path or undefined if the exception is invalid
|
|
827
|
+
*/const getTopStackPath=exception=>{const errorOrigin=exception.stacktrace[0]?.file;if(!errorOrigin||typeof errorOrigin!=='string'){return undefined;}return errorOrigin;};/**
|
|
820
828
|
* A function to determine if the error is from Rudder SDK
|
|
821
829
|
* @param {Error} exception
|
|
822
830
|
* @returns
|
|
823
|
-
*/const isSDKError=exception=>{const errorOrigin=exception
|
|
824
|
-
// Ex: parentFolderName will be 'sample' for url: https://example.com/sample/file.min.js
|
|
825
|
-
const parentFolderName=paths[paths.length-2];return parentFolderName===CDN_INT_DIR||SDK_FILE_NAME_PREFIXES().some(prefix=>srcFileName.startsWith(prefix)&&srcFileName.endsWith('.js'));};const getErrorDeliveryPayload=(payload,state,category)=>{const data={version:METRICS_PAYLOAD_VERSION,message_id:generateUUID(),source:{name:SOURCE_NAME,sdk_version:state.context.app.value.version,write_key:state.lifecycle.writeKey.value,install_type:state.context.app.value.installType,category:category??DEFAULT_ERROR_CATEGORY},errors:payload};return stringifyWithoutCircular(data);};/**
|
|
831
|
+
*/const isSDKError=exception=>{const errorOrigin=getTopStackPath(exception);if(!errorOrigin){return false;}const srcFileName=errorOrigin.substring(errorOrigin.lastIndexOf('/')+1);const parentFolderName=getDirectoryName(errorOrigin);return parentFolderName===CDN_INT_DIR||SDK_FILE_NAME_PREFIXES().some(prefix=>srcFileName.startsWith(prefix)&&srcFileName.endsWith('.js'));};const getErrorCategory=(exception,category)=>{if(category){return category;}const errorOrigin=getTopStackPath(exception);const directoryName=getDirectoryName(errorOrigin);if(directoryName===CDN_INT_DIR){return INTEGRATIONS_ERROR_CATEGORY;}return DEFAULT_ERROR_CATEGORY;};const getErrorDeliveryPayload=(payload,state,category)=>{const data={version:METRICS_PAYLOAD_VERSION,message_id:generateUUID(),source:{name:SOURCE_NAME,sdk_version:state.context.app.value.version,write_key:state.lifecycle.writeKey.value,install_type:state.context.app.value.installType,category},errors:payload};return stringifyWithoutCircular(data);};/**
|
|
826
832
|
* A function to get the grouping hash value to be used for the error event.
|
|
827
|
-
* Grouping hash is suppressed for non-cdn installs.
|
|
828
833
|
* If the grouping hash is an error instance, the normalized error message is used as the grouping hash.
|
|
829
834
|
* If the grouping hash is an empty string or not specified, the default grouping hash is used.
|
|
830
835
|
* If the grouping hash is a string, it is used as is.
|
|
831
836
|
* @param curErrGroupingHash The grouping hash value part of the error event
|
|
832
837
|
* @param defaultGroupingHash The default grouping hash value. It is the error message.
|
|
833
|
-
* @param state The application state
|
|
834
838
|
* @param logger The logger instance
|
|
835
839
|
* @returns The final grouping hash value to be used for the error event
|
|
836
|
-
*/const getErrorGroupingHash=(curErrGroupingHash,defaultGroupingHash,
|
|
840
|
+
*/const getErrorGroupingHash=(curErrGroupingHash,defaultGroupingHash,logger)=>{let normalizedGroupingHash;if(!isDefined(curErrGroupingHash)){normalizedGroupingHash=defaultGroupingHash;}else if(isString(curErrGroupingHash)){normalizedGroupingHash=curErrGroupingHash;}else {const normalizedErrorInstance=normalizeError(curErrGroupingHash,logger);if(isDefined(normalizedErrorInstance)){normalizedGroupingHash=normalizedErrorInstance.message;}else {normalizedGroupingHash=defaultGroupingHash;}}return normalizedGroupingHash;};
|
|
837
841
|
|
|
838
842
|
/**
|
|
839
843
|
* A service to handle errors
|
|
@@ -857,15 +861,15 @@
|
|
|
857
861
|
*/async onError(errorInfo){try{const{error,context,customMessage,groupingHash,category}=errorInfo;const errorType=errorInfo.errorType??ErrorType.HANDLEDEXCEPTION;const errInstance=getErrInstance(error,errorType);const normalizedError=normalizeError(errInstance,this.logger);if(isUndefined(normalizedError)){return;}const customMsgVal=customMessage?`${customMessage} - `:'';const errorMsgPrefix=`${context}${LOG_CONTEXT_SEPARATOR}${customMsgVal}`;const bsException=createBugsnagException(normalizedError,errorMsgPrefix);const stacktrace=getStacktrace(normalizedError);const isSdkDispatched=stacktrace.includes(MANUAL_ERROR_IDENTIFIER);// Filter errors that are not originated in the SDK.
|
|
858
862
|
// In case of NPM installations, the unhandled errors from the SDK cannot be identified
|
|
859
863
|
// and will NOT be reported unless they occur in plugins or integrations.
|
|
860
|
-
if(!isSdkDispatched&&!isSDKError(bsException)&&errorType!==ErrorType.HANDLEDEXCEPTION){return;}if(state.reporting.isErrorReportingEnabled.value){const isAllowed=await checkIfAllowedToBeNotified(bsException,state,this.httpClient);if(isAllowed){const errorState={severity:'error',unhandled:errorType!==ErrorType.HANDLEDEXCEPTION,severityReason:{type:errorType}};//
|
|
861
|
-
//
|
|
862
|
-
//
|
|
864
|
+
if(!isSdkDispatched&&!isSDKError(bsException)&&errorType!==ErrorType.HANDLEDEXCEPTION){return;}if(state.reporting.isErrorReportingEnabled.value){const isAllowed=await checkIfAllowedToBeNotified(bsException,state,this.httpClient);if(isAllowed){const errorState={severity:'error',unhandled:errorType!==ErrorType.HANDLEDEXCEPTION,severityReason:{type:errorType}};// This will allow custom grouping of errors.
|
|
865
|
+
// In case of NPM installations, the default grouping by surrounding code
|
|
866
|
+
// does not make sense as each user application is different and will create a lot of noise in the alerts.
|
|
863
867
|
// References:
|
|
864
868
|
// https://docs.bugsnag.com/platforms/javascript/customizing-error-reports/#groupinghash
|
|
865
869
|
// https://docs.bugsnag.com/product/error-grouping/#user_defined
|
|
866
|
-
const normalizedGroupingHash=getErrorGroupingHash(groupingHash,bsException.message,
|
|
867
|
-
const bugsnagPayload=getBugsnagErrorEvent(bsException,errorState,state,normalizedGroupingHash);// send it to metrics service
|
|
868
|
-
this.httpClient.getAsyncData({url:state.metrics.metricsServiceUrl.value,options:{method:'POST',data:getErrorDeliveryPayload(bugsnagPayload,state,
|
|
870
|
+
const normalizedGroupingHash=getErrorGroupingHash(groupingHash,bsException.message,this.logger);// Get the final payload to be sent to the metrics service
|
|
871
|
+
const bugsnagPayload=getBugsnagErrorEvent(bsException,errorState,state,normalizedGroupingHash);const errorCategory=getErrorCategory(bsException,category);// send it to metrics service
|
|
872
|
+
this.httpClient.getAsyncData({url:state.metrics.metricsServiceUrl.value,options:{method:'POST',data:getErrorDeliveryPayload(bugsnagPayload,state,errorCategory),sendRawData:true},isRawResponse:true});}}// Log handled errors and errors dispatched by the SDK
|
|
869
873
|
if(errorType===ErrorType.HANDLEDEXCEPTION||isSdkDispatched){this.logger.error(bsException.message);}}catch(err){// If an error occurs while handling an error, log it
|
|
870
874
|
this.logger.error(HANDLE_ERROR_FAILURE(ERROR_HANDLER),err);}}/**
|
|
871
875
|
* Add breadcrumbs to add insight of a user's journey before an error
|
|
@@ -1158,7 +1162,8 @@
|
|
|
1158
1162
|
this.set(key,value);}else {const customMessage=STORE_DATA_SAVE_ERROR(key);this.onError(err,customMessage,customMessage);}}}/**
|
|
1159
1163
|
* Get by Key.
|
|
1160
1164
|
*/get(key){const validKey=this.createValidKey(key);let decryptedValue;try{if(!validKey){return null;}decryptedValue=this.decrypt(this.engine.getItem(validKey));if(isNullOrUndefined(decryptedValue)||decryptedValue===''){return null;}// storejs that is used in localstorage engine already deserializes json strings but swallows errors
|
|
1161
|
-
return JSON.parse(decryptedValue);}catch(err){const
|
|
1165
|
+
return JSON.parse(decryptedValue);}catch(err){const encryptionPluginName=state.storage.encryptionPluginName.value;// Skip error reporting only when the encryption plugin is configured but failed to load
|
|
1166
|
+
const shouldReportError=!encryptionPluginName||!state.plugins.failedPlugins.value.includes(encryptionPluginName);if(shouldReportError){const customMessage=STORE_DATA_FETCH_ERROR(key);this.onError(err,customMessage,customMessage);}return null;}}/**
|
|
1162
1167
|
* Remove by Key.
|
|
1163
1168
|
*/remove(key){const validKey=this.createValidKey(key);if(validKey){this.engine.removeItem(validKey);}}/**
|
|
1164
1169
|
* Get original engine
|
|
@@ -1861,7 +1866,7 @@
|
|
|
1861
1866
|
*/// eslint-disable-next-line class-methods-use-this
|
|
1862
1867
|
onDestinationsReady(){// May be do any destination specific actions here
|
|
1863
1868
|
// Mark the ready status if not already done
|
|
1864
|
-
if(state.lifecycle.status.value!=='ready'){state.lifecycle.status.value='ready';}}// End lifecycle methods
|
|
1869
|
+
if(state.lifecycle.status.value!=='ready'){r(()=>{state.lifecycle.status.value='ready';});}}// End lifecycle methods
|
|
1865
1870
|
// Start consumer exposed methods
|
|
1866
1871
|
ready(callback,isBufferedInvocation=false){const type='ready';if(!state.lifecycle.loaded.value){state.eventBuffer.toBeProcessedArray.value=[...state.eventBuffer.toBeProcessedArray.value,[type,callback]];return;}this.errorHandler.leaveBreadcrumb(`New ${type} invocation`);if(!isFunction(callback)){this.logger.error(INVALID_CALLBACK_FN_ERROR(READY_API));return;}/**
|
|
1867
1872
|
* If destinations are loaded or no integration is available for loading
|