diff --git a/packages/analytics-browser/src/gtm-snippet-index.ts b/packages/analytics-browser/src/gtm-snippet-index.ts index 3e47b409b..a5ad62d7d 100644 --- a/packages/analytics-browser/src/gtm-snippet-index.ts +++ b/packages/analytics-browser/src/gtm-snippet-index.ts @@ -27,12 +27,20 @@ import { runQueuedFunctions } from './utils/snippet-helper'; createInstance: createNamedInstance, }); + // Normalize proxy queues so a pre-existing global with `invoked = true` but + // missing `_q` / `_iq` doesn't crash either this bootstrap or downstream + // GTM wrapper code that reads `amplitudeGTM._iq[name]` directly. + GlobalScope.amplitudeGTM._q = GlobalScope.amplitudeGTM._q || []; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + GlobalScope.amplitudeGTM._iq = GlobalScope.amplitudeGTM._iq || {}; + if (GlobalScope.amplitudeGTM.invoked) { const queue = GlobalScope.amplitudeGTM._q; GlobalScope.amplitudeGTM._q = []; runQueuedFunctions(amplitudeGTM, queue); - const instanceNames = Object.keys(GlobalScope.amplitudeGTM._iq) || []; + const instanceNames = Object.keys(GlobalScope.amplitudeGTM._iq); for (let i = 0; i < instanceNames.length; i++) { const instanceName = instanceNames[i]; const instance = Object.assign(GlobalScope.amplitudeGTM._iq[instanceName], createNamedInstance(instanceName)); diff --git a/packages/analytics-browser/src/snippet-index.ts b/packages/analytics-browser/src/snippet-index.ts index d85b2481d..58c3b90bf 100644 --- a/packages/analytics-browser/src/snippet-index.ts +++ b/packages/analytics-browser/src/snippet-index.ts @@ -44,12 +44,20 @@ function resolveCurrentScriptUrl(): string | undefined { createInstance: createNamedInstance, }); + // Normalize proxy queues so a pre-existing global with `invoked = true` but + // missing `_q` / `_iq` doesn't crash either this bootstrap or downstream + // code (e.g. GTM wrappers that read `amplitude._iq[name]` directly). + GlobalScope.amplitude._q = GlobalScope.amplitude._q || []; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + GlobalScope.amplitude._iq = GlobalScope.amplitude._iq || {}; + if (GlobalScope.amplitude.invoked) { const queue = GlobalScope.amplitude._q; GlobalScope.amplitude._q = []; runQueuedFunctions(amplitude, queue); - const instanceNames = Object.keys(GlobalScope.amplitude._iq) || []; + const instanceNames = Object.keys(GlobalScope.amplitude._iq); for (let i = 0; i < instanceNames.length; i++) { const instanceName = instanceNames[i]; const instance = Object.assign(GlobalScope.amplitude._iq[instanceName], createNamedInstance(instanceName)); diff --git a/packages/analytics-browser/src/utils/snippet-helper.ts b/packages/analytics-browser/src/utils/snippet-helper.ts index 9a0b67363..ed3fd4920 100644 --- a/packages/analytics-browser/src/utils/snippet-helper.ts +++ b/packages/analytics-browser/src/utils/snippet-helper.ts @@ -17,7 +17,7 @@ interface InstanceProxy { * Applies the proxied functions on the proxied amplitude snippet to an instance of the real object. * @ignore */ -export const runQueuedFunctions = (instance: object, queue: QueueProxy) => { +export const runQueuedFunctions = (instance: object, queue: QueueProxy | undefined) => { convertProxyObjectToRealObject(instance, queue); }; @@ -25,7 +25,10 @@ export const runQueuedFunctions = (instance: object, queue: QueueProxy) => { * Applies the proxied functions on the proxied object to an instance of the real object. * Used to convert proxied Identify and Revenue objects. */ -export const convertProxyObjectToRealObject = (instance: T, queue: QueueProxy): T => { +export const convertProxyObjectToRealObject = (instance: T, queue: QueueProxy | undefined): T => { + if (!queue) { + return instance; + } for (let i = 0; i < queue.length; i++) { const { name, args, resolve } = queue[i]; const fn = instance && instance[name as keyof T]; diff --git a/packages/analytics-browser/test/utils/snippet-helper.test.ts b/packages/analytics-browser/test/utils/snippet-helper.test.ts index c6f6cbe0b..e435fa102 100644 --- a/packages/analytics-browser/test/utils/snippet-helper.test.ts +++ b/packages/analytics-browser/test/utils/snippet-helper.test.ts @@ -47,5 +47,11 @@ describe('snippet-helper', () => { expect(SnippetHelper.convertProxyObjectToRealObject(instance, queue)).toBe(instance); expect(resolve).toHaveBeenCalledTimes(1); }); + + test('should return instance when queue is undefined', () => { + const instance = { init: jest.fn() }; + expect(SnippetHelper.convertProxyObjectToRealObject(instance, undefined)).toBe(instance); + expect(instance.init).not.toHaveBeenCalled(); + }); }); });