From fce2141462fde46ace311daf45585a50e38b9b97 Mon Sep 17 00:00:00 2001 From: Robbie Date: Sat, 30 Nov 2024 09:34:08 +0000 Subject: [PATCH] fix: Don't crash on bigints (#1573) --- src/__tests__/request.test.ts | 15 +++++++++++++++ src/request.ts | 17 +++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/__tests__/request.test.ts b/src/__tests__/request.test.ts index f5ecd72d2..6456321d0 100644 --- a/src/__tests__/request.test.ts +++ b/src/__tests__/request.test.ts @@ -343,6 +343,21 @@ describe('request', () => { 'application/x-www-form-urlencoded' ) }) + + it('converts bigint properties to string without throwing', () => { + request( + createRequest({ + url: 'https://any.posthog-instance.com/', + method: 'POST', + compression: Compression.Base64, + data: { foo: BigInt('999999999999999999999') }, + }) + ) + expect(mockedXHR.send.mock.calls[0][0]).toMatchInlineSnapshot( + `"data=eyJmb28iOiI5OTk5OTk5OTk5OTk5OTk5OTk5OTkifQ%3D%3D"` + ) + expect(mockedXHR.setRequestHeader).toHaveBeenCalledWith('Content-Type', 'application/x-www-form-urlencoded') + }) }) describe('sendBeacon', () => { diff --git a/src/request.ts b/src/request.ts index 0bef5bb6e..3d5a46bb7 100644 --- a/src/request.ts +++ b/src/request.ts @@ -41,8 +41,17 @@ export const extendURLParams = (url: string, params: Record): strin return `${baseUrl}?${newSearch}` } +export const jsonStringify = (data: any, space?: string | number): string => { + // With plain JSON.stringify, we get an exception when a property is a BigInt. This has caused problems for some users, + // see https://github.com/PostHog/posthog-js/issues/1440 + // To work around this, we convert BigInts to strings before stringifying the data. This is not ideal, as we lose + // information that this was originally a number, but given ClickHouse doesn't support BigInts, the customer + // would not be able to operate on these numerically anyway. + return JSON.stringify(data, (_, value) => (typeof value === 'bigint' ? value.toString() : value), space) +} + const encodeToDataString = (data: string | Record): string => { - return 'data=' + encodeURIComponent(typeof data === 'string' ? data : JSON.stringify(data)) + return 'data=' + encodeURIComponent(typeof data === 'string' ? data : jsonStringify(data)) } const encodePostData = ({ data, compression }: RequestOptions): EncodedBody | undefined => { @@ -51,7 +60,7 @@ const encodePostData = ({ data, compression }: RequestOptions): EncodedBody | un } if (compression === Compression.GZipJS) { - const gzipData = gzipSync(strToU8(JSON.stringify(data)), { mtime: 0 }) + const gzipData = gzipSync(strToU8(jsonStringify(data)), { mtime: 0 }) const blob = new Blob([gzipData], { type: CONTENT_TYPE_PLAIN }) return { contentType: CONTENT_TYPE_PLAIN, @@ -61,7 +70,7 @@ const encodePostData = ({ data, compression }: RequestOptions): EncodedBody | un } if (compression === Compression.Base64) { - const b64data = _base64Encode(JSON.stringify(data)) + const b64data = _base64Encode(jsonStringify(data)) const encodedBody = encodeToDataString(b64data) return { @@ -71,7 +80,7 @@ const encodePostData = ({ data, compression }: RequestOptions): EncodedBody | un } } - const jsonBody = JSON.stringify(data) + const jsonBody = jsonStringify(data) return { contentType: CONTENT_TYPE_JSON, body: jsonBody,