diff --git a/packages/cookie-banner/CHANGELOG.md b/packages/cookie-banner/CHANGELOG.md index bc671994b..241fa6bbf 100644 --- a/packages/cookie-banner/CHANGELOG.md +++ b/packages/cookie-banner/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to the `@probo/cookie-banner` SDK will be documented in this ## Unreleased +### Fixed + +- Grant all Google Consent Mode types when the banner config is unavailable (discovery mode), so GTM-managed tags can fire and be detected instead of staying blocked by the eager deny-all bootstrap + ## [0.10.0] - 2026-06-19 ### Changed diff --git a/packages/cookie-banner/src/client.ts b/packages/cookie-banner/src/client.ts index 10a6abdb0..db496818d 100644 --- a/packages/cookie-banner/src/client.ts +++ b/packages/cookie-banner/src/client.ts @@ -98,6 +98,12 @@ export class CookieBannerClient { try { config = await fetchJSON(configUrl); } catch { + // Discovery mode: no published banner config, but detectors still run + // so admins can inventory trackers. Grant GCM so GTM-managed tags can + // fire; otherwise bootstrap's deny-all would hide them from discovery. + for (const integration of this.integrations) { + integration.grantAll(); + } this.startDetector(); if (this.observer) { this.observer.disconnect(); diff --git a/packages/cookie-banner/src/integrations/gcm.ts b/packages/cookie-banner/src/integrations/gcm.ts index 3126ae170..5f73f1926 100644 --- a/packages/cookie-banner/src/integrations/gcm.ts +++ b/packages/cookie-banner/src/integrations/gcm.ts @@ -45,17 +45,32 @@ export class GoogleConsentModeIntegration implements ConsentIntegration { }; } + private static readonly ALL_TYPES = [ + "ad_storage", + "ad_user_data", + "ad_personalization", + "analytics_storage", + "functionality_storage", + "personalization_storage", + "security_storage", + ] as const; + bootstrap(): void { const consentFn = this.getConsentFn(); - consentFn("consent", "default", { - ad_storage: "denied", - ad_user_data: "denied", - ad_personalization: "denied", - analytics_storage: "denied", - functionality_storage: "denied", - personalization_storage: "denied", - security_storage: "denied", - }); + const defaults: Record = {}; + for (const type of GoogleConsentModeIntegration.ALL_TYPES) { + defaults[type] = "denied"; + } + consentFn("consent", "default", defaults); + } + + grantAll(): void { + const consentFn = this.getConsentFn(); + const update: Record = {}; + for (const type of GoogleConsentModeIntegration.ALL_TYPES) { + update[type] = "granted"; + } + consentFn("consent", "update", update); } setDefaults(categories: Category[]): void { diff --git a/packages/cookie-banner/src/integrations/integration.ts b/packages/cookie-banner/src/integrations/integration.ts index 15a296ff2..6d8924ad7 100644 --- a/packages/cookie-banner/src/integrations/integration.ts +++ b/packages/cookie-banner/src/integrations/integration.ts @@ -24,6 +24,12 @@ export interface ConsentIntegration { /** Called eagerly before config fetch to deny all tracking by default. */ bootstrap(): void; + /** + * Called when the banner config is unavailable (discovery mode). + * Grants all consent types so trackers can fire and be detected. + */ + grantAll(): void; + /** Called once after config is loaded, before any consent is applied. */ setDefaults(categories: Category[]): void;