Skip to content

Commit 4e4f1da

Browse files
feat(utils): Allow text encoder/decoder polyfill from global __SENTRY__ (#11283)
The TextEncoder option was removed in #10701 because all of the newly supported platforms support TextEncoder/Decoder. Sadly that's not true for React Native, yet. TextEncoder will be included in the next release of RN with Hermes. See the PR facebook/hermes@3863a36#diff-4bf4a4ab271f254baf2097be87be98333ec69eb2d41e074b81147656c33145eb We can not drop support of all the previous RN versions in the RN SDK v6 (the next major). To avoid passing the encoder option around the SDK, I propose adding polyfill properties to the global __SENTRY__ object which RN can use.
1 parent 3ae830c commit 4e4f1da

File tree

3 files changed

+42
-5
lines changed

3 files changed

+42
-5
lines changed

packages/utils/src/envelope.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type {
1616
import { dsnToString } from './dsn';
1717
import { normalize } from './normalize';
1818
import { dropUndefinedKeys } from './object';
19+
import { GLOBAL_OBJ } from './worldwide';
1920

2021
/**
2122
* Creates an envelope.
@@ -71,14 +72,18 @@ export function envelopeContainsItemType(envelope: Envelope, types: EnvelopeItem
7172
* Encode a string to UTF8 array.
7273
*/
7374
function encodeUTF8(input: string): Uint8Array {
74-
return new TextEncoder().encode(input);
75+
return GLOBAL_OBJ.__SENTRY__ && GLOBAL_OBJ.__SENTRY__.encodePolyfill
76+
? GLOBAL_OBJ.__SENTRY__.encodePolyfill(input)
77+
: new TextEncoder().encode(input);
7578
}
7679

7780
/**
7881
* Decode a UTF8 array to string.
7982
*/
8083
function decodeUTF8(input: Uint8Array): string {
81-
return new TextDecoder().decode(input);
84+
return GLOBAL_OBJ.__SENTRY__ && GLOBAL_OBJ.__SENTRY__.decodePolyfill
85+
? GLOBAL_OBJ.__SENTRY__.decodePolyfill(input)
86+
: new TextDecoder().decode(input);
8287
}
8388

8489
/**

packages/utils/src/worldwide.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ export interface InternalGlobal {
5757
defaultCurrentScope: Scope | undefined;
5858
defaultIsolationScope: Scope | undefined;
5959
globalMetricsAggregators: WeakMap<Client, MetricsAggregator> | undefined;
60+
/** Overwrites TextEncoder used in `@sentry/utils`, need for `[email protected]` and older */
61+
encodePolyfill?: (input: string) => Uint8Array;
62+
/** Overwrites TextDecoder used in `@sentry/utils`, need for `[email protected]` and older */
63+
decodePolyfill?: (input: Uint8Array) => string;
6064
};
6165
/**
6266
* Raw module metadata that is injected by bundler plugins.

packages/utils/test/envelope.test.ts

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
parseEnvelope,
88
serializeEnvelope,
99
} from '../src/envelope';
10+
import type { InternalGlobal } from '../src/worldwide';
11+
import { GLOBAL_OBJ } from '../src/worldwide';
1012

1113
describe('envelope', () => {
1214
describe('createEnvelope()', () => {
@@ -23,6 +25,10 @@ describe('envelope', () => {
2325
});
2426

2527
describe('serializeEnvelope and parseEnvelope', () => {
28+
afterEach(() => {
29+
delete (GLOBAL_OBJ as Partial<InternalGlobal>).__SENTRY__;
30+
});
31+
2632
it('serializes an envelope', () => {
2733
const env = createEnvelope<EventEnvelope>({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' }, []);
2834
const serializedEnvelope = serializeEnvelope(env);
@@ -32,7 +38,29 @@ describe('envelope', () => {
3238
expect(headers).toEqual({ event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2', sent_at: '123' });
3339
});
3440

35-
it('serializes an envelope with attachments', () => {
41+
test.each([
42+
{
43+
name: 'with TextEncoder/Decoder polyfill',
44+
before: () => {
45+
GLOBAL_OBJ.__SENTRY__ = {} as InternalGlobal['__SENTRY__'];
46+
GLOBAL_OBJ.__SENTRY__.encodePolyfill = jest.fn<Uint8Array, [string]>((input: string) =>
47+
new TextEncoder().encode(input),
48+
);
49+
GLOBAL_OBJ.__SENTRY__.decodePolyfill = jest.fn<string, [Uint8Array]>((input: Uint8Array) =>
50+
new TextDecoder().decode(input),
51+
);
52+
},
53+
after: () => {
54+
expect(GLOBAL_OBJ.__SENTRY__.encodePolyfill).toHaveBeenCalled();
55+
expect(GLOBAL_OBJ.__SENTRY__.decodePolyfill).toHaveBeenCalled();
56+
},
57+
},
58+
{
59+
name: 'with default TextEncoder/Decoder',
60+
},
61+
])('serializes an envelope with attachments $name', ({ before, after }) => {
62+
before?.();
63+
3664
const items: EventEnvelope[1] = [
3765
[{ type: 'event' }, { event_id: 'aa3ff046696b4bc6b609ce6d28fde9e2' }],
3866
[{ type: 'attachment', filename: 'bar.txt', length: 6 }, Uint8Array.from([1, 2, 3, 4, 5, 6])],
@@ -44,8 +72,6 @@ describe('envelope', () => {
4472
items,
4573
);
4674

47-
expect.assertions(6);
48-
4975
const serializedEnvelope = serializeEnvelope(env);
5076
expect(serializedEnvelope).toBeInstanceOf(Uint8Array);
5177

@@ -61,6 +87,8 @@ describe('envelope', () => {
6187
{ type: 'attachment', filename: 'foo.txt', length: 6 },
6288
Uint8Array.from([7, 8, 9, 10, 11, 12]),
6389
]);
90+
91+
after?.();
6492
});
6593

6694
it("doesn't throw when being passed a an envelope that contains a circular item payload", () => {

0 commit comments

Comments
 (0)