Next.js website for Rocky Mountain Vending company featuring: - Product catalog with Stripe integration - Service areas and parts pages - Admin dashboard with Clerk authentication - SEO optimized pages with JSON-LD structured data Co-authored-by: Cursor <cursoragent@cursor.com>
258 lines
8.3 KiB
Text
258 lines
8.3 KiB
Text
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
|
const currentScopes = require('./currentScopes.js');
|
|
const semanticAttributes = require('./semanticAttributes.js');
|
|
const spanUtils = require('./utils/spanUtils.js');
|
|
const spanstatus = require('./tracing/spanstatus.js');
|
|
const is = require('./utils/is.js');
|
|
const hasSpansEnabled = require('./utils/hasSpansEnabled.js');
|
|
const baggage = require('./utils/baggage.js');
|
|
const sentryNonRecordingSpan = require('./tracing/sentryNonRecordingSpan.js');
|
|
const trace = require('./tracing/trace.js');
|
|
const traceData = require('./utils/traceData.js');
|
|
const url = require('./utils/url.js');
|
|
|
|
/**
|
|
* Create and track fetch request spans for usage in combination with `addFetchInstrumentationHandler`.
|
|
*
|
|
* @returns Span if a span was created, otherwise void.
|
|
*/
|
|
function instrumentFetchRequest(
|
|
handlerData,
|
|
shouldCreateSpan,
|
|
shouldAttachHeaders,
|
|
spans,
|
|
spanOrigin = 'auto.http.browser',
|
|
) {
|
|
if (!handlerData.fetchData) {
|
|
return undefined;
|
|
}
|
|
|
|
const { method, url } = handlerData.fetchData;
|
|
|
|
const shouldCreateSpanResult = hasSpansEnabled.hasSpansEnabled() && shouldCreateSpan(url);
|
|
|
|
if (handlerData.endTimestamp && shouldCreateSpanResult) {
|
|
const spanId = handlerData.fetchData.__span;
|
|
if (!spanId) return;
|
|
|
|
const span = spans[spanId];
|
|
if (span) {
|
|
endSpan(span, handlerData);
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
delete spans[spanId];
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
const hasParent = !!spanUtils.getActiveSpan();
|
|
|
|
const span =
|
|
shouldCreateSpanResult && hasParent
|
|
? trace.startInactiveSpan(getSpanStartOptions(url, method, spanOrigin))
|
|
: new sentryNonRecordingSpan.SentryNonRecordingSpan();
|
|
|
|
handlerData.fetchData.__span = span.spanContext().spanId;
|
|
spans[span.spanContext().spanId] = span;
|
|
|
|
if (shouldAttachHeaders(handlerData.fetchData.url)) {
|
|
const request = handlerData.args[0];
|
|
|
|
const options = handlerData.args[1] || {};
|
|
|
|
const headers = _addTracingHeadersToFetchRequest(
|
|
request,
|
|
options,
|
|
// If performance is disabled (TWP) or there's no active root span (pageload/navigation/interaction),
|
|
// we do not want to use the span as base for the trace headers,
|
|
// which means that the headers will be generated from the scope and the sampling decision is deferred
|
|
hasSpansEnabled.hasSpansEnabled() && hasParent ? span : undefined,
|
|
);
|
|
if (headers) {
|
|
// Ensure this is actually set, if no options have been passed previously
|
|
handlerData.args[1] = options;
|
|
options.headers = headers;
|
|
}
|
|
}
|
|
|
|
const client = currentScopes.getClient();
|
|
|
|
if (client) {
|
|
const fetchHint = {
|
|
input: handlerData.args,
|
|
response: handlerData.response,
|
|
startTimestamp: handlerData.startTimestamp,
|
|
endTimestamp: handlerData.endTimestamp,
|
|
} ;
|
|
|
|
client.emit('beforeOutgoingRequestSpan', span, fetchHint);
|
|
}
|
|
|
|
return span;
|
|
}
|
|
|
|
/**
|
|
* Adds sentry-trace and baggage headers to the various forms of fetch headers.
|
|
* exported only for testing purposes
|
|
*
|
|
* When we determine if we should add a baggage header, there are 3 cases:
|
|
* 1. No previous baggage header -> add baggage
|
|
* 2. Previous baggage header has no sentry baggage values -> add our baggage
|
|
* 3. Previous baggage header has sentry baggage values -> do nothing (might have been added manually by users)
|
|
*/
|
|
// eslint-disable-next-line complexity -- yup it's this complicated :(
|
|
function _addTracingHeadersToFetchRequest(
|
|
request,
|
|
fetchOptionsObj
|
|
|
|
,
|
|
span,
|
|
) {
|
|
const traceHeaders = traceData.getTraceData({ span });
|
|
const sentryTrace = traceHeaders['sentry-trace'];
|
|
const baggage = traceHeaders.baggage;
|
|
|
|
// Nothing to do, when we return undefined here, the original headers will be used
|
|
if (!sentryTrace) {
|
|
return undefined;
|
|
}
|
|
|
|
const originalHeaders = fetchOptionsObj.headers || (is.isRequest(request) ? request.headers : undefined);
|
|
|
|
if (!originalHeaders) {
|
|
return { ...traceHeaders };
|
|
} else if (isHeaders(originalHeaders)) {
|
|
const newHeaders = new Headers(originalHeaders);
|
|
|
|
// We don't want to override manually added sentry headers
|
|
if (!newHeaders.get('sentry-trace')) {
|
|
newHeaders.set('sentry-trace', sentryTrace);
|
|
}
|
|
|
|
if (baggage) {
|
|
const prevBaggageHeader = newHeaders.get('baggage');
|
|
|
|
if (!prevBaggageHeader) {
|
|
newHeaders.set('baggage', baggage);
|
|
} else if (!baggageHeaderHasSentryBaggageValues(prevBaggageHeader)) {
|
|
newHeaders.set('baggage', `${prevBaggageHeader},${baggage}`);
|
|
}
|
|
}
|
|
|
|
return newHeaders;
|
|
} else if (Array.isArray(originalHeaders)) {
|
|
const newHeaders = [...originalHeaders];
|
|
|
|
if (!originalHeaders.find(header => header[0] === 'sentry-trace')) {
|
|
newHeaders.push(['sentry-trace', sentryTrace]);
|
|
}
|
|
|
|
const prevBaggageHeaderWithSentryValues = originalHeaders.find(
|
|
header => header[0] === 'baggage' && baggageHeaderHasSentryBaggageValues(header[1]),
|
|
);
|
|
|
|
if (baggage && !prevBaggageHeaderWithSentryValues) {
|
|
// If there are multiple entries with the same key, the browser will merge the values into a single request header.
|
|
// Its therefore safe to simply push a "baggage" entry, even though there might already be another baggage header.
|
|
newHeaders.push(['baggage', baggage]);
|
|
}
|
|
|
|
return newHeaders ;
|
|
} else {
|
|
const existingSentryTraceHeader = 'sentry-trace' in originalHeaders ? originalHeaders['sentry-trace'] : undefined;
|
|
|
|
const existingBaggageHeader = 'baggage' in originalHeaders ? originalHeaders.baggage : undefined;
|
|
const newBaggageHeaders = existingBaggageHeader
|
|
? Array.isArray(existingBaggageHeader)
|
|
? [...existingBaggageHeader]
|
|
: [existingBaggageHeader]
|
|
: [];
|
|
|
|
const prevBaggageHeaderWithSentryValues =
|
|
existingBaggageHeader &&
|
|
(Array.isArray(existingBaggageHeader)
|
|
? existingBaggageHeader.find(headerItem => baggageHeaderHasSentryBaggageValues(headerItem))
|
|
: baggageHeaderHasSentryBaggageValues(existingBaggageHeader));
|
|
|
|
if (baggage && !prevBaggageHeaderWithSentryValues) {
|
|
newBaggageHeaders.push(baggage);
|
|
}
|
|
|
|
return {
|
|
...(originalHeaders ),
|
|
'sentry-trace': (existingSentryTraceHeader ) ?? sentryTrace,
|
|
baggage: newBaggageHeaders.length > 0 ? newBaggageHeaders.join(',') : undefined,
|
|
};
|
|
}
|
|
}
|
|
|
|
function endSpan(span, handlerData) {
|
|
if (handlerData.response) {
|
|
spanstatus.setHttpStatus(span, handlerData.response.status);
|
|
|
|
const contentLength = handlerData.response?.headers?.get('content-length');
|
|
|
|
if (contentLength) {
|
|
const contentLengthNum = parseInt(contentLength);
|
|
if (contentLengthNum > 0) {
|
|
span.setAttribute('http.response_content_length', contentLengthNum);
|
|
}
|
|
}
|
|
} else if (handlerData.error) {
|
|
span.setStatus({ code: spanstatus.SPAN_STATUS_ERROR, message: 'internal_error' });
|
|
}
|
|
span.end();
|
|
}
|
|
|
|
function baggageHeaderHasSentryBaggageValues(baggageHeader) {
|
|
return baggageHeader.split(',').some(baggageEntry => baggageEntry.trim().startsWith(baggage.SENTRY_BAGGAGE_KEY_PREFIX));
|
|
}
|
|
|
|
function isHeaders(headers) {
|
|
return typeof Headers !== 'undefined' && is.isInstanceOf(headers, Headers);
|
|
}
|
|
|
|
function getSpanStartOptions(
|
|
url$1,
|
|
method,
|
|
spanOrigin,
|
|
) {
|
|
const parsedUrl = url.parseStringToURLObject(url$1);
|
|
return {
|
|
name: parsedUrl ? `${method} ${url.getSanitizedUrlStringFromUrlObject(parsedUrl)}` : method,
|
|
attributes: getFetchSpanAttributes(url$1, parsedUrl, method, spanOrigin),
|
|
};
|
|
}
|
|
|
|
function getFetchSpanAttributes(
|
|
url$1,
|
|
parsedUrl,
|
|
method,
|
|
spanOrigin,
|
|
) {
|
|
const attributes = {
|
|
url: url$1,
|
|
type: 'fetch',
|
|
'http.method': method,
|
|
[semanticAttributes.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: spanOrigin,
|
|
[semanticAttributes.SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.client',
|
|
};
|
|
if (parsedUrl) {
|
|
if (!url.isURLObjectRelative(parsedUrl)) {
|
|
attributes['http.url'] = parsedUrl.href;
|
|
attributes['server.address'] = parsedUrl.host;
|
|
}
|
|
if (parsedUrl.search) {
|
|
attributes['http.query'] = parsedUrl.search;
|
|
}
|
|
if (parsedUrl.hash) {
|
|
attributes['http.fragment'] = parsedUrl.hash;
|
|
}
|
|
}
|
|
return attributes;
|
|
}
|
|
|
|
exports._addTracingHeadersToFetchRequest = _addTracingHeadersToFetchRequest;
|
|
exports.instrumentFetchRequest = instrumentFetchRequest;
|
|
//# sourceMappingURL=fetch.js.map
|