diff --git a/packages/utils/src/envelope.ts b/packages/utils/src/envelope.ts index e165b33f78eb..461bf6fcb47b 100644 --- a/packages/utils/src/envelope.ts +++ b/packages/utils/src/envelope.ts @@ -16,6 +16,7 @@ import type { import { dsnToString } from './dsn'; import { normalize } from './normalize'; import { dropUndefinedKeys } from './object'; +import { GLOBAL_OBJ } from './worldwide'; /** * Creates an envelope. @@ -71,14 +72,18 @@ export function envelopeContainsItemType(envelope: Envelope, types: EnvelopeItem * Encode a string to UTF8 array. */ function encodeUTF8(input: string): Uint8Array { - return new TextEncoder().encode(input); + return GLOBAL_OBJ.__SENTRY__ && GLOBAL_OBJ.__SENTRY__.encodePolyfill + ? GLOBAL_OBJ.__SENTRY__.encodePolyfill(input) + : new TextEncoder().encode(input); } /** * Decode a UTF8 array to string. */ function decodeUTF8(input: Uint8Array): string { - return new TextDecoder().decode(input); + return GLOBAL_OBJ.__SENTRY__ && GLOBAL_OBJ.__SENTRY__.decodePolyfill + ? GLOBAL_OBJ.__SENTRY__.decodePolyfill(input) + : new TextDecoder().decode(input); } /** diff --git a/packages/utils/src/worldwide.ts b/packages/utils/src/worldwide.ts index c1da12f85135..6ac98ac0f6ae 100644 --- a/packages/utils/src/worldwide.ts +++ b/packages/utils/src/worldwide.ts @@ -57,6 +57,10 @@ export interface InternalGlobal { defaultCurrentScope: Scope | undefined; defaultIsolationScope: Scope | undefined; globalMetricsAggregators: WeakMap | undefined; + /** Overwrites TextEncoder used in `@sentry/utils`, need for `react-native@0.73` and older */ + encodePolyfill?: (input: string) => Uint8Array; + /** Overwrites TextDecoder used in `@sentry/utils`, need for `react-native@0.73` and older */ + decodePolyfill?: (input: Uint8Array) => string; }; /** * Raw module metadata that is injected by bundler plugins. diff --git a/packages/utils/test/envelope.test.ts b/packages/utils/test/envelope.test.ts index 8c4d1eccc9ac..efb3e873a0a6 100644 --- a/packages/utils/test/envelope.test.ts +++ b/packages/utils/test/envelope.test.ts @@ -7,6 +7,8 @@ import { parseEnvelope, serializeEnvelope, } from '../src/envelope'; +import type { InternalGlobal } from '../src/worldwide'; +import { GLOBAL_OBJ } from '../src/worldwide'; describe('envelope', () => { describe('createEnvelope()', () => { @@ -23,6 +25,10 @@ describe('envelope', () => { }); describe('serializeEnvelope and parseEnvelope', () => { + afterEach(() => { + delete (GLOBAL_OBJ as Partial).__SENTRY__; + }); + it('serializes an envelope', () => { const env = createEnvelope({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, []); const serializedEnvelope = serializeEnvelope(env); @@ -32,7 +38,29 @@ describe('envelope', () => { expect(headers).toEqual({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }); }); - it('serializes an envelope with attachments', () => { + test.each([ + { + name: 'with TextEncoder/Decoder polyfill', + before: () => { + GLOBAL_OBJ.__SENTRY__ = {} as InternalGlobal['__SENTRY__']; + GLOBAL_OBJ.__SENTRY__.encodePolyfill = jest.fn((input: string) => + new TextEncoder().encode(input), + ); + GLOBAL_OBJ.__SENTRY__.decodePolyfill = jest.fn((input: Uint8Array) => + new TextDecoder().decode(input), + ); + }, + after: () => { + expect(GLOBAL_OBJ.__SENTRY__.encodePolyfill).toHaveBeenCalled(); + expect(GLOBAL_OBJ.__SENTRY__.decodePolyfill).toHaveBeenCalled(); + }, + }, + { + name: 'with default TextEncoder/Decoder', + }, + ])('serializes an envelope with attachments $name', ({ before, after }) => { + before?.(); + const items: EventEnvelope[1] = [ [{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }], [{ type: 'attachment', filename: 'bar.txt', length: 6 }, Uint8Array.from([1, 2, 3, 4, 5, 6])], @@ -44,8 +72,6 @@ describe('envelope', () => { items, ); - expect.assertions(6); - const serializedEnvelope = serializeEnvelope(env); expect(serializedEnvelope).toBeInstanceOf(Uint8Array); @@ -61,6 +87,8 @@ describe('envelope', () => { { type: 'attachment', filename: 'foo.txt', length: 6 }, Uint8Array.from([7, 8, 9, 10, 11, 12]), ]); + + after?.(); }); it("doesn't throw when being passed a an envelope that contains a circular item payload", () => {