envelope.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. Object.defineProperty(exports, '__esModule', { value: true });
  2. const dsn = require('./dsn.js');
  3. const normalize = require('./normalize.js');
  4. const object = require('./object.js');
  5. /**
  6. * Creates an envelope.
  7. * Make sure to always explicitly provide the generic to this function
  8. * so that the envelope types resolve correctly.
  9. */
  10. function createEnvelope(headers, items = []) {
  11. return [headers, items] ;
  12. }
  13. /**
  14. * Add an item to an envelope.
  15. * Make sure to always explicitly provide the generic to this function
  16. * so that the envelope types resolve correctly.
  17. */
  18. function addItemToEnvelope(envelope, newItem) {
  19. const [headers, items] = envelope;
  20. return [headers, [...items, newItem]] ;
  21. }
  22. /**
  23. * Convenience function to loop through the items and item types of an envelope.
  24. * (This function was mostly created because working with envelope types is painful at the moment)
  25. *
  26. * If the callback returns true, the rest of the items will be skipped.
  27. */
  28. function forEachEnvelopeItem(
  29. envelope,
  30. callback,
  31. ) {
  32. const envelopeItems = envelope[1];
  33. for (const envelopeItem of envelopeItems) {
  34. const envelopeItemType = envelopeItem[0].type;
  35. const result = callback(envelopeItem, envelopeItemType);
  36. if (result) {
  37. return true;
  38. }
  39. }
  40. return false;
  41. }
  42. /**
  43. * Returns true if the envelope contains any of the given envelope item types
  44. */
  45. function envelopeContainsItemType(envelope, types) {
  46. return forEachEnvelopeItem(envelope, (_, type) => types.includes(type));
  47. }
  48. /**
  49. * Encode a string to UTF8.
  50. */
  51. function encodeUTF8(input, textEncoder) {
  52. const utf8 = textEncoder || new TextEncoder();
  53. return utf8.encode(input);
  54. }
  55. /**
  56. * Serializes an envelope.
  57. */
  58. function serializeEnvelope(envelope, textEncoder) {
  59. const [envHeaders, items] = envelope;
  60. // Initially we construct our envelope as a string and only convert to binary chunks if we encounter binary data
  61. let parts = JSON.stringify(envHeaders);
  62. function append(next) {
  63. if (typeof parts === 'string') {
  64. parts = typeof next === 'string' ? parts + next : [encodeUTF8(parts, textEncoder), next];
  65. } else {
  66. parts.push(typeof next === 'string' ? encodeUTF8(next, textEncoder) : next);
  67. }
  68. }
  69. for (const item of items) {
  70. const [itemHeaders, payload] = item;
  71. append(`\n${JSON.stringify(itemHeaders)}\n`);
  72. if (typeof payload === 'string' || payload instanceof Uint8Array) {
  73. append(payload);
  74. } else {
  75. let stringifiedPayload;
  76. try {
  77. stringifiedPayload = JSON.stringify(payload);
  78. } catch (e) {
  79. // In case, despite all our efforts to keep `payload` circular-dependency-free, `JSON.strinify()` still
  80. // fails, we try again after normalizing it again with infinite normalization depth. This of course has a
  81. // performance impact but in this case a performance hit is better than throwing.
  82. stringifiedPayload = JSON.stringify(normalize.normalize(payload));
  83. }
  84. append(stringifiedPayload);
  85. }
  86. }
  87. return typeof parts === 'string' ? parts : concatBuffers(parts);
  88. }
  89. function concatBuffers(buffers) {
  90. const totalLength = buffers.reduce((acc, buf) => acc + buf.length, 0);
  91. const merged = new Uint8Array(totalLength);
  92. let offset = 0;
  93. for (const buffer of buffers) {
  94. merged.set(buffer, offset);
  95. offset += buffer.length;
  96. }
  97. return merged;
  98. }
  99. /**
  100. * Parses an envelope
  101. */
  102. function parseEnvelope(
  103. env,
  104. textEncoder,
  105. textDecoder,
  106. ) {
  107. let buffer = typeof env === 'string' ? textEncoder.encode(env) : env;
  108. function readBinary(length) {
  109. const bin = buffer.subarray(0, length);
  110. // Replace the buffer with the remaining data excluding trailing newline
  111. buffer = buffer.subarray(length + 1);
  112. return bin;
  113. }
  114. function readJson() {
  115. let i = buffer.indexOf(0xa);
  116. // If we couldn't find a newline, we must have found the end of the buffer
  117. if (i < 0) {
  118. i = buffer.length;
  119. }
  120. return JSON.parse(textDecoder.decode(readBinary(i))) ;
  121. }
  122. const envelopeHeader = readJson();
  123. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  124. const items = [];
  125. while (buffer.length) {
  126. const itemHeader = readJson();
  127. const binaryLength = typeof itemHeader.length === 'number' ? itemHeader.length : undefined;
  128. items.push([itemHeader, binaryLength ? readBinary(binaryLength) : readJson()]);
  129. }
  130. return [envelopeHeader, items];
  131. }
  132. /**
  133. * Creates attachment envelope items
  134. */
  135. function createAttachmentEnvelopeItem(
  136. attachment,
  137. textEncoder,
  138. ) {
  139. const buffer = typeof attachment.data === 'string' ? encodeUTF8(attachment.data, textEncoder) : attachment.data;
  140. return [
  141. object.dropUndefinedKeys({
  142. type: 'attachment',
  143. length: buffer.length,
  144. filename: attachment.filename,
  145. content_type: attachment.contentType,
  146. attachment_type: attachment.attachmentType,
  147. }),
  148. buffer,
  149. ];
  150. }
  151. const ITEM_TYPE_TO_DATA_CATEGORY_MAP = {
  152. session: 'session',
  153. sessions: 'session',
  154. attachment: 'attachment',
  155. transaction: 'transaction',
  156. event: 'error',
  157. client_report: 'internal',
  158. user_report: 'default',
  159. profile: 'profile',
  160. replay_event: 'replay',
  161. replay_recording: 'replay',
  162. check_in: 'monitor',
  163. feedback: 'feedback',
  164. // TODO: This is a temporary workaround until we have a proper data category for metrics
  165. statsd: 'unknown',
  166. };
  167. /**
  168. * Maps the type of an envelope item to a data category.
  169. */
  170. function envelopeItemTypeToDataCategory(type) {
  171. return ITEM_TYPE_TO_DATA_CATEGORY_MAP[type];
  172. }
  173. /** Extracts the minimal SDK info from from the metadata or an events */
  174. function getSdkMetadataForEnvelopeHeader(metadataOrEvent) {
  175. if (!metadataOrEvent || !metadataOrEvent.sdk) {
  176. return;
  177. }
  178. const { name, version } = metadataOrEvent.sdk;
  179. return { name, version };
  180. }
  181. /**
  182. * Creates event envelope headers, based on event, sdk info and tunnel
  183. * Note: This function was extracted from the core package to make it available in Replay
  184. */
  185. function createEventEnvelopeHeaders(
  186. event,
  187. sdkInfo,
  188. tunnel,
  189. dsn$1,
  190. ) {
  191. const dynamicSamplingContext = event.sdkProcessingMetadata && event.sdkProcessingMetadata.dynamicSamplingContext;
  192. return {
  193. event_id: event.event_id ,
  194. sent_at: new Date().toISOString(),
  195. ...(sdkInfo && { sdk: sdkInfo }),
  196. ...(!!tunnel && dsn$1 && { dsn: dsn.dsnToString(dsn$1) }),
  197. ...(dynamicSamplingContext && {
  198. trace: object.dropUndefinedKeys({ ...dynamicSamplingContext }),
  199. }),
  200. };
  201. }
  202. exports.addItemToEnvelope = addItemToEnvelope;
  203. exports.createAttachmentEnvelopeItem = createAttachmentEnvelopeItem;
  204. exports.createEnvelope = createEnvelope;
  205. exports.createEventEnvelopeHeaders = createEventEnvelopeHeaders;
  206. exports.envelopeContainsItemType = envelopeContainsItemType;
  207. exports.envelopeItemTypeToDataCategory = envelopeItemTypeToDataCategory;
  208. exports.forEachEnvelopeItem = forEachEnvelopeItem;
  209. exports.getSdkMetadataForEnvelopeHeader = getSdkMetadataForEnvelopeHeader;
  210. exports.parseEnvelope = parseEnvelope;
  211. exports.serializeEnvelope = serializeEnvelope;
  212. //# sourceMappingURL=envelope.js.map