UNPKG

@sentry/nextjs

Version:
206 lines (202 loc) 10 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const core = require('@sentry/core'); const react = require('@sentry/react'); const parameterization = require('./parameterization.js'); const attributes = require('@sentry/conventions/attributes'); function stripTrailingSlash(pathname) { return pathname.length > 1 && pathname.endsWith("/") ? pathname.slice(0, -1) : pathname; } function setNavigationSpanUrlAttributes(span, urlPath, urlOrPath) { span.setAttributes({ [attributes.URL_PATH]: urlPath, [attributes.URL_FULL]: react.getAbsoluteUrl(urlOrPath) }); } const INCOMPLETE_APP_ROUTER_INSTRUMENTATION_TRANSACTION_NAME = "incomplete-app-router-transaction"; let navigationRoutingMode = "router-patch"; const currentRouterPatchingNavigationSpanRef = { current: void 0 }; function appRouterInstrumentPageLoad(client) { const pathname = stripTrailingSlash(react.WINDOW.location.pathname); const parameterizedPathname = parameterization.maybeParameterizeRoute(pathname); const origin = core.browserPerformanceTimeOrigin(); react.startBrowserTracingPageLoadSpan(client, { name: parameterizedPathname ?? pathname, // pageload should always start at timeOrigin (and needs to be in s, not ms) startTime: origin ? origin / 1e3 : void 0, attributes: { [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "pageload", [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.pageload.nextjs.app_router_instrumentation", [core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: parameterizedPathname ? "route" : "url", ...parameterizedPathname && { [attributes.URL_TEMPLATE]: parameterizedPathname } } }); } const GLOBAL_OBJ_WITH_NEXT_ROUTER = core.GLOBAL_OBJ; const globalWithInjectedBasePath = core.GLOBAL_OBJ; function appRouterInstrumentNavigation(client) { routerTransitionHandler = (href, navigationType) => { const basePath = process.env._sentryBasePath ?? globalWithInjectedBasePath._sentryBasePath; const normalizedHref = basePath && !href.startsWith(basePath) ? `${basePath}${href}` : href; const unparameterizedPathname = stripTrailingSlash(new URL(normalizedHref, react.WINDOW.location.href).pathname); const parameterizedPathname = parameterization.maybeParameterizeRoute(unparameterizedPathname); const pathname = parameterizedPathname ?? unparameterizedPathname; if (navigationRoutingMode === "router-patch") { navigationRoutingMode = "transition-start-hook"; } const currentNavigationSpan = currentRouterPatchingNavigationSpanRef.current; if (currentNavigationSpan) { currentNavigationSpan.updateName(pathname); currentNavigationSpan.setAttributes({ "navigation.type": `router.${navigationType}`, [core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: parameterizedPathname ? "route" : "url", ...parameterizedPathname && { [attributes.URL_TEMPLATE]: parameterizedPathname } }); setNavigationSpanUrlAttributes(currentNavigationSpan, unparameterizedPathname, normalizedHref); currentRouterPatchingNavigationSpanRef.current = void 0; } else { react.startBrowserTracingNavigationSpan( client, { name: pathname, attributes: { [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "navigation", [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.navigation.nextjs.app_router_instrumentation", [core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: parameterizedPathname ? "route" : "url", "navigation.type": `router.${navigationType}`, ...parameterizedPathname && { [attributes.URL_TEMPLATE]: parameterizedPathname } } }, { url: react.getAbsoluteUrl(normalizedHref) } ); } }; react.WINDOW.addEventListener("popstate", () => { const pathname = stripTrailingSlash(react.WINDOW.location.pathname); const parameterizedPathname = parameterization.maybeParameterizeRoute(pathname); if (currentRouterPatchingNavigationSpanRef.current?.isRecording()) { currentRouterPatchingNavigationSpanRef.current.updateName(parameterizedPathname ?? pathname); currentRouterPatchingNavigationSpanRef.current.setAttribute( core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, parameterizedPathname ? "route" : "url" ); if (parameterizedPathname) { currentRouterPatchingNavigationSpanRef.current.setAttribute(attributes.URL_TEMPLATE, parameterizedPathname); } setNavigationSpanUrlAttributes(currentRouterPatchingNavigationSpanRef.current, pathname, react.WINDOW.location.href); } else { currentRouterPatchingNavigationSpanRef.current = react.startBrowserTracingNavigationSpan( client, { name: parameterizedPathname ?? pathname, attributes: { [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.navigation.nextjs.app_router_instrumentation", [core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: parameterizedPathname ? "route" : "url", "navigation.type": "browser.popstate", ...parameterizedPathname && { [attributes.URL_TEMPLATE]: parameterizedPathname } } }, { url: react.getAbsoluteUrl(pathname) } ); } }); let routerPatched = false; let triesToFindRouter = 0; const MAX_TRIES_TO_FIND_ROUTER = 500; const ROUTER_AVAILABILITY_CHECK_INTERVAL_MS = 20; const checkForRouterAvailabilityInterval = setInterval(() => { triesToFindRouter++; const router = GLOBAL_OBJ_WITH_NEXT_ROUTER?.next?.router ?? GLOBAL_OBJ_WITH_NEXT_ROUTER?.nd?.router; if (routerPatched || triesToFindRouter > MAX_TRIES_TO_FIND_ROUTER) { clearInterval(checkForRouterAvailabilityInterval); } else if (router) { clearInterval(checkForRouterAvailabilityInterval); routerPatched = true; patchRouter(client, router, currentRouterPatchingNavigationSpanRef); ["nd", "next"].forEach((globalValueName) => { const globalValue = GLOBAL_OBJ_WITH_NEXT_ROUTER[globalValueName]; if (globalValue) { GLOBAL_OBJ_WITH_NEXT_ROUTER[globalValueName] = new Proxy(globalValue, { set(target, p, newValue) { if (p === "router" && typeof newValue === "object" && newValue !== null) { patchRouter(client, newValue, currentRouterPatchingNavigationSpanRef); } target[p] = newValue; return true; } }); } }); } }, ROUTER_AVAILABILITY_CHECK_INTERVAL_MS); } function transactionNameifyRouterArgument(target) { try { return new URL(target, "http://example.com/").pathname; } catch { return "/"; } } const patchedRouters = /* @__PURE__ */ new WeakSet(); function patchRouter(client, router, currentNavigationSpanRef) { if (patchedRouters.has(router)) { return; } patchedRouters.add(router); ["back", "forward", "push", "replace"].forEach((routerFunctionName) => { if (router?.[routerFunctionName]) { router[routerFunctionName] = new Proxy(router[routerFunctionName], { apply(target, thisArg, argArray) { if (navigationRoutingMode !== "router-patch") { return target.apply(thisArg, argArray); } let transactionName = INCOMPLETE_APP_ROUTER_INSTRUMENTATION_TRANSACTION_NAME; const transactionAttributes = { [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "navigation", [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.navigation.nextjs.app_router_instrumentation", [core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "url" }; const href = argArray[0]; const basePath = process.env._sentryBasePath ?? globalWithInjectedBasePath._sentryBasePath; const normalizedHref = basePath && typeof href === "string" && !href.startsWith(basePath) ? `${basePath}${href}` : href; if (routerFunctionName === "push") { transactionName = stripTrailingSlash(transactionNameifyRouterArgument(normalizedHref)); transactionAttributes["navigation.type"] = "router.push"; } else if (routerFunctionName === "replace") { transactionName = stripTrailingSlash(transactionNameifyRouterArgument(normalizedHref)); transactionAttributes["navigation.type"] = "router.replace"; } else if (routerFunctionName === "back") { transactionAttributes["navigation.type"] = "router.back"; } else if (routerFunctionName === "forward") { transactionAttributes["navigation.type"] = "router.forward"; } const parameterizedPathname = parameterization.maybeParameterizeRoute(transactionName); const navigationUrl = routerFunctionName === "back" || routerFunctionName === "forward" ? void 0 : react.getAbsoluteUrl(normalizedHref); currentNavigationSpanRef.current = react.startBrowserTracingNavigationSpan( client, { name: parameterizedPathname ?? transactionName, attributes: { ...transactionAttributes, [core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: parameterizedPathname ? "route" : "url", ...parameterizedPathname && { [attributes.URL_TEMPLATE]: parameterizedPathname } } }, navigationUrl ? { url: navigationUrl } : void 0 ); return target.apply(thisArg, argArray); } }); } }); } let routerTransitionHandler = void 0; function captureRouterTransitionStart(href, navigationType) { if (routerTransitionHandler) { routerTransitionHandler(href, navigationType); } } exports.INCOMPLETE_APP_ROUTER_INSTRUMENTATION_TRANSACTION_NAME = INCOMPLETE_APP_ROUTER_INSTRUMENTATION_TRANSACTION_NAME; exports.appRouterInstrumentNavigation = appRouterInstrumentNavigation; exports.appRouterInstrumentPageLoad = appRouterInstrumentPageLoad; exports.captureRouterTransitionStart = captureRouterTransitionStart; //# sourceMappingURL=appRouterRoutingInstrumentation.js.map