diff --git a/src/lib/types/fbq.d.ts b/src/lib/types/fbq.d.ts index 01bb570..31cf35a 100644 --- a/src/lib/types/fbq.d.ts +++ b/src/lib/types/fbq.d.ts @@ -340,6 +340,11 @@ export interface FBQ { // consent and LDU (cmd: 'consent', state: 'grant' | 'revoke'): void; (cmd: 'dataProcessingOptions', options: string[], countryCode?: number, stateCode?: number): void; + + /** Prevent automatic listening to history.pushState/popstate */ + disablePushState?: boolean; + /** Allow duplicate page view events (legacy / undocumented behavior) */ + allowDuplicatePageViews?: boolean; } declare global { diff --git a/src/lib/util/meta-pixel-loader.ts b/src/lib/util/meta-pixel-loader.ts index f9c3384..c609585 100644 --- a/src/lib/util/meta-pixel-loader.ts +++ b/src/lib/util/meta-pixel-loader.ts @@ -9,22 +9,48 @@ type QueuedFBQ = ((...args: unknown[]) => void) & { loaded?: boolean; version?: string; push?: unknown; + disablePushState?: boolean; + allowDuplicatePageViews?: boolean; }; /** * Loads the Meta Pixel script and configures the `fbq` function to queue * commands until the script is fully loaded. You may optionally await the * returned Promise to ensure the script has loaded before proceeding. + * + * Options: + * - `disablePushState` (default: true) — when true, sets + * `window.fbq.disablePushState = true` before the pixel script loads so the + * pixel does not auto-listen to `history.pushState`/`popstate` (recommended + * for SPA frameworks like Svelte). + * - `allowDuplicatePageViews` (default: false) — when true, sets + * `window.fbq.allowDuplicatePageViews = true` on the stub. */ -export const loadMetaPixel = (): Promise => { +export const loadMetaPixel = (opts?: { + disablePushState?: boolean; + allowDuplicatePageViews?: boolean; +}): Promise => { // Make sure we're using the browser if (!browser || !window) { return Promise.reject(new Error(`Not in browser, can't access window`)); } + // Default behavior: disable pushState handling since Svelte apps manage + // navigation themselves and Meta's auto-patching of history APIs can + // cause duplicate/incorrect pageview events. Consumers can pass + // `opts.disablePushState = false` to opt out. + const disablePushState = opts?.disablePushState ?? true; + const allowDuplicatePageViews = opts?.allowDuplicatePageViews ?? false; + // If fbq is already defined, resolve immediately const existing = window.fbq as QueuedFBQ | undefined; if (existing) { + // If the existing stub is present but hasn't set these flags yet, set + // them now so the loaded library (if it inspects them) sees intended + // behavior. Setting these is a no-op if initialization already + // completed. + if (disablePushState) existing.disablePushState = true; + if (allowDuplicatePageViews) existing.allowDuplicatePageViews = true; const existingScript = getExistingScript(); if (existingScript) { return new Promise((resolve, reject) => { @@ -53,6 +79,9 @@ export const loadMetaPixel = (): Promise => { q.push = q; q.loaded = true; q.version = '2.0'; + // set control flags on the stub before the meta script runs + if (disablePushState) q.disablePushState = true; + if (allowDuplicatePageViews) q.allowDuplicatePageViews = true; window.fbq = q; window._fbq = q;