57 lines
1.8 KiB
TypeScript
57 lines
1.8 KiB
TypeScript
export type CookEventName
|
|
= | 'click_stuff' | 'click_tool' | 'click_dish'
|
|
| 'view_cook' | 'select_ingredients' | 'view_results'
|
|
| 'results_empty' | 'view_recipe_detail' | 'click_recipe_video'
|
|
|
|
interface ClarityConfig {
|
|
projectId: string
|
|
domains: string
|
|
}
|
|
|
|
type EventTracker = (name: CookEventName) => unknown
|
|
let eventTracker: EventTracker | undefined
|
|
|
|
/** Do not start a replay on login callbacks or URLs carrying credentials. */
|
|
export function isPrivateAnalyticsUrl(href: string) {
|
|
try {
|
|
const url = new URL(href)
|
|
const keys = [...url.searchParams.keys(), ...new URLSearchParams(url.hash.slice(1)).keys()]
|
|
return /^\/auth(?:\/|$)/.test(url.pathname)
|
|
|| keys.some(key => /^(?:ticket|code|state|token|access_token|refresh_token|id_token|password)$/i.test(key))
|
|
}
|
|
catch {
|
|
return true
|
|
}
|
|
}
|
|
|
|
/** Keep development, previews and native WebViews out of the production project. */
|
|
export function resolveClarityConfig(config: ClarityConfig, href: string, development: boolean) {
|
|
const projectId = config.projectId.trim()
|
|
if (development || !/^[a-z\d]{10,}$/i.test(projectId) || isPrivateAnalyticsUrl(href))
|
|
return null
|
|
try {
|
|
const page = new URL(href)
|
|
const domains = config.domains.split(',').map(domain => domain.trim().toLowerCase()).filter(Boolean)
|
|
if (!['https:', 'http:'].includes(page.protocol) || !domains.includes(page.hostname))
|
|
return null
|
|
return { projectId }
|
|
}
|
|
catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export function setCookEventTracker(tracker: EventTracker) {
|
|
eventTracker = tracker
|
|
}
|
|
|
|
/** Clarity events carry fixed action names, never recipe names or account data. */
|
|
export function trackCookEvent(name: CookEventName) {
|
|
try {
|
|
void Promise.resolve(eventTracker?.(name)).catch(() => undefined)
|
|
}
|
|
catch {
|
|
// Analytics must not interrupt selections or recipe links.
|
|
}
|
|
}
|