UNPKG

@sentry/remix

Version:
131 lines (127 loc) • 4.43 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const browser = require('@sentry/browser'); const attributes = require('@sentry/conventions/attributes'); const core = require('@sentry/core'); const routeTiming = require('../routeTiming.js'); const FRAME_REQUEST_HEADER = "x-remix-frame"; function browserTracingIntegration(options = {}) { const integration = browser.browserTracingIntegration({ ...options, instrumentNavigation: false }); return { ...integration, afterAllSetup(client) { integration.afterAllSetup(client); if (options.instrumentPageLoad !== false) { nameActivePageLoadSpan(client); } if (options.instrumentNavigation !== false) { instrumentNavigationApi(client); } } }; } function nameActivePageLoadSpan(client) { const activeSpan = browser.getActiveSpan(); if (!activeSpan) { return; } const rootSpan = browser.getRootSpan(activeSpan); if (core.spanToJSON(rootSpan).attributes[attributes.SENTRY_OP] !== "pageload") { return; } recordRoute(client, browser.WINDOW.location?.pathname, getDocumentRoute()); const route = browser.resolveCurrentRoute(client); if (route) { applyRoute(rootSpan, route); } } function instrumentNavigationApi(client) { const navigation = browser.WINDOW.navigation; if (!navigation) { return; } let pending; client.on("spanEnd", (span) => { if (pending?.span === span) { pending = void 0; } }); navigation.addEventListener("navigate", (event) => { const url = event.destination?.url; if (!url || !isRuntimeNavigation(event, url)) { return; } const pathname = pathnameOf(url) || "/"; const route = browser.resolveRoute(url, client); const span = browser.startBrowserTracingNavigationSpan( client, { name: route ?? (core.hasSpanStreamingEnabled(client) ? core.NAVIGATION_SPAN_NAME_FALLBACK : pathname), attributes: { [attributes.SENTRY_SEGMENT_NAME_SOURCE]: route ? "route" : "url", ...route && { [attributes.URL_TEMPLATE]: route }, [attributes.SENTRY_ORIGIN]: "auto.navigation.remix_v3" } }, // The span needs the destination: `location` still points at the previous page until the // navigation finishes. { url } ); pending = span && !route ? { span, url, pathname } : void 0; }); core.addFetchInstrumentationHandler(({ fetchData, headers, response }) => { if (!pending || !response) { return; } if (headers?.get(FRAME_REQUEST_HEADER) !== "true") { return; } if (pathnameOf(fetchData.url, browser.WINDOW.location?.href) !== pending.pathname) { return; } const { span, url, pathname } = pending; pending = void 0; const servedPathname = pathnameOf(response.url, browser.WINDOW.location?.href) || pathname; recordRoute(client, servedPathname, routeTiming.parseRouteTiming(response.headers.get("server-timing"))); const route = browser.resolveRoute(response.url || url, client); if (route) { applyRoute(span, route); } }); } function recordRoute(client, pathname, route) { const provider = browser.getRouteProvider(client); if (provider && "record" in provider && typeof provider.record === "function") { provider.record(pathname, route); } } function applyRoute(span, route) { span.updateName(route); span.setAttributes({ [attributes.SENTRY_SEGMENT_NAME_SOURCE]: "route", [attributes.URL_TEMPLATE]: route }); const activeSpan = browser.getActiveSpan(); if (activeSpan && browser.getRootSpan(activeSpan) === span) { browser.getCurrentScope().setTransactionName(route); } } function getDocumentRoute() { const entry = browser.WINDOW.performance?.getEntriesByType?.("navigation")[0]; return entry?.serverTiming?.find((timing) => timing.name === routeTiming.ROUTE_TIMING_NAME)?.description; } function isRuntimeNavigation(event, url) { return event.canIntercept && event.info !== "remix-document-reload" && isSameOrigin(url); } function isSameOrigin(url) { try { return new URL(url).origin === browser.WINDOW.location?.origin; } catch { return false; } } function pathnameOf(url, base) { try { return new URL(url, base).pathname; } catch { return void 0; } } exports.browserTracingIntegration = browserTracingIntegration; //# sourceMappingURL=browserTracingIntegration.js.map