@sentry/remix
Version:
Official Sentry SDK for Remix
118 lines (114 loc) • 3.84 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const attributes = require('@sentry/conventions/attributes');
const core = require('@sentry/core');
const routeTiming = require('../routeTiming.js');
const errorFilter = require('./errorFilter.js');
const route = require('./route.js');
function sentryRemixMiddleware(matcher) {
return async function sentryMiddleware(context, next) {
const isolationScope = core.getIsolationScope();
isolationScope.setSDKProcessingMetadata({ normalizedRequest: core.winterCGRequestToRequestData(context.request) });
const route$1 = route.resolveRoutePattern(matcher, context);
if (route$1) {
applyRoute(isolationScope, route$1, context.method);
}
let response;
try {
response = await next();
} catch (error) {
errorFilter.captureRequestError(error, context.request, "auto.http.remix_v3.middleware");
throw error;
}
setResponseStatus(response);
addServerTimingHeaders(response, context.request, route$1);
return response;
};
}
function applyRoute(isolationScope, route, method) {
const name = `${method} ${route}`;
isolationScope.setTransactionName(name);
const activeSpan = core.getActiveSpan();
if (!activeSpan) {
return;
}
const rootSpan = core.getRootSpan(activeSpan);
core.updateSpanName(rootSpan, name);
core.INTERNAL_setSegmentNameSourceIfSegment(rootSpan, "route");
rootSpan.setAttribute(attributes.HTTP_ROUTE, route);
}
function addServerTimingHeaders(response, request, route) {
if (!request.headers.get("accept")?.includes("text/html")) {
return;
}
const entries = [];
if (!isSharedCacheable(response)) {
const traceData = core.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(routeTiming.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 = core.getActiveSpan();
if (!activeSpan || typeof response?.status !== "number") {
return;
}
const rootSpan = core.getRootSpan(activeSpan);
rootSpan.setAttribute(attributes.HTTP_RESPONSE_STATUS_CODE, response.status);
rootSpan.setStatus(core.getSpanStatusFromHttpCode(response.status));
}
exports.sentryRemixMiddleware = sentryRemixMiddleware;
//# sourceMappingURL=middleware.js.map