UNPKG

@sentry/remix

Version:
116 lines (113 loc) • 3.86 kB
import { HTTP_ROUTE, HTTP_RESPONSE_STATUS_CODE } from '@sentry/conventions/attributes'; import { getIsolationScope, winterCGRequestToRequestData, getActiveSpan, getRootSpan, updateSpanName, INTERNAL_setSegmentNameSourceIfSegment, getSpanStatusFromHttpCode, getTraceData } from '@sentry/core'; import { formatRouteTiming } from '../routeTiming.js'; import { captureRequestError } from './errorFilter.js'; import { resolveRoutePattern } from './route.js'; function sentryRemixMiddleware(matcher) { return async function sentryMiddleware(context, next) { const isolationScope = getIsolationScope(); isolationScope.setSDKProcessingMetadata({ normalizedRequest: winterCGRequestToRequestData(context.request) }); const route = resolveRoutePattern(matcher, context); if (route) { applyRoute(isolationScope, route, context.method); } let response; try { response = await next(); } catch (error) { captureRequestError(error, context.request, "auto.http.remix_v3.middleware"); throw error; } setResponseStatus(response); addServerTimingHeaders(response, context.request, route); return response; }; } function applyRoute(isolationScope, route, method) { const name = `${method} ${route}`; isolationScope.setTransactionName(name); const activeSpan = getActiveSpan(); if (!activeSpan) { return; } const rootSpan = getRootSpan(activeSpan); updateSpanName(rootSpan, name); INTERNAL_setSegmentNameSourceIfSegment(rootSpan, "route"); rootSpan.setAttribute(HTTP_ROUTE, route); } function addServerTimingHeaders(response, request, route) { if (!request.headers.get("accept")?.includes("text/html")) { return; } const entries = []; if (!isSharedCacheable(response)) { const traceData = getTraceData(); if (traceData["sentry-trace"]) { entries.push(`sentry-trace;desc="${traceData["sentry-trace"]}"`); } if (traceData.baggage) { entries.push(`baggage;desc="${traceData.baggage}"`); } } if (route) { entries.push(formatRouteTiming(route)); } for (const entry of entries) { try { response.headers.append("Server-Timing", entry); } catch { return; } } } const CDN_CACHE_CONTROL_HEADERS = [ "cdn-cache-control", "cloudflare-cdn-cache-control", "vercel-cdn-cache-control", "surrogate-control" ]; function isSharedCacheable(response) { for (const name of CDN_CACHE_CONTROL_HEADERS) { const value = response.headers.get(name); if (value !== null && sharedCachingVerdict(value.toLowerCase()) === true) { return true; } } const verdict = sharedCachingVerdict(response.headers.get("cache-control")?.toLowerCase() ?? ""); if (verdict !== void 0) { return verdict; } const expires = response.headers.get("expires"); return expires !== null && Date.parse(expires) > Date.now(); } function sharedCachingVerdict(directives) { if (/\b(?:no-store|private)\b/.test(directives)) { return false; } if (/\b(?:stale-while-revalidate|stale-if-error)\s*=\s*[1-9]/.test(directives)) { return true; } const sharedMaxAge = directives.match(/\bs-maxage\s*=\s*(\d+)/)?.[1]; if (sharedMaxAge !== void 0) { return Number(sharedMaxAge) > 0; } if (/\bpublic\b/.test(directives)) { return true; } const maxAge = directives.match(/\bmax-age\s*=\s*(\d+)/)?.[1]; if (maxAge !== void 0) { return Number(maxAge) > 0; } return void 0; } function setResponseStatus(response) { const activeSpan = getActiveSpan(); if (!activeSpan || typeof response?.status !== "number") { return; } const rootSpan = getRootSpan(activeSpan); rootSpan.setAttribute(HTTP_RESPONSE_STATUS_CODE, response.status); rootSpan.setStatus(getSpanStatusFromHttpCode(response.status)); } export { sentryRemixMiddleware }; //# sourceMappingURL=middleware.js.map