| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 | import { DEBUG_BUILD } from './debug-build.js';import { isString } from './is.js';import { logger } from './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,) {  if (!isString(baggageHeader) && !Array.isArray(baggageHeader)) {    return undefined;  }  // Intermediary object to store baggage key value pairs of incoming baggage headers on.  // It is later used to read Sentry-DSC-values from.  let baggageObject = {};  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    baggageObject = baggageHeader.reduce((acc, curr) => {      const currBaggageObject = baggageHeaderToObject(curr);      for (const key of Object.keys(currBaggageObject)) {        acc[key] = currBaggageObject[key];      }      return acc;    }, {});  } else {    // Return undefined if baggage header is an empty string (technically an empty baggage header is not spec conform but    // this is how we choose to handle it)    if (!baggageHeader) {      return undefined;    }    baggageObject = baggageHeaderToObject(baggageHeader);  }  // 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);}/** * 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]) => {      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) {      DEBUG_BUILD &&        logger.warn(          `Not adding key: ${objectKey} with val: ${objectValue} to baggage header due to exceeding baggage size limits.`,        );      return baggageHeader;    } else {      return newBaggageHeader;    }  }, '');}export { BAGGAGE_HEADER_NAME, MAX_BAGGAGE_STRING_LENGTH, SENTRY_BAGGAGE_KEY_PREFIX, SENTRY_BAGGAGE_KEY_PREFIX_REGEX, baggageHeaderToDynamicSamplingContext, dynamicSamplingContextToSentryBaggageHeader };//# sourceMappingURL=baggage.js.map
 |