@sentry/browser
Advanced tools
Comparing version
@@ -24,2 +24,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
ignorePerformanceApiSpans: [], | ||
detectRedirects: true, | ||
linkPreviousTrace: 'in-memory', | ||
@@ -72,2 +73,3 @@ consistentTraceSampling: false, | ||
instrumentNavigation, | ||
detectRedirects, | ||
linkPreviousTrace, | ||
@@ -82,5 +84,6 @@ consistentTraceSampling, | ||
let _collectWebVitals; | ||
let lastInteractionTimestamp; | ||
/** Create routing idle transaction. */ | ||
function _createRouteSpan(client, startSpanOptions) { | ||
function _createRouteSpan(client, startSpanOptions, makeActive = true) { | ||
const isPageloadTransaction = startSpanOptions.op === 'pageload'; | ||
@@ -101,2 +104,12 @@ | ||
if (!makeActive) { | ||
// We want to ensure this has 0s duration | ||
const now = core.dateTimestampInSeconds(); | ||
core.startInactiveSpan({ | ||
...finalStartSpanOptions, | ||
startTime: now, | ||
}).end(now); | ||
return; | ||
} | ||
latestRoute.name = finalStartSpanOptions.name; | ||
@@ -188,2 +201,10 @@ latestRoute.source = attributes[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]; | ||
if (detectRedirects && optionalWindowDocument) { | ||
const interactionHandler = () => { | ||
lastInteractionTimestamp = core.timestampInSeconds(); | ||
}; | ||
addEventListener('click', interactionHandler, { capture: true }); | ||
addEventListener('keydown', interactionHandler, { capture: true, passive: true }); | ||
} | ||
function maybeEndActiveSpan() { | ||
@@ -200,3 +221,3 @@ const activeSpan = getActiveIdleSpan(client); | ||
client.on('startNavigationSpan', startSpanOptions => { | ||
client.on('startNavigationSpan', (startSpanOptions, navigationOptions) => { | ||
if (core.getClient() !== client) { | ||
@@ -206,2 +227,16 @@ return; | ||
if (navigationOptions?.isRedirect) { | ||
debugBuild.DEBUG_BUILD && | ||
core.logger.warn('[Tracing] Detected redirect, navigation span will not be the root span, but a child span.'); | ||
_createRouteSpan( | ||
client, | ||
{ | ||
op: 'navigation.redirect', | ||
...startSpanOptions, | ||
}, | ||
false, | ||
); | ||
return; | ||
} | ||
maybeEndActiveSpan(); | ||
@@ -290,19 +325,16 @@ | ||
const parsed = core.parseStringToURLObject(to); | ||
startBrowserTracingNavigationSpan(client, { | ||
name: parsed?.pathname || helpers.WINDOW.location.pathname, | ||
attributes: { | ||
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', | ||
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.browser', | ||
const activeSpan = getActiveIdleSpan(client); | ||
const navigationIsRedirect = | ||
activeSpan && detectRedirects && isRedirect(activeSpan, lastInteractionTimestamp); | ||
startBrowserTracingNavigationSpan( | ||
client, | ||
{ | ||
name: parsed?.pathname || helpers.WINDOW.location.pathname, | ||
attributes: { | ||
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', | ||
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.browser', | ||
}, | ||
}, | ||
}); | ||
// We store the normalized request data on the scope, so we get the request data at time of span creation | ||
// otherwise, the URL etc. may already be of the following navigation, and we'd report the wrong URL | ||
core.getCurrentScope().setSDKProcessingMetadata({ | ||
normalizedRequest: { | ||
...helpers.getHttpRequestData(), | ||
// Ensure to set this, so this matches the target route even if the URL has not yet been updated | ||
url: to, | ||
}, | ||
}); | ||
{ url: to, isRedirect: navigationIsRedirect }, | ||
); | ||
}); | ||
@@ -359,7 +391,25 @@ } | ||
*/ | ||
function startBrowserTracingNavigationSpan(client, spanOptions) { | ||
client.emit('startNavigationSpan', spanOptions); | ||
function startBrowserTracingNavigationSpan( | ||
client, | ||
spanOptions, | ||
options, | ||
) { | ||
const { url, isRedirect } = options || {}; | ||
client.emit('beforeStartNavigationSpan', spanOptions, { isRedirect }); | ||
client.emit('startNavigationSpan', spanOptions, { isRedirect }); | ||
core.getCurrentScope().setTransactionName(spanOptions.name); | ||
const scope = core.getCurrentScope(); | ||
scope.setTransactionName(spanOptions.name); | ||
// We store the normalized request data on the scope, so we get the request data at time of span creation | ||
// otherwise, the URL etc. may already be of the following navigation, and we'd report the wrong URL | ||
if (url && !isRedirect) { | ||
scope.setSDKProcessingMetadata({ | ||
normalizedRequest: { | ||
...helpers.getHttpRequestData(), | ||
url, | ||
}, | ||
}); | ||
} | ||
return getActiveIdleSpan(client); | ||
@@ -436,3 +486,3 @@ } | ||
if (optionalWindowDocument) { | ||
addEventListener('click', registerInteractionTransaction, { once: false, capture: true }); | ||
addEventListener('click', registerInteractionTransaction, { capture: true }); | ||
} | ||
@@ -451,2 +501,26 @@ } | ||
// The max. time in seconds between two pageload/navigation spans that makes us consider the second one a redirect | ||
const REDIRECT_THRESHOLD = 0.3; | ||
function isRedirect(activeSpan, lastInteractionTimestamp) { | ||
const spanData = core.spanToJSON(activeSpan); | ||
const now = core.dateTimestampInSeconds(); | ||
// More than 300ms since last navigation/pageload span? | ||
// --> never consider this a redirect | ||
const startTimestamp = spanData.start_timestamp; | ||
if (now - startTimestamp > REDIRECT_THRESHOLD) { | ||
return false; | ||
} | ||
// A click happened in the last 300ms? | ||
// --> never consider this a redirect | ||
if (lastInteractionTimestamp && now - lastInteractionTimestamp <= REDIRECT_THRESHOLD) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
exports.BROWSER_TRACING_INTEGRATION_ID = BROWSER_TRACING_INTEGRATION_ID; | ||
@@ -453,0 +527,0 @@ exports.browserTracingIntegration = browserTracingIntegration; |
@@ -1,1 +0,1 @@ | ||
{"type":"module","version":"9.36.0","sideEffects":false} | ||
{"type":"module","version":"9.37.0","sideEffects":false} |
@@ -1,2 +0,2 @@ | ||
import { TRACING_DEFAULTS, getLocationHref, browserPerformanceTimeOrigin, parseStringToURLObject, getCurrentScope, registerSpanErrorInstrumentation, GLOBAL_OBJ, getClient, getIsolationScope, generateTraceId, propagationContextFromHeaders, spanToJSON, logger, SEMANTIC_ATTRIBUTE_SENTRY_IDLE_SPAN_FINISH_REASON, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, startIdleSpan, getDynamicSamplingContextFromSpan, spanIsSampled, addNonEnumerableProperty, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; | ||
import { TRACING_DEFAULTS, getLocationHref, browserPerformanceTimeOrigin, parseStringToURLObject, registerSpanErrorInstrumentation, GLOBAL_OBJ, getClient, logger, getIsolationScope, generateTraceId, getCurrentScope, propagationContextFromHeaders, spanToJSON, dateTimestampInSeconds, timestampInSeconds, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, startInactiveSpan, startIdleSpan, getDynamicSamplingContextFromSpan, spanIsSampled, SEMANTIC_ATTRIBUTE_SENTRY_IDLE_SPAN_FINISH_REASON, addNonEnumerableProperty, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; | ||
import { addHistoryInstrumentationHandler, registerInpInteractionListener, startTrackingWebVitals, startTrackingINP, startTrackingElementTiming, startTrackingLongAnimationFrames, startTrackingLongTasks, startTrackingInteractions, addPerformanceEntries } from '@sentry-internal/browser-utils'; | ||
@@ -22,2 +22,3 @@ import { DEBUG_BUILD } from '../debug-build.js'; | ||
ignorePerformanceApiSpans: [], | ||
detectRedirects: true, | ||
linkPreviousTrace: 'in-memory', | ||
@@ -70,2 +71,3 @@ consistentTraceSampling: false, | ||
instrumentNavigation, | ||
detectRedirects, | ||
linkPreviousTrace, | ||
@@ -80,5 +82,6 @@ consistentTraceSampling, | ||
let _collectWebVitals; | ||
let lastInteractionTimestamp; | ||
/** Create routing idle transaction. */ | ||
function _createRouteSpan(client, startSpanOptions) { | ||
function _createRouteSpan(client, startSpanOptions, makeActive = true) { | ||
const isPageloadTransaction = startSpanOptions.op === 'pageload'; | ||
@@ -99,2 +102,12 @@ | ||
if (!makeActive) { | ||
// We want to ensure this has 0s duration | ||
const now = dateTimestampInSeconds(); | ||
startInactiveSpan({ | ||
...finalStartSpanOptions, | ||
startTime: now, | ||
}).end(now); | ||
return; | ||
} | ||
latestRoute.name = finalStartSpanOptions.name; | ||
@@ -186,2 +199,10 @@ latestRoute.source = attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]; | ||
if (detectRedirects && optionalWindowDocument) { | ||
const interactionHandler = () => { | ||
lastInteractionTimestamp = timestampInSeconds(); | ||
}; | ||
addEventListener('click', interactionHandler, { capture: true }); | ||
addEventListener('keydown', interactionHandler, { capture: true, passive: true }); | ||
} | ||
function maybeEndActiveSpan() { | ||
@@ -198,3 +219,3 @@ const activeSpan = getActiveIdleSpan(client); | ||
client.on('startNavigationSpan', startSpanOptions => { | ||
client.on('startNavigationSpan', (startSpanOptions, navigationOptions) => { | ||
if (getClient() !== client) { | ||
@@ -204,2 +225,16 @@ return; | ||
if (navigationOptions?.isRedirect) { | ||
DEBUG_BUILD && | ||
logger.warn('[Tracing] Detected redirect, navigation span will not be the root span, but a child span.'); | ||
_createRouteSpan( | ||
client, | ||
{ | ||
op: 'navigation.redirect', | ||
...startSpanOptions, | ||
}, | ||
false, | ||
); | ||
return; | ||
} | ||
maybeEndActiveSpan(); | ||
@@ -288,19 +323,16 @@ | ||
const parsed = parseStringToURLObject(to); | ||
startBrowserTracingNavigationSpan(client, { | ||
name: parsed?.pathname || WINDOW.location.pathname, | ||
attributes: { | ||
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', | ||
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.browser', | ||
const activeSpan = getActiveIdleSpan(client); | ||
const navigationIsRedirect = | ||
activeSpan && detectRedirects && isRedirect(activeSpan, lastInteractionTimestamp); | ||
startBrowserTracingNavigationSpan( | ||
client, | ||
{ | ||
name: parsed?.pathname || WINDOW.location.pathname, | ||
attributes: { | ||
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', | ||
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.browser', | ||
}, | ||
}, | ||
}); | ||
// We store the normalized request data on the scope, so we get the request data at time of span creation | ||
// otherwise, the URL etc. may already be of the following navigation, and we'd report the wrong URL | ||
getCurrentScope().setSDKProcessingMetadata({ | ||
normalizedRequest: { | ||
...getHttpRequestData(), | ||
// Ensure to set this, so this matches the target route even if the URL has not yet been updated | ||
url: to, | ||
}, | ||
}); | ||
{ url: to, isRedirect: navigationIsRedirect }, | ||
); | ||
}); | ||
@@ -357,7 +389,25 @@ } | ||
*/ | ||
function startBrowserTracingNavigationSpan(client, spanOptions) { | ||
client.emit('startNavigationSpan', spanOptions); | ||
function startBrowserTracingNavigationSpan( | ||
client, | ||
spanOptions, | ||
options, | ||
) { | ||
const { url, isRedirect } = options || {}; | ||
client.emit('beforeStartNavigationSpan', spanOptions, { isRedirect }); | ||
client.emit('startNavigationSpan', spanOptions, { isRedirect }); | ||
getCurrentScope().setTransactionName(spanOptions.name); | ||
const scope = getCurrentScope(); | ||
scope.setTransactionName(spanOptions.name); | ||
// We store the normalized request data on the scope, so we get the request data at time of span creation | ||
// otherwise, the URL etc. may already be of the following navigation, and we'd report the wrong URL | ||
if (url && !isRedirect) { | ||
scope.setSDKProcessingMetadata({ | ||
normalizedRequest: { | ||
...getHttpRequestData(), | ||
url, | ||
}, | ||
}); | ||
} | ||
return getActiveIdleSpan(client); | ||
@@ -434,3 +484,3 @@ } | ||
if (optionalWindowDocument) { | ||
addEventListener('click', registerInteractionTransaction, { once: false, capture: true }); | ||
addEventListener('click', registerInteractionTransaction, { capture: true }); | ||
} | ||
@@ -449,3 +499,27 @@ } | ||
// The max. time in seconds between two pageload/navigation spans that makes us consider the second one a redirect | ||
const REDIRECT_THRESHOLD = 0.3; | ||
function isRedirect(activeSpan, lastInteractionTimestamp) { | ||
const spanData = spanToJSON(activeSpan); | ||
const now = dateTimestampInSeconds(); | ||
// More than 300ms since last navigation/pageload span? | ||
// --> never consider this a redirect | ||
const startTimestamp = spanData.start_timestamp; | ||
if (now - startTimestamp > REDIRECT_THRESHOLD) { | ||
return false; | ||
} | ||
// A click happened in the last 300ms? | ||
// --> never consider this a redirect | ||
if (lastInteractionTimestamp && now - lastInteractionTimestamp <= REDIRECT_THRESHOLD) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
export { BROWSER_TRACING_INTEGRATION_ID, browserTracingIntegration, getMetaContent, startBrowserTracingNavigationSpan, startBrowserTracingPageLoadSpan }; | ||
//# sourceMappingURL=browserTracingIntegration.js.map |
@@ -137,2 +137,9 @@ import { Client, Span, StartSpanOptions, WebFetchHeaders } from '@sentry/core'; | ||
/** | ||
* By default, the SDK will try to detect redirects and avoid creating separate spans for them. | ||
* If you want to opt-out of this behavior, you can set this option to `false`. | ||
* | ||
* Default: true | ||
*/ | ||
detectRedirects: boolean; | ||
/** | ||
* Link the currently started trace to a previous trace (e.g. a prior pageload, navigation or | ||
@@ -237,5 +244,8 @@ * manually started span). When enabled, this option will allow you to navigate between traces | ||
*/ | ||
export declare function startBrowserTracingNavigationSpan(client: Client, spanOptions: StartSpanOptions): Span | undefined; | ||
export declare function startBrowserTracingNavigationSpan(client: Client, spanOptions: StartSpanOptions, options?: { | ||
url?: string; | ||
isRedirect?: boolean; | ||
}): Span | undefined; | ||
/** Returns the value of a meta tag */ | ||
export declare function getMetaContent(metaName: string): string | undefined; | ||
//# sourceMappingURL=browserTracingIntegration.d.ts.map |
@@ -137,2 +137,9 @@ import type { Client, Span, StartSpanOptions, WebFetchHeaders } from '@sentry/core'; | ||
/** | ||
* By default, the SDK will try to detect redirects and avoid creating separate spans for them. | ||
* If you want to opt-out of this behavior, you can set this option to `false`. | ||
* | ||
* Default: true | ||
*/ | ||
detectRedirects: boolean; | ||
/** | ||
* Link the currently started trace to a previous trace (e.g. a prior pageload, navigation or | ||
@@ -237,5 +244,8 @@ * manually started span). When enabled, this option will allow you to navigate between traces | ||
*/ | ||
export declare function startBrowserTracingNavigationSpan(client: Client, spanOptions: StartSpanOptions): Span | undefined; | ||
export declare function startBrowserTracingNavigationSpan(client: Client, spanOptions: StartSpanOptions, options?: { | ||
url?: string; | ||
isRedirect?: boolean; | ||
}): Span | undefined; | ||
/** Returns the value of a meta tag */ | ||
export declare function getMetaContent(metaName: string): string | undefined; | ||
//# sourceMappingURL=browserTracingIntegration.d.ts.map |
{ | ||
"name": "@sentry/browser", | ||
"version": "9.36.0", | ||
"version": "9.37.0", | ||
"description": "Official Sentry SDK for browsers", | ||
@@ -42,10 +42,10 @@ "repository": "git://github.com/getsentry/sentry-javascript.git", | ||
"dependencies": { | ||
"@sentry-internal/browser-utils": "9.36.0", | ||
"@sentry-internal/feedback": "9.36.0", | ||
"@sentry-internal/replay": "9.36.0", | ||
"@sentry-internal/replay-canvas": "9.36.0", | ||
"@sentry/core": "9.36.0" | ||
"@sentry-internal/browser-utils": "9.37.0", | ||
"@sentry-internal/feedback": "9.37.0", | ||
"@sentry-internal/replay": "9.37.0", | ||
"@sentry-internal/replay-canvas": "9.37.0", | ||
"@sentry/core": "9.37.0" | ||
}, | ||
"devDependencies": { | ||
"@sentry-internal/integration-shims": "9.36.0", | ||
"@sentry-internal/integration-shims": "9.37.0", | ||
"fake-indexeddb": "^4.0.1" | ||
@@ -52,0 +52,0 @@ }, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
1222625
1.14%13186
1.16%+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
Updated