@rudderstack/analytics-js 3.7.6 → 3.7.8

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.
@@ -376,7 +376,7 @@
376
376
 
377
377
  const CAPABILITIES_MANAGER='CapabilitiesManager';const CONFIG_MANAGER='ConfigManager';const EVENT_MANAGER='EventManager';const PLUGINS_MANAGER='PluginsManager';const USER_SESSION_MANAGER='UserSessionManager';const ERROR_HANDLER='ErrorHandler';const PLUGIN_ENGINE='PluginEngine';const STORE_MANAGER='StoreManager';const READY_API='readyApi';const EVENT_REPOSITORY='EventRepository';const EXTERNAL_SRC_LOADER='ExternalSrcLoader';const HTTP_CLIENT='HttpClient';const RS_APP='RudderStackApplication';const ANALYTICS_CORE='AnalyticsCore';
378
378
 
379
- const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.6';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';
379
+ const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.8';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';
380
380
 
381
381
  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';
382
382
 
@@ -581,35 +581,14 @@
581
581
  extensionPointName=extensionPointName.replace(/(^!|!$)/g,'');if(!extensionPointName){throw new Error(PLUGIN_EXT_POINT_INVALID_ERROR);}const extensionPointNameParts=extensionPointName.split('.');extensionPointNameParts.pop();const pluginMethodPath=extensionPointNameParts.join('.');const pluginsToInvoke=allowMultiple?this.getPlugins(extensionPointName):[this.getPlugins(extensionPointName)[0]];return pluginsToInvoke.map(plugin=>{const method=getValueByPath(plugin,extensionPointName);if(!isFunction(method)||noCall){return method;}try{return method.apply(getValueByPath(plugin,pluginMethodPath),args);}catch(err){// When a plugin failed, doesn't break the app
582
582
  if(throws){throw err;}else {this.logger?.error(PLUGIN_INVOCATION_ERROR(PLUGIN_ENGINE,extensionPointName,plugin.name),err);}}return null;});}invokeSingle(extPoint,...args){return this.invoke(extPoint,false,...args)[0];}invokeMultiple(extPoint,...args){return this.invoke(extPoint,true,...args);}}const defaultPluginEngine=new PluginEngine({throws:true},defaultLogger);
583
583
 
584
- const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const UNHANDLEDEXCEPTION_FOR_NON_ERROR_OBJECT='unhandledException handler received a non-error';const ERROR_MESSAGES_TO_BE_FILTERED=[FAILED_REQUEST_ERR_MSG_PREFIX,UNHANDLEDEXCEPTION_FOR_NON_ERROR_OBJECT];
584
+ const LOAD_ORIGIN='RS_JS_SDK';
585
585
 
586
586
  /**
587
587
  * Utility method to normalise errors
588
588
  */const processError=error=>{let errorMessage;try{if(isString(error)){errorMessage=error;}else if(error instanceof Error){errorMessage=error.message;}else if(error instanceof ErrorEvent){errorMessage=error.message;}else {errorMessage=error.message?error.message:stringifyWithoutCircular(error);}}catch(e){errorMessage=`Unknown error: ${e.message}`;}return errorMessage;};const getNormalizedErrorForUnhandledError=error=>{try{if(error instanceof Error||error instanceof ErrorEvent||error instanceof PromiseRejectionEvent&&error.reason){return error;}// TODO: remove this block once all device mode integrations start using the v3 script loader module (TS)
589
- // if (error instanceof Event) {
590
- // const eventTarget = error.target as ErrorTarget;
591
- // // Discard all the non-script loading errors
592
- // if (eventTarget && eventTarget.localName !== 'script') {
593
- // return undefined;
594
- // }
595
- // // Discard script errors that are not originated at SDK or from native SDKs
596
- // if (
597
- // eventTarget?.dataset &&
598
- // (eventTarget.dataset.loader !== LOAD_ORIGIN ||
599
- // eventTarget.dataset.isnonnativesdk !== 'true')
600
- // ) {
601
- // return undefined;
602
- // }
603
- // const errorMessage = `Error in loading a third-party script from URL ${eventTarget?.src} with ID ${eventTarget?.id}.`;
604
- // return Object.create(error, {
605
- // message: { value: errorMessage },
606
- // });
607
- // }
608
- return undefined;}catch(e){return e;}};/**
609
- * A function to determine whether the error should be promoted to notify or not
610
- * @param {Error} error
611
- * @returns
612
- */const isAllowedToBeNotified=error=>{if((error instanceof Error||error instanceof ErrorEvent)&&error.message){return !ERROR_MESSAGES_TO_BE_FILTERED.some(e=>error.message.includes(e));}if(error instanceof PromiseRejectionEvent&&typeof error.reason==='string'){return !ERROR_MESSAGES_TO_BE_FILTERED.some(e=>error.reason.includes(e));}return true;};
589
+ if(error instanceof Event){const eventTarget=error.target;// Discard all the non-script loading errors
590
+ if(eventTarget&&eventTarget.localName!=='script'){return undefined;}// Discard script errors that are not originated at SDK or from native SDKs
591
+ if(eventTarget?.dataset&&(eventTarget.dataset.loader!==LOAD_ORIGIN||eventTarget.dataset.isnonnativesdk!=='true')){return undefined;}const errorMessage=`Error in loading a third-party script from URL ${eventTarget?.src} with ID ${eventTarget?.id}.`;return Object.create(error,{message:{value:errorMessage}});}return error;}catch(e){return e;}};
613
592
 
614
593
  /**
615
594
  * A service to handle errors
@@ -630,7 +609,7 @@
630
609
  * Send handled errors to external error monitoring service via a plugin
631
610
  *
632
611
  * @param {Error} error Error instance from handled error
633
- */notifyError(error,errorState){if(this.pluginEngine&&this.httpClient&&isAllowedToBeNotified(error)){try{this.pluginEngine.invokeSingle('errorReporting.notify',this.pluginEngine,// deprecated parameter
612
+ */notifyError(error,errorState){if(this.pluginEngine&&this.httpClient){try{this.pluginEngine.invokeSingle('errorReporting.notify',this.pluginEngine,// deprecated parameter
634
613
  this.errReportingClient,// deprecated parameter
635
614
  error,state,this.logger,this.httpClient,errorState);}catch(err){// Not calling onError here as we don't want to go into infinite loop
636
615
  this.logger?.error(NOTIFY_FAILURE_ERROR(ERROR_HANDLER),err);}}}}const defaultErrorHandler=new ErrorHandler(defaultLogger,defaultPluginEngine);
@@ -1137,6 +1116,8 @@
1137
1116
 
1138
1117
  const METRICS_PAYLOAD_VERSION='1';
1139
1118
 
1119
+ const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const ERROR_MESSAGES_TO_BE_FILTERED=[FAILED_REQUEST_ERR_MSG_PREFIX];
1120
+
1140
1121
  // Errors from the below scripts are NOT allowed to reach Bugsnag
1141
1122
  const SDK_FILE_NAME_PREFIXES=()=>['rsa'// Prefix for all the SDK scripts including plugins and module federated chunks
1142
1123
  ];const DEV_HOSTS=['www.test-host.com','localhost','127.0.0.1','[::1]'];// List of keys to exclude from the metadata
@@ -1144,11 +1125,19 @@
1144
1125
  const APP_STATE_EXCLUDE_KEYS=['userId','userTraits','groupId','groupTraits','anonymousId','config','instance',// destination instance objects
1145
1126
  'eventBuffer',// pre-load event buffer (may contain PII)
1146
1127
  'traits'];const REQUEST_TIMEOUT_MS$1=10*1000;// 10 seconds
1147
- const NOTIFIER_NAME='RudderStack JavaScript SDK Error Notifier';const SDK_GITHUB_URL='https://github.com/rudderlabs/rudder-sdk-js';const SOURCE_NAME='js';
1128
+ const NOTIFIER_NAME='RudderStack JavaScript SDK Error Notifier';const SDK_GITHUB_URL='https://github.com/rudderlabs/rudder-sdk-js';const SOURCE_NAME='js';const ERROR_REPORTING_PLUGIN='ErrorReportingPlugin';
1148
1129
 
1149
1130
  const getConfigForPayloadCreation=(err,errorType)=>{switch(errorType){case ErrorType.UNHANDLEDEXCEPTION:{const{error}=err;return {component:'unhandledException handler',tolerateNonErrors:true,errorFramesToSkip:1,normalizedError:error||err};}case ErrorType.UNHANDLEDREJECTION:{const error=err;return {component:'unhandledrejection handler',tolerateNonErrors:false,errorFramesToSkip:1,normalizedError:error.reason};}case ErrorType.HANDLEDEXCEPTION:default:return {component:'notify()',tolerateNonErrors:true,errorFramesToSkip:2,normalizedError:err};}};const createNewBreadcrumb=(message,metaData)=>({type:'manual',name:message,timestamp:new Date(),metaData:metaData??{}});const getReleaseStage=()=>{const host=globalThis.location.hostname;return host&&DEV_HOSTS.includes(host)?'development':'production';};const getAppStateForMetadata=state=>{const stateStr=stringifyWithoutCircular(state,false,APP_STATE_EXCLUDE_KEYS);return stateStr!==null?JSON.parse(stateStr):{};};const getURLWithoutQueryString=()=>{const url=globalThis.location.href.split('?');return url[0];};const getErrorContext=event=>{const{message}=event;let context=message;// Hack for easily grouping the script load errors
1150
1131
  // on the dashboard
1151
- if(message.includes('Error in loading a third-party script')){context='Script load failures';}return context;};const getBugsnagErrorEvent=(payload,errorState,state)=>({notifier:{name:NOTIFIER_NAME,version:state.context.app.value.version,url:SDK_GITHUB_URL},events:[{payloadVersion:'5',exceptions:clone(payload.errors),severity:errorState.severity,unhandled:errorState.unhandled,severityReason:errorState.severityReason,app:{version:state.context.app.value.version,releaseStage:getReleaseStage()},device:{locale:state.context.locale.value??undefined,userAgent:state.context.userAgent.value??undefined,time:new Date()},request:{url:getURLWithoutQueryString(),clientIp:'[NOT COLLECTED]'},breadcrumbs:clone(state.reporting.breadcrumbs.value),context:getErrorContext(payload.errors[0]),metaData:{sdk:{name:'JS',installType:state.context.app.value.installType},state:getAppStateForMetadata(state)??{},source:{snippetVersion:globalThis.RudderSnippetVersion}},user:{id:state.source.value?.id??state.lifecycle.writeKey.value}}]});const isRudderSDKError=event=>{const errorOrigin=event.stacktrace?.[0]?.file;if(!errorOrigin||typeof errorOrigin!=='string'){return false;}const srcFileName=errorOrigin.substring(errorOrigin.lastIndexOf('/')+1);const paths=errorOrigin.split('/');// extract the parent folder name from the error origin file path
1132
+ if(message.includes('Error in loading a third-party script')){context='Script load failures';}return context;};const getBugsnagErrorEvent=(payload,errorState,state)=>({notifier:{name:NOTIFIER_NAME,version:state.context.app.value.version,url:SDK_GITHUB_URL},events:[{payloadVersion:'5',exceptions:clone(payload.errors),severity:errorState.severity,unhandled:errorState.unhandled,severityReason:errorState.severityReason,app:{version:state.context.app.value.version,releaseStage:getReleaseStage()},device:{locale:state.context.locale.value??undefined,userAgent:state.context.userAgent.value??undefined,time:new Date()},request:{url:getURLWithoutQueryString(),clientIp:'[NOT COLLECTED]'},breadcrumbs:clone(state.reporting.breadcrumbs.value),context:getErrorContext(payload.errors[0]),metaData:{sdk:{name:'JS',installType:state.context.app.value.installType},state:getAppStateForMetadata(state)??{},source:{snippetVersion:globalThis.RudderSnippetVersion}},user:{id:state.source.value?.id??state.lifecycle.writeKey.value}}]});/**
1133
+ * A function to determine whether the error should be promoted to notify or not
1134
+ * @param {Error} error
1135
+ * @returns
1136
+ */const isAllowedToBeNotified=event=>{const errorMessage=event.message;if(errorMessage&&typeof errorMessage==='string'){return !ERROR_MESSAGES_TO_BE_FILTERED.some(e=>errorMessage.includes(e));}return true;};/**
1137
+ * A function to determine if the error is from Rudder SDK
1138
+ * @param {Error} event
1139
+ * @returns
1140
+ */const isRudderSDKError=event=>{const errorOrigin=event.stacktrace?.[0]?.file;if(!errorOrigin||typeof errorOrigin!=='string'){return false;}const srcFileName=errorOrigin.substring(errorOrigin.lastIndexOf('/')+1);const paths=errorOrigin.split('/');// extract the parent folder name from the error origin file path
1152
1141
  // Ex: parentFolderName will be 'sample' for url: https://example.com/sample/file.min.js
1153
1142
  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)=>{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},errors:payload};return stringifyWithoutCircular(data);};
1154
1143
 
@@ -1192,19 +1181,10 @@
1192
1181
  // This adds one.
1193
1182
  if(f.lineNumber>-1&&!f.file&&!f.method){f.file='global code';}return f;};const ensureString=str=>typeof str==='string'?str:'';function createBugsnagError(errorClass,errorMessage,stacktrace){return {errorClass:ensureString(errorClass),message:ensureString(errorMessage),type:'browserjs',stacktrace:stacktrace.reduce((accum,frame)=>{const f=formatStackframe(frame);// don't include a stackframe if none of its properties are defined
1194
1183
  try{if(JSON.stringify(f)==='{}')return accum;return accum.concat(f);}catch(e){return accum;}},[])};}// Helpers
1195
- const getStacktrace=(error,errorFramesToSkip)=>{if(hasStack(error))return ErrorStackParser.parse(error).slice(errorFramesToSkip);return [];};const hasNecessaryFields=error=>(typeof error.name==='string'||typeof error.errorClass==='string')&&(typeof error.message==='string'||typeof error.errorMessage==='string');const normaliseError=(maybeError,tolerateNonErrors,component,logger)=>{let error;let internalFrames=0;const createAndLogInputError=reason=>{const verb=component==='error cause'?'was':'received';if(logger)logger.warn(`${component} ${verb} a non-error: "${reason}"`);const err=new Error(`${component} ${verb} a non-error. See "${component}" tab for more detail.`);err.name='InvalidError';return err;};// In some cases:
1196
- //
1197
- // - the promise rejection handler (both in the browser and node)
1198
- // - the node uncaughtException handler
1199
- //
1200
- // We are really limited in what we can do to get a stacktrace. So we use the
1201
- // tolerateNonErrors option to ensure that the resulting error communicates as
1202
- // such.
1203
- if(!tolerateNonErrors){if(isError(maybeError)){error=maybeError;}else {error=createAndLogInputError(typeof maybeError);internalFrames+=2;}}else {switch(typeof maybeError){case'string':case'number':case'boolean':error=new Error(String(maybeError));internalFrames+=1;break;case'function':error=createAndLogInputError('function');internalFrames+=2;break;case'object':if(maybeError!==null&&isError(maybeError)){error=maybeError;}else if(maybeError!==null&&hasNecessaryFields(maybeError)){error=new Error(maybeError.message||maybeError.errorMessage);error.name=maybeError.name||maybeError.errorClass;internalFrames+=1;}else {error=createAndLogInputError(maybeError===null?'null':'unsupported object');internalFrames+=2;}break;default:error=createAndLogInputError('nothing');internalFrames+=2;}}if(!hasStack(error)){// in IE10/11 a new Error() doesn't have a stacktrace until you throw it, so try that here
1184
+ const getStacktrace=(error,errorFramesToSkip)=>{if(hasStack(error))return ErrorStackParser.parse(error).slice(errorFramesToSkip);return [];};const hasNecessaryFields=error=>(typeof error.name==='string'||typeof error.errorClass==='string')&&(typeof error.message==='string'||typeof error.errorMessage==='string');const normaliseError=(maybeError,component,logger)=>{let error;let internalFrames=0;if(isError(maybeError)){error=maybeError;}else if(typeof maybeError==='object'&&hasNecessaryFields(maybeError)){error=new Error(maybeError.message||maybeError.errorMessage);error.name=maybeError.name||maybeError.errorClass;internalFrames+=1;}else {logger?.warn(`${ERROR_REPORTING_PLUGIN}:: ${component} received a non-error: ${stringifyWithoutCircular(error)}`);error=undefined;}if(error&&!hasStack(error)){// in IE10/11 a new Error() doesn't have a stacktrace until you throw it, so try that here
1204
1185
  try{throw error;}catch(e){if(hasStack(e)){error=e;// if the error only got a stacktrace after we threw it here, we know it
1205
- // will only have one extra internal frame from this function, regardless
1206
- // of whether it went through createAndLogInputError() or not
1207
- internalFrames=1;}}}return [error,internalFrames];};class ErrorFormat{constructor(errorClass,errorMessage,stacktrace){this.errors=[createBugsnagError(errorClass,errorMessage,stacktrace)];}static create(maybeError,tolerateNonErrors,handledState,component,errorFramesToSkip=0,logger){const[error,internalFrames]=normaliseError(maybeError,tolerateNonErrors,component,logger);let event;try{const stacktrace=getStacktrace(error,// if an error was created/throw in the normaliseError() function, we need to
1186
+ // will only have one extra internal frame from this function
1187
+ internalFrames=1;}}}return [error,internalFrames];};class ErrorFormat{constructor(errorClass,errorMessage,stacktrace){this.errors=[createBugsnagError(errorClass,errorMessage,stacktrace)];}static create(maybeError,tolerateNonErrors,handledState,component,errorFramesToSkip=0,logger){const[error,internalFrames]=normaliseError(maybeError,component,logger);if(!error){return undefined;}let event;try{const stacktrace=getStacktrace(error,// if an error was created/throw in the normaliseError() function, we need to
1208
1188
  // tell the getStacktrace() function to skip the number of frames we know will
1209
1189
  // be from our own functions. This is added to the number of frames deep we
1210
1190
  // were told about
@@ -1215,7 +1195,7 @@
1215
1195
  const pluginName$9='ErrorReporting';const ErrorReporting=()=>({name:pluginName$9,deps:[],initialize:state=>{state.plugins.loadedPlugins.value=[...state.plugins.loadedPlugins.value,pluginName$9];state.reporting.isErrorReportingPluginLoaded.value=true;if(state.reporting.breadcrumbs?.value){state.reporting.breadcrumbs.value=[createNewBreadcrumb('Error Reporting Plugin Loaded')];}},errorReporting:{// This extension point is deprecated
1216
1196
  // TODO: Remove this in the next major release
1217
1197
  init:(state,pluginEngine,externalSrcLoader,logger,isInvokedFromLatestCore)=>{if(isInvokedFromLatestCore){return undefined;}if(!state.source.value?.config||!state.source.value?.id){return Promise.reject(new Error(INVALID_SOURCE_CONFIG_ERROR));}return pluginEngine.invokeSingle('errorReportingProvider.init',state,externalSrcLoader,logger);},notify:(pluginEngine,client,error,state,logger,httpClient,errorState)=>{if(httpClient){const{component,tolerateNonErrors,errorFramesToSkip,normalizedError}=getConfigForPayloadCreation(error,errorState?.severityReason.type);// Generate the error payload
1218
- const errorPayload=ErrorFormat.create(normalizedError,tolerateNonErrors,errorState,component,errorFramesToSkip,logger);// filter errors
1198
+ const errorPayload=ErrorFormat.create(normalizedError,tolerateNonErrors,errorState,component,errorFramesToSkip,logger);if(!errorPayload||!isAllowedToBeNotified(errorPayload.errors[0])){return;}// filter errors
1219
1199
  if(!isRudderSDKError(errorPayload.errors[0])){return;}// enrich error payload
1220
1200
  const bugsnagPayload=getBugsnagErrorEvent(errorPayload,errorState,state);// send it to metrics service
1221
1201
  httpClient?.getAsyncData({url:state.metrics.metricsServiceUrl.value,options:{method:'POST',data:getErrorDeliveryPayload(bugsnagPayload,state),sendRawData:true},isRawResponse:true,timeout:REQUEST_TIMEOUT_MS$1,callback:(result,details)=>{// do nothing
@@ -2734,9 +2714,14 @@
2734
2714
  */const getUrlWithoutHash=url=>{let urlWithoutHash=url;try{const urlObj=new URL(url);urlWithoutHash=urlObj.origin+urlObj.pathname+urlObj.search;}catch(error){// Do nothing
2735
2715
  }return urlWithoutHash;};
2736
2716
 
2717
+ /**
2718
+ * Determines if the SDK is running inside a chrome extension
2719
+ * @returns boolean
2720
+ */const isSDKRunningInChromeExtension=()=>!!(window.chrome&&window.chrome.runtime&&window.chrome.runtime.id);
2721
+
2737
2722
  const DEFAULT_PRE_CONSENT_STORAGE_STRATEGY='none';const DEFAULT_PRE_CONSENT_EVENTS_DELIVERY_TYPE='immediate';
2738
2723
 
2739
- const isMetricsReportingEnabled=sourceConfig=>sourceConfig?.statsCollection?.metrics?.enabled===true;
2724
+ const isErrorReportingEnabled=sourceConfig=>sourceConfig?.statsCollection?.errors?.enabled===true;const isMetricsReportingEnabled=sourceConfig=>sourceConfig?.statsCollection?.metrics?.enabled===true;
2740
2725
 
2741
2726
  /**
2742
2727
  * Validates and normalizes the consent options provided by the user
@@ -2770,10 +2755,7 @@
2770
2755
  * Updates the reporting state variables from the source config data
2771
2756
  * @param res Source config
2772
2757
  * @param logger Logger instance
2773
- */const updateReportingState=res=>{state.reporting.isErrorReportingEnabled.value=false;// TODO: Enable this once the error reporting is tested properly
2774
- // state.reporting.isErrorReportingEnabled.value =
2775
- // isErrorReportingEnabled(res.source.config) && !isSDKRunningInChromeExtension();
2776
- state.reporting.isMetricsReportingEnabled.value=isMetricsReportingEnabled(res.source.config);};const updateStorageStateFromLoadOptions=logger=>{const{useServerSideCookies,dataServiceEndpoint,storage:storageOptsFromLoad,setCookieDomain,sameDomainCookiesOnly}=state.loadOptions.value;let storageType=storageOptsFromLoad?.type;if(isDefined(storageType)&&!isValidStorageType(storageType)){logger?.warn(STORAGE_TYPE_VALIDATION_WARNING(CONFIG_MANAGER,storageType,DEFAULT_STORAGE_TYPE));storageType=DEFAULT_STORAGE_TYPE;}let storageEncryptionVersion=storageOptsFromLoad?.encryption?.version;const encryptionPluginName=storageEncryptionVersion&&StorageEncryptionVersionsToPluginNameMap[storageEncryptionVersion];if(!isUndefined(storageEncryptionVersion)&&isUndefined(encryptionPluginName)){// set the default encryption plugin
2758
+ */const updateReportingState=res=>{state.reporting.isErrorReportingEnabled.value=isErrorReportingEnabled(res.source.config)&&!isSDKRunningInChromeExtension();state.reporting.isMetricsReportingEnabled.value=isMetricsReportingEnabled(res.source.config);};const updateStorageStateFromLoadOptions=logger=>{const{useServerSideCookies,dataServiceEndpoint,storage:storageOptsFromLoad,setCookieDomain,sameDomainCookiesOnly}=state.loadOptions.value;let storageType=storageOptsFromLoad?.type;if(isDefined(storageType)&&!isValidStorageType(storageType)){logger?.warn(STORAGE_TYPE_VALIDATION_WARNING(CONFIG_MANAGER,storageType,DEFAULT_STORAGE_TYPE));storageType=DEFAULT_STORAGE_TYPE;}let storageEncryptionVersion=storageOptsFromLoad?.encryption?.version;const encryptionPluginName=storageEncryptionVersion&&StorageEncryptionVersionsToPluginNameMap[storageEncryptionVersion];if(!isUndefined(storageEncryptionVersion)&&isUndefined(encryptionPluginName)){// set the default encryption plugin
2777
2759
  logger?.warn(UNSUPPORTED_STORAGE_ENCRYPTION_VERSION_WARNING(CONFIG_MANAGER,storageEncryptionVersion,StorageEncryptionVersionsToPluginNameMap,DEFAULT_STORAGE_ENCRYPTION_VERSION));storageEncryptionVersion=DEFAULT_STORAGE_ENCRYPTION_VERSION;}else if(isUndefined(storageEncryptionVersion)){storageEncryptionVersion=DEFAULT_STORAGE_ENCRYPTION_VERSION;}// Allow migration only if the configured encryption version is the default encryption version
2778
2760
  const configuredMigrationValue=storageOptsFromLoad?.migrate;const finalMigrationVal=configuredMigrationValue&&storageEncryptionVersion===DEFAULT_STORAGE_ENCRYPTION_VERSION;if(configuredMigrationValue===true&&finalMigrationVal!==configuredMigrationValue){logger?.warn(STORAGE_DATA_MIGRATION_OVERRIDE_WARNING(CONFIG_MANAGER,storageEncryptionVersion,DEFAULT_STORAGE_ENCRYPTION_VERSION));}r(()=>{state.storage.type.value=storageType;let cookieOptions=storageOptsFromLoad?.cookie??{};if(useServerSideCookies){state.serverCookies.isEnabledServerSideCookies.value=useServerSideCookies;const providedCookieDomain=cookieOptions.domain??setCookieDomain;/**
2779
2761
  * Based on the following conditions, we decide whether to use the exact domain or not to determine the data service URL:
@@ -365,7 +365,7 @@ payload.groupId=tryStringify(payload.groupId);if(isObjectLiteralAndNotNull(paylo
365
365
 
366
366
  const CAPABILITIES_MANAGER='CapabilitiesManager';const CONFIG_MANAGER='ConfigManager';const EVENT_MANAGER='EventManager';const PLUGINS_MANAGER='PluginsManager';const USER_SESSION_MANAGER='UserSessionManager';const ERROR_HANDLER='ErrorHandler';const PLUGIN_ENGINE='PluginEngine';const STORE_MANAGER='StoreManager';const READY_API='readyApi';const EVENT_REPOSITORY='EventRepository';const EXTERNAL_SRC_LOADER='ExternalSrcLoader';const HTTP_CLIENT='HttpClient';const RS_APP='RudderStackApplication';const ANALYTICS_CORE='AnalyticsCore';
367
367
 
368
- const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.6';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';
368
+ const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.8';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';
369
369
 
370
370
  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';
371
371
 
@@ -570,35 +570,14 @@ processRawPlugins(callback){callback(this.plugins);this.cache={};}invoke(extPoin
570
570
  extensionPointName=extensionPointName.replace(/(^!|!$)/g,'');if(!extensionPointName){throw new Error(PLUGIN_EXT_POINT_INVALID_ERROR);}const extensionPointNameParts=extensionPointName.split('.');extensionPointNameParts.pop();const pluginMethodPath=extensionPointNameParts.join('.');const pluginsToInvoke=allowMultiple?this.getPlugins(extensionPointName):[this.getPlugins(extensionPointName)[0]];return pluginsToInvoke.map(plugin=>{const method=getValueByPath(plugin,extensionPointName);if(!isFunction(method)||noCall){return method;}try{return method.apply(getValueByPath(plugin,pluginMethodPath),args);}catch(err){// When a plugin failed, doesn't break the app
571
571
  if(throws){throw err;}else {this.logger?.error(PLUGIN_INVOCATION_ERROR(PLUGIN_ENGINE,extensionPointName,plugin.name),err);}}return null;});}invokeSingle(extPoint,...args){return this.invoke(extPoint,false,...args)[0];}invokeMultiple(extPoint,...args){return this.invoke(extPoint,true,...args);}}const defaultPluginEngine=new PluginEngine({throws:true},defaultLogger);
572
572
 
573
- const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const UNHANDLEDEXCEPTION_FOR_NON_ERROR_OBJECT='unhandledException handler received a non-error';const ERROR_MESSAGES_TO_BE_FILTERED=[FAILED_REQUEST_ERR_MSG_PREFIX,UNHANDLEDEXCEPTION_FOR_NON_ERROR_OBJECT];
573
+ const LOAD_ORIGIN='RS_JS_SDK';
574
574
 
575
575
  /**
576
576
  * Utility method to normalise errors
577
577
  */const processError=error=>{let errorMessage;try{if(isString(error)){errorMessage=error;}else if(error instanceof Error){errorMessage=error.message;}else if(error instanceof ErrorEvent){errorMessage=error.message;}else {errorMessage=error.message?error.message:stringifyWithoutCircular(error);}}catch(e){errorMessage=`Unknown error: ${e.message}`;}return errorMessage;};const getNormalizedErrorForUnhandledError=error=>{try{if(error instanceof Error||error instanceof ErrorEvent||error instanceof PromiseRejectionEvent&&error.reason){return error;}// TODO: remove this block once all device mode integrations start using the v3 script loader module (TS)
578
- // if (error instanceof Event) {
579
- // const eventTarget = error.target as ErrorTarget;
580
- // // Discard all the non-script loading errors
581
- // if (eventTarget && eventTarget.localName !== 'script') {
582
- // return undefined;
583
- // }
584
- // // Discard script errors that are not originated at SDK or from native SDKs
585
- // if (
586
- // eventTarget?.dataset &&
587
- // (eventTarget.dataset.loader !== LOAD_ORIGIN ||
588
- // eventTarget.dataset.isnonnativesdk !== 'true')
589
- // ) {
590
- // return undefined;
591
- // }
592
- // const errorMessage = `Error in loading a third-party script from URL ${eventTarget?.src} with ID ${eventTarget?.id}.`;
593
- // return Object.create(error, {
594
- // message: { value: errorMessage },
595
- // });
596
- // }
597
- return undefined;}catch(e){return e;}};/**
598
- * A function to determine whether the error should be promoted to notify or not
599
- * @param {Error} error
600
- * @returns
601
- */const isAllowedToBeNotified=error=>{if((error instanceof Error||error instanceof ErrorEvent)&&error.message){return !ERROR_MESSAGES_TO_BE_FILTERED.some(e=>error.message.includes(e));}if(error instanceof PromiseRejectionEvent&&typeof error.reason==='string'){return !ERROR_MESSAGES_TO_BE_FILTERED.some(e=>error.reason.includes(e));}return true;};
578
+ if(error instanceof Event){const eventTarget=error.target;// Discard all the non-script loading errors
579
+ if(eventTarget&&eventTarget.localName!=='script'){return undefined;}// Discard script errors that are not originated at SDK or from native SDKs
580
+ if(eventTarget?.dataset&&(eventTarget.dataset.loader!==LOAD_ORIGIN||eventTarget.dataset.isnonnativesdk!=='true')){return undefined;}const errorMessage=`Error in loading a third-party script from URL ${eventTarget?.src} with ID ${eventTarget?.id}.`;return Object.create(error,{message:{value:errorMessage}});}return error;}catch(e){return e;}};
602
581
 
603
582
  /**
604
583
  * A service to handle errors
@@ -619,7 +598,7 @@ breadcrumb,this.logger,state);}catch(err){this.onError(err,ERROR_HANDLER,'errorR
619
598
  * Send handled errors to external error monitoring service via a plugin
620
599
  *
621
600
  * @param {Error} error Error instance from handled error
622
- */notifyError(error,errorState){if(this.pluginEngine&&this.httpClient&&isAllowedToBeNotified(error)){try{this.pluginEngine.invokeSingle('errorReporting.notify',this.pluginEngine,// deprecated parameter
601
+ */notifyError(error,errorState){if(this.pluginEngine&&this.httpClient){try{this.pluginEngine.invokeSingle('errorReporting.notify',this.pluginEngine,// deprecated parameter
623
602
  this.errReportingClient,// deprecated parameter
624
603
  error,state,this.logger,this.httpClient,errorState);}catch(err){// Not calling onError here as we don't want to go into infinite loop
625
604
  this.logger?.error(NOTIFY_FAILURE_ERROR(ERROR_HANDLER),err);}}}}const defaultErrorHandler=new ErrorHandler(defaultLogger,defaultPluginEngine);
@@ -786,6 +765,8 @@ unregisterLocalPlugins(){Object.values(pluginsInventory).forEach(localPlugin=>{t
786
765
  * Utility to parse XHR JSON response
787
766
  */const responseTextToJson=(responseText,onError)=>{try{return JSON.parse(responseText||'');}catch(err){const error=getMutatedError(err,'Failed to parse response data');if(isFunction(onError)){onError(error);}else {throw error;}}return undefined;};
788
767
 
768
+ const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';
769
+
789
770
  const DEFAULT_XHR_REQUEST_OPTIONS={headers:{Accept:'application/json','Content-Type':'application/json;charset=UTF-8'},method:'GET'};/**
790
771
  * Utility to create request configuration based on default options
791
772
  */const createXhrRequestOptions=(url,options,basicAuthHeader)=>{const requestOptions=mergeDeepRight(DEFAULT_XHR_REQUEST_OPTIONS,options||{});if(basicAuthHeader){requestOptions.headers=mergeDeepRight(requestOptions.headers,{Authorization:basicAuthHeader});}requestOptions.url=url;return requestOptions;};/**
@@ -1057,9 +1038,14 @@ if(utmParam==='campaign'){utmParam='name';}result[utmParam]=value;}});}catch(err
1057
1038
  */const getUrlWithoutHash=url=>{let urlWithoutHash=url;try{const urlObj=new URL(url);urlWithoutHash=urlObj.origin+urlObj.pathname+urlObj.search;}catch(error){// Do nothing
1058
1039
  }return urlWithoutHash;};
1059
1040
 
1041
+ /**
1042
+ * Determines if the SDK is running inside a chrome extension
1043
+ * @returns boolean
1044
+ */const isSDKRunningInChromeExtension=()=>!!(window.chrome&&window.chrome.runtime&&window.chrome.runtime.id);
1045
+
1060
1046
  const DEFAULT_PRE_CONSENT_STORAGE_STRATEGY='none';const DEFAULT_PRE_CONSENT_EVENTS_DELIVERY_TYPE='immediate';
1061
1047
 
1062
- const isMetricsReportingEnabled=sourceConfig=>sourceConfig?.statsCollection?.metrics?.enabled===true;
1048
+ const isErrorReportingEnabled=sourceConfig=>sourceConfig?.statsCollection?.errors?.enabled===true;const isMetricsReportingEnabled=sourceConfig=>sourceConfig?.statsCollection?.metrics?.enabled===true;
1063
1049
 
1064
1050
  const DEFAULT_INTEGRATIONS_CONFIG={All:true};
1065
1051
 
@@ -1095,10 +1081,7 @@ for(const script of scripts){const src=script.getAttribute('src');if(src&&sdkFil
1095
1081
  * Updates the reporting state variables from the source config data
1096
1082
  * @param res Source config
1097
1083
  * @param logger Logger instance
1098
- */const updateReportingState=res=>{state.reporting.isErrorReportingEnabled.value=false;// TODO: Enable this once the error reporting is tested properly
1099
- // state.reporting.isErrorReportingEnabled.value =
1100
- // isErrorReportingEnabled(res.source.config) && !isSDKRunningInChromeExtension();
1101
- state.reporting.isMetricsReportingEnabled.value=isMetricsReportingEnabled(res.source.config);};const updateStorageStateFromLoadOptions=logger=>{const{useServerSideCookies,dataServiceEndpoint,storage:storageOptsFromLoad,setCookieDomain,sameDomainCookiesOnly}=state.loadOptions.value;let storageType=storageOptsFromLoad?.type;if(isDefined(storageType)&&!isValidStorageType(storageType)){logger?.warn(STORAGE_TYPE_VALIDATION_WARNING(CONFIG_MANAGER,storageType,DEFAULT_STORAGE_TYPE));storageType=DEFAULT_STORAGE_TYPE;}let storageEncryptionVersion=storageOptsFromLoad?.encryption?.version;const encryptionPluginName=storageEncryptionVersion&&StorageEncryptionVersionsToPluginNameMap[storageEncryptionVersion];if(!isUndefined(storageEncryptionVersion)&&isUndefined(encryptionPluginName)){// set the default encryption plugin
1084
+ */const updateReportingState=res=>{state.reporting.isErrorReportingEnabled.value=isErrorReportingEnabled(res.source.config)&&!isSDKRunningInChromeExtension();state.reporting.isMetricsReportingEnabled.value=isMetricsReportingEnabled(res.source.config);};const updateStorageStateFromLoadOptions=logger=>{const{useServerSideCookies,dataServiceEndpoint,storage:storageOptsFromLoad,setCookieDomain,sameDomainCookiesOnly}=state.loadOptions.value;let storageType=storageOptsFromLoad?.type;if(isDefined(storageType)&&!isValidStorageType(storageType)){logger?.warn(STORAGE_TYPE_VALIDATION_WARNING(CONFIG_MANAGER,storageType,DEFAULT_STORAGE_TYPE));storageType=DEFAULT_STORAGE_TYPE;}let storageEncryptionVersion=storageOptsFromLoad?.encryption?.version;const encryptionPluginName=storageEncryptionVersion&&StorageEncryptionVersionsToPluginNameMap[storageEncryptionVersion];if(!isUndefined(storageEncryptionVersion)&&isUndefined(encryptionPluginName)){// set the default encryption plugin
1102
1085
  logger?.warn(UNSUPPORTED_STORAGE_ENCRYPTION_VERSION_WARNING(CONFIG_MANAGER,storageEncryptionVersion,StorageEncryptionVersionsToPluginNameMap,DEFAULT_STORAGE_ENCRYPTION_VERSION));storageEncryptionVersion=DEFAULT_STORAGE_ENCRYPTION_VERSION;}else if(isUndefined(storageEncryptionVersion)){storageEncryptionVersion=DEFAULT_STORAGE_ENCRYPTION_VERSION;}// Allow migration only if the configured encryption version is the default encryption version
1103
1086
  const configuredMigrationValue=storageOptsFromLoad?.migrate;const finalMigrationVal=configuredMigrationValue&&storageEncryptionVersion===DEFAULT_STORAGE_ENCRYPTION_VERSION;if(configuredMigrationValue===true&&finalMigrationVal!==configuredMigrationValue){logger?.warn(STORAGE_DATA_MIGRATION_OVERRIDE_WARNING(CONFIG_MANAGER,storageEncryptionVersion,DEFAULT_STORAGE_ENCRYPTION_VERSION));}r(()=>{state.storage.type.value=storageType;let cookieOptions=storageOptsFromLoad?.cookie??{};if(useServerSideCookies){state.serverCookies.isEnabledServerSideCookies.value=useServerSideCookies;const providedCookieDomain=cookieOptions.domain??setCookieDomain;/**
1104
1087
  * Based on the following conditions, we decide whether to use the exact domain or not to determine the data service URL:
@@ -374,7 +374,7 @@ payload.groupId=tryStringify(payload.groupId);if(isObjectLiteralAndNotNull(paylo
374
374
 
375
375
  const CAPABILITIES_MANAGER='CapabilitiesManager';const CONFIG_MANAGER='ConfigManager';const EVENT_MANAGER='EventManager';const PLUGINS_MANAGER='PluginsManager';const USER_SESSION_MANAGER='UserSessionManager';const ERROR_HANDLER='ErrorHandler';const PLUGIN_ENGINE='PluginEngine';const STORE_MANAGER='StoreManager';const READY_API='readyApi';const EVENT_REPOSITORY='EventRepository';const EXTERNAL_SRC_LOADER='ExternalSrcLoader';const HTTP_CLIENT='HttpClient';const RS_APP='RudderStackApplication';const ANALYTICS_CORE='AnalyticsCore';
376
376
 
377
- const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.6';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';
377
+ const APP_NAME='RudderLabs JavaScript SDK';const APP_VERSION='3.7.8';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';
378
378
 
379
379
  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';
380
380
 
@@ -579,35 +579,14 @@ processRawPlugins(callback){callback(this.plugins);this.cache={};}invoke(extPoin
579
579
  extensionPointName=extensionPointName.replace(/(^!|!$)/g,'');if(!extensionPointName){throw new Error(PLUGIN_EXT_POINT_INVALID_ERROR);}const extensionPointNameParts=extensionPointName.split('.');extensionPointNameParts.pop();const pluginMethodPath=extensionPointNameParts.join('.');const pluginsToInvoke=allowMultiple?this.getPlugins(extensionPointName):[this.getPlugins(extensionPointName)[0]];return pluginsToInvoke.map(plugin=>{const method=getValueByPath(plugin,extensionPointName);if(!isFunction(method)||noCall){return method;}try{return method.apply(getValueByPath(plugin,pluginMethodPath),args);}catch(err){// When a plugin failed, doesn't break the app
580
580
  if(throws){throw err;}else {this.logger?.error(PLUGIN_INVOCATION_ERROR(PLUGIN_ENGINE,extensionPointName,plugin.name),err);}}return null;});}invokeSingle(extPoint,...args){return this.invoke(extPoint,false,...args)[0];}invokeMultiple(extPoint,...args){return this.invoke(extPoint,true,...args);}}const defaultPluginEngine=new PluginEngine({throws:true},defaultLogger);
581
581
 
582
- const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const UNHANDLEDEXCEPTION_FOR_NON_ERROR_OBJECT='unhandledException handler received a non-error';const ERROR_MESSAGES_TO_BE_FILTERED=[FAILED_REQUEST_ERR_MSG_PREFIX,UNHANDLEDEXCEPTION_FOR_NON_ERROR_OBJECT];
582
+ const LOAD_ORIGIN='RS_JS_SDK';
583
583
 
584
584
  /**
585
585
  * Utility method to normalise errors
586
586
  */const processError=error=>{let errorMessage;try{if(isString(error)){errorMessage=error;}else if(error instanceof Error){errorMessage=error.message;}else if(error instanceof ErrorEvent){errorMessage=error.message;}else {errorMessage=error.message?error.message:stringifyWithoutCircular(error);}}catch(e){errorMessage=`Unknown error: ${e.message}`;}return errorMessage;};const getNormalizedErrorForUnhandledError=error=>{try{if(error instanceof Error||error instanceof ErrorEvent||error instanceof PromiseRejectionEvent&&error.reason){return error;}// TODO: remove this block once all device mode integrations start using the v3 script loader module (TS)
587
- // if (error instanceof Event) {
588
- // const eventTarget = error.target as ErrorTarget;
589
- // // Discard all the non-script loading errors
590
- // if (eventTarget && eventTarget.localName !== 'script') {
591
- // return undefined;
592
- // }
593
- // // Discard script errors that are not originated at SDK or from native SDKs
594
- // if (
595
- // eventTarget?.dataset &&
596
- // (eventTarget.dataset.loader !== LOAD_ORIGIN ||
597
- // eventTarget.dataset.isnonnativesdk !== 'true')
598
- // ) {
599
- // return undefined;
600
- // }
601
- // const errorMessage = `Error in loading a third-party script from URL ${eventTarget?.src} with ID ${eventTarget?.id}.`;
602
- // return Object.create(error, {
603
- // message: { value: errorMessage },
604
- // });
605
- // }
606
- return undefined;}catch(e){return e;}};/**
607
- * A function to determine whether the error should be promoted to notify or not
608
- * @param {Error} error
609
- * @returns
610
- */const isAllowedToBeNotified=error=>{if((error instanceof Error||error instanceof ErrorEvent)&&error.message){return !ERROR_MESSAGES_TO_BE_FILTERED.some(e=>error.message.includes(e));}if(error instanceof PromiseRejectionEvent&&typeof error.reason==='string'){return !ERROR_MESSAGES_TO_BE_FILTERED.some(e=>error.reason.includes(e));}return true;};
587
+ if(error instanceof Event){const eventTarget=error.target;// Discard all the non-script loading errors
588
+ if(eventTarget&&eventTarget.localName!=='script'){return undefined;}// Discard script errors that are not originated at SDK or from native SDKs
589
+ if(eventTarget?.dataset&&(eventTarget.dataset.loader!==LOAD_ORIGIN||eventTarget.dataset.isnonnativesdk!=='true')){return undefined;}const errorMessage=`Error in loading a third-party script from URL ${eventTarget?.src} with ID ${eventTarget?.id}.`;return Object.create(error,{message:{value:errorMessage}});}return error;}catch(e){return e;}};
611
590
 
612
591
  /**
613
592
  * A service to handle errors
@@ -628,7 +607,7 @@ breadcrumb,this.logger,state);}catch(err){this.onError(err,ERROR_HANDLER,'errorR
628
607
  * Send handled errors to external error monitoring service via a plugin
629
608
  *
630
609
  * @param {Error} error Error instance from handled error
631
- */notifyError(error,errorState){if(this.pluginEngine&&this.httpClient&&isAllowedToBeNotified(error)){try{this.pluginEngine.invokeSingle('errorReporting.notify',this.pluginEngine,// deprecated parameter
610
+ */notifyError(error,errorState){if(this.pluginEngine&&this.httpClient){try{this.pluginEngine.invokeSingle('errorReporting.notify',this.pluginEngine,// deprecated parameter
632
611
  this.errReportingClient,// deprecated parameter
633
612
  error,state,this.logger,this.httpClient,errorState);}catch(err){// Not calling onError here as we don't want to go into infinite loop
634
613
  this.logger?.error(NOTIFY_FAILURE_ERROR(ERROR_HANDLER),err);}}}}const defaultErrorHandler=new ErrorHandler(defaultLogger,defaultPluginEngine);
@@ -1109,6 +1088,8 @@ const queueErrResp=isErrRetryable(details)?details:null;if(!queueErrResp||attemp
1109
1088
 
1110
1089
  const METRICS_PAYLOAD_VERSION='1';
1111
1090
 
1091
+ const FAILED_REQUEST_ERR_MSG_PREFIX='The request failed';const ERROR_MESSAGES_TO_BE_FILTERED=[FAILED_REQUEST_ERR_MSG_PREFIX];
1092
+
1112
1093
  // Errors from the below scripts are NOT allowed to reach Bugsnag
1113
1094
  const SDK_FILE_NAME_PREFIXES=()=>['rsa'// Prefix for all the SDK scripts including plugins and module federated chunks
1114
1095
  ];const DEV_HOSTS=['www.test-host.com','localhost','127.0.0.1','[::1]'];// List of keys to exclude from the metadata
@@ -1116,11 +1097,19 @@ const SDK_FILE_NAME_PREFIXES=()=>['rsa'// Prefix for all the SDK scripts includi
1116
1097
  const APP_STATE_EXCLUDE_KEYS=['userId','userTraits','groupId','groupTraits','anonymousId','config','instance',// destination instance objects
1117
1098
  'eventBuffer',// pre-load event buffer (may contain PII)
1118
1099
  'traits'];const REQUEST_TIMEOUT_MS$1=10*1000;// 10 seconds
1119
- const NOTIFIER_NAME='RudderStack JavaScript SDK Error Notifier';const SDK_GITHUB_URL='https://github.com/rudderlabs/rudder-sdk-js';const SOURCE_NAME='js';
1100
+ const NOTIFIER_NAME='RudderStack JavaScript SDK Error Notifier';const SDK_GITHUB_URL='https://github.com/rudderlabs/rudder-sdk-js';const SOURCE_NAME='js';const ERROR_REPORTING_PLUGIN='ErrorReportingPlugin';
1120
1101
 
1121
1102
  const getConfigForPayloadCreation=(err,errorType)=>{switch(errorType){case ErrorType.UNHANDLEDEXCEPTION:{const{error}=err;return {component:'unhandledException handler',tolerateNonErrors:true,errorFramesToSkip:1,normalizedError:error||err};}case ErrorType.UNHANDLEDREJECTION:{const error=err;return {component:'unhandledrejection handler',tolerateNonErrors:false,errorFramesToSkip:1,normalizedError:error.reason};}case ErrorType.HANDLEDEXCEPTION:default:return {component:'notify()',tolerateNonErrors:true,errorFramesToSkip:2,normalizedError:err};}};const createNewBreadcrumb=(message,metaData)=>({type:'manual',name:message,timestamp:new Date(),metaData:metaData??{}});const getReleaseStage=()=>{const host=globalThis.location.hostname;return host&&DEV_HOSTS.includes(host)?'development':'production';};const getAppStateForMetadata=state=>{const stateStr=stringifyWithoutCircular(state,false,APP_STATE_EXCLUDE_KEYS);return stateStr!==null?JSON.parse(stateStr):{};};const getURLWithoutQueryString=()=>{const url=globalThis.location.href.split('?');return url[0];};const getErrorContext=event=>{const{message}=event;let context=message;// Hack for easily grouping the script load errors
1122
1103
  // on the dashboard
1123
- if(message.includes('Error in loading a third-party script')){context='Script load failures';}return context;};const getBugsnagErrorEvent=(payload,errorState,state)=>({notifier:{name:NOTIFIER_NAME,version:state.context.app.value.version,url:SDK_GITHUB_URL},events:[{payloadVersion:'5',exceptions:clone(payload.errors),severity:errorState.severity,unhandled:errorState.unhandled,severityReason:errorState.severityReason,app:{version:state.context.app.value.version,releaseStage:getReleaseStage()},device:{locale:state.context.locale.value??undefined,userAgent:state.context.userAgent.value??undefined,time:new Date()},request:{url:getURLWithoutQueryString(),clientIp:'[NOT COLLECTED]'},breadcrumbs:clone(state.reporting.breadcrumbs.value),context:getErrorContext(payload.errors[0]),metaData:{sdk:{name:'JS',installType:state.context.app.value.installType},state:getAppStateForMetadata(state)??{},source:{snippetVersion:globalThis.RudderSnippetVersion}},user:{id:state.source.value?.id??state.lifecycle.writeKey.value}}]});const isRudderSDKError=event=>{const errorOrigin=event.stacktrace?.[0]?.file;if(!errorOrigin||typeof errorOrigin!=='string'){return false;}const srcFileName=errorOrigin.substring(errorOrigin.lastIndexOf('/')+1);const paths=errorOrigin.split('/');// extract the parent folder name from the error origin file path
1104
+ if(message.includes('Error in loading a third-party script')){context='Script load failures';}return context;};const getBugsnagErrorEvent=(payload,errorState,state)=>({notifier:{name:NOTIFIER_NAME,version:state.context.app.value.version,url:SDK_GITHUB_URL},events:[{payloadVersion:'5',exceptions:clone(payload.errors),severity:errorState.severity,unhandled:errorState.unhandled,severityReason:errorState.severityReason,app:{version:state.context.app.value.version,releaseStage:getReleaseStage()},device:{locale:state.context.locale.value??undefined,userAgent:state.context.userAgent.value??undefined,time:new Date()},request:{url:getURLWithoutQueryString(),clientIp:'[NOT COLLECTED]'},breadcrumbs:clone(state.reporting.breadcrumbs.value),context:getErrorContext(payload.errors[0]),metaData:{sdk:{name:'JS',installType:state.context.app.value.installType},state:getAppStateForMetadata(state)??{},source:{snippetVersion:globalThis.RudderSnippetVersion}},user:{id:state.source.value?.id??state.lifecycle.writeKey.value}}]});/**
1105
+ * A function to determine whether the error should be promoted to notify or not
1106
+ * @param {Error} error
1107
+ * @returns
1108
+ */const isAllowedToBeNotified=event=>{const errorMessage=event.message;if(errorMessage&&typeof errorMessage==='string'){return !ERROR_MESSAGES_TO_BE_FILTERED.some(e=>errorMessage.includes(e));}return true;};/**
1109
+ * A function to determine if the error is from Rudder SDK
1110
+ * @param {Error} event
1111
+ * @returns
1112
+ */const isRudderSDKError=event=>{const errorOrigin=event.stacktrace?.[0]?.file;if(!errorOrigin||typeof errorOrigin!=='string'){return false;}const srcFileName=errorOrigin.substring(errorOrigin.lastIndexOf('/')+1);const paths=errorOrigin.split('/');// extract the parent folder name from the error origin file path
1124
1113
  // Ex: parentFolderName will be 'sample' for url: https://example.com/sample/file.min.js
1125
1114
  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)=>{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},errors:payload};return stringifyWithoutCircular(data);};
1126
1115
 
@@ -1164,19 +1153,10 @@ const formatStackframe=frame=>{const f={file:frame.fileName,method:normaliseFunc
1164
1153
  // This adds one.
1165
1154
  if(f.lineNumber>-1&&!f.file&&!f.method){f.file='global code';}return f;};const ensureString=str=>typeof str==='string'?str:'';function createBugsnagError(errorClass,errorMessage,stacktrace){return {errorClass:ensureString(errorClass),message:ensureString(errorMessage),type:'browserjs',stacktrace:stacktrace.reduce((accum,frame)=>{const f=formatStackframe(frame);// don't include a stackframe if none of its properties are defined
1166
1155
  try{if(JSON.stringify(f)==='{}')return accum;return accum.concat(f);}catch(e){return accum;}},[])};}// Helpers
1167
- const getStacktrace=(error,errorFramesToSkip)=>{if(hasStack(error))return ErrorStackParser.parse(error).slice(errorFramesToSkip);return [];};const hasNecessaryFields=error=>(typeof error.name==='string'||typeof error.errorClass==='string')&&(typeof error.message==='string'||typeof error.errorMessage==='string');const normaliseError=(maybeError,tolerateNonErrors,component,logger)=>{let error;let internalFrames=0;const createAndLogInputError=reason=>{const verb=component==='error cause'?'was':'received';if(logger)logger.warn(`${component} ${verb} a non-error: "${reason}"`);const err=new Error(`${component} ${verb} a non-error. See "${component}" tab for more detail.`);err.name='InvalidError';return err;};// In some cases:
1168
- //
1169
- // - the promise rejection handler (both in the browser and node)
1170
- // - the node uncaughtException handler
1171
- //
1172
- // We are really limited in what we can do to get a stacktrace. So we use the
1173
- // tolerateNonErrors option to ensure that the resulting error communicates as
1174
- // such.
1175
- if(!tolerateNonErrors){if(isError(maybeError)){error=maybeError;}else {error=createAndLogInputError(typeof maybeError);internalFrames+=2;}}else {switch(typeof maybeError){case'string':case'number':case'boolean':error=new Error(String(maybeError));internalFrames+=1;break;case'function':error=createAndLogInputError('function');internalFrames+=2;break;case'object':if(maybeError!==null&&isError(maybeError)){error=maybeError;}else if(maybeError!==null&&hasNecessaryFields(maybeError)){error=new Error(maybeError.message||maybeError.errorMessage);error.name=maybeError.name||maybeError.errorClass;internalFrames+=1;}else {error=createAndLogInputError(maybeError===null?'null':'unsupported object');internalFrames+=2;}break;default:error=createAndLogInputError('nothing');internalFrames+=2;}}if(!hasStack(error)){// in IE10/11 a new Error() doesn't have a stacktrace until you throw it, so try that here
1156
+ const getStacktrace=(error,errorFramesToSkip)=>{if(hasStack(error))return ErrorStackParser.parse(error).slice(errorFramesToSkip);return [];};const hasNecessaryFields=error=>(typeof error.name==='string'||typeof error.errorClass==='string')&&(typeof error.message==='string'||typeof error.errorMessage==='string');const normaliseError=(maybeError,component,logger)=>{let error;let internalFrames=0;if(isError(maybeError)){error=maybeError;}else if(typeof maybeError==='object'&&hasNecessaryFields(maybeError)){error=new Error(maybeError.message||maybeError.errorMessage);error.name=maybeError.name||maybeError.errorClass;internalFrames+=1;}else {logger?.warn(`${ERROR_REPORTING_PLUGIN}:: ${component} received a non-error: ${stringifyWithoutCircular(error)}`);error=undefined;}if(error&&!hasStack(error)){// in IE10/11 a new Error() doesn't have a stacktrace until you throw it, so try that here
1176
1157
  try{throw error;}catch(e){if(hasStack(e)){error=e;// if the error only got a stacktrace after we threw it here, we know it
1177
- // will only have one extra internal frame from this function, regardless
1178
- // of whether it went through createAndLogInputError() or not
1179
- internalFrames=1;}}}return [error,internalFrames];};class ErrorFormat{constructor(errorClass,errorMessage,stacktrace){this.errors=[createBugsnagError(errorClass,errorMessage,stacktrace)];}static create(maybeError,tolerateNonErrors,handledState,component,errorFramesToSkip=0,logger){const[error,internalFrames]=normaliseError(maybeError,tolerateNonErrors,component,logger);let event;try{const stacktrace=getStacktrace(error,// if an error was created/throw in the normaliseError() function, we need to
1158
+ // will only have one extra internal frame from this function
1159
+ internalFrames=1;}}}return [error,internalFrames];};class ErrorFormat{constructor(errorClass,errorMessage,stacktrace){this.errors=[createBugsnagError(errorClass,errorMessage,stacktrace)];}static create(maybeError,tolerateNonErrors,handledState,component,errorFramesToSkip=0,logger){const[error,internalFrames]=normaliseError(maybeError,component,logger);if(!error){return undefined;}let event;try{const stacktrace=getStacktrace(error,// if an error was created/throw in the normaliseError() function, we need to
1180
1160
  // tell the getStacktrace() function to skip the number of frames we know will
1181
1161
  // be from our own functions. This is added to the number of frames deep we
1182
1162
  // were told about
@@ -1187,7 +1167,7 @@ const INVALID_SOURCE_CONFIG_ERROR=`Invalid source configuration or source id.`;
1187
1167
  const pluginName$9='ErrorReporting';const ErrorReporting=()=>({name:pluginName$9,deps:[],initialize:state=>{state.plugins.loadedPlugins.value=[...state.plugins.loadedPlugins.value,pluginName$9];state.reporting.isErrorReportingPluginLoaded.value=true;if(state.reporting.breadcrumbs?.value){state.reporting.breadcrumbs.value=[createNewBreadcrumb('Error Reporting Plugin Loaded')];}},errorReporting:{// This extension point is deprecated
1188
1168
  // TODO: Remove this in the next major release
1189
1169
  init:(state,pluginEngine,externalSrcLoader,logger,isInvokedFromLatestCore)=>{if(isInvokedFromLatestCore){return undefined;}if(!state.source.value?.config||!state.source.value?.id){return Promise.reject(new Error(INVALID_SOURCE_CONFIG_ERROR));}return pluginEngine.invokeSingle('errorReportingProvider.init',state,externalSrcLoader,logger);},notify:(pluginEngine,client,error,state,logger,httpClient,errorState)=>{if(httpClient){const{component,tolerateNonErrors,errorFramesToSkip,normalizedError}=getConfigForPayloadCreation(error,errorState?.severityReason.type);// Generate the error payload
1190
- const errorPayload=ErrorFormat.create(normalizedError,tolerateNonErrors,errorState,component,errorFramesToSkip,logger);// filter errors
1170
+ const errorPayload=ErrorFormat.create(normalizedError,tolerateNonErrors,errorState,component,errorFramesToSkip,logger);if(!errorPayload||!isAllowedToBeNotified(errorPayload.errors[0])){return;}// filter errors
1191
1171
  if(!isRudderSDKError(errorPayload.errors[0])){return;}// enrich error payload
1192
1172
  const bugsnagPayload=getBugsnagErrorEvent(errorPayload,errorState,state);// send it to metrics service
1193
1173
  httpClient?.getAsyncData({url:state.metrics.metricsServiceUrl.value,options:{method:'POST',data:getErrorDeliveryPayload(bugsnagPayload,state),sendRawData:true},isRawResponse:true,timeout:REQUEST_TIMEOUT_MS$1,callback:(result,details)=>{// do nothing
@@ -2706,9 +2686,14 @@ if(utmParam==='campaign'){utmParam='name';}result[utmParam]=value;}});}catch(err
2706
2686
  */const getUrlWithoutHash=url=>{let urlWithoutHash=url;try{const urlObj=new URL(url);urlWithoutHash=urlObj.origin+urlObj.pathname+urlObj.search;}catch(error){// Do nothing
2707
2687
  }return urlWithoutHash;};
2708
2688
 
2689
+ /**
2690
+ * Determines if the SDK is running inside a chrome extension
2691
+ * @returns boolean
2692
+ */const isSDKRunningInChromeExtension=()=>!!(window.chrome&&window.chrome.runtime&&window.chrome.runtime.id);
2693
+
2709
2694
  const DEFAULT_PRE_CONSENT_STORAGE_STRATEGY='none';const DEFAULT_PRE_CONSENT_EVENTS_DELIVERY_TYPE='immediate';
2710
2695
 
2711
- const isMetricsReportingEnabled=sourceConfig=>sourceConfig?.statsCollection?.metrics?.enabled===true;
2696
+ const isErrorReportingEnabled=sourceConfig=>sourceConfig?.statsCollection?.errors?.enabled===true;const isMetricsReportingEnabled=sourceConfig=>sourceConfig?.statsCollection?.metrics?.enabled===true;
2712
2697
 
2713
2698
  /**
2714
2699
  * Validates and normalizes the consent options provided by the user
@@ -2742,10 +2727,7 @@ for(const script of scripts){const src=script.getAttribute('src');if(src&&sdkFil
2742
2727
  * Updates the reporting state variables from the source config data
2743
2728
  * @param res Source config
2744
2729
  * @param logger Logger instance
2745
- */const updateReportingState=res=>{state.reporting.isErrorReportingEnabled.value=false;// TODO: Enable this once the error reporting is tested properly
2746
- // state.reporting.isErrorReportingEnabled.value =
2747
- // isErrorReportingEnabled(res.source.config) && !isSDKRunningInChromeExtension();
2748
- state.reporting.isMetricsReportingEnabled.value=isMetricsReportingEnabled(res.source.config);};const updateStorageStateFromLoadOptions=logger=>{const{useServerSideCookies,dataServiceEndpoint,storage:storageOptsFromLoad,setCookieDomain,sameDomainCookiesOnly}=state.loadOptions.value;let storageType=storageOptsFromLoad?.type;if(isDefined(storageType)&&!isValidStorageType(storageType)){logger?.warn(STORAGE_TYPE_VALIDATION_WARNING(CONFIG_MANAGER,storageType,DEFAULT_STORAGE_TYPE));storageType=DEFAULT_STORAGE_TYPE;}let storageEncryptionVersion=storageOptsFromLoad?.encryption?.version;const encryptionPluginName=storageEncryptionVersion&&StorageEncryptionVersionsToPluginNameMap[storageEncryptionVersion];if(!isUndefined(storageEncryptionVersion)&&isUndefined(encryptionPluginName)){// set the default encryption plugin
2730
+ */const updateReportingState=res=>{state.reporting.isErrorReportingEnabled.value=isErrorReportingEnabled(res.source.config)&&!isSDKRunningInChromeExtension();state.reporting.isMetricsReportingEnabled.value=isMetricsReportingEnabled(res.source.config);};const updateStorageStateFromLoadOptions=logger=>{const{useServerSideCookies,dataServiceEndpoint,storage:storageOptsFromLoad,setCookieDomain,sameDomainCookiesOnly}=state.loadOptions.value;let storageType=storageOptsFromLoad?.type;if(isDefined(storageType)&&!isValidStorageType(storageType)){logger?.warn(STORAGE_TYPE_VALIDATION_WARNING(CONFIG_MANAGER,storageType,DEFAULT_STORAGE_TYPE));storageType=DEFAULT_STORAGE_TYPE;}let storageEncryptionVersion=storageOptsFromLoad?.encryption?.version;const encryptionPluginName=storageEncryptionVersion&&StorageEncryptionVersionsToPluginNameMap[storageEncryptionVersion];if(!isUndefined(storageEncryptionVersion)&&isUndefined(encryptionPluginName)){// set the default encryption plugin
2749
2731
  logger?.warn(UNSUPPORTED_STORAGE_ENCRYPTION_VERSION_WARNING(CONFIG_MANAGER,storageEncryptionVersion,StorageEncryptionVersionsToPluginNameMap,DEFAULT_STORAGE_ENCRYPTION_VERSION));storageEncryptionVersion=DEFAULT_STORAGE_ENCRYPTION_VERSION;}else if(isUndefined(storageEncryptionVersion)){storageEncryptionVersion=DEFAULT_STORAGE_ENCRYPTION_VERSION;}// Allow migration only if the configured encryption version is the default encryption version
2750
2732
  const configuredMigrationValue=storageOptsFromLoad?.migrate;const finalMigrationVal=configuredMigrationValue&&storageEncryptionVersion===DEFAULT_STORAGE_ENCRYPTION_VERSION;if(configuredMigrationValue===true&&finalMigrationVal!==configuredMigrationValue){logger?.warn(STORAGE_DATA_MIGRATION_OVERRIDE_WARNING(CONFIG_MANAGER,storageEncryptionVersion,DEFAULT_STORAGE_ENCRYPTION_VERSION));}r(()=>{state.storage.type.value=storageType;let cookieOptions=storageOptsFromLoad?.cookie??{};if(useServerSideCookies){state.serverCookies.isEnabledServerSideCookies.value=useServerSideCookies;const providedCookieDomain=cookieOptions.domain??setCookieDomain;/**
2751
2733
  * Based on the following conditions, we decide whether to use the exact domain or not to determine the data service URL: