Skip to content

Commit 3b68b6c

Browse files
committed
fix(consent): tiktok hold defaultConsent + mixpanel stub opt-in/out
- TikTok defaultConsent accepts 'hold' so users can defer consent without the composable (addresses CodeRabbit #712). - Mixpanel stub methods now include opt_in_tracking/opt_out_tracking so pre-load calls queue correctly.
1 parent 58303b8 commit 3b68b6c

File tree

3 files changed

+11
-9
lines changed

3 files changed

+11
-9
lines changed

packages/script/src/runtime/registry/mixpanel-analytics.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ declare global {
2929
}
3030
}
3131

32-
const methods = ['track', 'identify', 'reset', 'register'] as const
32+
const methods = ['track', 'identify', 'reset', 'register', 'opt_in_tracking', 'opt_out_tracking'] as const
3333
const peopleMethods = ['set'] as const
3434

3535
export function useScriptMixpanelAnalytics<T extends MixpanelAnalyticsApi>(_options?: MixpanelAnalyticsInput) {
@@ -78,8 +78,9 @@ export function useScriptMixpanelAnalytics<T extends MixpanelAnalyticsApi>(_opti
7878
const optOutByDefault = options?.defaultConsent === 'opt-out'
7979
mp.init(options.token, optOutByDefault ? { opt_out_tracking_by_default: true } : undefined)
8080
if (options?.defaultConsent === 'opt-in') {
81-
// opt_in_tracking isn't part of the stub; push so the real SDK runs it on load.
82-
mp.push(['opt_in_tracking'])
81+
// After init, opt_in_tracking is wired on the stub (see methods array)
82+
// so the real SDK drains it on load.
83+
mp.opt_in_tracking?.()
8384
}
8485
}
8586
},

packages/script/src/runtime/registry/schemas.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -923,11 +923,13 @@ export const TikTokPixelOptions = object({
923923
*/
924924
trackPageView: optional(boolean()),
925925
/**
926-
* Default consent state. `'granted'` fires `ttq.grantConsent()`,
927-
* `'denied'` fires `ttq.revokeConsent()`, both called before `ttq('init', id)`.
926+
* Default consent state, applied before `ttq('init', id)`.
927+
* - `'granted'` fires `ttq.grantConsent()`
928+
* - `'denied'` fires `ttq.revokeConsent()`
929+
* - `'hold'` fires `ttq.holdConsent()` to defer until an explicit update
928930
* @see https://business-api.tiktok.com/portal/docs?id=1739585600931842
929931
*/
930-
defaultConsent: optional(union([literal('granted'), literal('denied')])),
932+
defaultConsent: optional(union([literal('granted'), literal('denied'), literal('hold')])),
931933
})
932934

933935
export const UmamiAnalyticsOptions = object({

packages/script/src/runtime/registry/tiktok-pixel.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,8 @@ export function useScriptTikTokPixel<T extends TikTokPixelApi>(_options?: TikTok
111111
ttq.grantConsent()
112112
else if (options?.defaultConsent === 'denied')
113113
ttq.revokeConsent()
114-
// When neither is set, tracking runs as before. Users wanting hold mode
115-
// pass `scriptOptions: { consent }` -- the adapter's applyDefault fires
116-
// `holdConsent()` when `ad_storage` is undecided.
114+
else if (options?.defaultConsent === 'hold')
115+
ttq.holdConsent()
117116
if (options?.id) {
118117
ttq('init', options.id)
119118
if (options?.trackPageView !== false) {

0 commit comments

Comments
 (0)