package.build.cjs.baggage.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of utils Show documentation
Show all versions of utils Show documentation
Utilities for all Sentry JavaScript SDKs
The newest version!
Object.defineProperty(exports, '__esModule', { value: true });
const debugBuild = require('./debug-build.js');
const is = require('./is.js');
const logger = require('./logger.js');
const BAGGAGE_HEADER_NAME = 'baggage';
const SENTRY_BAGGAGE_KEY_PREFIX = 'sentry-';
const SENTRY_BAGGAGE_KEY_PREFIX_REGEX = /^sentry-/;
/**
* Max length of a serialized baggage string
*
* https://www.w3.org/TR/baggage/#limits
*/
const MAX_BAGGAGE_STRING_LENGTH = 8192;
/**
* Takes a baggage header and turns it into Dynamic Sampling Context, by extracting all the "sentry-" prefixed values
* from it.
*
* @param baggageHeader A very bread definition of a baggage header as it might appear in various frameworks.
* @returns The Dynamic Sampling Context that was found on `baggageHeader`, if there was any, `undefined` otherwise.
*/
function baggageHeaderToDynamicSamplingContext(
// Very liberal definition of what any incoming header might look like
baggageHeader,
) {
const baggageObject = parseBaggageHeader(baggageHeader);
if (!baggageObject) {
return undefined;
}
// Read all "sentry-" prefixed values out of the baggage object and put it onto a dynamic sampling context object.
const dynamicSamplingContext = Object.entries(baggageObject).reduce((acc, [key, value]) => {
if (key.match(SENTRY_BAGGAGE_KEY_PREFIX_REGEX)) {
const nonPrefixedKey = key.slice(SENTRY_BAGGAGE_KEY_PREFIX.length);
acc[nonPrefixedKey] = value;
}
return acc;
}, {});
// Only return a dynamic sampling context object if there are keys in it.
// A keyless object means there were no sentry values on the header, which means that there is no DSC.
if (Object.keys(dynamicSamplingContext).length > 0) {
return dynamicSamplingContext ;
} else {
return undefined;
}
}
/**
* Turns a Dynamic Sampling Object into a baggage header by prefixing all the keys on the object with "sentry-".
*
* @param dynamicSamplingContext The Dynamic Sampling Context to turn into a header. For convenience and compatibility
* with the `getDynamicSamplingContext` method on the Transaction class ,this argument can also be `undefined`. If it is
* `undefined` the function will return `undefined`.
* @returns a baggage header, created from `dynamicSamplingContext`, or `undefined` either if `dynamicSamplingContext`
* was `undefined`, or if `dynamicSamplingContext` didn't contain any values.
*/
function dynamicSamplingContextToSentryBaggageHeader(
// this also takes undefined for convenience and bundle size in other places
dynamicSamplingContext,
) {
if (!dynamicSamplingContext) {
return undefined;
}
// Prefix all DSC keys with "sentry-" and put them into a new object
const sentryPrefixedDSC = Object.entries(dynamicSamplingContext).reduce(
(acc, [dscKey, dscValue]) => {
if (dscValue) {
acc[`${SENTRY_BAGGAGE_KEY_PREFIX}${dscKey}`] = dscValue;
}
return acc;
},
{},
);
return objectToBaggageHeader(sentryPrefixedDSC);
}
/**
* Take a baggage header and parse it into an object.
*/
function parseBaggageHeader(
baggageHeader,
) {
if (!baggageHeader || (!is.isString(baggageHeader) && !Array.isArray(baggageHeader))) {
return undefined;
}
if (Array.isArray(baggageHeader)) {
// Combine all baggage headers into one object containing the baggage values so we can later read the Sentry-DSC-values from it
return baggageHeader.reduce((acc, curr) => {
const currBaggageObject = baggageHeaderToObject(curr);
Object.entries(currBaggageObject).forEach(([key, value]) => {
acc[key] = value;
});
return acc;
}, {});
}
return baggageHeaderToObject(baggageHeader);
}
/**
* Will parse a baggage header, which is a simple key-value map, into a flat object.
*
* @param baggageHeader The baggage header to parse.
* @returns a flat object containing all the key-value pairs from `baggageHeader`.
*/
function baggageHeaderToObject(baggageHeader) {
return baggageHeader
.split(',')
.map(baggageEntry => baggageEntry.split('=').map(keyOrValue => decodeURIComponent(keyOrValue.trim())))
.reduce((acc, [key, value]) => {
if (key && value) {
acc[key] = value;
}
return acc;
}, {});
}
/**
* Turns a flat object (key-value pairs) into a baggage header, which is also just key-value pairs.
*
* @param object The object to turn into a baggage header.
* @returns a baggage header string, or `undefined` if the object didn't have any values, since an empty baggage header
* is not spec compliant.
*/
function objectToBaggageHeader(object) {
if (Object.keys(object).length === 0) {
// An empty baggage header is not spec compliant: We return undefined.
return undefined;
}
return Object.entries(object).reduce((baggageHeader, [objectKey, objectValue], currentIndex) => {
const baggageEntry = `${encodeURIComponent(objectKey)}=${encodeURIComponent(objectValue)}`;
const newBaggageHeader = currentIndex === 0 ? baggageEntry : `${baggageHeader},${baggageEntry}`;
if (newBaggageHeader.length > MAX_BAGGAGE_STRING_LENGTH) {
debugBuild.DEBUG_BUILD &&
logger.logger.warn(
`Not adding key: ${objectKey} with val: ${objectValue} to baggage header due to exceeding baggage size limits.`,
);
return baggageHeader;
} else {
return newBaggageHeader;
}
}, '');
}
exports.BAGGAGE_HEADER_NAME = BAGGAGE_HEADER_NAME;
exports.MAX_BAGGAGE_STRING_LENGTH = MAX_BAGGAGE_STRING_LENGTH;
exports.SENTRY_BAGGAGE_KEY_PREFIX = SENTRY_BAGGAGE_KEY_PREFIX;
exports.SENTRY_BAGGAGE_KEY_PREFIX_REGEX = SENTRY_BAGGAGE_KEY_PREFIX_REGEX;
exports.baggageHeaderToDynamicSamplingContext = baggageHeaderToDynamicSamplingContext;
exports.dynamicSamplingContextToSentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader;
exports.parseBaggageHeader = parseBaggageHeader;
//# sourceMappingURL=baggage.js.map