From 69d0af87c244e532cafd233e3638643bad95c42d Mon Sep 17 00:00:00 2001 From: Simon Kohlmeyer Date: Sun, 16 Mar 2025 19:23:40 +0100 Subject: [PATCH] fix(ga4): never let exceptions bubble to callers This avoids applications being broken by issues with tracking. A specific case in which this can happen is if navigator.sendBeacon is undefined. Closes #56 --- packages/ga4/src/index.ts | 25 ++++++++++++++----------- packages/ga4/tests/index.test.ts | 13 +++++++++++++ 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/packages/ga4/src/index.ts b/packages/ga4/src/index.ts index 9f70da1..a041c14 100644 --- a/packages/ga4/src/index.ts +++ b/packages/ga4/src/index.ts @@ -345,22 +345,25 @@ function bindEvents(trackingId: string) { function track(trackingId: string, props?: IProps); function track(props?: IProps); function track(...args: any[]) { - const [trackingId, { type, event, debug }] = getArguments(args); + try { + const [trackingId, { type, event, debug }] = getArguments(args); - if (!trackingId) { - console.error('GA4: Tracking ID is missing or undefined'); + if (!trackingId) { + console.error('GA4: Tracking ID is missing or undefined'); + return; + } - return; - } + const queryParams = getQueryParams(trackingId, { type, event, debug }); + const endpoint = window.minimalAnalytics?.analyticsEndpoint || analyticsEndpoint; - const queryParams = getQueryParams(trackingId, { type, event, debug }); - const endpoint = window.minimalAnalytics?.analyticsEndpoint || analyticsEndpoint; + navigator.sendBeacon(`${endpoint}?${queryParams}`); - navigator.sendBeacon(`${endpoint}?${queryParams}`); + bindEvents(trackingId); - bindEvents(trackingId); - - trackCalled = true; + trackCalled = true; + } catch (error) { + console.error('GA4: ', error); + } } /* ----------------------------------- diff --git a/packages/ga4/tests/index.test.ts b/packages/ga4/tests/index.test.ts index 5e0ee39..bd362dc 100644 --- a/packages/ga4/tests/index.test.ts +++ b/packages/ga4/tests/index.test.ts @@ -99,6 +99,19 @@ describe('ga4 -> track()', () => { expect(navigator.sendBeacon).not.toBeCalled(); }); + it("doesn't ppopagate exceptions", () => { + const errorLogMock = jest.spyOn(console, 'error'); + const testError = new Error('test error'); + + jest.spyOn(navigator, 'sendBeacon').mockImplementation(() => { + throw testError; + }); + + expect(() => track(trackingId)).not.toThrow(); + + expect(errorLogMock).toHaveBeenCalledWith('GA4: ', testError); + }); + it('can be called directly with a tracking ID', () => { track(trackingId);