diff --git a/docs/content/scripts/bing-uet.md b/docs/content/scripts/bing-uet.md index 2b7486007..da016ea10 100644 --- a/docs/content/scripts/bing-uet.md +++ b/docs/content/scripts/bing-uet.md @@ -54,3 +54,25 @@ function trackSignup() { } ``` + +### Consent Mode + +Bing UET supports [advanced consent mode](https://help.ads.microsoft.com/#apex/ads/en/60119/1-500). Use `onBeforeUetStart` to set the default consent state before the script loads. If consent is denied, UET only sends anonymous data. + +```vue + +``` diff --git a/packages/script/src/registry-types.json b/packages/script/src/registry-types.json index 01404dc17..e1692b2fc 100644 --- a/packages/script/src/registry-types.json +++ b/packages/script/src/registry-types.json @@ -6,10 +6,65 @@ "kind": "const", "code": "export const BingUetOptions = object({\n /**\n * Your Bing UET tag ID.\n * @see https://help.ads.microsoft.com/#apex/ads/en/56682/2-500\n */\n id: string(),\n /**\n * Enable automatic SPA page tracking.\n * @default true\n */\n enableAutoSpaTracking: optional(boolean()),\n})" }, + { + "name": "BingUetConsentStatus", + "kind": "type", + "code": "export type BingUetConsentStatus = 'granted' | 'denied'" + }, + { + "name": "BingUetConsentOptions", + "kind": "interface", + "code": "export interface BingUetConsentOptions {\n /**\n * Controls storage of advertising identifiers. Currently the only field UET honors.\n */\n ad_storage?: BingUetConsentStatus\n}" + }, + { + "name": "BingUetEventName", + "kind": "type", + "code": "export type BingUetEventName\n = | 'page_view'\n | 'screen_view'\n | 'login'\n | 'sign_up'\n | 'subscribe'\n | 'start_trial'\n | 'lead'\n | 'generate_lead'\n | 'submit_lead_form'\n | 'contact'\n | 'search'\n | 'view_search_results'\n | 'select_content'\n | 'share'\n | 'exception'\n | 'find_location'\n | 'book_appointment'\n | 'get_route'\n | 'view_item'\n | 'view_item_list'\n | 'view_promotion'\n | 'add_to_cart'\n | 'remove_from_cart'\n | 'add_to_wishlist'\n | 'add_payment_info'\n | 'begin_checkout'\n | 'checkout_progress'\n | 'set_checkout_option'\n | 'purchase'\n | 'refund'\n | (string & {})" + }, + { + "name": "BingUetItem", + "kind": "interface", + "code": "export interface BingUetItem {\n id?: string\n name?: string\n brand?: string\n category?: string\n variant?: string\n price?: number\n quantity?: number\n list_name?: string\n list_position?: number\n creative_name?: string\n creative_slot?: string\n location_id?: string\n [key: string]: any\n}" + }, + { + "name": "BingUetPromotion", + "kind": "interface", + "code": "export interface BingUetPromotion {\n id?: string\n name?: string\n creative_name?: string\n creative_slot?: string\n [key: string]: any\n}" + }, + { + "name": "BingUetEventParams", + "kind": "interface", + "code": "export interface BingUetEventParams {\n /** Event category (beacon shortcut). */\n ec?: string\n /** Event action (beacon shortcut). */\n ea?: string\n /** Event label (beacon shortcut). */\n el?: string\n /** Event value, numeric (beacon shortcut). */\n ev?: number\n /** Goal category / goal completion. */\n gc?: string\n /** Goal value. */\n gv?: number\n /** Long-form alias for `ec`. */\n event_category?: string\n /** Long-form alias for `ea`. */\n event_action?: string\n /** Long-form alias for `el`. */\n event_label?: string\n /** Long-form alias for `ev`. */\n event_value?: number\n /** Unique event identifier (dedup). */\n event_id?: string\n /** Variable revenue amount. */\n revenue_value?: number\n /** ISO 4217 currency code (e.g. \"USD\"). */\n currency?: string\n /** Unique order/transaction ID (dedup). */\n transaction_id?: string\n /** Ecommerce items. */\n items?: BingUetItem[]\n /** Promotions associated with the event. */\n promotions?: BingUetPromotion[]\n /** Sign-up/login method (e.g. \"Google\"). */\n method?: string\n coupon?: string\n tax?: number\n shipping?: number\n affiliation?: string\n /** Search query. */\n search_term?: string\n content_type?: string\n content_id?: string\n checkout_step?: number\n checkout_option?: string\n description?: string\n name?: string\n screen_name?: string\n /** Whether an exception is fatal. */\n fatal?: boolean\n /** Flags a new customer conversion. */\n new_customer?: boolean | number\n /** Retail vertical: product ID. */\n ecomm_prodid?: string | string[]\n /** Retail vertical: page type. */\n ecomm_pagetype?: 'home' | 'searchresults' | 'category' | 'product' | 'cart' | 'purchase' | 'other' | (string & {})\n ecomm_totalvalue?: number\n ecomm_category?: string | string[]\n ecomm_query?: string\n /** Override page path. */\n page_path?: string\n /** Override page title. */\n page_title?: string\n /** Override full page URL. */\n page_location?: string\n [key: string]: any\n}" + }, + { + "name": "BingUetEnhancedConversionsPid", + "kind": "interface", + "code": "export interface BingUetEnhancedConversionsPid {\n /** SHA-256 hashed email (lowercase, trimmed). Alias: `email`. */\n em?: string\n /** SHA-256 hashed phone (E.164 digits only). Alias: `phone_number`. */\n ph?: string\n /** Long-form alias for `em`. */\n email?: string\n /** Long-form alias for `ph`. */\n phone_number?: string\n [key: string]: any\n}" + }, + { + "name": "BingUetSetParams", + "kind": "interface", + "code": "export interface BingUetSetParams {\n /** Partner identifiers for enhanced conversions. */\n pid?: BingUetEnhancedConversionsPid\n page_path?: string\n page_title?: string\n page_location?: string\n [key: string]: any\n}" + }, + { + "name": "BingUetTcfConfig", + "kind": "interface", + "code": "export interface BingUetTcfConfig {\n /** Enable TCF v2.0 auto-consent handling (Microsoft vendorId 1126). */\n enabled?: boolean\n [key: string]: any\n}" + }, + { + "name": "BingUetConstructorOptions", + "kind": "interface", + "code": "export interface BingUetConstructorOptions {\n /** UET tag ID. */\n ti: string\n /** Alternate tag ID field (same effect as `ti`). */\n tagId?: string\n /** Auto-fire pageLoad on SPA route changes. */\n enableAutoSpaTracking?: boolean\n /** Enable TCF v2.0 auto-consent handling. */\n enableAutoConsent?: boolean\n /** Suppress automatic page_view on load. */\n disableAutoPageView?: boolean\n /** Disable the UET container entirely. */\n disableContainer?: boolean\n /** Disable writing the UET visitor ID cookie. */\n disableUetVid?: boolean\n /** Disable visibility (tab focus/blur) events. */\n disableVisibilityEvents?: boolean\n /** Defer loading the beacon. */\n deferLoad?: boolean\n /** Strip query strings from reported URLs. */\n removeQueryFromUrls?: boolean\n /** Store conversion tracking cookies. */\n storeConvTrackCookies?: boolean\n /** Cookie domain. */\n cookieDomain?: string\n /** Cookie flags (e.g. \"SameSite=None; Secure\"). */\n cookieFlags?: string\n /** Enable MS DNS cookie. */\n msDnsCookie?: boolean\n /** UID cookie name. */\n uidCookie?: string\n /** Error beacon verbosity level. */\n errorBeaconLevel?: number\n /** Microsoft Clarity project ID for integration. */\n clarityProjectId?: string\n /** When true, reads `window.enhanced_conversion_data` for gtag-style enhanced conversions. */\n gtagPid?: boolean\n /** Beacon protocol version. */\n Ver?: 1 | 2\n /** Associated advertiser ID. */\n advertiserId?: string\n /** Source tag for GTM integration. */\n gtmTagSource?: string\n /** Queue reference, typically `window.uetq`. */\n q?: any[]\n [key: string]: any\n}" + }, + { + "name": "BingUetQueue", + "kind": "interface", + "code": "export interface BingUetQueue {\n push: {\n // Legacy custom event object form (beacon shortcuts).\n (event: BingUetEventParams): void\n // Manual page load (maps internally to `page_view`).\n (command: 'pageLoad'): void\n // Standard and custom events.\n (command: 'event', eventName: BingUetEventName, eventParams?: BingUetEventParams): void\n // Consent mode (default on init, update after user interaction).\n (command: 'consent', action: 'default' | 'update', consentParams: BingUetConsentOptions): void\n // Enhanced conversions / sticky page overrides.\n (command: 'set', params: BingUetSetParams): void\n // TCF v2.0 auto-consent configuration.\n (command: 'config', target: 'tcf', params: BingUetTcfConfig): void\n }\n}" + }, { "name": "BingUetApi", "kind": "interface", - "code": "export interface BingUetApi {\n uetq: {\n push: (event: string | Record) => void\n }\n}" + "code": "export interface BingUetApi {\n uetq: BingUetQueue\n}" } ], "bluesky-embed": [ diff --git a/packages/script/src/runtime/registry/bing-uet.ts b/packages/script/src/runtime/registry/bing-uet.ts index 1f3ac778b..9c4920eb9 100644 --- a/packages/script/src/runtime/registry/bing-uet.ts +++ b/packages/script/src/runtime/registry/bing-uet.ts @@ -6,20 +6,245 @@ export { BingUetOptions } export type BingUetInput = RegistryScriptInput -export interface BingUetApi { - uetq: { - push: (event: string | Record) => void +export type BingUetConsentStatus = 'granted' | 'denied' + +export interface BingUetConsentOptions { + /** + * Controls storage of advertising identifiers. Currently the only field UET honors. + */ + ad_storage?: BingUetConsentStatus +} + +// Standard event names recognized by UET (from bat.js `knownEvents`), plus string fallback for custom events. +export type BingUetEventName + = | 'page_view' + | 'screen_view' + | 'login' + | 'sign_up' + | 'subscribe' + | 'start_trial' + | 'lead' + | 'generate_lead' + | 'submit_lead_form' + | 'contact' + | 'search' + | 'view_search_results' + | 'select_content' + | 'share' + | 'exception' + | 'find_location' + | 'book_appointment' + | 'get_route' + | 'view_item' + | 'view_item_list' + | 'view_promotion' + | 'add_to_cart' + | 'remove_from_cart' + | 'add_to_wishlist' + | 'add_payment_info' + | 'begin_checkout' + | 'checkout_progress' + | 'set_checkout_option' + | 'purchase' + | 'refund' + | (string & {}) + +export interface BingUetItem { + id?: string + name?: string + brand?: string + category?: string + variant?: string + price?: number + quantity?: number + list_name?: string + list_position?: number + creative_name?: string + creative_slot?: string + location_id?: string + [key: string]: any +} + +export interface BingUetPromotion { + id?: string + name?: string + creative_name?: string + creative_slot?: string + [key: string]: any +} + +export interface BingUetEventParams { + /** Event category (beacon shortcut). */ + ec?: string + /** Event action (beacon shortcut). */ + ea?: string + /** Event label (beacon shortcut). */ + el?: string + /** Event value, numeric (beacon shortcut). */ + ev?: number + /** Goal category / goal completion. */ + gc?: string + /** Goal value. */ + gv?: number + /** Long-form alias for `ec`. */ + event_category?: string + /** Long-form alias for `ea`. */ + event_action?: string + /** Long-form alias for `el`. */ + event_label?: string + /** Long-form alias for `ev`. */ + event_value?: number + /** Unique event identifier (dedup). */ + event_id?: string + /** Variable revenue amount. */ + revenue_value?: number + /** ISO 4217 currency code (e.g. "USD"). */ + currency?: string + /** Unique order/transaction ID (dedup). */ + transaction_id?: string + /** Ecommerce items. */ + items?: BingUetItem[] + /** Promotions associated with the event. */ + promotions?: BingUetPromotion[] + /** Sign-up/login method (e.g. "Google"). */ + method?: string + coupon?: string + tax?: number + shipping?: number + affiliation?: string + /** Search query. */ + search_term?: string + content_type?: string + content_id?: string + checkout_step?: number + checkout_option?: string + description?: string + name?: string + screen_name?: string + /** Whether an exception is fatal. */ + fatal?: boolean + /** Flags a new customer conversion. */ + new_customer?: boolean | number + /** Retail vertical: product ID. */ + ecomm_prodid?: string | string[] + /** Retail vertical: page type. */ + ecomm_pagetype?: 'home' | 'searchresults' | 'category' | 'product' | 'cart' | 'purchase' | 'other' | (string & {}) + ecomm_totalvalue?: number + ecomm_category?: string | string[] + ecomm_query?: string + /** Override page path. */ + page_path?: string + /** Override page title. */ + page_title?: string + /** Override full page URL. */ + page_location?: string + [key: string]: any +} + +export interface BingUetEnhancedConversionsPid { + /** SHA-256 hashed email (lowercase, trimmed). Alias: `email`. */ + em?: string + /** SHA-256 hashed phone (E.164 digits only). Alias: `phone_number`. */ + ph?: string + /** Long-form alias for `em`. */ + email?: string + /** Long-form alias for `ph`. */ + phone_number?: string + [key: string]: any +} + +export interface BingUetSetParams { + /** Partner identifiers for enhanced conversions. */ + pid?: BingUetEnhancedConversionsPid + page_path?: string + page_title?: string + page_location?: string + [key: string]: any +} + +export interface BingUetTcfConfig { + /** Enable TCF v2.0 auto-consent handling (Microsoft vendorId 1126). */ + enabled?: boolean + [key: string]: any +} + +export interface BingUetConstructorOptions { + /** UET tag ID. */ + ti: string + /** Alternate tag ID field (same effect as `ti`). */ + tagId?: string + /** Auto-fire pageLoad on SPA route changes. */ + enableAutoSpaTracking?: boolean + /** Enable TCF v2.0 auto-consent handling. */ + enableAutoConsent?: boolean + /** Suppress automatic page_view on load. */ + disableAutoPageView?: boolean + /** Disable the UET container entirely. */ + disableContainer?: boolean + /** Disable writing the UET visitor ID cookie. */ + disableUetVid?: boolean + /** Disable visibility (tab focus/blur) events. */ + disableVisibilityEvents?: boolean + /** Defer loading the beacon. */ + deferLoad?: boolean + /** Strip query strings from reported URLs. */ + removeQueryFromUrls?: boolean + /** Store conversion tracking cookies. */ + storeConvTrackCookies?: boolean + /** Cookie domain. */ + cookieDomain?: string + /** Cookie flags (e.g. "SameSite=None; Secure"). */ + cookieFlags?: string + /** Enable MS DNS cookie. */ + msDnsCookie?: boolean + /** UID cookie name. */ + uidCookie?: string + /** Error beacon verbosity level. */ + errorBeaconLevel?: number + /** Microsoft Clarity project ID for integration. */ + clarityProjectId?: string + /** When true, reads `window.enhanced_conversion_data` for gtag-style enhanced conversions. */ + gtagPid?: boolean + /** Beacon protocol version. */ + Ver?: 1 | 2 + /** Associated advertiser ID. */ + advertiserId?: string + /** Source tag for GTM integration. */ + gtmTagSource?: string + /** Queue reference, typically `window.uetq`. */ + q?: any[] + [key: string]: any +} + +export interface BingUetQueue { + push: { + // Legacy custom event object form (beacon shortcuts). + (event: BingUetEventParams): void + // Manual page load (maps internally to `page_view`). + (command: 'pageLoad'): void + // Standard and custom events. + (command: 'event', eventName: BingUetEventName, eventParams?: BingUetEventParams): void + // Consent mode (default on init, update after user interaction). + (command: 'consent', action: 'default' | 'update', consentParams: BingUetConsentOptions): void + // Enhanced conversions / sticky page overrides. + (command: 'set', params: BingUetSetParams): void + // TCF v2.0 auto-consent configuration. + (command: 'config', target: 'tcf', params: BingUetTcfConfig): void } } +export interface BingUetApi { + uetq: BingUetQueue +} + declare global { interface Window { - UET: new (options: { ti: string, enableAutoSpaTracking?: boolean, q?: any[] }) => BingUetApi['uetq'] - uetq: any[] | BingUetApi['uetq'] + UET: new (options: BingUetConstructorOptions) => BingUetQueue + uetq: any[] | BingUetQueue } } -export function useScriptBingUet(_options?: BingUetInput) { +export function useScriptBingUet(_options?: BingUetInput & { onBeforeUetStart?: (uetq: BingUetQueue) => void }) { return useRegistryScript('bingUet', options => ({ scriptInput: { src: 'https://bat.bing.com/bat.js', @@ -44,7 +269,9 @@ export function useScriptBingUet(_options?: BingUetInput) clientInit: import.meta.server ? undefined : () => { - window.uetq = window.uetq || [] + const uetq = window.uetq || [] + window.uetq = uetq + _options?.onBeforeUetStart?.(uetq as unknown as BingUetQueue) }, }), _options) } diff --git a/playground/pages/third-parties/bing-uet.vue b/playground/pages/third-parties/bing-uet.vue index c1707d3df..dab7712e7 100644 --- a/playground/pages/third-parties/bing-uet.vue +++ b/playground/pages/third-parties/bing-uet.vue @@ -5,7 +5,14 @@ useHead({ title: 'Bing UET', }) -const { status, proxy } = useScriptBingUet({ id: '247021147' }) +const { status, proxy } = useScriptBingUet({ + id: '247021147', + onBeforeUetStart(uetq) { + uetq.push('consent', 'default', { + ad_storage: 'denied', + }) + }, +}) function triggerEvent() { proxy.uetq.push('event', 'purchase', { @@ -13,6 +20,12 @@ function triggerEvent() { currency: 'USD', }) } + +function grantConsent() { + proxy.uetq.push('consent', 'update', { + ad_storage: 'granted', + }) +}