Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions docs/content/scripts/bing-uet.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,25 @@ function trackSignup() {
}
</script>
```

### 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
<script setup lang="ts">
const { proxy } = useScriptBingUet({
onBeforeUetStart(uetq) {
uetq.push('consent', 'default', {
ad_storage: 'denied',
})
},
})

function grantConsent() {
proxy.uetq.push('consent', 'update', {
ad_storage: 'granted',
})
}
</script>
```
57 changes: 56 additions & 1 deletion packages/script/src/registry-types.json
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, any>) => void\n }\n}"
"code": "export interface BingUetApi {\n uetq: BingUetQueue\n}"
}
],
"bluesky-embed": [
Expand Down
241 changes: 234 additions & 7 deletions packages/script/src/runtime/registry/bing-uet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,245 @@ export { BingUetOptions }

export type BingUetInput = RegistryScriptInput<typeof BingUetOptions, true, false>

export interface BingUetApi {
uetq: {
push: (event: string | Record<string, any>) => 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<T extends BingUetApi>(_options?: BingUetInput) {
export function useScriptBingUet<T extends BingUetApi>(_options?: BingUetInput & { onBeforeUetStart?: (uetq: BingUetQueue) => void }) {
return useRegistryScript<T, typeof BingUetOptions>('bingUet', options => ({
scriptInput: {
src: 'https://bat.bing.com/bat.js',
Expand All @@ -44,7 +269,9 @@ export function useScriptBingUet<T extends BingUetApi>(_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)
}
18 changes: 17 additions & 1 deletion playground/pages/third-parties/bing-uet.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,27 @@ 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', {
revenue_value: 49.99,
currency: 'USD',
})
}

function grantConsent() {
proxy.uetq.push('consent', 'update', {
ad_storage: 'granted',
})
}
</script>

<template>
Expand All @@ -24,6 +37,9 @@ function triggerEvent() {
<button @click="triggerEvent">
Trigger Event
</button>
<button @click="grantConsent">
Grant Consent
</button>
</ClientOnly>
</div>
</template>
Loading