doxa-maps-cdn/app/plugins/statinator.client.ts
2026-05-20 16:39:58 +01:00

86 lines
2.9 KiB
TypeScript

import { ENABLED_LANGUAGE_CODES } from '~~/config/languages'
declare global {
interface Window {
goStats?: {
track: (eventType: string, options?: Record<string, unknown>) => void
pageview: (options?: Record<string, unknown>, callback?: (resp: unknown) => void) => void
}
}
}
const EXCLUDED_PREFIXES = [
'/admin',
'/dashboard',
'/profile',
'/login',
'/register',
'/reset-password',
'/accept-invite',
'/oauth'
]
const NON_DEFAULT_LOCALES = ENABLED_LANGUAGE_CODES.filter(code => code !== 'en')
const LOCALE_PREFIX_RE = new RegExp(`^/(?:${NON_DEFAULT_LOCALES.join('|')})(?=/|$)`)
function stripLocale(path: string): string {
return path.replace(LOCALE_PREFIX_RE, '') || '/'
}
function isExcluded(path: string): boolean {
const stripped = stripLocale(path)
return EXCLUDED_PREFIXES.some(prefix => stripped === prefix || stripped.startsWith(prefix + '/'))
}
export default defineNuxtPlugin(() => {
const { statinatorEnabled, statinatorUrl, statinatorProjectId, statinatorCookieDomain } = useRuntimeConfig().public
if (!statinatorEnabled) return
const url = String(statinatorUrl || '').replace(/\/$/, '')
const projectId = String(statinatorProjectId || '')
const cookieDomain = String(statinatorCookieDomain || '')
if (!url || !projectId) return
// The plugin owns when pageviews fire (data-auto-pageview="false"),
// so SPA route changes and the initial load go through one code path.
let scriptReady: Promise<void> | null = null
function injectScript(): Promise<void> {
if (scriptReady) return scriptReady
scriptReady = new Promise<void>((resolve, reject) => {
const script = document.createElement('script')
script.src = `${url}/api/script.js`
script.async = true
script.setAttribute('data-project', projectId)
script.setAttribute('data-storage', 'cookie')
script.setAttribute('data-cookie-name', 'doxa_vid')
if (cookieDomain) script.setAttribute('data-cookie-domain', cookieDomain)
script.setAttribute('data-auto-pageview', 'false')
script.addEventListener('load', () => resolve())
script.addEventListener('error', () => reject(new Error('statinator script failed to load')))
document.head.appendChild(script)
})
return scriptReady
}
function firePageview() {
injectScript().then(() => window.goStats?.pageview()).catch(() => {})
}
// Defer the initial pageview's script injection until the browser is idle so
// the analytics bundle stays off the initial load critical path. SPA route
// changes (below) still fire immediately — they happen after load.
if (!isExcluded(useRoute().path)) {
if ('requestIdleCallback' in window) {
requestIdleCallback(() => firePageview(), { timeout: 3000 })
} else {
setTimeout(firePageview, 2000)
}
}
const router = useRouter()
router.afterEach((to, from) => {
if (to.fullPath === from.fullPath) return
if (isExcluded(to.path)) return
firePageview()
})
})