From 23f4b29a86d71dfd10fc9d88a0cbf10bfe99a15f Mon Sep 17 00:00:00 2001 From: Tim Fish Date: Sat, 30 Apr 2022 23:23:27 +0100 Subject: [PATCH] Much improved --- packages/browser/jest.config.js | 2 +- packages/browser/jest.env.js | 15 +++ packages/browser/src/client.ts | 4 +- packages/core/src/baseclient.ts | 20 ++-- packages/core/src/envelope.ts | 4 +- packages/core/src/transports/base.ts | 2 +- packages/hub/src/scope.ts | 5 +- packages/node/src/client.ts | 17 ++-- packages/node/src/transports/http.ts | 3 + packages/node/test/index.test.ts | 6 +- packages/node/test/transports/http.test.ts | 3 +- packages/node/test/transports/https.test.ts | 3 +- packages/types/src/attachment.ts | 6 +- packages/types/src/index.ts | 2 +- packages/types/src/scope.ts | 7 +- packages/types/src/transport.ts | 1 + packages/utils/src/envelope.ts | 104 +++++++++----------- packages/utils/test/clientreport.test.ts | 23 +++-- packages/utils/test/envelope.test.ts | 15 +-- packages/utils/test/testutils.ts | 18 ++-- 20 files changed, 137 insertions(+), 123 deletions(-) create mode 100644 packages/browser/jest.env.js diff --git a/packages/browser/jest.config.js b/packages/browser/jest.config.js index f9cd8056a454..264feb13e33a 100644 --- a/packages/browser/jest.config.js +++ b/packages/browser/jest.config.js @@ -2,6 +2,6 @@ const baseConfig = require('../../jest/jest.config.js'); module.exports = { ...baseConfig, - testEnvironment: 'jsdom', + testEnvironment: './jest.env.js', testMatch: ['/test/unit/**/*.test.ts'], }; diff --git a/packages/browser/jest.env.js b/packages/browser/jest.env.js new file mode 100644 index 000000000000..3a7ac5019468 --- /dev/null +++ b/packages/browser/jest.env.js @@ -0,0 +1,15 @@ +const Environment = require('jest-environment-jsdom'); + +// Looks like jsdom does not support global TextEncoder/TextDecoder +// https://github.com/jsdom/jsdom/issues/2524 + +module.exports = class CustomTestEnvironment extends Environment { + async setup() { + await super.setup(); + if (typeof this.global.TextEncoder === 'undefined') { + const { TextEncoder, TextDecoder } = require('util'); + this.global.TextEncoder = TextEncoder; + this.global.TextDecoder = TextDecoder; + } + } +}; diff --git a/packages/browser/src/client.ts b/packages/browser/src/client.ts index 7601856df186..3eaf26a73066 100644 --- a/packages/browser/src/client.ts +++ b/packages/browser/src/client.ts @@ -1,5 +1,5 @@ import { BaseClient, getEnvelopeEndpointWithUrlEncodedAuth, Scope, SDK_VERSION } from '@sentry/core'; -import { AttachmentItem, ClientOptions, Event, EventHint, Options, Severity, SeverityLevel } from '@sentry/types'; +import { Attachment, ClientOptions, Event, EventHint, Options, Severity, SeverityLevel } from '@sentry/types'; import { createClientReportEnvelope, dsnToString, getGlobalObject, logger, serializeEnvelope } from '@sentry/utils'; import { eventFromException, eventFromMessage } from './eventbuilder'; @@ -103,7 +103,7 @@ export class BrowserClient extends BaseClient { /** * @inheritDoc */ - protected _sendEvent(event: Event, attachments: AttachmentItem[]): void { + protected _sendEvent(event: Event, attachments: Attachment[]): void { const integration = this.getIntegration(Breadcrumbs); if (integration) { integration.addSentryBreadcrumb(event); diff --git a/packages/core/src/baseclient.ts b/packages/core/src/baseclient.ts index a315283cebb4..d9393c3e938e 100644 --- a/packages/core/src/baseclient.ts +++ b/packages/core/src/baseclient.ts @@ -1,7 +1,7 @@ /* eslint-disable max-lines */ import { Scope, Session } from '@sentry/hub'; import { - AttachmentItem, + Attachment, Client, ClientOptions, DataCategory, @@ -19,6 +19,7 @@ import { Transport, } from '@sentry/types'; import { + addItemToEnvelope, checkOrSetAlreadyCaught, createAttachmentEnvelopeItem, dateTimestampInSeconds, @@ -275,9 +276,14 @@ export abstract class BaseClient implements Client { /** * @inheritDoc */ - public sendEvent(event: Event, attachments?: AttachmentItem[]): void { + public sendEvent(event: Event, attachments?: Attachment[]): void { if (this._dsn) { - const env = createEventEnvelope(event, this._dsn, attachments, this._options._metadata, this._options.tunnel); + const env = createEventEnvelope(event, this._dsn, this._options._metadata, this._options.tunnel); + + for (const attachment of attachments || []) { + addItemToEnvelope(env, createAttachmentEnvelopeItem(attachment)); + } + this._sendEnvelope(env); } } @@ -543,7 +549,7 @@ export abstract class BaseClient implements Client { * @param event The Sentry event to send */ // TODO(v7): refactor: get rid of method? - protected _sendEvent(event: Event, attachments?: AttachmentItem[]): void { + protected _sendEvent(event: Event, attachments?: Attachment[]): void { this.sendEvent(event, attachments); } @@ -662,10 +668,10 @@ export abstract class BaseClient implements Client { } /** - * Loads attachment items from scope + * Loads attachments from scope */ - protected _attachmentsFromScope(scope: Scope | undefined): AttachmentItem[] { - return scope?.getAttachments()?.map(a => createAttachmentEnvelopeItem(a)) || []; + protected _attachmentsFromScope(scope: Scope | undefined): Attachment[] { + return scope?.getAttachments() || []; } /** diff --git a/packages/core/src/envelope.ts b/packages/core/src/envelope.ts index 4378809b2ad9..2f69884e8bc3 100644 --- a/packages/core/src/envelope.ts +++ b/packages/core/src/envelope.ts @@ -1,5 +1,4 @@ import { - AttachmentItem, DsnComponents, Event, EventEnvelope, @@ -64,7 +63,6 @@ export function createSessionEnvelope( export function createEventEnvelope( event: Event, dsn: DsnComponents, - attachments: AttachmentItem[] = [], metadata?: SdkMetadata, tunnel?: string, ): EventEnvelope { @@ -116,5 +114,5 @@ export function createEventEnvelope( }, event, ]; - return createEnvelope(envelopeHeaders, [eventItem, ...attachments]); + return createEnvelope(envelopeHeaders, [eventItem]); } diff --git a/packages/core/src/transports/base.ts b/packages/core/src/transports/base.ts index 4e6c04e8c516..410705fdf497 100644 --- a/packages/core/src/transports/base.ts +++ b/packages/core/src/transports/base.ts @@ -69,7 +69,7 @@ export function createTransport( }; const requestTask = (): PromiseLike => - makeRequest({ body: serializeEnvelope(filteredEnvelope) }).then( + makeRequest({ body: (options.serializeEnvelope || serializeEnvelope)(filteredEnvelope) }).then( ({ headers }): void => { if (headers) { rateLimits = updateRateLimits(rateLimits, headers); diff --git a/packages/hub/src/scope.ts b/packages/hub/src/scope.ts index 36f56a9d6d8b..28187671a054 100644 --- a/packages/hub/src/scope.ts +++ b/packages/hub/src/scope.ts @@ -1,7 +1,6 @@ /* eslint-disable max-lines */ import { Attachment, - AttachmentOptions, Breadcrumb, CaptureContext, Context, @@ -408,8 +407,8 @@ export class Scope implements ScopeInterface { /** * @inheritDoc */ - public addAttachment(pathOrData: string | Uint8Array, options?: AttachmentOptions): this { - this._attachments.push([pathOrData, options]); + public addAttachment(attachment: Attachment): this { + this._attachments.push(attachment); return this; } diff --git a/packages/node/src/client.ts b/packages/node/src/client.ts index 5ddafe8efdfd..a7b837c1e9ac 100644 --- a/packages/node/src/client.ts +++ b/packages/node/src/client.ts @@ -1,7 +1,7 @@ import { BaseClient, Scope, SDK_VERSION } from '@sentry/core'; import { SessionFlusher } from '@sentry/hub'; -import { AttachmentItem, Event, EventHint, Severity, SeverityLevel } from '@sentry/types'; -import { basename, createAttachmentEnvelopeItem, logger, resolvedSyncPromise } from '@sentry/utils'; +import { Attachment, Event, EventHint, Severity, SeverityLevel } from '@sentry/types'; +import { basename, logger, resolvedSyncPromise } from '@sentry/utils'; import { existsSync, readFileSync } from 'fs'; import { eventFromMessage, eventFromUnknownInput } from './eventbuilder'; @@ -155,18 +155,15 @@ export class NodeClient extends BaseClient { /** * @inheritDoc */ - protected _attachmentsFromScope(scope: Scope | undefined): AttachmentItem[] { + protected _attachmentsFromScope(scope: Scope | undefined): Attachment[] { return ( scope?.getAttachments()?.map(attachment => { - let [pathOrData, options] = attachment; - - if (typeof pathOrData === 'string' && existsSync(pathOrData)) { - options = options || {}; - options.filename = basename(pathOrData); - pathOrData = readFileSync(pathOrData); + if (attachment.path && existsSync(attachment.path)) { + attachment.filename = basename(attachment.path); + attachment.data = readFileSync(attachment.path); } - return createAttachmentEnvelopeItem([pathOrData, options]); + return attachment; }) || [] ); } diff --git a/packages/node/src/transports/http.ts b/packages/node/src/transports/http.ts index dbd51448e0e0..9e141cc0e10f 100644 --- a/packages/node/src/transports/http.ts +++ b/packages/node/src/transports/http.ts @@ -6,6 +6,7 @@ import { TransportRequest, TransportRequestExecutor, } from '@sentry/types'; +import { serializeEnvelope } from '@sentry/utils'; import * as http from 'http'; import * as https from 'https'; import { Readable, Writable } from 'stream'; @@ -45,6 +46,8 @@ function streamFromBody(body: Uint8Array | string): Readable { * Creates a Transport that uses native the native 'http' and 'https' modules to send events to Sentry. */ export function makeNodeTransport(options: NodeTransportOptions): Transport { + options.serializeEnvelope = s => serializeEnvelope(s, () => new TextEncoder()); + const urlSegments = new URL(options.url); const isHttps = urlSegments.protocol === 'https:'; diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index 1caed705577e..edef1b70fc19 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -1,6 +1,6 @@ import { initAndBind, SDK_VERSION } from '@sentry/core'; import { getMainCarrier } from '@sentry/hub'; -import { AttachmentItem, Integration } from '@sentry/types'; +import { Attachment, Integration } from '@sentry/types'; import { createStackParser } from '@sentry/utils'; import * as domain from 'domain'; @@ -79,7 +79,7 @@ describe('SentryNode', () => { }); describe('breadcrumbs', () => { - let s: jest.SpyInstance; + let s: jest.SpyInstance; beforeEach(() => { s = jest.spyOn(NodeClient.prototype, 'sendEvent').mockImplementation(async () => Promise.resolve({ code: 200 })); @@ -110,7 +110,7 @@ describe('SentryNode', () => { }); describe('capture', () => { - let s: jest.SpyInstance; + let s: jest.SpyInstance; beforeEach(() => { s = jest.spyOn(NodeClient.prototype, 'sendEvent').mockImplementation(async () => Promise.resolve({ code: 200 })); diff --git a/packages/node/test/transports/http.test.ts b/packages/node/test/transports/http.test.ts index 013468c3eb5d..b9ed28585b1c 100644 --- a/packages/node/test/transports/http.test.ts +++ b/packages/node/test/transports/http.test.ts @@ -2,6 +2,7 @@ import { createTransport } from '@sentry/core'; import { EventEnvelope, EventItem } from '@sentry/types'; import { createEnvelope, serializeEnvelope } from '@sentry/utils'; import * as http from 'http'; +import { TextEncoder } from 'util'; import { makeNodeTransport } from '../../src/transports'; @@ -66,7 +67,7 @@ const EVENT_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4b [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem, ]); -const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE); +const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE, () => new TextEncoder()); const defaultOptions = { url: TEST_SERVER_URL, diff --git a/packages/node/test/transports/https.test.ts b/packages/node/test/transports/https.test.ts index 118a9a70dc88..8edec23145b2 100644 --- a/packages/node/test/transports/https.test.ts +++ b/packages/node/test/transports/https.test.ts @@ -3,6 +3,7 @@ import { EventEnvelope, EventItem } from '@sentry/types'; import { createEnvelope, serializeEnvelope } from '@sentry/utils'; import * as http from 'http'; import * as https from 'https'; +import { TextEncoder } from 'util'; import { makeNodeTransport } from '../../src/transports'; import { HTTPModule, HTTPModuleRequestIncomingMessage } from '../../src/transports/http-module'; @@ -69,7 +70,7 @@ const EVENT_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4b [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }] as EventItem, ]); -const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE); +const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE, () => new TextEncoder()); const unsafeHttpsModule: HTTPModule = { request: jest diff --git a/packages/types/src/attachment.ts b/packages/types/src/attachment.ts index f4e0d3dec666..3f6cb9089182 100644 --- a/packages/types/src/attachment.ts +++ b/packages/types/src/attachment.ts @@ -1,7 +1,7 @@ -export interface AttachmentOptions { +export interface Attachment { + path?: string; + data?: string | Uint8Array; filename?: string; contentType?: string; attachmentType?: string; } - -export type Attachment = [string | Uint8Array, AttachmentOptions | undefined]; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 3ee9892f6496..8e4a3e533253 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,4 +1,4 @@ -export type { Attachment, AttachmentOptions } from './attachment'; +export type { Attachment } from './attachment'; export type { Breadcrumb, BreadcrumbHint } from './breadcrumb'; export type { Client } from './client'; export type { ClientReport, Outcome, EventDropReason } from './clientreport'; diff --git a/packages/types/src/scope.ts b/packages/types/src/scope.ts index f3b990dd591c..a2a14ffd4664 100644 --- a/packages/types/src/scope.ts +++ b/packages/types/src/scope.ts @@ -1,4 +1,4 @@ -import { Attachment, AttachmentOptions } from './attachment'; +import { Attachment } from './attachment'; import { Breadcrumb } from './breadcrumb'; import { Context, Contexts } from './context'; import { EventProcessor } from './eventprocessor'; @@ -162,10 +162,9 @@ export interface Scope { /** * Adds an attachment to the scope - * @param pathOrData A Uint8Array containing the attachment bytes - * @param options Attachment options + * @param attachment Attachment options */ - addAttachment(pathOrData: string | Uint8Array, options?: AttachmentOptions): this; + addAttachment(attachment: Attachment): this; /** * Returns an array of attachments on the scope diff --git a/packages/types/src/transport.ts b/packages/types/src/transport.ts index dd0133083253..d2e6d599d28d 100644 --- a/packages/types/src/transport.ts +++ b/packages/types/src/transport.ts @@ -17,6 +17,7 @@ export type TransportMakeRequestResponse = { export interface InternalBaseTransportOptions { bufferSize?: number; recordDroppedEvent: (reason: EventDropReason, dataCategory: DataCategory) => void; + serializeEnvelope?: (env: Envelope) => string | Uint8Array; } export interface BaseTransportOptions extends InternalBaseTransportOptions { diff --git a/packages/utils/src/envelope.ts b/packages/utils/src/envelope.ts index 9b07a06b5ea1..396302bdbdbf 100644 --- a/packages/utils/src/envelope.ts +++ b/packages/utils/src/envelope.ts @@ -1,7 +1,5 @@ import { Attachment, AttachmentItem, DataCategory, Envelope, EnvelopeItem, EnvelopeItemType } from '@sentry/types'; -import { isPrimitive } from './is'; - /** * Creates an envelope. * Make sure to always explicitly provide the generic to this function @@ -36,61 +34,57 @@ export function forEachEnvelopeItem( }); } -/** - * Serializes an envelope. - */ -export function serializeEnvelope(envelope: Envelope): string | Uint8Array { - const [, items] = envelope; - - // Have to cast items to any here since Envelope is a union type - // Fixed in Typescript 4.2 - // TODO: Remove any[] cast when we upgrade to TS 4.2 - // https://github.com/microsoft/TypeScript/issues/36390 - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const hasBinaryAttachment = (items as any[]).some( - (item: typeof items[number]) => item[0].type === 'attachment' && item[1] instanceof Uint8Array, - ); - - return hasBinaryAttachment ? serializeBinaryEnvelope(envelope) : serializeStringEnvelope(envelope); -} +// Cached UTF8 string encoder +let encoder: TextEncoder | undefined; -/** - * Serializes an envelope to a string. - */ -export function serializeStringEnvelope(envelope: Envelope): string { - const [headers, items] = envelope; - const serializedHeaders = JSON.stringify(headers); - - // TODO: Remove any[] cast when we upgrade to TS 4.2 - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (items as any[]).reduce((acc, item: typeof items[number]) => { - const [itemHeaders, payload] = item; - // We do not serialize payloads that are primitives - const serializedPayload = isPrimitive(payload) ? String(payload) : JSON.stringify(payload); - return `${acc}\n${JSON.stringify(itemHeaders)}\n${serializedPayload}`; - }, serializedHeaders); +function getCachedEncoder(): TextEncoder { + if (!encoder) { + encoder = new TextEncoder(); + } + + return encoder; } -function serializeBinaryEnvelope(envelope: Envelope): Uint8Array { - const encoder = new TextEncoder(); - const [headers, items] = envelope; - const serializedHeaders = JSON.stringify(headers); +// Combination of global TextEncoder and Node require('util').TextEncoder +interface TextEncoderInternal extends TextEncoderCommon { + encode(input?: string): Uint8Array; +} - const chunks = [encoder.encode(serializedHeaders)]; +/** + * Serializes an envelope. + */ +export function serializeEnvelope( + envelope: Envelope, + textEncoderOverride?: () => TextEncoderInternal, +): string | Uint8Array { + const textEncoder = textEncoderOverride || getCachedEncoder; + + const [envHeaders, items] = envelope; + + // Initially we construct our envelope as a string and only convert to binary if we encounter binary data + let parts: string | Uint8Array[] = JSON.stringify(envHeaders); + + function append(next: string | Uint8Array): void { + if (typeof parts === 'string') { + if (typeof next === 'string') { + parts += next; + } else { + parts = [textEncoder().encode(parts), next]; + } + } else { + parts.push(typeof next === 'string' ? textEncoder().encode(next) : next); + } + } for (const item of items) { const [itemHeaders, payload] = item as typeof items[number]; - chunks.push(encoder.encode(`\n${JSON.stringify(itemHeaders)}\n`)); - if (typeof payload === 'string') { - chunks.push(encoder.encode(payload)); - } else if (payload instanceof Uint8Array) { - chunks.push(payload); - } else { - chunks.push(encoder.encode(JSON.stringify(payload))); - } + append(`\n${JSON.stringify(itemHeaders)}\n`); + append(typeof payload === 'string' || payload instanceof Uint8Array ? payload : JSON.stringify(payload)); } - return concatBuffers(chunks); + append('\n'); + + return typeof parts === 'string' ? parts : concatBuffers(parts); } function concatBuffers(buffers: Uint8Array[]): Uint8Array { @@ -110,19 +104,17 @@ function concatBuffers(buffers: Uint8Array[]): Uint8Array { * Creates attachment envelope items */ export function createAttachmentEnvelopeItem(attachment: Attachment): AttachmentItem { - const [pathOrData, options] = attachment; - - const buffer = typeof pathOrData === 'string' ? new TextEncoder().encode(pathOrData) : pathOrData; + const buffer = typeof attachment.data === 'string' ? new TextEncoder().encode(attachment.data) : attachment.data; return [ { type: 'attachment', - length: buffer.length, - filename: options?.filename || 'No filename', - content_type: options?.contentType, - attachment_type: options?.attachmentType, + length: buffer?.length || 0, + filename: attachment?.filename || 'No filename', + content_type: attachment?.contentType, + attachment_type: attachment?.attachmentType, }, - buffer, + buffer || new Uint8Array(0), ]; } diff --git a/packages/utils/test/clientreport.test.ts b/packages/utils/test/clientreport.test.ts index f2abca98e798..f52c51d6f578 100644 --- a/packages/utils/test/clientreport.test.ts +++ b/packages/utils/test/clientreport.test.ts @@ -2,6 +2,7 @@ import { ClientReport } from '@sentry/types'; import { createClientReportEnvelope } from '../src/clientreport'; import { serializeEnvelope } from '../src/envelope'; +import { parseEnvelope } from './testutils'; const DEFAULT_DISCARDED_EVENTS: ClientReport['discarded_events'] = [ { @@ -41,11 +42,21 @@ describe('createClientReportEnvelope', () => { it('serializes an envelope', () => { const env = createClientReportEnvelope(DEFAULT_DISCARDED_EVENTS, MOCK_DSN, 123456); - const serializedEnv = serializeEnvelope(env); - expect(serializedEnv).toMatchInlineSnapshot(` - "{\\"dsn\\":\\"https://public@example.com/1\\"} - {\\"type\\":\\"client_report\\"} - {\\"timestamp\\":123456,\\"discarded_events\\":[{\\"reason\\":\\"before_send\\",\\"category\\":\\"error\\",\\"quantity\\":30},{\\"reason\\":\\"network_error\\",\\"category\\":\\"transaction\\",\\"quantity\\":23}]}" - `); + + const [headers, items] = parseEnvelope(serializeEnvelope(env)); + + expect(headers).toEqual({ dsn: 'https://public@example.com/1' }); + expect(items).toEqual([ + [ + { type: 'client_report' }, + { + timestamp: 123456, + discarded_events: [ + { reason: 'before_send', category: 'error', quantity: 30 }, + { reason: 'network_error', category: 'transaction', quantity: 23 }, + ], + }, + ], + ]); }); }); diff --git a/packages/utils/test/envelope.test.ts b/packages/utils/test/envelope.test.ts index 32cb88aefd71..b105e4111aa5 100644 --- a/packages/utils/test/envelope.test.ts +++ b/packages/utils/test/envelope.test.ts @@ -1,12 +1,6 @@ import { EventEnvelope } from '@sentry/types'; -import { - addItemToEnvelope, - createEnvelope, - forEachEnvelopeItem, - serializeStringEnvelope, - serializeEnvelope, -} from '../src/envelope'; +import { addItemToEnvelope, createEnvelope, forEachEnvelopeItem, serializeEnvelope } from '../src/envelope'; import { parseEnvelope } from './testutils'; describe('envelope', () => { @@ -23,12 +17,11 @@ describe('envelope', () => { }); }); - describe('serializeStringEnvelope()', () => { + describe('serializeEnvelope()', () => { it('serializes an envelope', () => { const env = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, []); - expect(serializeStringEnvelope(env)).toMatchInlineSnapshot( - '"{\\"event_id\\":\\"aa3ff046696b4bc6b609ce6d28fde9e2\\",\\"sent_at\\":\\"123\\"}"', - ); + const [headers] = parseEnvelope(serializeEnvelope(env)); + expect(headers).toEqual({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }); }); }); diff --git a/packages/utils/test/testutils.ts b/packages/utils/test/testutils.ts index c87aa0384b53..b94fbd854892 100644 --- a/packages/utils/test/testutils.ts +++ b/packages/utils/test/testutils.ts @@ -1,4 +1,4 @@ -import { Envelope, BaseEnvelopeHeaders, BaseEnvelopeItemHeaders } from '@sentry/types'; +import { BaseEnvelopeHeaders, BaseEnvelopeItemHeaders, Envelope } from '@sentry/types'; export const testOnlyIfNodeVersionAtLeast = (minVersion: number): jest.It => { const currentNodeVersion = process.env.NODE_VERSION; @@ -18,32 +18,30 @@ export const testOnlyIfNodeVersionAtLeast = (minVersion: number): jest.It => { * A naive binary envelope parser */ export function parseEnvelope(env: string | Uint8Array): Envelope { - if (typeof env === 'string') { - env = new TextEncoder().encode(env); - } + let buf = typeof env === 'string' ? new TextEncoder().encode(env) : env; let envelopeHeaders: BaseEnvelopeHeaders | undefined; let lastItemHeader: BaseEnvelopeItemHeaders | undefined; const items: [any, any][] = []; let binaryLength = 0; - while (env.length) { + while (buf.length) { // Next length is either the binary length from the previous header // or the next newline character - let i = binaryLength || env.indexOf(0xa); + let i = binaryLength || buf.indexOf(0xa); // If no newline was found, assume this is the last block if (i < 0) { - i = env.length; + i = buf.length; } // If we read out a length in the previous header, assume binary if (binaryLength > 0) { - const bin = env.slice(0, binaryLength); + const bin = buf.slice(0, binaryLength); binaryLength = 0; items.push([lastItemHeader, bin]); } else { - const json = JSON.parse(new TextDecoder().decode(env.slice(0, i + 1))); + const json = JSON.parse(new TextDecoder().decode(buf.slice(0, i + 1))); if (typeof json.length === 'number') { binaryLength = json.length; @@ -63,7 +61,7 @@ export function parseEnvelope(env: string | Uint8Array): Envelope { } // Replace the buffer with the previous block and newline removed - env = env.slice(i + 1); + buf = buf.slice(i + 1); } return [envelopeHeaders as BaseEnvelopeHeaders, items];