pixel: add ensure fbc fallback

This commit is contained in:
Elijah Duffy
2025-12-18 19:22:54 -08:00
parent f2d389ee64
commit 0cd3f10da6
5 changed files with 114 additions and 7 deletions

88
src/lib/metapixel/fbc.ts Normal file
View File

@@ -0,0 +1,88 @@
import log from 'loglevel';
export type EnsureFbcOptions = {
days?: number; // cookie lifetime, default 180
domain?: string; // optional cookie domain
sameSite?: 'Lax' | 'Strict' | 'None'; // default Lax
secure?: boolean; // default inferred from location.protocol === 'https:'
pixelLoaded?: boolean; // if true, skip manual set and let Pixel handle it
};
function getParam(name: string): string | undefined {
const params = new URLSearchParams(location.search);
const v = params.get(name);
return v || undefined;
}
function getCookie(name: string): string | undefined {
return document.cookie
.split('; ')
.find((c) => c.startsWith(name + '='))
?.split('=')[1];
}
function setCookie(
name: string,
value: string,
{
days = 180,
domain,
sameSite = 'Lax',
secure
}: Pick<EnsureFbcOptions, 'days' | 'domain' | 'sameSite' | 'secure'> = {}
) {
const d = new Date();
d.setTime(d.getTime() + days * 864e5);
const parts = [
`${name}=${encodeURIComponent(value)}`,
`expires=${d.toUTCString()}`,
'path=/',
`SameSite=${sameSite}`
];
if (domain) parts.push(`domain=${domain}`);
const isSecure = secure ?? location.protocol === 'https:';
if (isSecure) parts.push('Secure');
document.cookie = parts.join('; ');
}
function isValidFbc(value: string | undefined): boolean {
if (!value) return false;
// Expect "fb.1.<unix>.<fbclid>"
if (!value.startsWith('fb.1.')) return false;
const parts = value.split('.');
return parts.length >= 4 && /^\d+$/.test(parts[2]) && parts[3].length > 0;
}
/**
* Ensure _fbc cookie exists when landing URL contains fbclid.
* Call after consent. If pixelLoaded is true, it skips manual setting.
*/
export function ensureFbc(options: EnsureFbcOptions = {}) {
try {
const { pixelLoaded = false, ...cookieOpts } = options;
if (pixelLoaded) throw new Error('Pixel loaded, skipping manual _fbc set'); // Let the Pixel set _fbc if its active
const fbclid = getParam('fbclid');
if (!fbclid) throw new Error('No fbclid param present');
const existing = getCookie('_fbc');
if (isValidFbc(existing)) throw new Error('_fbc cookie already present and valid');
const ts = Math.floor(Date.now() / 1000);
const fbc = `fb.1.${ts}.${fbclid}`;
setCookie('_fbc', fbc, cookieOpts);
log.debug('[ensureFbc] Set _fbc cookie:', fbc);
} catch (e) {
log.debug('[ensureFbc]', (e as Error).message);
}
}
/**
* Helper to read both _fbp and _fbc for your CAPI payload.
*/
export function getFbpFbc(): { fbp?: string; fbc?: string } {
const fbp = getCookie('_fbp');
const fbc = getCookie('_fbc');
return { fbp, fbc };
}