UNPKG

@sentry/core

Version:
251 lines (214 loc) 6.97 kB
import { getSentryCarrier } from '../carrier.js'; import { dsnToString } from './dsn.js'; import { normalize } from './normalize.js'; import { GLOBAL_OBJ } from './worldwide.js'; /** * Creates an envelope. * Make sure to always explicitly provide the generic to this function * so that the envelope types resolve correctly. */ function createEnvelope(headers, items = []) { return [headers, items] ; } /** * Add an item to an envelope. * Make sure to always explicitly provide the generic to this function * so that the envelope types resolve correctly. */ function addItemToEnvelope(envelope, newItem) { const [headers, items] = envelope; return [headers, [...items, newItem]] ; } /** * Convenience function to loop through the items and item types of an envelope. * (This function was mostly created because working with envelope types is painful at the moment) * * If the callback returns true, the rest of the items will be skipped. */ function forEachEnvelopeItem( envelope, callback, ) { const envelopeItems = envelope[1]; for (const envelopeItem of envelopeItems) { const envelopeItemType = envelopeItem[0].type; const result = callback(envelopeItem, envelopeItemType); if (result) { return true; } } return false; } /** * Returns true if the envelope contains any of the given envelope item types */ function envelopeContainsItemType(envelope, types) { return forEachEnvelopeItem(envelope, (_, type) => types.includes(type)); } /** * Encode a string to UTF8 array. */ function encodeUTF8(input) { const carrier = getSentryCarrier(GLOBAL_OBJ); return carrier.encodePolyfill ? carrier.encodePolyfill(input) : new TextEncoder().encode(input); } /** * Decode a UTF8 array to string. */ function decodeUTF8(input) { const carrier = getSentryCarrier(GLOBAL_OBJ); return carrier.decodePolyfill ? carrier.decodePolyfill(input) : new TextDecoder().decode(input); } /** * Serializes an envelope. */ function serializeEnvelope(envelope) { const [envHeaders, items] = envelope; // Initially we construct our envelope as a string and only convert to binary chunks if we encounter binary data let parts = JSON.stringify(envHeaders); function append(next) { if (typeof parts === 'string') { parts = typeof next === 'string' ? parts + next : [encodeUTF8(parts), next]; } else { parts.push(typeof next === 'string' ? encodeUTF8(next) : next); } } for (const item of items) { const [itemHeaders, payload] = item; append(`\n${JSON.stringify(itemHeaders)}\n`); if (typeof payload === 'string' || payload instanceof Uint8Array) { append(payload); } else { let stringifiedPayload; try { stringifiedPayload = JSON.stringify(payload); } catch (e) { // In case, despite all our efforts to keep `payload` circular-dependency-free, `JSON.stringify()` still // fails, we try again after normalizing it again with infinite normalization depth. This of course has a // performance impact but in this case a performance hit is better than throwing. stringifiedPayload = JSON.stringify(normalize(payload)); } append(stringifiedPayload); } } return typeof parts === 'string' ? parts : concatBuffers(parts); } function concatBuffers(buffers) { const totalLength = buffers.reduce((acc, buf) => acc + buf.length, 0); const merged = new Uint8Array(totalLength); let offset = 0; for (const buffer of buffers) { merged.set(buffer, offset); offset += buffer.length; } return merged; } /** * Parses an envelope */ function parseEnvelope(env) { let buffer = typeof env === 'string' ? encodeUTF8(env) : env; function readBinary(length) { const bin = buffer.subarray(0, length); // Replace the buffer with the remaining data excluding trailing newline buffer = buffer.subarray(length + 1); return bin; } function readJson() { let i = buffer.indexOf(0xa); // If we couldn't find a newline, we must have found the end of the buffer if (i < 0) { i = buffer.length; } return JSON.parse(decodeUTF8(readBinary(i))) ; } const envelopeHeader = readJson(); // eslint-disable-next-line @typescript-eslint/no-explicit-any const items = []; while (buffer.length) { const itemHeader = readJson(); const binaryLength = typeof itemHeader.length === 'number' ? itemHeader.length : undefined; items.push([itemHeader, binaryLength ? readBinary(binaryLength) : readJson()]); } return [envelopeHeader, items]; } /** * Creates envelope item for a single span */ function createSpanEnvelopeItem(spanJson) { const spanHeaders = { type: 'span', }; return [spanHeaders, spanJson]; } /** * Creates attachment envelope items */ function createAttachmentEnvelopeItem(attachment) { const buffer = typeof attachment.data === 'string' ? encodeUTF8(attachment.data) : attachment.data; return [ { type: 'attachment', length: buffer.length, filename: attachment.filename, content_type: attachment.contentType, attachment_type: attachment.attachmentType, }, buffer, ]; } const ITEM_TYPE_TO_DATA_CATEGORY_MAP = { session: 'session', sessions: 'session', attachment: 'attachment', transaction: 'transaction', event: 'error', client_report: 'internal', user_report: 'default', profile: 'profile', profile_chunk: 'profile', replay_event: 'replay', replay_recording: 'replay', check_in: 'monitor', feedback: 'feedback', span: 'span', raw_security: 'security', otel_log: 'log_item', }; /** * Maps the type of an envelope item to a data category. */ function envelopeItemTypeToDataCategory(type) { return ITEM_TYPE_TO_DATA_CATEGORY_MAP[type]; } /** Extracts the minimal SDK info from the metadata or an events */ function getSdkMetadataForEnvelopeHeader(metadataOrEvent) { if (!metadataOrEvent?.sdk) { return; } const { name, version } = metadataOrEvent.sdk; return { name, version }; } /** * Creates event envelope headers, based on event, sdk info and tunnel * Note: This function was extracted from the core package to make it available in Replay */ function createEventEnvelopeHeaders( event, sdkInfo, tunnel, dsn, ) { const dynamicSamplingContext = event.sdkProcessingMetadata?.dynamicSamplingContext; return { event_id: event.event_id , sent_at: new Date().toISOString(), ...(sdkInfo && { sdk: sdkInfo }), ...(!!tunnel && dsn && { dsn: dsnToString(dsn) }), ...(dynamicSamplingContext && { trace: dynamicSamplingContext, }), }; } export { addItemToEnvelope, createAttachmentEnvelopeItem, createEnvelope, createEventEnvelopeHeaders, createSpanEnvelopeItem, envelopeContainsItemType, envelopeItemTypeToDataCategory, forEachEnvelopeItem, getSdkMetadataForEnvelopeHeader, parseEnvelope, serializeEnvelope }; //# sourceMappingURL=envelope.js.map