diff --git a/packages/cookie-banner/src/client.ts b/packages/cookie-banner/src/client.ts index b9754c956..38ccd9e1e 100644 --- a/packages/cookie-banner/src/client.ts +++ b/packages/cookie-banner/src/client.ts @@ -33,7 +33,7 @@ import type { Regulation, VisitorConsent, } from "./types"; -import { getOrCreateVisitorId } from "./visitor"; +import { getOrCreateVisitorId, getVisitorId } from "./visitor"; export type { BannerConfig, @@ -49,7 +49,7 @@ export type { export class CookieBannerClient { private readonly baseUrl: URL; private readonly bannerId: string; - private readonly visitorId: string; + private visitorId: string | null; private readonly lang: string; private readonly integrations: ConsentIntegration[]; @@ -68,7 +68,7 @@ export class CookieBannerClient { } this.baseUrl = new URL(base); this.bannerId = config.bannerId; - this.visitorId = getOrCreateVisitorId(config.bannerId); + this.visitorId = getVisitorId(config.bannerId); this.lang = detectLanguage(config.lang); this.integrations = createDefaultIntegrations(); } @@ -97,50 +97,52 @@ export class CookieBannerClient { } this.startDetector(config); - const cookie = getConsentCookie(); - if (cookie && cookie.bid === this.bannerId && cookie.v === config.version && cookie.vid === this.visitorId) { - this.consent = { - visitor_id: cookie.vid, - version: cookie.v, - action: cookie.action, - consent_data: cookie.data, - created_at: "", - }; - this._gpcApplied = cookie.action === "GPC"; - this.activate(cookie.data); - void flush(this.bannerId); - return; - } + if (this.visitorId) { + const cookie = getConsentCookie(); + if (cookie && cookie.bid === this.bannerId && cookie.v === config.version && cookie.vid === this.visitorId) { + this.consent = { + visitor_id: cookie.vid, + version: cookie.v, + action: cookie.action, + consent_data: cookie.data, + created_at: "", + }; + this._gpcApplied = cookie.action === "GPC"; + this.activate(cookie.data); + void flush(this.bannerId); + return; + } - const consentUrl = new URL( - `${this.bannerId}/consents/${this.visitorId}`, - this.baseUrl, - ); - const apiConsent = await fetchJSON(consentUrl).catch( - (err) => { - if (err instanceof NotFoundError) { - return null; - } - throw err; - }, - ); - - if (apiConsent && apiConsent.version === config.version) { - this.consent = apiConsent; - this._gpcApplied = apiConsent.action === "GPC"; - setConsentCookie( - { - bid: this.bannerId, - v: apiConsent.version, - vid: apiConsent.visitor_id, - action: apiConsent.action, - data: apiConsent.consent_data, - }, - config.consent_expiry_days, + const consentUrl = new URL( + `${this.bannerId}/consents/${this.visitorId}`, + this.baseUrl, ); - this.activate(apiConsent.consent_data); - } else { - this.consent = null; + const apiConsent = await fetchJSON(consentUrl).catch( + (err) => { + if (err instanceof NotFoundError) { + return null; + } + throw err; + }, + ); + + if (apiConsent && apiConsent.version === config.version) { + this.consent = apiConsent; + this._gpcApplied = apiConsent.action === "GPC"; + setConsentCookie( + { + bid: this.bannerId, + v: apiConsent.version, + vid: apiConsent.visitor_id, + action: apiConsent.action, + data: apiConsent.consent_data, + }, + config.consent_expiry_days, + ); + this.activate(apiConsent.consent_data); + } else { + this.consent = null; + } } if (!this.consent && this.gpcDetected) { @@ -225,6 +227,13 @@ export class CookieBannerClient { this.recordConsent("CUSTOMIZE", consentData); } + private ensureVisitorId(): string { + if (!this.visitorId) { + this.visitorId = getOrCreateVisitorId(this.bannerId); + } + return this.visitorId; + } + private recordConsent( action: ConsentAction, consentData: Record, @@ -232,9 +241,10 @@ export class CookieBannerClient { this._gpcApplied = action === "GPC"; const cfg = this.config; + const visitorId = this.ensureVisitorId(); this.consent = { - visitor_id: this.visitorId, + visitor_id: visitorId, version: cfg.version, action, consent_data: consentData, @@ -245,7 +255,7 @@ export class CookieBannerClient { { bid: this.bannerId, v: cfg.version, - vid: this.visitorId, + vid: visitorId, action, data: consentData, }, @@ -256,7 +266,7 @@ export class CookieBannerClient { const url = new URL(`${this.bannerId}/consents`, this.baseUrl); const body = { - visitor_id: this.visitorId, + visitor_id: visitorId, version: cfg.version, action, consent_data: consentData, diff --git a/packages/cookie-banner/src/visitor.ts b/packages/cookie-banner/src/visitor.ts index e58257d45..ddc75e2a1 100644 --- a/packages/cookie-banner/src/visitor.ts +++ b/packages/cookie-banner/src/visitor.ts @@ -14,18 +14,23 @@ import { COOKIE_NAME } from "./cookie"; -export function getOrCreateVisitorId(bannerId: string): string { +export function getVisitorId(bannerId: string): string | null { const key = `${COOKIE_NAME}:${bannerId}:vid`; try { - const stored = localStorage.getItem(key); - if (stored) { - return stored; - } + return localStorage.getItem(key); } catch { - // localStorage unavailable + return null; + } +} + +export function getOrCreateVisitorId(bannerId: string): string { + const existing = getVisitorId(bannerId); + if (existing) { + return existing; } + const key = `${COOKIE_NAME}:${bannerId}:vid`; const array = new Uint8Array(16); crypto.getRandomValues(array); const id = Array.from(array, (b) => b.toString(16).padStart(2, "0")).join("");