@aranova/tracking-react 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ConsentBanner.tsx","../../tracking-core/src/consent.ts","../../tracking-core/src/payloads.ts","../../tracking-core/src/gtag.ts","../../tracking-core/src/tracking.ts","../../tracking-core/src/session.ts","../../tracking-core/src/events/page-view.ts","../../tracking-core/src/page-view.ts","../../tracking-core/src/heartbeat.ts","../../tracking-core/src/ingest.ts","../../tracking-core/src/events/cta-click.ts","../../tracking-core/src/events/sdk-heartbeat.ts","../../tracking-core/src/events/form-start.ts","../../tracking-core/src/events/form-submit.ts","../../tracking-core/src/events/multi-page-session.ts","../../tracking-core/src/events/phone-click.ts","../../tracking-core/src/events/scroll-depth.ts","../../tracking-core/src/events/specific-page-visit.ts","../../tracking-core/src/events/time-on-site.ts","../../tracking-core/src/events/registry.ts","../../tracking-core/src/ingest-typed.ts","../../tracking-core/src/triggers/time-on-site.ts","../../tracking-core/src/triggers/specific-page-visit.ts","../../tracking-core/src/triggers/scroll-depth.ts","../../tracking-core/src/triggers/multi-page-session.ts","../../tracking-core/src/triggers/form-start.ts","../src/GoogleAdsTracking.tsx","../src/hooks.ts","../src/factory.tsx","../package.json"],"sourcesContent":["'use client';\n\nimport { useEffect, useState } from 'react';\n\nimport { getConsentState, setConsentState, type ConsentState } from '../../tracking-core/src/index';\n\nexport function ConsentBanner() {\n const [consentState, setBannerConsentState] = useState<ConsentState>('pending');\n const [isReady, setIsReady] = useState(false);\n\n useEffect(() => {\n setBannerConsentState(getConsentState());\n setIsReady(true);\n }, []);\n\n if (!isReady || consentState !== 'pending')\n return null;\n\n const handleAccept = (): void => {\n setConsentState('granted');\n setBannerConsentState('granted');\n };\n\n const handleDecline = (): void => {\n setConsentState('denied');\n setBannerConsentState('denied');\n };\n\n return (\n <div className=\"fixed bottom-0 left-0 right-0 z-50 border-t border-gray-200 bg-white p-4 shadow-lg\">\n <div className=\"mx-auto flex max-w-5xl flex-col gap-4 md:flex-row md:items-center md:justify-between\">\n <p className=\"text-sm text-gray-700\">\n We use cookies to understand ad performance and improve how our marketing works across visits.\n You can accept or decline this tracking.\n </p>\n <div className=\"flex flex-col gap-2 sm:flex-row\">\n <button\n type=\"button\"\n onClick={handleDecline}\n className=\"rounded-md border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 transition hover:bg-gray-100\"\n >\n Decline\n </button>\n <button\n type=\"button\"\n onClick={handleAccept}\n className=\"rounded-md bg-gray-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-gray-800\"\n >\n Accept\n </button>\n </div>\n </div>\n </div>\n );\n}\n","import type { ConsentState } from './types';\n\nexport const CONSENT_STATE_KEY = 'consent_state';\nexport const CONSENT_TIMESTAMP_KEY = 'consent_timestamp';\n\nexport type GtagConsentValue = 'granted' | 'denied';\n\nexport interface ConsentUpdatePayload {\n ad_storage: GtagConsentValue;\n ad_user_data: GtagConsentValue;\n ad_personalization: GtagConsentValue;\n analytics_storage: GtagConsentValue;\n}\n\nexport function buildConsentPayload(state: GtagConsentValue): ConsentUpdatePayload {\n return {\n ad_storage: state,\n ad_user_data: state,\n ad_personalization: state,\n analytics_storage: state,\n };\n}\n\nexport function getConsentState(): ConsentState {\n if (typeof window === 'undefined')\n return 'pending';\n\n const storedState = window.localStorage.getItem(CONSENT_STATE_KEY);\n\n if (storedState === 'granted' || storedState === 'denied')\n return storedState;\n\n return 'pending';\n}\n\nexport function setConsentState(state: GtagConsentValue): void {\n if (typeof window === 'undefined')\n return;\n\n window.localStorage.setItem(CONSENT_STATE_KEY, state);\n window.localStorage.setItem(CONSENT_TIMESTAMP_KEY, new Date().toISOString());\n\n if (typeof window.gtag === 'function')\n window.gtag('consent', 'update', buildConsentPayload(state));\n}\n\nexport function restoreStoredConsent(): ConsentState {\n const consentState = getConsentState();\n\n if (consentState === 'granted' || consentState === 'denied')\n setConsentState(consentState);\n\n return consentState;\n}\n","import type {\n TrackingClientContext,\n TrackingEventCreatePayload,\n TrackingInstallSurface,\n TrackingParams,\n TrackingSessionUpsertPayload,\n} from './types';\n\ninterface TrackingContextInput {\n packageName?: string | null;\n pageTitle?: string | null;\n referrer?: string | null;\n sdkVersion?: string | null;\n siteOrigin?: string | null;\n}\n\ninterface TrackingEventInput {\n eventType: string;\n metadata?: Record<string, unknown> | null;\n pageUrl?: string | null;\n sessionId: string;\n}\n\ninterface TrackingSessionInput {\n consentState?: Record<string, unknown> | null;\n firstPage?: string | null;\n sessionId: string;\n visitorId?: string | null;\n}\n\nexport function createTrackingClientContext(\n surface: TrackingInstallSurface,\n input: TrackingContextInput = {},\n): TrackingClientContext {\n return {\n surface,\n sdk_version: input.sdkVersion ?? null,\n package_name: input.packageName ?? null,\n site_origin: input.siteOrigin ?? (typeof window === 'undefined' ? null : window.location.origin),\n page_title: input.pageTitle ?? (typeof document === 'undefined' ? null : document.title || null),\n referrer: input.referrer ?? (typeof document === 'undefined' ? null : document.referrer || null),\n };\n}\n\nexport function createTrackingSessionUpsertPayload(\n trackingParams: TrackingParams,\n input: TrackingSessionInput,\n context: TrackingClientContext,\n): TrackingSessionUpsertPayload {\n return {\n session_id: input.sessionId,\n visitor_id: input.visitorId ?? null,\n gclid: trackingParams.gclid,\n fbclid: trackingParams.fbclid,\n utm_source: trackingParams.utm_source,\n utm_medium: trackingParams.utm_medium,\n utm_campaign: trackingParams.utm_campaign,\n utm_term: trackingParams.utm_term,\n utm_content: trackingParams.utm_content,\n first_page: input.firstPage ?? null,\n consent_state: input.consentState ?? null,\n context,\n };\n}\n\nexport function createTrackingEventCreatePayload(\n trackingParams: TrackingParams,\n input: TrackingEventInput,\n context: TrackingClientContext,\n): TrackingEventCreatePayload {\n return {\n session_id: input.sessionId,\n event_type: input.eventType,\n gclid: trackingParams.gclid,\n fbclid: trackingParams.fbclid,\n page_url: input.pageUrl ?? (typeof window === 'undefined' ? null : window.location.href),\n metadata: input.metadata ?? null,\n context,\n };\n}\n","import { buildConsentPayload, getConsentState, restoreStoredConsent } from './consent';\n\nexport const GTAG_SCRIPT_HOST = 'https://www.googletagmanager.com/gtag/js';\nexport const TRACKING_SCRIPT_ATTRIBUTE = 'data-aranova-tracking';\n\nfunction getScriptMarker(id: string): string {\n return `aranova-${id}`;\n}\n\nexport function createConsentDefaultScript(): string {\n return `\nwindow.dataLayer = window.dataLayer || [];\nfunction gtag(){dataLayer.push(arguments);}\nwindow.gtag = gtag;\ngtag('consent', 'default', {\n ad_storage: 'denied',\n ad_user_data: 'denied',\n ad_personalization: 'denied',\n analytics_storage: 'denied',\n wait_for_update: 500,\n});\n`.trim();\n}\n\nexport function createGtagInitScript(gtagId: string): string {\n return `\nwindow.dataLayer = window.dataLayer || [];\nfunction gtag(){dataLayer.push(arguments);}\nwindow.gtag = gtag;\ngtag('js', new Date());\ngtag('config', '${gtagId}');\n`.trim();\n}\n\nexport function createConsentRestoreScript(): string {\n return `\nconst consentState = window.localStorage.getItem('consent_state');\nif (window.gtag && (consentState === 'granted' || consentState === 'denied')) {\n window.gtag('consent', 'update', {\n ad_storage: consentState,\n ad_user_data: consentState,\n ad_personalization: consentState,\n analytics_storage: consentState,\n });\n}\n`.trim();\n}\n\nexport function ensureGtagFunction(): NonNullable<Window['gtag']> {\n window.dataLayer = window.dataLayer || [];\n\n if (typeof window.gtag === 'function')\n return window.gtag;\n\n window.gtag = (...args: unknown[]) => {\n window.dataLayer?.push(args);\n };\n\n return window.gtag;\n}\n\nexport function applyDefaultConsentState(): void {\n const gtag = ensureGtagFunction();\n gtag('consent', 'default', {\n ad_storage: 'denied',\n ad_user_data: 'denied',\n ad_personalization: 'denied',\n analytics_storage: 'denied',\n wait_for_update: 500,\n });\n}\n\nexport function loadGtagScript(gtagId: string): void {\n if (typeof document === 'undefined')\n return;\n\n const marker = getScriptMarker('gtag-loader');\n const existingScript = document.querySelector<HTMLScriptElement>(`script[${TRACKING_SCRIPT_ATTRIBUTE}=\"${marker}\"]`);\n if (existingScript)\n return;\n\n const script = document.createElement('script');\n script.async = true;\n script.src = `${GTAG_SCRIPT_HOST}?id=${encodeURIComponent(gtagId)}`;\n script.setAttribute(TRACKING_SCRIPT_ATTRIBUTE, marker);\n document.head.appendChild(script);\n}\n\nexport function initializeGtag(gtagId: string): void {\n const gtag = ensureGtagFunction();\n gtag('js', new Date());\n gtag('config', gtagId);\n}\n\nexport function restoreConsentState(): void {\n if (typeof window === 'undefined')\n return;\n\n const consentState = getConsentState();\n if (consentState === 'granted' || consentState === 'denied')\n window.gtag?.('consent', 'update', buildConsentPayload(consentState));\n}\n\nexport function bootstrapGoogleAdsTracking(gtagId: string): void {\n if (typeof window === 'undefined' || typeof document === 'undefined')\n return;\n\n applyDefaultConsentState();\n loadGtagScript(gtagId);\n initializeGtag(gtagId);\n restoreStoredConsent();\n}\n","import type { TrackingParams } from './types';\n\nexport const TRACKING_COOKIE_MAX_AGE_SECONDS = 7_776_000;\n\nexport const TRACKING_PARAM_KEYS = [\n 'gclid',\n 'fbclid',\n 'utm_source',\n 'utm_medium',\n 'utm_campaign',\n 'utm_term',\n 'utm_content',\n] as const;\n\nexport type TrackingParamKey = (typeof TRACKING_PARAM_KEYS)[number];\n\nexport function createEmptyTrackingParams(): TrackingParams {\n return {\n gclid: null,\n fbclid: null,\n utm_source: null,\n utm_medium: null,\n utm_campaign: null,\n utm_term: null,\n utm_content: null,\n };\n}\n\nexport function normalizeTrackingCookieValue(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n\nexport function getTrackingParamsFromCookieReader(\n readCookie: (key: TrackingParamKey) => unknown,\n): TrackingParams {\n return TRACKING_PARAM_KEYS.reduce<TrackingParams>((params, key) => {\n params[key] = normalizeTrackingCookieValue(readCookie(key));\n return params;\n }, createEmptyTrackingParams());\n}\n\nexport function getTrackingQueryValues(\n searchParams: Pick<URLSearchParams, 'get'>,\n): Partial<Record<TrackingParamKey, string>> {\n return TRACKING_PARAM_KEYS.reduce<Partial<Record<TrackingParamKey, string>>>((params, key) => {\n const value = searchParams.get(key);\n\n if (typeof value === 'string' && value.trim().length > 0) {\n params[key] = value;\n }\n\n return params;\n }, {});\n}\n\nexport function getCookieValueFromDocument(key: TrackingParamKey): string | null {\n if (typeof document === 'undefined')\n return null;\n\n const cookies = document.cookie ? document.cookie.split('; ') : [];\n const match = cookies.find((cookie) => cookie.startsWith(`${key}=`));\n\n if (!match)\n return null;\n\n const [, rawValue = ''] = match.split('=');\n return normalizeTrackingCookieValue(decodeURIComponent(rawValue));\n}\n\nexport function setTrackingCookie(\n key: TrackingParamKey,\n value: string,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): void {\n if (typeof document === 'undefined')\n return;\n\n const encodedValue = encodeURIComponent(value);\n document.cookie = `${key}=${encodedValue}; Max-Age=${maxAgeSeconds}; Path=/; SameSite=Lax`;\n}\n\nexport function persistTrackingParamsFromSearchParams(\n searchParams: Pick<URLSearchParams, 'get'>,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): Partial<Record<TrackingParamKey, string>> {\n const trackingValues = getTrackingQueryValues(searchParams);\n\n Object.entries(trackingValues).forEach(([key, value]) => {\n setTrackingCookie(key as TrackingParamKey, value, maxAgeSeconds);\n });\n\n return trackingValues;\n}\n\nexport function captureTrackingParamsFromLocation(\n url = typeof window === 'undefined' ? '' : window.location.href,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): TrackingParams {\n const resolvedUrl = typeof window === 'undefined'\n ? new URL(url || 'https://example.invalid')\n : new URL(url, window.location.origin);\n persistTrackingParamsFromSearchParams(resolvedUrl.searchParams, maxAgeSeconds);\n return getTrackingParamsFromCookieReader(getCookieValueFromDocument);\n}\n","// Visitor + session identity for the tracking SDK.\n//\n// Visitor: persistent localStorage UUID, never expires until the user clears\n// browser storage. Used for cross-session correlation.\n//\n// Session: rolling 30-minute idle window. Regenerated when more than\n// SESSION_IDLE_MS has passed since the last event. Matches the behavior of\n// GA4, PostHog, Mixpanel, etc., so analytics is comparable.\n\nexport const VISITOR_STORAGE_KEY = 'aranova_tracking_visitor';\nexport const SESSION_STORAGE_KEY = 'aranova_tracking_session';\nexport const SESSION_IDLE_MS = 30 * 60 * 1000;\n\nexport interface StoredSession {\n id: string;\n last_event_at: number;\n}\n\nfunction safeUuid(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function')\n return crypto.randomUUID();\n // Fallback for ancient browsers — not cryptographically perfect but unique enough.\n return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}-${Math.random().toString(16).slice(2)}`;\n}\n\nfunction readLocalStorage(key: string): string | null {\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeLocalStorage(key: string, value: string): void {\n try {\n window.localStorage.setItem(key, value);\n } catch {\n // Storage may be denied (private mode, blocked cookies, quota). Caller is\n // responsible for degrading gracefully.\n }\n}\n\nexport function getVisitorId(): string {\n if (typeof window === 'undefined')\n return safeUuid();\n\n const existing = readLocalStorage(VISITOR_STORAGE_KEY);\n if (existing && existing.length > 0)\n return existing;\n\n const fresh = safeUuid();\n writeLocalStorage(VISITOR_STORAGE_KEY, fresh);\n return fresh;\n}\n\nexport interface SessionIdResult {\n id: string;\n isNew: boolean;\n}\n\nexport function getOrRotateSessionId(now: number = Date.now()): SessionIdResult {\n if (typeof window === 'undefined')\n return { id: safeUuid(), isNew: true };\n\n const raw = readLocalStorage(SESSION_STORAGE_KEY);\n if (raw) {\n try {\n const parsed = JSON.parse(raw) as Partial<StoredSession>;\n if (typeof parsed.id === 'string' && typeof parsed.last_event_at === 'number') {\n if (now - parsed.last_event_at <= SESSION_IDLE_MS) {\n const refreshed: StoredSession = { id: parsed.id, last_event_at: now };\n writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(refreshed));\n return { id: parsed.id, isNew: false };\n }\n }\n } catch {\n // Fall through to a fresh session.\n }\n }\n\n const fresh: StoredSession = { id: safeUuid(), last_event_at: now };\n writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(fresh));\n return { id: fresh.id, isNew: true };\n}\n\n/** Test/debug helper — wipes both ids. */\nexport function resetTrackingIdentity(): void {\n if (typeof window === 'undefined')\n return;\n try {\n window.localStorage.removeItem(VISITOR_STORAGE_KEY);\n window.localStorage.removeItem(SESSION_STORAGE_KEY);\n } catch {\n // ignored\n }\n}\n","import { z } from 'zod';\n\n// page_view is automatic and auto-emitted by attachAutoPageView() on initial\n// load, SPA route changes (pushState / replaceState / popstate), and bfcache\n// restore (pageshow with event.persisted === true).\n\nexport const pageViewMetadataSchema = z\n .object({\n page: z\n .object({\n title: z.string().nullable(),\n path: z.string(),\n search: z.string(),\n hash: z.string(),\n })\n .strict(),\n referrer: z.string().nullable(),\n // `.nullable().optional()` — absent (undefined) OR explicit null OR a\n // real viewport object. Mirrors Pydantic's `_Viewport | None = None`\n // on the backend side so the drift test stays clean.\n viewport: z\n .object({\n w: z.number(),\n h: z.number(),\n })\n .strict()\n .nullable()\n .optional(),\n })\n .strict();\n\nexport type PageViewMetadata = z.infer<typeof pageViewMetadataSchema>;\n\n// No configurable options today, but must be registered for the SDK to attach\n// the auto-page-view handler. Reserved for future options like `throttleMs`\n// or `includeHash`.\nexport const pageViewConfigSchema = z.object({}).strict();\nexport type PageViewConfig = z.infer<typeof pageViewConfigSchema>;\n","// Auto page_view emission. Fires once on attach, then again on every SPA\n// navigation. Detects navigation via three signals:\n// - history.pushState (patched)\n// - history.replaceState (patched)\n// - popstate event (back/forward buttons)\n// - pageshow with event.persisted === true (back-forward cache restore)\n//\n// The Next.js provider can opt out of this and use `usePathname` instead so\n// it picks up App Router transitions reliably without monkey-patching globals.\n\nimport { pageViewMetadataSchema, type PageViewMetadata } from './events/page-view';\nimport type { TrackingClient } from './ingest';\n\n// Re-export so existing callers that import `PageViewMetadata` from\n// `./page-view` keep compiling. The canonical definition now lives in\n// `events/page-view.ts` and is derived from a Zod schema.\nexport type { PageViewMetadata } from './events/page-view';\n\nexport const LAST_FIRED_URL_STORAGE_KEY = 'aranova_tracking_last_fired_url';\n\n// Tracks the previous in-session URL we fired a page_view for. SPA\n// navigation never updates `document.referrer` (the document is never\n// reloaded), so all events would otherwise share the same stale referrer\n// value. We track it ourselves: the referrer for an event at URL X is the\n// last URL we fired a page_view for, falling back to document.referrer for\n// the very first event of the session.\n//\n// Hybrid storage: the in-memory variable is the runtime source of truth\n// (cheap reads, no quota concerns), and sessionStorage is the durability\n// layer (survives full-page reloads and bfcache restoration). On first\n// read per JS context we lazily hydrate from sessionStorage; on every\n// write we update both layers. If sessionStorage is unavailable (private\n// mode, blocked extensions, quota errors) we silently fall back to\n// memory-only — the SDK stays functional, just loses cross-reload\n// referrer continuity in that one tab.\nlet lastFiredUrl: string | null = null;\nlet lastFiredUrlHydrated = false;\n\nfunction readSessionStorage(key: string): string | null {\n try {\n if (typeof window === 'undefined') return null;\n return window.sessionStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeSessionStorage(key: string, value: string): void {\n try {\n if (typeof window === 'undefined') return;\n window.sessionStorage.setItem(key, value);\n } catch {\n // private mode / blocked / quota — memory-only is fine\n }\n}\n\nfunction deleteSessionStorage(key: string): void {\n try {\n if (typeof window === 'undefined') return;\n window.sessionStorage.removeItem(key);\n } catch {\n // ignore\n }\n}\n\nfunction getLastFiredUrl(): string | null {\n if (!lastFiredUrlHydrated) {\n lastFiredUrlHydrated = true;\n const stored = readSessionStorage(LAST_FIRED_URL_STORAGE_KEY);\n if (stored !== null) lastFiredUrl = stored;\n }\n return lastFiredUrl;\n}\n\nfunction setLastFiredUrl(url: string): void {\n lastFiredUrl = url;\n lastFiredUrlHydrated = true;\n writeSessionStorage(LAST_FIRED_URL_STORAGE_KEY, url);\n}\n\n/**\n * Clear the in-session URL tracking. Primarily an escape hatch for tests\n * so each test starts with a fresh referrer chain; production code\n * doesn't need this — the singleton client lives for the page lifetime.\n */\nexport function resetPageViewState(): void {\n lastFiredUrl = null;\n lastFiredUrlHydrated = false;\n deleteSessionStorage(LAST_FIRED_URL_STORAGE_KEY);\n}\n\nexport function buildPageViewMetadata(referrerOverride?: string | null): PageViewMetadata | null {\n if (typeof window === 'undefined' || typeof document === 'undefined')\n return null;\n // Run the constructed object through the Zod schema so the shape is\n // validated even when the SDK itself is the one building it. If the\n // schema ever drifts from what buildPageViewMetadata emits, the parse\n // throws here and the event is dropped rather than being sent malformed.\n return pageViewMetadataSchema.parse({\n page: {\n title: document.title || null,\n path: window.location.pathname,\n search: window.location.search,\n hash: window.location.hash,\n },\n referrer:\n referrerOverride !== undefined ? referrerOverride : document.referrer || null,\n viewport: { w: window.innerWidth, h: window.innerHeight },\n });\n}\n\nexport function fireManualPageView(client: TrackingClient): void {\n if (typeof window === 'undefined')\n return;\n\n const currentHref = window.location.href;\n const previousFiredUrl = getLastFiredUrl();\n\n // Internal SPA referrer: the previous URL we fired a page_view for in this\n // tab. Only counts when it differs from the current URL (a same-URL re-fire\n // — e.g. React StrictMode double-mount in dev, or a bfcache restore to the\n // same page — should not produce a self-referencing referrer).\n const internalReferrer =\n previousFiredUrl !== null && previousFiredUrl !== currentHref\n ? previousFiredUrl\n : null;\n\n // External referrer: where the user came from before loading the app.\n // Used as the fallback for the very first page_view of the session.\n const externalReferrer =\n typeof document !== 'undefined' ? document.referrer || null : null;\n\n const referrer = internalReferrer ?? externalReferrer;\n\n const metadata = buildPageViewMetadata(referrer);\n client.trackEvent({\n eventType: 'page_view',\n pageUrl: currentHref,\n metadata: metadata as unknown as Record<string, unknown> | null,\n });\n\n // Only advance the in-session pointer when the URL actually changed, so a\n // same-URL re-fire never poisons the next event's referrer.\n if (currentHref !== previousFiredUrl) {\n setLastFiredUrl(currentHref);\n }\n}\n\n/**\n * Attach a back-forward cache restore listener in isolation. Used by\n * surfaces that have their own SPA detection (e.g. the Next.js provider\n * uses `usePathname`) but still want to capture bfcache restores, which\n * don't go through any router state change.\n *\n * Returns a detach function that removes the listener.\n */\nexport function attachBfcacheRestore(client: TrackingClient): () => void {\n if (typeof window === 'undefined') return () => {};\n\n function handlePageShow(event: PageTransitionEvent): void {\n if (!event.persisted) return;\n fireManualPageView(client);\n }\n\n window.addEventListener('pageshow', handlePageShow);\n return () => {\n window.removeEventListener('pageshow', handlePageShow);\n };\n}\n\nexport interface AttachAutoPageViewOptions {\n /** Skip the initial fire on attach (useful when the host already fired one). */\n skipInitial?: boolean;\n}\n\nexport function attachAutoPageView(\n client: TrackingClient,\n options: AttachAutoPageViewOptions = {},\n): () => void {\n if (typeof window === 'undefined' || typeof history === 'undefined') {\n return () => {};\n }\n\n let lastPath = window.location.pathname + window.location.search;\n\n function maybeFire(): void {\n const current = window.location.pathname + window.location.search;\n if (current === lastPath)\n return;\n lastPath = current;\n fireManualPageView(client);\n }\n\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n function patchedPushState(this: History, ...args: Parameters<History['pushState']>): void {\n originalPushState(...args);\n // Defer one tick so the URL is settled before we read it.\n setTimeout(maybeFire, 0);\n }\n function patchedReplaceState(this: History, ...args: Parameters<History['replaceState']>): void {\n originalReplaceState(...args);\n setTimeout(maybeFire, 0);\n }\n\n // bfcache restore: when the user clicks an external link and hits the\n // back button, the browser can restore the page from its back-forward\n // cache without re-running any JS lifecycles. `popstate` does NOT fire\n // for bfcache restores, but `pageshow` does, with event.persisted === true.\n // When that happens we fire a fresh page_view so the session reflects\n // the re-entry.\n function handlePageShow(event: PageTransitionEvent): void {\n if (!event.persisted) return;\n fireManualPageView(client);\n }\n\n history.pushState = patchedPushState as History['pushState'];\n history.replaceState = patchedReplaceState as History['replaceState'];\n window.addEventListener('popstate', maybeFire);\n window.addEventListener('pageshow', handlePageShow);\n\n if (!options.skipInitial)\n fireManualPageView(client);\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener('popstate', maybeFire);\n window.removeEventListener('pageshow', handlePageShow);\n };\n}\n","// Builds the metadata payload for the sdk_heartbeat event. Called once per\n// new session to report the SDK version and registered trigger configuration\n// back to the Aranova API.\n\nimport type { SdkHeartbeatMetadata } from './events/sdk-heartbeat';\nimport type { TriggerRegistryConfig } from './events/registry';\nimport type { TrackingInstallSurface } from './types';\n\n/**\n * Recursively serialize a value into a JSON-safe form. Converts RegExp\n * instances to their `.source` string so the trigger config can survive\n * `JSON.stringify` without losing information.\n */\nfunction serializeValue(value: unknown): unknown {\n if (value instanceof RegExp) return value.source;\n if (Array.isArray(value)) return value.map(serializeValue);\n if (value !== null && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n out[k] = serializeValue(v);\n }\n return out;\n }\n return value;\n}\n\nexport function buildHeartbeatMetadata(\n surface: TrackingInstallSurface,\n sdkVersion: string | null,\n packageName: string | null,\n triggers: TriggerRegistryConfig | null,\n): SdkHeartbeatMetadata {\n const automaticNames = triggers\n ? Object.keys(triggers.automatic)\n : [];\n const manualNames = triggers?.manual\n ? Object.keys(triggers.manual)\n : [];\n\n // Build trigger_config: the init params per event, with RegExp values\n // converted to strings. Skip events with empty config objects.\n let triggerConfig: Record<string, Record<string, unknown>> | null = null;\n if (triggers) {\n const cfg: Record<string, Record<string, unknown>> = {};\n for (const [name, config] of Object.entries(triggers.automatic)) {\n const serialized = serializeValue(config) as Record<string, unknown>;\n if (Object.keys(serialized).length > 0) {\n cfg[name] = serialized;\n }\n }\n if (triggers.manual) {\n for (const [name, config] of Object.entries(triggers.manual)) {\n if (config === undefined) continue;\n const serialized = serializeValue(config) as Record<string, unknown>;\n if (Object.keys(serialized).length > 0) {\n cfg[name] = serialized;\n }\n }\n }\n if (Object.keys(cfg).length > 0) {\n triggerConfig = cfg;\n }\n }\n\n return {\n sdk_version: sdkVersion ?? 'unknown',\n package_name: packageName,\n surface,\n triggers: {\n automatic: automaticNames,\n manual: manualNames,\n },\n trigger_config: triggerConfig,\n };\n}\n","// Tracking ingest client. Queues events, debounce-flushes them to the\n// /tracking/events endpoint, and degrades silently on errors so a broken\n// network never breaks the host site.\n\nimport { getConsentState } from './consent';\nimport { resetPageViewState } from './page-view';\nimport {\n captureTrackingParamsFromLocation,\n createEmptyTrackingParams,\n getCookieValueFromDocument,\n getTrackingParamsFromCookieReader,\n} from './tracking';\nimport type { TriggerRegistryConfig } from './events/registry';\nimport { buildHeartbeatMetadata } from './heartbeat';\nimport { getOrRotateSessionId, getVisitorId } from './session';\nimport type {\n TrackingClientContext,\n TrackingEventCreatePayload,\n TrackingInstallSurface,\n TrackingParams,\n TrackingSessionUpsertPayload,\n} from './types';\n\nexport const DEFAULT_FLUSH_INTERVAL_MS = 2000;\nexport const DEFAULT_MAX_QUEUE_SIZE = 10;\nexport const HARD_MAX_BATCH = 50;\nexport const API_KEY_HEADER = 'X-Aranova-Api-Key';\n\nexport interface TrackingClientConfig {\n apiKey: string;\n endpoint: string;\n surface: TrackingInstallSurface;\n sdkVersion?: string;\n packageName?: string;\n /** Trigger registry so the heartbeat can report registered events. */\n triggers?: TriggerRegistryConfig;\n /** Override the default 2s debounce window. */\n flushIntervalMs?: number;\n /** Override the default 10-event batch trigger. */\n maxQueueSize?: number;\n /** When true, swallow nothing — useful for tests. */\n debug?: boolean;\n}\n\nexport interface TrackEventInput {\n eventType: string;\n pageUrl?: string | null;\n metadata?: Record<string, unknown> | null;\n occurredAt?: Date | string | null;\n}\n\ninterface QueuedEvent {\n event_type: string;\n page_url: string | null;\n metadata: Record<string, unknown> | null;\n occurred_at: string | null;\n}\n\nexport interface TrackingClient {\n trackEvent: (input: TrackEventInput) => void;\n flush: () => Promise<void>;\n getSessionId: () => string;\n getVisitorId: () => string;\n destroy: () => void;\n}\n\ninterface IngestRequestBody {\n session: TrackingSessionUpsertPayload;\n events: Array<{\n event_type: string;\n page_url: string | null;\n metadata: Record<string, unknown> | null;\n occurred_at: string | null;\n }>;\n}\n\nfunction buildContext(\n surface: TrackingInstallSurface,\n sdkVersion: string | null,\n packageName: string | null,\n): TrackingClientContext {\n return {\n surface,\n sdk_version: sdkVersion,\n package_name: packageName,\n site_origin: typeof window === 'undefined' ? null : window.location.origin,\n page_title: typeof document === 'undefined' ? null : document.title || null,\n referrer: typeof document === 'undefined' ? null : document.referrer || null,\n };\n}\n\nfunction readTrackingParams(): TrackingParams {\n if (typeof window === 'undefined')\n return createEmptyTrackingParams();\n // Capture from URL on every read so the first event in a session reflects the\n // landing-page params even if the cookie helper hasn't run yet.\n try {\n captureTrackingParamsFromLocation();\n } catch {\n // ignore\n }\n return getTrackingParamsFromCookieReader(getCookieValueFromDocument);\n}\n\nfunction consentSnapshot(): Record<string, unknown> | null {\n try {\n return { state: getConsentState() };\n } catch {\n return null;\n }\n}\n\nasync function postWithFetch(url: string, body: string, apiKey: string, keepalive: boolean): Promise<void> {\n if (typeof fetch !== 'function')\n return;\n try {\n await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n [API_KEY_HEADER]: apiKey,\n },\n body,\n keepalive,\n // CORS is open on the tracking endpoint; never send cookies.\n credentials: 'omit',\n mode: 'cors',\n });\n } catch {\n // fire-and-forget — never throw to the host site\n }\n}\n\nlet globalClient: TrackingClient | null = null;\nlet globalClientKey: string | null = null;\n\nfunction clientConfigKey(config: TrackingClientConfig): string {\n return `${config.apiKey}@${config.endpoint}#${config.surface}`;\n}\n\n/**\n * Return a page-level singleton tracking client. Creating the client anew on\n * every component mount is wrong — React StrictMode double-mounts dev-only,\n * and destroying+recreating the client between the cleanup and re-run strips\n * away the pushState patch that SPA auto page view relies on. A singleton\n * survives all of that: the client lives for the entire page, and providers\n * just attach/detach auto page view against it.\n *\n * If `apiKey` / `endpoint` / `surface` change between calls, the previous\n * singleton is destroyed and a new one replaces it. This covers hot-config\n * changes without leaking state.\n */\nexport function getOrCreateTrackingClient(config: TrackingClientConfig): TrackingClient {\n const key = clientConfigKey(config);\n if (globalClient !== null && globalClientKey === key) {\n return globalClient;\n }\n if (globalClient !== null) {\n globalClient.destroy();\n }\n globalClient = createTrackingClient(config);\n globalClientKey = key;\n return globalClient;\n}\n\n/**\n * Tear down the singleton if any. Primarily an escape hatch for tests where\n * each test should see a fresh client; production code rarely needs this.\n * Also clears the in-session SPA referrer so the next test starts with a\n * fresh referrer chain.\n */\nexport function resetGlobalTrackingClient(): void {\n if (globalClient !== null) {\n globalClient.destroy();\n }\n globalClient = null;\n globalClientKey = null;\n resetPageViewState();\n}\n\nexport function createTrackingClient(config: TrackingClientConfig): TrackingClient {\n const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n const maxQueueSize = Math.min(config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE, HARD_MAX_BATCH);\n const sdkVersion = config.sdkVersion ?? null;\n const packageName = config.packageName ?? null;\n const endpointBase = config.endpoint.replace(/\\/$/, '');\n const eventsUrl = `${endpointBase}/events`;\n\n let queue: QueuedEvent[] = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n let firstPage: string | null = null;\n let destroyed = false;\n\n // Initialize identity early so the first POST has stable values.\n const visitorId = getVisitorId();\n const initialSession = getOrRotateSessionId();\n let sessionId = initialSession.id;\n\n if (typeof window !== 'undefined')\n firstPage = window.location.href;\n\n // Queue an sdk_heartbeat event at the start of every new session.\n function enqueueHeartbeat(): void {\n const metadata = buildHeartbeatMetadata(\n config.surface,\n sdkVersion,\n packageName,\n config.triggers ?? null,\n );\n queue.push({\n event_type: 'sdk_heartbeat',\n page_url: typeof window === 'undefined' ? null : window.location.href,\n metadata: metadata as unknown as Record<string, unknown>,\n occurred_at: new Date().toISOString(),\n });\n }\n\n if (initialSession.isNew) {\n enqueueHeartbeat();\n }\n\n function buildSessionPayload(): TrackingSessionUpsertPayload {\n const rotated = getOrRotateSessionId();\n if (rotated.isNew && rotated.id !== sessionId) {\n // Session rotated mid-page (idle > 30min then user returned).\n enqueueHeartbeat();\n }\n sessionId = rotated.id;\n const params = readTrackingParams();\n const context = buildContext(config.surface, sdkVersion, packageName);\n return {\n session_id: sessionId,\n visitor_id: visitorId,\n gclid: params.gclid,\n fbclid: params.fbclid,\n utm_source: params.utm_source,\n utm_medium: params.utm_medium,\n utm_campaign: params.utm_campaign,\n utm_term: params.utm_term,\n utm_content: params.utm_content,\n first_page: firstPage,\n consent_state: consentSnapshot(),\n context,\n };\n }\n\n function scheduleFlush(): void {\n if (flushTimer !== null || destroyed)\n return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n void flush();\n }, flushIntervalMs);\n }\n\n function clearScheduledFlush(): void {\n if (flushTimer !== null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n }\n\n async function flush(): Promise<void> {\n if (queue.length === 0)\n return;\n\n const events = queue.slice(0, HARD_MAX_BATCH);\n queue = queue.slice(events.length);\n clearScheduledFlush();\n\n const body: IngestRequestBody = {\n session: buildSessionPayload(),\n events,\n };\n\n const serialized = JSON.stringify(body);\n\n // Prefer fetch + keepalive for better error visibility. sendBeacon is the\n // pagehide fallback path because it survives navigation away.\n await postWithFetch(eventsUrl, serialized, config.apiKey, false);\n }\n\n function trackEvent(input: TrackEventInput): void {\n if (destroyed)\n return;\n if (!input || typeof input.eventType !== 'string' || input.eventType.length === 0)\n return;\n\n const occurredAt =\n input.occurredAt instanceof Date\n ? input.occurredAt.toISOString()\n : typeof input.occurredAt === 'string'\n ? input.occurredAt\n : new Date().toISOString();\n\n queue.push({\n event_type: input.eventType,\n page_url:\n input.pageUrl ?? (typeof window === 'undefined' ? null : window.location.href),\n metadata: input.metadata ?? null,\n occurred_at: occurredAt,\n });\n\n if (queue.length >= maxQueueSize) {\n void flush();\n } else {\n scheduleFlush();\n }\n }\n\n function flushOnUnload(): void {\n if (queue.length === 0)\n return;\n const events = queue.slice(0, HARD_MAX_BATCH);\n queue = queue.slice(events.length);\n clearScheduledFlush();\n\n const body: IngestRequestBody = {\n session: buildSessionPayload(),\n events,\n };\n const serialized = JSON.stringify(body);\n void postWithFetch(eventsUrl, serialized, config.apiKey, true);\n }\n\n if (typeof window !== 'undefined') {\n window.addEventListener('pagehide', flushOnUnload);\n window.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') flushOnUnload();\n });\n }\n\n return {\n trackEvent,\n flush,\n getSessionId: () => sessionId,\n getVisitorId: () => visitorId,\n destroy: () => {\n destroyed = true;\n // Fire any pending events through the keepalive path before tearing\n // down. Critical for React StrictMode in dev, where the provider's\n // first mount is immediately unmounted and its 2s debounce would\n // otherwise drop the initial page_view on the floor. Uses fetch\n // keepalive so the request survives the component tearing down.\n if (queue.length > 0) {\n flushOnUnload();\n }\n clearScheduledFlush();\n queue = [];\n if (typeof window !== 'undefined') {\n window.removeEventListener('pagehide', flushOnUnload);\n }\n },\n };\n}\n","import { z } from 'zod';\n\n// cta_click is manual. Consumer code calls trackEvent('cta_click', ...) from\n// click handlers on CTAs like \"Get Directions\", \"Book Appointment\", etc.\n// The cta_name is a free-form string chosen by the consumer to identify the\n// action (e.g. \"get_directions\", \"book_appointment\", \"download_brochure\").\n\nexport const ctaClickMetadataSchema = z\n .object({\n cta_name: z.string(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n section: z.string().nullable().optional(),\n destination_url: z.string().nullable().optional(),\n })\n .strict();\n\nexport type CtaClickMetadata = z.infer<typeof ctaClickMetadataSchema>;\n\nexport const ctaClickConfigSchema = z.object({}).strict();\nexport type CtaClickConfig = z.infer<typeof ctaClickConfigSchema>;\n","import { z } from 'zod';\n\n// sdk_heartbeat is an SDK-internal automatic event fired exactly once at the\n// start of each new session. It carries the SDK version and the complete\n// trigger registry the consumer registered, so the backend knows which\n// events a client site is configured to capture without requiring that\n// information on every single event payload.\n\nexport const sdkHeartbeatTriggersSchema = z.object({\n automatic: z.array(z.string()),\n manual: z.array(z.string()),\n}).strict();\n\nexport const sdkHeartbeatMetadataSchema = z\n .object({\n sdk_version: z.string(),\n package_name: z.string().nullable(),\n surface: z.enum(['next', 'react', 'script']),\n triggers: sdkHeartbeatTriggersSchema,\n trigger_config: z.record(z.string(), z.record(z.string(), z.unknown())).nullable().optional(),\n })\n .strict();\n\nexport type SdkHeartbeatMetadata = z.infer<typeof sdkHeartbeatMetadataSchema>;\n\n// No consumer-facing config — heartbeat is SDK-internal.\nexport const sdkHeartbeatConfigSchema = z.object({}).strict();\nexport type SdkHeartbeatConfig = z.infer<typeof sdkHeartbeatConfigSchema>;\n","import { z } from 'zod';\n\n// form_start is automatic. The SDK uses focusin event delegation on document\n// to detect the first field interaction inside a <form>. Fires once per form\n// per page lifecycle. Resets on SPA navigation.\n\nexport const formStartMetadataSchema = z\n .object({\n form: z\n .object({\n id: z.string(),\n action: z.string().nullable(),\n })\n .strict(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type FormStartMetadata = z.infer<typeof formStartMetadataSchema>;\n\nexport const formStartConfigSchema = z\n .object({\n selector: z.string().optional(),\n })\n .strict();\n\nexport type FormStartConfig = z.infer<typeof formStartConfigSchema>;\n","import { z } from 'zod';\n\n// form_submit is manual — the SDK never auto-fires this. Consumer code calls\n// `tracking.trackEvent('form_submit', { form, page })` from their own submit\n// handler. Registering it enables the type-level permission; omitting it\n// turns manual calls into a compile error.\n//\n// The optional `fields` array captures submitted form field metadata and JSON\n// values. Consumers explicitly build the fields array themselves so they\n// control exactly what is sent.\n\nexport type JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nexport const jsonValueSchema: z.ZodType<JsonValue> = z.lazy(() =>\n z.union([\n z.string(),\n z.number().finite(),\n z.boolean(),\n z.null(),\n z.array(jsonValueSchema),\n z.record(jsonValueSchema),\n ]),\n);\n\nexport const formSubmitMetadataSchema = z\n .object({\n form: z\n .object({\n id: z.string(),\n action: z.string().nullable(),\n fields: z\n .array(\n z\n .object({\n name: z.string(),\n type: z.string(),\n label: z.string().nullable(),\n value: jsonValueSchema,\n })\n .strict(),\n )\n .optional(),\n })\n .strict(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type FormSubmitMetadata = z.infer<typeof formSubmitMetadataSchema>;\n\nexport const formSubmitConfigSchema = z.object({}).strict();\nexport type FormSubmitConfig = z.infer<typeof formSubmitConfigSchema>;\n","import { z } from 'zod';\n\n// multi_page_session is automatic. The SDK tracks distinct page paths visited\n// in the current session via sessionStorage. When the count reaches the\n// configured threshold, fires once. Resets on session rotation.\n\nexport const multiPageSessionMetadataSchema = z\n .object({\n page_count: z.number().int().min(2),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type MultiPageSessionMetadata = z.infer<typeof multiPageSessionMetadataSchema>;\n\nexport const multiPageSessionConfigSchema = z\n .object({\n pageThreshold: z.number().int().min(2),\n })\n .strict();\n\nexport type MultiPageSessionConfig = z.infer<typeof multiPageSessionConfigSchema>;\n","import { z } from 'zod';\n\n// phone_click is manual. Consumer code calls trackEvent('phone_click', ...)\n// from a click handler on a tel: anchor or similar. The phone_number is the\n// business's number (extracted from the href), not the visitor's. The optional\n// section helps distinguish multiple phone links on the same page.\n\nexport const phoneClickMetadataSchema = z\n .object({\n phone_number: z.string(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n section: z.string().nullable().optional(),\n })\n .strict();\n\nexport type PhoneClickMetadata = z.infer<typeof phoneClickMetadataSchema>;\n\nexport const phoneClickConfigSchema = z.object({}).strict();\nexport type PhoneClickConfig = z.infer<typeof phoneClickConfigSchema>;\n","import { z } from 'zod';\n\n// scroll_depth is automatic. The SDK installs a requestAnimationFrame-throttled\n// scroll listener and fires once per configured threshold per page. Resets on\n// SPA navigation.\n\nexport const scrollDepthMetadataSchema = z\n .object({\n depth_percent: z.number().int().min(1).max(100),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type ScrollDepthMetadata = z.infer<typeof scrollDepthMetadataSchema>;\n\nexport const scrollDepthConfigSchema = z\n .object({\n thresholds: z.array(z.number().int().min(1).max(100)).min(1),\n })\n .strict();\n\nexport type ScrollDepthConfig = z.infer<typeof scrollDepthConfigSchema>;\n","import { z } from 'zod';\n\n// specific_page_visit is automatic. The SDK observes every navigation and\n// emits a specific_page_visit event when the current pathname matches one of\n// the consumer-provided patterns. Each pattern has a strongly typed name\n// (contact_page, services_page, etc.) so the event stream is self-describing\n// and downstream analytics can group by page intent without re-applying regexes.\n\nexport const SPECIFIC_PAGE_NAMES = [\n 'contact_page',\n 'about_page',\n 'services_page',\n 'booking_page',\n 'location_page',\n 'pricing_page',\n 'faq_page',\n 'testimonials_page',\n] as const;\n\nexport type SpecificPageName = (typeof SPECIFIC_PAGE_NAMES)[number];\n\nexport const specificPageNameSchema = z.enum(SPECIFIC_PAGE_NAMES);\n\nexport const specificPageVisitMetadataSchema = z\n .object({\n page_name: specificPageNameSchema,\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type SpecificPageVisitMetadata = z.infer<typeof specificPageVisitMetadataSchema>;\n\nexport const specificPageVisitConfigSchema = z\n .object({\n pages: z\n .array(\n z\n .object({\n name: specificPageNameSchema,\n pathPattern: z.custom<RegExp>((value) => value instanceof RegExp, {\n message: 'pathPattern must be a RegExp',\n }),\n })\n .strict(),\n )\n .min(1),\n })\n .strict();\n\nexport type SpecificPageVisitConfig = z.infer<typeof specificPageVisitConfigSchema>;\n","import { z } from 'zod';\n\n// time_on_site is automatic. The SDK starts a visibility-aware timer on page\n// load and fires once when the visible-time threshold is crossed. The timer\n// pauses when the tab goes hidden so a user who walks away from a tab\n// doesn't generate fake engagement.\n\nexport const timeOnSiteMetadataSchema = z\n .object({\n duration_ms: z.number().int().nonnegative(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type TimeOnSiteMetadata = z.infer<typeof timeOnSiteMetadataSchema>;\n\nexport const timeOnSiteConfigSchema = z\n .object({\n thresholdSeconds: z.number().int().positive(),\n })\n .strict();\nexport type TimeOnSiteConfig = z.infer<typeof timeOnSiteConfigSchema>;\n","import type { z } from 'zod';\n\nimport {\n ctaClickConfigSchema,\n ctaClickMetadataSchema,\n type CtaClickConfig,\n type CtaClickMetadata,\n} from './cta-click';\nimport {\n sdkHeartbeatConfigSchema,\n sdkHeartbeatMetadataSchema,\n type SdkHeartbeatConfig,\n type SdkHeartbeatMetadata,\n} from './sdk-heartbeat';\nimport {\n formStartConfigSchema,\n formStartMetadataSchema,\n type FormStartConfig,\n type FormStartMetadata,\n} from './form-start';\nimport {\n formSubmitConfigSchema,\n formSubmitMetadataSchema,\n type FormSubmitConfig,\n type FormSubmitMetadata,\n} from './form-submit';\nimport {\n multiPageSessionConfigSchema,\n multiPageSessionMetadataSchema,\n type MultiPageSessionConfig,\n type MultiPageSessionMetadata,\n} from './multi-page-session';\nimport {\n pageViewConfigSchema,\n pageViewMetadataSchema,\n type PageViewConfig,\n type PageViewMetadata,\n} from './page-view';\nimport {\n phoneClickConfigSchema,\n phoneClickMetadataSchema,\n type PhoneClickConfig,\n type PhoneClickMetadata,\n} from './phone-click';\nimport {\n scrollDepthConfigSchema,\n scrollDepthMetadataSchema,\n type ScrollDepthConfig,\n type ScrollDepthMetadata,\n} from './scroll-depth';\nimport {\n specificPageVisitConfigSchema,\n specificPageVisitMetadataSchema,\n type SpecificPageVisitConfig,\n type SpecificPageVisitMetadata,\n} from './specific-page-visit';\nimport {\n timeOnSiteConfigSchema,\n timeOnSiteMetadataSchema,\n type TimeOnSiteConfig,\n type TimeOnSiteMetadata,\n} from './time-on-site';\n\n// Event kind — automatic events are fired by the SDK itself when their\n// client-side signal fires (page_view on navigation, time_on_site on timer,\n// etc). Manual events are only fireable via explicit consumer code.\nexport type EventKind = 'automatic' | 'manual';\n\n// The registry is the single client-side source of truth for \"what events\n// exist, what shape does their metadata take, and what kind are they\". The\n// backend mirror lives in apps/api/src/schemas/tracking_events.py and is\n// kept in sync via the drift test in apps/api/tests/test_event_schema_drift.py.\nexport const EVENT_REGISTRY = {\n // --- automatic triggers ---\n page_view: {\n kind: 'automatic',\n metadataSchema: pageViewMetadataSchema,\n configSchema: pageViewConfigSchema,\n },\n time_on_site: {\n kind: 'automatic',\n metadataSchema: timeOnSiteMetadataSchema,\n configSchema: timeOnSiteConfigSchema,\n },\n specific_page_visit: {\n kind: 'automatic',\n metadataSchema: specificPageVisitMetadataSchema,\n configSchema: specificPageVisitConfigSchema,\n },\n scroll_depth: {\n kind: 'automatic',\n metadataSchema: scrollDepthMetadataSchema,\n configSchema: scrollDepthConfigSchema,\n },\n multi_page_session: {\n kind: 'automatic',\n metadataSchema: multiPageSessionMetadataSchema,\n configSchema: multiPageSessionConfigSchema,\n },\n form_start: {\n kind: 'automatic',\n metadataSchema: formStartMetadataSchema,\n configSchema: formStartConfigSchema,\n },\n // --- SDK-internal automatic (not consumer-configurable) ---\n sdk_heartbeat: {\n kind: 'automatic',\n metadataSchema: sdkHeartbeatMetadataSchema,\n configSchema: sdkHeartbeatConfigSchema,\n },\n // --- manual triggers ---\n form_submit: {\n kind: 'manual',\n metadataSchema: formSubmitMetadataSchema,\n configSchema: formSubmitConfigSchema,\n },\n phone_click: {\n kind: 'manual',\n metadataSchema: phoneClickMetadataSchema,\n configSchema: phoneClickConfigSchema,\n },\n cta_click: {\n kind: 'manual',\n metadataSchema: ctaClickMetadataSchema,\n configSchema: ctaClickConfigSchema,\n },\n} as const;\n\nexport type EventName = keyof typeof EVENT_REGISTRY;\n\n// Narrow the event name union based on `kind`.\nexport type AutomaticEventName = {\n [K in EventName]: (typeof EVENT_REGISTRY)[K]['kind'] extends 'automatic' ? K : never;\n}[EventName];\n\nexport type ManualEventName = {\n [K in EventName]: (typeof EVENT_REGISTRY)[K]['kind'] extends 'manual' ? K : never;\n}[EventName];\n\n// Map an event name to its explicit metadata / config TS type. We pull the\n// inferred types directly from the per-event files rather than deriving\n// via `z.infer<typeof EVENT_REGISTRY[K]['metadataSchema']>` so that error\n// messages name the event-specific type (PageViewMetadata, not an\n// anonymous zod inference).\ntype MetadataByName = {\n page_view: PageViewMetadata;\n time_on_site: TimeOnSiteMetadata;\n specific_page_visit: SpecificPageVisitMetadata;\n scroll_depth: ScrollDepthMetadata;\n multi_page_session: MultiPageSessionMetadata;\n form_start: FormStartMetadata;\n sdk_heartbeat: SdkHeartbeatMetadata;\n form_submit: FormSubmitMetadata;\n phone_click: PhoneClickMetadata;\n cta_click: CtaClickMetadata;\n};\n\ntype ConfigByName = {\n page_view: PageViewConfig;\n time_on_site: TimeOnSiteConfig;\n specific_page_visit: SpecificPageVisitConfig;\n scroll_depth: ScrollDepthConfig;\n multi_page_session: MultiPageSessionConfig;\n form_start: FormStartConfig;\n sdk_heartbeat: SdkHeartbeatConfig;\n form_submit: FormSubmitConfig;\n phone_click: PhoneClickConfig;\n cta_click: CtaClickConfig;\n};\n\nexport type EventMetadata<K extends EventName> = MetadataByName[K];\nexport type EventConfig<K extends EventName> = ConfigByName[K];\n\n// Runtime constant arrays for iteration at consumer / factory time.\nexport const ALL_AUTOMATIC_EVENT_NAMES: readonly AutomaticEventName[] = (\n Object.entries(EVENT_REGISTRY) as Array<[EventName, (typeof EVENT_REGISTRY)[EventName]]>\n)\n .filter(([, def]) => def.kind === 'automatic')\n .map(([name]) => name) as AutomaticEventName[];\n\nexport const ALL_MANUAL_EVENT_NAMES: readonly ManualEventName[] = (\n Object.entries(EVENT_REGISTRY) as Array<[EventName, (typeof EVENT_REGISTRY)[EventName]]>\n)\n .filter(([, def]) => def.kind === 'manual')\n .map(([name]) => name) as ManualEventName[];\n\n// The shape passed to createTracking({ triggers: ... }).\n//\n// `automatic.page_view` is **required** — if you're installing tracking you\n// always want page views, and forgetting to register it is a footgun we\n// prefer to catch at compile time. Every other automatic event is optional.\n// Manual events are all optional — register only the ones you actually\n// intend to fire from consumer code.\nexport type TriggerRegistryConfig = {\n automatic: {\n page_view: EventConfig<'page_view'>;\n } & Partial<{\n time_on_site: EventConfig<'time_on_site'>;\n specific_page_visit: EventConfig<'specific_page_visit'>;\n scroll_depth: EventConfig<'scroll_depth'>;\n multi_page_session: EventConfig<'multi_page_session'>;\n form_start: EventConfig<'form_start'>;\n }>;\n manual?: Partial<{\n form_submit: EventConfig<'form_submit'>;\n phone_click: EventConfig<'phone_click'>;\n cta_click: EventConfig<'cta_click'>;\n }>;\n};\n\n// Given a registry, which manual event names did the consumer actually\n// register? These are the only ones `trackEvent()` will accept as its\n// first argument. `NonNullable` collapses the case where `manual` was\n// omitted entirely.\nexport type RegisteredManualEvents<TRegistry extends TriggerRegistryConfig> =\n Extract<keyof NonNullable<TRegistry['manual']>, ManualEventName>;\n\n// Given a registry, which automatic event names did the consumer register?\n// Used by the factory to decide which auto-trigger attach helpers to wire up.\nexport type RegisteredAutomaticEvents<TRegistry extends TriggerRegistryConfig> =\n Extract<keyof TRegistry['automatic'], AutomaticEventName>;\n\n// Internal helper used by the factory: given a registry, return the full\n// discriminated union of valid (eventName, metadata) pairs the consumer\n// can pass to trackEvent().\nexport type TrackableEvent<TRegistry extends TriggerRegistryConfig> = {\n [K in RegisteredManualEvents<TRegistry>]: {\n eventType: K;\n metadata: EventMetadata<K>;\n };\n}[RegisteredManualEvents<TRegistry>];\n\n// Runtime helper: look up a schema pair by name. Cast through `unknown`\n// because the registry is `as const` and TS loses the specific schema type\n// when indexing via a dynamic key.\nexport function getEventDefinition(name: EventName): {\n kind: EventKind;\n metadataSchema: z.ZodTypeAny;\n configSchema: z.ZodTypeAny;\n} {\n return EVENT_REGISTRY[name] as unknown as {\n kind: EventKind;\n metadataSchema: z.ZodTypeAny;\n configSchema: z.ZodTypeAny;\n };\n}\n","// Typed facade over the low-level TrackingClient.\n//\n// The raw TrackingClient in ingest.ts is non-generic and accepts a\n// stringly-typed `TrackEventInput`. The typed facade wraps it to add:\n//\n// 1. A discriminated `trackEvent(eventType, metadata, options?)` signature\n// that only accepts manually-fireable events the consumer registered\n// via `createTracking({ triggers: { manual: { ... } } })`.\n// 2. Optional runtime validation that parses the metadata through the\n// event's Zod schema before forwarding to the raw client. Off by\n// default so prod pays zero cost, on when the factory is given\n// `debug: true`.\n//\n// Automatic events (page_view, time_on_site, specific_page_visit, etc.) are NOT\n// exposed through the typed facade — they're fired by the SDK internally\n// via the raw client, so consumer code that tries `trackEvent('page_view')`\n// is a compile error.\n\nimport { getEventDefinition } from './events/registry';\nimport type {\n EventMetadata,\n RegisteredManualEvents,\n TriggerRegistryConfig,\n} from './events/registry';\nimport type { TrackingClient } from './ingest';\n\nexport interface TypedTrackEventOptions {\n /** Override the page URL captured automatically. Rarely needed. */\n pageUrl?: string | null;\n /** Event timestamp override. Defaults to \"now\" at queue time. */\n occurredAt?: Date | string | null;\n}\n\nexport interface TypedTrackingClient<TRegistry extends TriggerRegistryConfig> {\n /**\n * Fire a manually-registered event. The event name must be present in\n * the registry's `manual` map and the metadata must match that event's\n * canonical Zod-derived shape.\n */\n trackEvent<K extends RegisteredManualEvents<TRegistry>>(\n eventType: K,\n metadata: EventMetadata<K>,\n options?: TypedTrackEventOptions,\n ): void;\n\n flush(): Promise<void>;\n getSessionId(): string;\n getVisitorId(): string;\n}\n\nexport interface CreateTypedClientOptions {\n /**\n * When true, every `trackEvent` call runs the metadata through the Zod\n * schema via `.parse()` before forwarding. Parse failures throw loudly,\n * which is what you want in dev. Prod should leave this off so a single\n * analytics event can never take down the host site.\n */\n debug?: boolean;\n}\n\nexport function createTypedClient<TRegistry extends TriggerRegistryConfig>(\n raw: TrackingClient,\n _registry: TRegistry,\n options: CreateTypedClientOptions = {},\n): TypedTrackingClient<TRegistry> {\n const debug = options.debug ?? false;\n\n return {\n trackEvent<K extends RegisteredManualEvents<TRegistry>>(\n eventType: K,\n metadata: EventMetadata<K>,\n opts?: TypedTrackEventOptions,\n ): void {\n // Runtime validation in dev only. Since `trackEvent` is typed at the\n // caller site, the TS compiler already guarantees the metadata shape\n // for well-behaved consumers — this parse() catches runtime bugs in\n // SDK-internal code that bypasses the types (e.g. dynamic payloads\n // passed from a JS-only consumer, or a string coming from localStorage).\n if (debug) {\n const def = getEventDefinition(eventType);\n def.metadataSchema.parse(metadata);\n }\n\n raw.trackEvent({\n eventType: eventType as string,\n metadata: metadata as unknown as Record<string, unknown> | null,\n pageUrl: opts?.pageUrl ?? null,\n occurredAt: opts?.occurredAt ?? null,\n });\n },\n\n flush: raw.flush.bind(raw),\n getSessionId: raw.getSessionId.bind(raw),\n getVisitorId: raw.getVisitorId.bind(raw),\n };\n}\n","// time_on_site automatic trigger.\n//\n// Fires a single `time_on_site` event once the user has been actively\n// engaged with the tab for at least `thresholdSeconds`. The timer is\n// visibility-aware — when the tab goes hidden (other tab focused, app\n// backgrounded) we pause the counter, and resume when it comes back. This\n// stops the \"user opened the tab in the background and walked away\"\n// scenario from artificially inflating engagement.\n//\n// The event fires at most once per page load.\n\nimport type { TimeOnSiteConfig } from '../events/time-on-site';\nimport type { TrackingClient } from '../ingest';\n\nexport function attachTimeOnSite(\n client: TrackingClient,\n config: TimeOnSiteConfig,\n): () => void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return () => {};\n }\n\n const thresholdMs = config.thresholdSeconds * 1000;\n let accumulatedMs = 0;\n let activeSince: number | null = document.visibilityState === 'visible' ? Date.now() : null;\n let timer: ReturnType<typeof setTimeout> | null = null;\n let fired = false;\n\n function fire(): void {\n if (fired) return;\n fired = true;\n client.trackEvent({\n eventType: 'time_on_site',\n metadata: {\n duration_ms: thresholdMs,\n page: { path: window.location.pathname },\n } as unknown as Record<string, unknown>,\n pageUrl: window.location.href,\n occurredAt: null,\n });\n }\n\n function scheduleNext(): void {\n if (fired || activeSince === null) return;\n const remaining = thresholdMs - accumulatedMs;\n if (remaining <= 0) {\n fire();\n return;\n }\n timer = setTimeout(fire, remaining);\n }\n\n function clearTimer(): void {\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n }\n\n function onVisibilityChange(): void {\n if (fired) return;\n if (document.visibilityState === 'hidden') {\n // Pause — drain the current interval into accumulatedMs.\n if (activeSince !== null) {\n accumulatedMs += Date.now() - activeSince;\n activeSince = null;\n }\n clearTimer();\n } else {\n // Resume.\n activeSince = Date.now();\n scheduleNext();\n }\n }\n\n document.addEventListener('visibilitychange', onVisibilityChange);\n scheduleNext();\n\n return () => {\n clearTimer();\n document.removeEventListener('visibilitychange', onVisibilityChange);\n };\n}\n","// specific_page_visit automatic trigger.\n//\n// Fires whenever the current path matches one of the consumer-supplied\n// patterns. Each pattern has a strongly typed page_name from the\n// SpecificPageName union. Dedupes so that repeated navigations to the same\n// (page_name, path) pair only fire once per page lifecycle.\n//\n// Replaces the old contact_page_visit trigger with multi-pattern support.\n\nimport type { SpecificPageVisitConfig } from '../events/specific-page-visit';\nimport type { TrackingClient } from '../ingest';\n\nexport function attachSpecificPageVisit(\n client: TrackingClient,\n config: SpecificPageVisitConfig,\n): () => void {\n if (typeof window === 'undefined' || typeof history === 'undefined') {\n return () => {};\n }\n\n const { pages } = config;\n const firedSet = new Set<string>();\n\n function check(): void {\n const path = window.location.pathname;\n for (const { name, pathPattern } of pages) {\n pathPattern.lastIndex = 0; // Reset in case consumer passed /g or /y flag\n if (!pathPattern.test(path)) continue;\n const key = `${name}:${path}`;\n if (firedSet.has(key)) continue;\n firedSet.add(key);\n client.trackEvent({\n eventType: 'specific_page_visit',\n metadata: { page_name: name, page: { path } } as unknown as Record<string, unknown>,\n pageUrl: window.location.href,\n occurredAt: null,\n });\n }\n }\n\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n function patchedPushState(this: History, ...args: Parameters<History['pushState']>): void {\n originalPushState(...args);\n setTimeout(check, 0);\n }\n function patchedReplaceState(this: History, ...args: Parameters<History['replaceState']>): void {\n originalReplaceState(...args);\n setTimeout(check, 0);\n }\n\n history.pushState = patchedPushState as History['pushState'];\n history.replaceState = patchedReplaceState as History['replaceState'];\n window.addEventListener('popstate', check);\n\n // Fire once on attach in case the current path already matches.\n check();\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener('popstate', check);\n };\n}\n","// scroll_depth automatic trigger.\n//\n// Installs a requestAnimationFrame-throttled scroll listener and fires once\n// per configured threshold per page. Resets the fired set on SPA navigation\n// when the pathname actually changes (pushState / popstate). replaceState\n// is ignored since it's commonly used for query param updates without a\n// real page transition.\n\nimport type { ScrollDepthConfig } from '../events/scroll-depth';\nimport type { TrackingClient } from '../ingest';\n\nexport function attachScrollDepth(\n client: TrackingClient,\n config: ScrollDepthConfig,\n): () => void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return () => {};\n }\n\n const thresholds = new Set(config.thresholds);\n let firedForPath = new Set<number>();\n let currentPath = window.location.pathname;\n let rafId: number | null = null;\n\n function getScrollPercent(): number {\n const doc = document.documentElement;\n const scrollTop = window.scrollY || doc.scrollTop;\n const scrollHeight = doc.scrollHeight;\n const clientHeight = doc.clientHeight;\n if (scrollHeight <= clientHeight) return 100;\n return Math.round(((scrollTop + clientHeight) / scrollHeight) * 100);\n }\n\n function checkThresholds(): void {\n const percent = getScrollPercent();\n for (const threshold of thresholds) {\n if (percent >= threshold && !firedForPath.has(threshold)) {\n firedForPath.add(threshold);\n client.trackEvent({\n eventType: 'scroll_depth',\n metadata: {\n depth_percent: threshold,\n page: { path: currentPath },\n } as unknown as Record<string, unknown>,\n pageUrl: window.location.href,\n occurredAt: null,\n });\n }\n }\n }\n\n function onScroll(): void {\n if (rafId !== null) return;\n rafId = requestAnimationFrame(() => {\n rafId = null;\n checkThresholds();\n });\n }\n\n function resetIfPathChanged(): void {\n const newPath = window.location.pathname;\n if (newPath === currentPath) return;\n currentPath = newPath;\n firedForPath = new Set();\n setTimeout(checkThresholds, 0);\n }\n\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n function patchedPushState(this: History, ...args: Parameters<History['pushState']>): void {\n originalPushState(...args);\n setTimeout(resetIfPathChanged, 0);\n }\n function patchedReplaceState(this: History, ...args: Parameters<History['replaceState']>): void {\n originalReplaceState(...args);\n // Only reset if pathname actually changed — replaceState is commonly\n // used for query param updates that shouldn't re-trigger thresholds.\n setTimeout(resetIfPathChanged, 0);\n }\n\n history.pushState = patchedPushState as History['pushState'];\n history.replaceState = patchedReplaceState as History['replaceState'];\n window.addEventListener('popstate', resetIfPathChanged);\n window.addEventListener('scroll', onScroll, { passive: true });\n\n // Initial check — handles short pages that don't require scrolling.\n setTimeout(checkThresholds, 0);\n\n return () => {\n if (rafId !== null) cancelAnimationFrame(rafId);\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener('popstate', resetIfPathChanged);\n window.removeEventListener('scroll', onScroll);\n };\n}\n","// multi_page_session automatic trigger.\n//\n// Tracks distinct page paths visited in the current session via sessionStorage.\n// Fires once when the count reaches the configured threshold. Resets when\n// the session ID changes (30-min idle rotation).\n\nimport type { MultiPageSessionConfig } from '../events/multi-page-session';\nimport type { TrackingClient } from '../ingest';\nimport { getOrRotateSessionId } from '../session';\n\nconst STORAGE_KEY = 'aranova_tracking_mps_paths';\nconst SESSION_KEY = 'aranova_tracking_mps_session';\nconst FIRED_KEY = 'aranova_tracking_mps_fired';\n\nfunction getSessionStorage(): Storage | null {\n try {\n return typeof window !== 'undefined' ? window.sessionStorage : null;\n } catch {\n return null;\n }\n}\n\nexport function attachMultiPageSession(\n client: TrackingClient,\n config: MultiPageSessionConfig,\n): () => void {\n if (typeof window === 'undefined' || typeof history === 'undefined') {\n return () => {};\n }\n\n const storage = getSessionStorage();\n if (!storage) return () => {};\n\n const { pageThreshold } = config;\n let lastCheckedPath = '';\n\n function getDistinctPaths(): Set<string> {\n try {\n const raw = storage!.getItem(STORAGE_KEY);\n return raw ? new Set(JSON.parse(raw) as string[]) : new Set();\n } catch {\n return new Set();\n }\n }\n\n function saveDistinctPaths(paths: Set<string>): void {\n try {\n storage!.setItem(STORAGE_KEY, JSON.stringify([...paths]));\n } catch {\n // sessionStorage full — degrade gracefully.\n }\n }\n\n function resetIfSessionChanged(): void {\n const currentSession = getOrRotateSessionId().id;\n const storedSession = storage!.getItem(SESSION_KEY);\n if (storedSession !== currentSession) {\n storage!.setItem(SESSION_KEY, currentSession);\n storage!.removeItem(STORAGE_KEY);\n storage!.removeItem(FIRED_KEY);\n }\n }\n\n function hasFired(): boolean {\n return storage!.getItem(FIRED_KEY) === '1';\n }\n\n function check(): void {\n // Skip if pathname hasn't changed — replaceState is commonly used for\n // query param updates and shouldn't trigger parse/serialize overhead.\n const currentPath = window.location.pathname;\n if (currentPath === lastCheckedPath) return;\n lastCheckedPath = currentPath;\n\n resetIfSessionChanged();\n\n if (hasFired()) return;\n\n const paths = getDistinctPaths();\n paths.add(currentPath);\n saveDistinctPaths(paths);\n\n if (paths.size >= pageThreshold) {\n storage!.setItem(FIRED_KEY, '1');\n client.trackEvent({\n eventType: 'multi_page_session',\n metadata: {\n page_count: paths.size,\n page: { path: window.location.pathname },\n } as unknown as Record<string, unknown>,\n pageUrl: window.location.href,\n occurredAt: null,\n });\n }\n }\n\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n function patchedPushState(this: History, ...args: Parameters<History['pushState']>): void {\n originalPushState(...args);\n setTimeout(check, 0);\n }\n function patchedReplaceState(this: History, ...args: Parameters<History['replaceState']>): void {\n originalReplaceState(...args);\n setTimeout(check, 0);\n }\n\n history.pushState = patchedPushState as History['pushState'];\n history.replaceState = patchedReplaceState as History['replaceState'];\n window.addEventListener('popstate', check);\n\n // Fire once on attach for the initial page.\n check();\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener('popstate', check);\n };\n}\n","// form_start automatic trigger.\n//\n// Uses focusin event delegation on document to detect the first field\n// interaction inside a <form>. Fires once per form per page lifecycle.\n// Dedupes via a Set keyed on form id/action. Resets on SPA navigation.\n\nimport type { FormStartConfig } from '../events/form-start';\nimport type { TrackingClient } from '../ingest';\n\nexport function attachFormStart(\n client: TrackingClient,\n config: FormStartConfig,\n): () => void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return () => {};\n }\n\n const selector = config.selector ?? 'form';\n let firedForms = new Set<string>();\n let currentPath = window.location.pathname;\n\n function getFormKey(form: HTMLFormElement): string {\n if (form.id) return `id:${form.id}`;\n // Use getAttribute — the DOM property always returns a resolved URL,\n // never \"\" or null, so it can't distinguish \"no action\" from \"action=current page\".\n const explicitAction = form.getAttribute('action');\n if (explicitAction) return `action:${explicitAction}`;\n const forms = Array.from(document.querySelectorAll(selector));\n return `index:${forms.indexOf(form)}`;\n }\n\n function onFocusIn(event: FocusEvent): void {\n const target = event.target;\n if (!(target instanceof HTMLElement)) return;\n\n // Walk up to the nearest matching <form>.\n const form = target.closest(selector) as HTMLFormElement | null;\n if (!form || form.tagName !== 'FORM') return;\n\n const key = getFormKey(form);\n if (firedForms.has(key)) return;\n firedForms.add(key);\n\n client.trackEvent({\n eventType: 'form_start',\n metadata: {\n form: {\n id: form.id || '',\n action: form.getAttribute('action') ?? null,\n },\n page: { path: window.location.pathname },\n } as unknown as Record<string, unknown>,\n pageUrl: window.location.href,\n occurredAt: null,\n });\n }\n\n function resetIfPathChanged(): void {\n const newPath = window.location.pathname;\n if (newPath === currentPath) return;\n currentPath = newPath;\n firedForms = new Set();\n }\n\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n function patchedPushState(this: History, ...args: Parameters<History['pushState']>): void {\n originalPushState(...args);\n setTimeout(resetIfPathChanged, 0);\n }\n function patchedReplaceState(this: History, ...args: Parameters<History['replaceState']>): void {\n originalReplaceState(...args);\n setTimeout(resetIfPathChanged, 0);\n }\n\n history.pushState = patchedPushState as History['pushState'];\n history.replaceState = patchedReplaceState as History['replaceState'];\n window.addEventListener('popstate', resetIfPathChanged);\n document.addEventListener('focusin', onFocusIn);\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener('popstate', resetIfPathChanged);\n document.removeEventListener('focusin', onFocusIn);\n };\n}\n","'use client';\n\nimport { useEffect } from 'react';\n\nimport { bootstrapGoogleAdsTracking } from '../../tracking-core/src/index';\n\nexport interface GoogleAdsTrackingProps {\n gtagId: string;\n}\n\nexport function GoogleAdsTracking({ gtagId }: GoogleAdsTrackingProps) {\n useEffect(() => {\n bootstrapGoogleAdsTracking(gtagId);\n }, [gtagId]);\n\n return null;\n}\n","'use client';\n\nimport { useEffect, useState } from 'react';\n\nimport { getConsentState } from '../../tracking-core/src/index';\nimport {\n createEmptyTrackingParams,\n getCookieValueFromDocument,\n getTrackingParamsFromCookieReader,\n type ConsentState,\n type TrackingParams,\n} from '../../tracking-core/src/index';\n\nexport function useGclid(): string | null {\n const [gclid, setGclid] = useState<string | null>(null);\n\n useEffect(() => {\n setGclid(getCookieValueFromDocument('gclid'));\n }, []);\n\n return gclid;\n}\n\nexport function useTrackingParams(): TrackingParams {\n const [trackingParams, setTrackingParams] = useState<TrackingParams>(createEmptyTrackingParams());\n\n useEffect(() => {\n setTrackingParams(getTrackingParamsFromCookieReader(getCookieValueFromDocument));\n }, []);\n\n return trackingParams;\n}\n\nexport function useConsentState(): ConsentState {\n const [consentState, setConsentStateValue] = useState<ConsentState>('pending');\n\n useEffect(() => {\n setConsentStateValue(getConsentState());\n\n const handleStorage = (event: StorageEvent): void => {\n if (event.key === 'consent_state')\n setConsentStateValue(getConsentState());\n };\n\n window.addEventListener('storage', handleStorage);\n\n return () => {\n window.removeEventListener('storage', handleStorage);\n };\n }, []);\n\n return consentState;\n}\n","'use client';\n\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n type ReactNode,\n} from 'react';\n\nimport { version } from '../package.json';\nimport {\n attachAutoPageView,\n attachBfcacheRestore,\n attachFormStart,\n attachMultiPageSession,\n attachScrollDepth,\n attachSpecificPageVisit,\n attachTimeOnSite,\n bootstrapGoogleAdsTracking,\n createTypedClient,\n getOrCreateTrackingClient,\n type TriggerRegistryConfig,\n type TypedTrackingClient,\n} from '../../tracking-core/src/index';\n\nexport interface CreateTrackingOptions<TRegistry extends TriggerRegistryConfig> {\n /** Public tracking API key issued for this business. Required. */\n apiKey: string;\n /** Full ingest endpoint base URL, e.g. https://api.aranova.io/tracking. Required. */\n endpoint: string;\n /**\n * Trigger registry. Determines which events the SDK fires automatically\n * and which ones the consumer can fire manually via `trackEvent()`.\n * `automatic.page_view` is required — every tracking install needs it.\n */\n triggers: TRegistry;\n /**\n * When true, the typed client validates every `trackEvent()` metadata\n * payload through the Zod schema before forwarding. Errors are thrown\n * loudly. Leave off in prod; turn on in dev to catch shape bugs early.\n */\n debug?: boolean;\n}\n\nexport interface TrackingProviderProps {\n /** Optional Google Ads gtag id. If omitted, no gtag script is loaded. */\n gtagId?: string;\n children: ReactNode;\n}\n\nexport interface CreateTrackingResult<TRegistry extends TriggerRegistryConfig> {\n TrackingProvider: (props: TrackingProviderProps) => ReactNode;\n useTracking: () => TypedTrackingClient<TRegistry>;\n}\n\n/**\n * Create a scoped TrackingProvider + useTracking hook for a specific\n * trigger registry. Call this once at app startup (e.g. in a shared\n * `lib/tracking.ts` file) and import the returned `TrackingProvider` /\n * `useTracking` from that module, not from `@aranova/tracking-react`\n * directly. This lets TypeScript thread the registry type through every\n * consumer so `trackEvent()` autocompletes + rejects unregistered events.\n *\n * Example:\n *\n * ```ts\n * // src/lib/tracking.ts\n * import { createTracking } from '@aranova/tracking-react';\n *\n * export const { TrackingProvider, useTracking } = createTracking({\n * apiKey: import.meta.env.VITE_ARANOVA_TRACKING_API_KEY,\n * endpoint: import.meta.env.VITE_ARANOVA_TRACKING_ENDPOINT,\n * triggers: {\n * automatic: {\n * page_view: {},\n * time_on_site: { thresholdSeconds: 60 },\n * },\n * manual: {\n * form_submit: {},\n * },\n * },\n * });\n * ```\n */\n// No-op client returned when apiKey/endpoint are missing. Every method\n// is a silent no-op so the host app continues running without tracking.\nconst NOOP_CLIENT: TypedTrackingClient<TriggerRegistryConfig> = {\n trackEvent: () => {},\n flush: async () => {},\n getSessionId: () => '',\n getVisitorId: () => '',\n};\n\nexport function createTracking<TRegistry extends TriggerRegistryConfig>(\n options: CreateTrackingOptions<TRegistry>,\n): CreateTrackingResult<TRegistry> {\n const { apiKey, endpoint, triggers, debug } = options;\n\n // Graceful degradation: if credentials are missing, warn once and return\n // a no-op provider + hook. The app keeps running — tracking is simply\n // disabled. This lets clients deploy without tracking env vars set (e.g.\n // preview environments, local dev without a seeded key) without crashing\n // on startup.\n if (!apiKey || !endpoint) {\n if (apiKey || endpoint) {\n // eslint-disable-next-line no-console\n console.warn(\n '[AranovaTracking] createTracking() requires both `apiKey` and `endpoint`. ' +\n 'Tracking is disabled for this session.',\n );\n }\n\n const noopTyped = NOOP_CLIENT as unknown as TypedTrackingClient<TRegistry>;\n return {\n TrackingProvider: ({ children }: TrackingProviderProps) => children,\n useTracking: () => noopTyped,\n };\n }\n\n const TrackingContext = createContext<TypedTrackingClient<TRegistry> | null>(null);\n\n function TrackingProvider({ gtagId, children }: TrackingProviderProps): ReactNode {\n // Page-level singleton raw client. Surviving strict-mode unmount +\n // remount is handled by getOrCreateTrackingClient itself.\n const client = useMemo(\n () =>\n createTypedClient<TRegistry>(\n getOrCreateTrackingClient({\n apiKey,\n endpoint,\n surface: 'react',\n packageName: '@aranova/tracking-react',\n sdkVersion: version,\n triggers,\n debug,\n }),\n triggers,\n { debug },\n ),\n [],\n );\n\n useEffect(() => {\n if (!gtagId) return;\n bootstrapGoogleAdsTracking(gtagId);\n }, [gtagId]);\n\n // Attach the automatic triggers declared in the registry. Each\n // returns its own detach function; we call them all in reverse order\n // on cleanup so layered pushState patches restore cleanly.\n useEffect(() => {\n const detachers: Array<() => void> = [];\n const rawClient = getOrCreateTrackingClient({\n apiKey,\n endpoint,\n surface: 'react',\n packageName: '@aranova/tracking-react',\n triggers,\n debug,\n });\n\n // page_view is always present (required in the type).\n detachers.push(attachAutoPageView(rawClient));\n detachers.push(attachBfcacheRestore(rawClient));\n\n const timeOnSite = triggers.automatic.time_on_site;\n if (timeOnSite) {\n detachers.push(attachTimeOnSite(rawClient, timeOnSite));\n }\n\n const specificPageVisit = triggers.automatic.specific_page_visit;\n if (specificPageVisit) {\n detachers.push(attachSpecificPageVisit(rawClient, specificPageVisit));\n }\n\n const scrollDepth = triggers.automatic.scroll_depth;\n if (scrollDepth) {\n detachers.push(attachScrollDepth(rawClient, scrollDepth));\n }\n\n const multiPageSession = triggers.automatic.multi_page_session;\n if (multiPageSession) {\n detachers.push(attachMultiPageSession(rawClient, multiPageSession));\n }\n\n const formStart = triggers.automatic.form_start;\n if (formStart) {\n detachers.push(attachFormStart(rawClient, formStart));\n }\n\n // IMPORTANT: Detach in reverse (LIFO) order. Multiple triggers patch\n // history.pushState by capturing the current value at attach time and\n // restoring it on detach. This only composes correctly when the last\n // attached trigger is the first to detach — otherwise stale function\n // references are restored and earlier triggers' patches are lost.\n return () => {\n for (let i = detachers.length - 1; i >= 0; i--) {\n detachers[i]();\n }\n };\n }, []);\n\n return <TrackingContext.Provider value={client}>{children}</TrackingContext.Provider>;\n }\n\n function useTracking(): TypedTrackingClient<TRegistry> {\n const client = useContext(TrackingContext);\n if (client === null) {\n throw new Error(\n 'useTracking must be called inside a <TrackingProvider> returned by createTracking()',\n );\n }\n return client;\n }\n\n return { TrackingProvider, useTracking };\n}\n","{\n \"name\": \"@aranova/tracking-react\",\n \"version\": \"0.6.0\",\n \"private\": false,\n \"description\": \"React tracking and consent utilities for Aranova client sites\",\n \"files\": [\n \"dist\",\n \"README.md\"\n ],\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.mjs\",\n \"types\": \"./dist/index.d.ts\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/AranovaIO/aranova_internal.git\",\n \"directory\": \"packages/tracking-react\"\n },\n \"homepage\": \"https://github.com/AranovaIO/aranova_internal/tree/master/packages/tracking-react\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.mjs\",\n \"require\": \"./dist/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"test\": \"vitest\",\n \"test:run\": \"vitest run\",\n \"prepublishOnly\": \"npm run build\"\n },\n \"peerDependencies\": {\n \"react\": \">=18.0.0\"\n },\n \"dependencies\": {\n \"zod\": \"^3.24.1\"\n },\n \"devDependencies\": {\n \"@types/react\": \"^19.2.2\",\n \"@types/react-dom\": \"^19.2.2\",\n \"jsdom\": \"^29.0.1\",\n \"react\": \"^19.2.0\",\n \"react-dom\": \"^19.2.0\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^5.7.3\",\n \"vitest\": \"^4.1.4\"\n },\n \"publishConfig\": {\n \"registry\": \"https://registry.npmjs.org\",\n \"access\": \"public\"\n }\n}\n"],"mappings":"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","names":["z","z","z","z","z","z","z","z","z","useEffect","useEffect","useEffect","useState","useState","useEffect","useEffect","jsx","useEffect"]}
1
+ {"version":3,"sources":["../src/ConsentBanner.tsx","../../tracking-core/src/consent.ts","../../tracking-core/src/payloads.ts","../../tracking-core/src/gtag.ts","../../tracking-core/src/tracking.ts","../../tracking-core/src/session.ts","../../tracking-core/src/events/page-view.ts","../../tracking-core/src/page-view.ts","../../tracking-core/src/heartbeat.ts","../../tracking-core/src/ingest.ts","../../tracking-core/src/events/cta-click.ts","../../tracking-core/src/events/sdk-heartbeat.ts","../../tracking-core/src/events/form-start.ts","../../tracking-core/src/events/form-submit.ts","../../tracking-core/src/events/multi-page-session.ts","../../tracking-core/src/events/phone-click.ts","../../tracking-core/src/events/scroll-depth.ts","../../tracking-core/src/events/specific-page-visit.ts","../../tracking-core/src/events/time-on-site.ts","../../tracking-core/src/events/registry.ts","../../tracking-core/src/ingest-typed.ts","../../tracking-core/src/triggers/time-on-site.ts","../../tracking-core/src/triggers/specific-page-visit.ts","../../tracking-core/src/triggers/scroll-depth.ts","../../tracking-core/src/triggers/multi-page-session.ts","../../tracking-core/src/triggers/form-start.ts","../src/GoogleAdsTracking.tsx","../src/hooks.ts","../src/factory.tsx","../package.json"],"sourcesContent":["'use client';\n\nimport { useEffect, useState } from 'react';\n\nimport { getConsentState, setConsentState, type ConsentState } from '../../tracking-core/src/index';\n\n/**\n * Default non-blocking consent banner.\n *\n * Renders only while consent is `pending`. Accept/decline choices are stored\n * in localStorage and propagated to Google Consent Mode when gtag is loaded.\n */\nexport function ConsentBanner() {\n const [consentState, setBannerConsentState] = useState<ConsentState>('pending');\n const [isReady, setIsReady] = useState(false);\n\n useEffect(() => {\n setBannerConsentState(getConsentState());\n setIsReady(true);\n }, []);\n\n if (!isReady || consentState !== 'pending')\n return null;\n\n const handleAccept = (): void => {\n setConsentState('granted');\n setBannerConsentState('granted');\n };\n\n const handleDecline = (): void => {\n setConsentState('denied');\n setBannerConsentState('denied');\n };\n\n return (\n <div className=\"fixed bottom-0 left-0 right-0 z-50 border-t border-gray-200 bg-white p-4 shadow-lg\">\n <div className=\"mx-auto flex max-w-5xl flex-col gap-4 md:flex-row md:items-center md:justify-between\">\n <p className=\"text-sm text-gray-700\">\n We use cookies to understand ad performance and improve how our marketing works across visits.\n You can accept or decline this tracking.\n </p>\n <div className=\"flex flex-col gap-2 sm:flex-row\">\n <button\n type=\"button\"\n onClick={handleDecline}\n className=\"rounded-md border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 transition hover:bg-gray-100\"\n >\n Decline\n </button>\n <button\n type=\"button\"\n onClick={handleAccept}\n className=\"rounded-md bg-gray-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-gray-800\"\n >\n Accept\n </button>\n </div>\n </div>\n </div>\n );\n}\n","import type { ConsentState } from './types';\n\n/**\n * localStorage key for the visitor's consent choice.\n */\nexport const CONSENT_STATE_KEY = 'consent_state';\n\n/**\n * localStorage key for the ISO timestamp when consent was last changed.\n */\nexport const CONSENT_TIMESTAMP_KEY = 'consent_timestamp';\n\n/**\n * Google Consent Mode value sent to `gtag('consent', 'update', ...)`.\n */\nexport type GtagConsentValue = 'granted' | 'denied';\n\n/**\n * Consent Mode v2 payload sent to Google Ads when consent changes.\n */\nexport interface ConsentUpdatePayload {\n ad_storage: GtagConsentValue;\n ad_user_data: GtagConsentValue;\n ad_personalization: GtagConsentValue;\n analytics_storage: GtagConsentValue;\n}\n\n/**\n * Build the Google Consent Mode v2 update payload for a single consent state.\n */\nexport function buildConsentPayload(state: GtagConsentValue): ConsentUpdatePayload {\n return {\n ad_storage: state,\n ad_user_data: state,\n ad_personalization: state,\n analytics_storage: state,\n };\n}\n\n/**\n * Read the persisted visitor consent state from localStorage.\n *\n * Returns `pending` when called during SSR or before the visitor has made a\n * choice.\n */\nexport function getConsentState(): ConsentState {\n if (typeof window === 'undefined')\n return 'pending';\n\n const storedState = window.localStorage.getItem(CONSENT_STATE_KEY);\n\n if (storedState === 'granted' || storedState === 'denied')\n return storedState;\n\n return 'pending';\n}\n\n/**\n * Persist a visitor consent choice and update Google Consent Mode when gtag is\n * loaded.\n */\nexport function setConsentState(state: GtagConsentValue): void {\n if (typeof window === 'undefined')\n return;\n\n window.localStorage.setItem(CONSENT_STATE_KEY, state);\n window.localStorage.setItem(CONSENT_TIMESTAMP_KEY, new Date().toISOString());\n\n if (typeof window.gtag === 'function')\n window.gtag('consent', 'update', buildConsentPayload(state));\n}\n\n/**\n * Re-apply a stored consent choice to Google Consent Mode.\n *\n * Useful after bootstrapping gtag on a page that already has a saved choice.\n */\nexport function restoreStoredConsent(): ConsentState {\n const consentState = getConsentState();\n\n if (consentState === 'granted' || consentState === 'denied')\n setConsentState(consentState);\n\n return consentState;\n}\n","import type {\n TrackingClientContext,\n TrackingEventCreatePayload,\n TrackingInstallSurface,\n TrackingParams,\n TrackingSessionUpsertPayload,\n} from './types';\n\ninterface TrackingContextInput {\n packageName?: string | null;\n pageTitle?: string | null;\n referrer?: string | null;\n sdkVersion?: string | null;\n siteOrigin?: string | null;\n}\n\ninterface TrackingEventInput {\n eventType: string;\n metadata?: Record<string, unknown> | null;\n pageUrl?: string | null;\n sessionId: string;\n}\n\ninterface TrackingSessionInput {\n consentState?: Record<string, unknown> | null;\n firstPage?: string | null;\n sessionId: string;\n visitorId?: string | null;\n}\n\n/**\n * Build runtime context attached to tracking sessions and events.\n */\nexport function createTrackingClientContext(\n surface: TrackingInstallSurface,\n input: TrackingContextInput = {},\n): TrackingClientContext {\n return {\n surface,\n sdk_version: input.sdkVersion ?? null,\n package_name: input.packageName ?? null,\n site_origin: input.siteOrigin ?? (typeof window === 'undefined' ? null : window.location.origin),\n page_title: input.pageTitle ?? (typeof document === 'undefined' ? null : document.title || null),\n referrer: input.referrer ?? (typeof document === 'undefined' ? null : document.referrer || null),\n };\n}\n\n/**\n * Build the session portion of a tracking ingest request.\n */\nexport function createTrackingSessionUpsertPayload(\n trackingParams: TrackingParams,\n input: TrackingSessionInput,\n context: TrackingClientContext,\n): TrackingSessionUpsertPayload {\n return {\n session_id: input.sessionId,\n visitor_id: input.visitorId ?? null,\n gclid: trackingParams.gclid,\n fbclid: trackingParams.fbclid,\n utm_source: trackingParams.utm_source,\n utm_medium: trackingParams.utm_medium,\n utm_campaign: trackingParams.utm_campaign,\n utm_term: trackingParams.utm_term,\n utm_content: trackingParams.utm_content,\n first_page: input.firstPage ?? null,\n consent_state: input.consentState ?? null,\n context,\n };\n}\n\n/**\n * Build one event payload before it is batched into a tracking ingest request.\n */\nexport function createTrackingEventCreatePayload(\n trackingParams: TrackingParams,\n input: TrackingEventInput,\n context: TrackingClientContext,\n): TrackingEventCreatePayload {\n return {\n session_id: input.sessionId,\n event_type: input.eventType,\n gclid: trackingParams.gclid,\n fbclid: trackingParams.fbclid,\n page_url: input.pageUrl ?? (typeof window === 'undefined' ? null : window.location.href),\n metadata: input.metadata ?? null,\n context,\n };\n}\n","import { buildConsentPayload, getConsentState, restoreStoredConsent } from './consent';\n\n/**\n * Google Tag Manager script host used for gtag loading.\n */\nexport const GTAG_SCRIPT_HOST = 'https://www.googletagmanager.com/gtag/js';\n\n/**\n * Attribute used to mark scripts inserted by the tracking SDK.\n */\nexport const TRACKING_SCRIPT_ATTRIBUTE = 'data-aranova-tracking';\n\nfunction getScriptMarker(id: string): string {\n return `aranova-${id}`;\n}\n\n/**\n * Create an inline script that initializes gtag with denied consent defaults.\n */\nexport function createConsentDefaultScript(): string {\n return `\nwindow.dataLayer = window.dataLayer || [];\nfunction gtag(){dataLayer.push(arguments);}\nwindow.gtag = gtag;\ngtag('consent', 'default', {\n ad_storage: 'denied',\n ad_user_data: 'denied',\n ad_personalization: 'denied',\n analytics_storage: 'denied',\n wait_for_update: 500,\n});\n`.trim();\n}\n\n/**\n * Create an inline script that initializes gtag for a Google Ads tag id.\n */\nexport function createGtagInitScript(gtagId: string): string {\n return `\nwindow.dataLayer = window.dataLayer || [];\nfunction gtag(){dataLayer.push(arguments);}\nwindow.gtag = gtag;\ngtag('js', new Date());\ngtag('config', '${gtagId}');\n`.trim();\n}\n\n/**\n * Create an inline script that restores a stored consent choice into gtag.\n */\nexport function createConsentRestoreScript(): string {\n return `\nconst consentState = window.localStorage.getItem('consent_state');\nif (window.gtag && (consentState === 'granted' || consentState === 'denied')) {\n window.gtag('consent', 'update', {\n ad_storage: consentState,\n ad_user_data: consentState,\n ad_personalization: consentState,\n analytics_storage: consentState,\n });\n}\n`.trim();\n}\n\n/**\n * Ensure `window.gtag` and `window.dataLayer` exist and return the gtag shim.\n */\nexport function ensureGtagFunction(): NonNullable<Window['gtag']> {\n window.dataLayer = window.dataLayer || [];\n\n if (typeof window.gtag === 'function')\n return window.gtag;\n\n window.gtag = (...args: unknown[]) => {\n window.dataLayer?.push(args);\n };\n\n return window.gtag;\n}\n\n/**\n * Apply denied-by-default Google Consent Mode state.\n */\nexport function applyDefaultConsentState(): void {\n const gtag = ensureGtagFunction();\n gtag('consent', 'default', {\n ad_storage: 'denied',\n ad_user_data: 'denied',\n ad_personalization: 'denied',\n analytics_storage: 'denied',\n wait_for_update: 500,\n });\n}\n\n/**\n * Load the external gtag script once for the provided Google Ads tag id.\n */\nexport function loadGtagScript(gtagId: string): void {\n if (typeof document === 'undefined')\n return;\n\n const marker = getScriptMarker('gtag-loader');\n const existingScript = document.querySelector<HTMLScriptElement>(`script[${TRACKING_SCRIPT_ATTRIBUTE}=\"${marker}\"]`);\n if (existingScript)\n return;\n\n const script = document.createElement('script');\n script.async = true;\n script.src = `${GTAG_SCRIPT_HOST}?id=${encodeURIComponent(gtagId)}`;\n script.setAttribute(TRACKING_SCRIPT_ATTRIBUTE, marker);\n document.head.appendChild(script);\n}\n\n/**\n * Send the initial `gtag('js')` and `gtag('config')` calls.\n */\nexport function initializeGtag(gtagId: string): void {\n const gtag = ensureGtagFunction();\n gtag('js', new Date());\n gtag('config', gtagId);\n}\n\n/**\n * Push the stored consent choice to gtag if one exists.\n */\nexport function restoreConsentState(): void {\n if (typeof window === 'undefined')\n return;\n\n const consentState = getConsentState();\n if (consentState === 'granted' || consentState === 'denied')\n window.gtag?.('consent', 'update', buildConsentPayload(consentState));\n}\n\n/**\n * Load and initialize Google Ads tracking with Consent Mode support.\n *\n * Used by framework components and browser-script initialization.\n */\nexport function bootstrapGoogleAdsTracking(gtagId: string): void {\n if (typeof window === 'undefined' || typeof document === 'undefined')\n return;\n\n applyDefaultConsentState();\n loadGtagScript(gtagId);\n initializeGtag(gtagId);\n restoreStoredConsent();\n}\n","import type { TrackingParams } from './types';\n\n/**\n * Default attribution cookie lifetime: 90 days.\n */\nexport const TRACKING_COOKIE_MAX_AGE_SECONDS = 7_776_000;\n\n/**\n * Attribution query/cookie keys captured by the SDK.\n */\nexport const TRACKING_PARAM_KEYS = [\n 'gclid',\n 'fbclid',\n 'utm_source',\n 'utm_medium',\n 'utm_campaign',\n 'utm_term',\n 'utm_content',\n] as const;\n\nexport type TrackingParamKey = (typeof TRACKING_PARAM_KEYS)[number];\n\n/**\n * Create an all-null attribution parameter object.\n */\nexport function createEmptyTrackingParams(): TrackingParams {\n return {\n gclid: null,\n fbclid: null,\n utm_source: null,\n utm_medium: null,\n utm_campaign: null,\n utm_term: null,\n utm_content: null,\n };\n}\n\n/**\n * Normalize a raw cookie value into a tracking value.\n *\n * Empty strings and non-string values become `null`.\n */\nexport function normalizeTrackingCookieValue(value: unknown): string | null {\n return typeof value === 'string' && value.length > 0 ? value : null;\n}\n\n/**\n * Read all tracking params using the provided cookie reader.\n *\n * This is shared by browser, React, and Next server integrations.\n */\nexport function getTrackingParamsFromCookieReader(\n readCookie: (key: TrackingParamKey) => unknown,\n): TrackingParams {\n return TRACKING_PARAM_KEYS.reduce<TrackingParams>((params, key) => {\n params[key] = normalizeTrackingCookieValue(readCookie(key));\n return params;\n }, createEmptyTrackingParams());\n}\n\n/**\n * Extract tracking params from URL search params.\n *\n * Only non-empty values are returned.\n */\nexport function getTrackingQueryValues(\n searchParams: Pick<URLSearchParams, 'get'>,\n): Partial<Record<TrackingParamKey, string>> {\n return TRACKING_PARAM_KEYS.reduce<Partial<Record<TrackingParamKey, string>>>((params, key) => {\n const value = searchParams.get(key);\n\n if (typeof value === 'string' && value.trim().length > 0) {\n params[key] = value;\n }\n\n return params;\n }, {});\n}\n\n/**\n * Read a tracking cookie from `document.cookie`.\n *\n * Returns `null` during SSR or when the cookie is missing.\n */\nexport function getCookieValueFromDocument(key: TrackingParamKey): string | null {\n if (typeof document === 'undefined')\n return null;\n\n const cookies = document.cookie ? document.cookie.split('; ') : [];\n const match = cookies.find((cookie) => cookie.startsWith(`${key}=`));\n\n if (!match)\n return null;\n\n const [, rawValue = ''] = match.split('=');\n return normalizeTrackingCookieValue(decodeURIComponent(rawValue));\n}\n\n/**\n * Persist one attribution value as a first-party cookie.\n */\nexport function setTrackingCookie(\n key: TrackingParamKey,\n value: string,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): void {\n if (typeof document === 'undefined')\n return;\n\n const encodedValue = encodeURIComponent(value);\n document.cookie = `${key}=${encodedValue}; Max-Age=${maxAgeSeconds}; Path=/; SameSite=Lax`;\n}\n\n/**\n * Capture any tracking params present in a URL search parameter source and\n * persist them to first-party cookies.\n *\n * Returns the subset of params that were found and persisted.\n */\nexport function persistTrackingParamsFromSearchParams(\n searchParams: Pick<URLSearchParams, 'get'>,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): Partial<Record<TrackingParamKey, string>> {\n const trackingValues = getTrackingQueryValues(searchParams);\n\n Object.entries(trackingValues).forEach(([key, value]) => {\n setTrackingCookie(key as TrackingParamKey, value, maxAgeSeconds);\n });\n\n return trackingValues;\n}\n\n/**\n * Capture tracking params from a URL, persist them to first-party cookies, and\n * return the current cookie-backed attribution state.\n *\n * Defaults to `window.location.href` in the browser.\n */\nexport function captureTrackingParamsFromLocation(\n url = typeof window === 'undefined' ? '' : window.location.href,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): TrackingParams {\n const resolvedUrl = typeof window === 'undefined'\n ? new URL(url || 'https://example.invalid')\n : new URL(url, window.location.origin);\n persistTrackingParamsFromSearchParams(resolvedUrl.searchParams, maxAgeSeconds);\n return getTrackingParamsFromCookieReader(getCookieValueFromDocument);\n}\n","// Visitor + session identity for the tracking SDK.\n//\n// Visitor: persistent localStorage UUID, never expires until the user clears\n// browser storage. Used for cross-session correlation.\n//\n// Session: rolling 30-minute idle window. Regenerated when more than\n// SESSION_IDLE_MS has passed since the last event. Matches the behavior of\n// GA4, PostHog, Mixpanel, etc., so analytics is comparable.\n\n/**\n * localStorage key for the persistent visitor id.\n */\nexport const VISITOR_STORAGE_KEY = 'aranova_tracking_visitor';\n\n/**\n * localStorage key for the rolling session id state.\n */\nexport const SESSION_STORAGE_KEY = 'aranova_tracking_session';\n\n/**\n * Idle window before a new session id is created.\n */\nexport const SESSION_IDLE_MS = 30 * 60 * 1000;\n\n/**\n * Serialized session state stored in localStorage.\n */\nexport interface StoredSession {\n /** Client-generated session UUID. */\n id: string;\n /** Unix timestamp in milliseconds for the most recent event/session touch. */\n last_event_at: number;\n}\n\nfunction safeUuid(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function')\n return crypto.randomUUID();\n // Fallback for ancient browsers — not cryptographically perfect but unique enough.\n return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}-${Math.random().toString(16).slice(2)}`;\n}\n\nfunction readLocalStorage(key: string): string | null {\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeLocalStorage(key: string, value: string): void {\n try {\n window.localStorage.setItem(key, value);\n } catch {\n // Storage may be denied (private mode, blocked cookies, quota). Caller is\n // responsible for degrading gracefully.\n }\n}\n\n/**\n * Return the persistent visitor id for this browser profile.\n *\n * Creates and stores a new id when one does not already exist. During SSR,\n * returns an ephemeral id because browser storage is unavailable.\n */\nexport function getVisitorId(): string {\n if (typeof window === 'undefined')\n return safeUuid();\n\n const existing = readLocalStorage(VISITOR_STORAGE_KEY);\n if (existing && existing.length > 0)\n return existing;\n\n const fresh = safeUuid();\n writeLocalStorage(VISITOR_STORAGE_KEY, fresh);\n return fresh;\n}\n\n/**\n * Result from `getOrRotateSessionId()`.\n */\nexport interface SessionIdResult {\n /** Current session id. */\n id: string;\n /** Whether this call created a new session. */\n isNew: boolean;\n}\n\n/**\n * Return the current session id, rotating it after the idle window expires.\n *\n * Also refreshes `last_event_at` for active sessions.\n */\nexport function getOrRotateSessionId(now: number = Date.now()): SessionIdResult {\n if (typeof window === 'undefined')\n return { id: safeUuid(), isNew: true };\n\n const raw = readLocalStorage(SESSION_STORAGE_KEY);\n if (raw) {\n try {\n const parsed = JSON.parse(raw) as Partial<StoredSession>;\n if (typeof parsed.id === 'string' && typeof parsed.last_event_at === 'number') {\n if (now - parsed.last_event_at <= SESSION_IDLE_MS) {\n const refreshed: StoredSession = { id: parsed.id, last_event_at: now };\n writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(refreshed));\n return { id: parsed.id, isNew: false };\n }\n }\n } catch {\n // Fall through to a fresh session.\n }\n }\n\n const fresh: StoredSession = { id: safeUuid(), last_event_at: now };\n writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(fresh));\n return { id: fresh.id, isNew: true };\n}\n\n/**\n * Clear visitor and session identity from localStorage.\n *\n * Intended for tests, debugging, and explicit user reset flows.\n */\nexport function resetTrackingIdentity(): void {\n if (typeof window === 'undefined')\n return;\n try {\n window.localStorage.removeItem(VISITOR_STORAGE_KEY);\n window.localStorage.removeItem(SESSION_STORAGE_KEY);\n } catch {\n // ignored\n }\n}\n","import { z } from 'zod';\n\n/**\n * Metadata for the automatic `page_view` event.\n *\n * The SDK emits this on initial load, SPA route changes, and bfcache restores.\n * Consumers do not call `trackEvent('page_view', ...)`; registering\n * `automatic: { page_view: {} }` enables the SDK-owned trigger.\n */\nexport const pageViewMetadataSchema = z\n .object({\n page: z\n .object({\n title: z.string().nullable(),\n path: z.string(),\n search: z.string(),\n hash: z.string(),\n })\n .strict(),\n referrer: z.string().nullable(),\n // `.nullable().optional()` — absent (undefined) OR explicit null OR a\n // real viewport object. Mirrors Pydantic's `_Viewport | None = None`\n // on the backend side so the drift test stays clean.\n viewport: z\n .object({\n w: z.number(),\n h: z.number(),\n })\n .strict()\n .nullable()\n .optional(),\n })\n .strict();\n\nexport type PageViewMetadata = z.infer<typeof pageViewMetadataSchema>;\n\n/**\n * Registration config for automatic `page_view`.\n *\n * `page_view` is required in every trigger registry and currently has no\n * options. Use `{ page_view: {} }`.\n */\nexport const pageViewConfigSchema = z.object({}).strict();\nexport type PageViewConfig = z.infer<typeof pageViewConfigSchema>;\n","// Auto page_view emission. Fires once on attach, then again on every SPA\n// navigation. Detects navigation via three signals:\n// - history.pushState (patched)\n// - history.replaceState (patched)\n// - popstate event (back/forward buttons)\n// - pageshow with event.persisted === true (back-forward cache restore)\n//\n// The Next.js provider can opt out of this and use `usePathname` instead so\n// it picks up App Router transitions reliably without monkey-patching globals.\n\nimport { pageViewMetadataSchema, type PageViewMetadata } from './events/page-view';\nimport type { TrackingClient } from './ingest';\n\n// Re-export so existing callers that import `PageViewMetadata` from\n// `./page-view` keep compiling. The canonical definition now lives in\n// `events/page-view.ts` and is derived from a Zod schema.\nexport type { PageViewMetadata } from './events/page-view';\n\n/**\n * Session-storage key used to maintain the in-tab SPA referrer chain.\n */\nexport const LAST_FIRED_URL_STORAGE_KEY = 'aranova_tracking_last_fired_url';\n\n// Tracks the previous in-session URL we fired a page_view for. SPA\n// navigation never updates `document.referrer` (the document is never\n// reloaded), so all events would otherwise share the same stale referrer\n// value. We track it ourselves: the referrer for an event at URL X is the\n// last URL we fired a page_view for, falling back to document.referrer for\n// the very first event of the session.\n//\n// Hybrid storage: the in-memory variable is the runtime source of truth\n// (cheap reads, no quota concerns), and sessionStorage is the durability\n// layer (survives full-page reloads and bfcache restoration). On first\n// read per JS context we lazily hydrate from sessionStorage; on every\n// write we update both layers. If sessionStorage is unavailable (private\n// mode, blocked extensions, quota errors) we silently fall back to\n// memory-only — the SDK stays functional, just loses cross-reload\n// referrer continuity in that one tab.\nlet lastFiredUrl: string | null = null;\nlet lastFiredUrlHydrated = false;\n\nfunction readSessionStorage(key: string): string | null {\n try {\n if (typeof window === 'undefined') return null;\n return window.sessionStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeSessionStorage(key: string, value: string): void {\n try {\n if (typeof window === 'undefined') return;\n window.sessionStorage.setItem(key, value);\n } catch {\n // private mode / blocked / quota — memory-only is fine\n }\n}\n\nfunction deleteSessionStorage(key: string): void {\n try {\n if (typeof window === 'undefined') return;\n window.sessionStorage.removeItem(key);\n } catch {\n // ignore\n }\n}\n\nfunction getLastFiredUrl(): string | null {\n if (!lastFiredUrlHydrated) {\n lastFiredUrlHydrated = true;\n const stored = readSessionStorage(LAST_FIRED_URL_STORAGE_KEY);\n if (stored !== null) lastFiredUrl = stored;\n }\n return lastFiredUrl;\n}\n\nfunction setLastFiredUrl(url: string): void {\n lastFiredUrl = url;\n lastFiredUrlHydrated = true;\n writeSessionStorage(LAST_FIRED_URL_STORAGE_KEY, url);\n}\n\n/**\n * Clear the in-session URL tracking. Primarily an escape hatch for tests\n * so each test starts with a fresh referrer chain; production code\n * doesn't need this — the singleton client lives for the page lifetime.\n */\nexport function resetPageViewState(): void {\n lastFiredUrl = null;\n lastFiredUrlHydrated = false;\n deleteSessionStorage(LAST_FIRED_URL_STORAGE_KEY);\n}\n\n/**\n * Build canonical `page_view` metadata from the current browser document.\n *\n * Returns `null` outside the browser.\n */\nexport function buildPageViewMetadata(referrerOverride?: string | null): PageViewMetadata | null {\n if (typeof window === 'undefined' || typeof document === 'undefined')\n return null;\n // Run the constructed object through the Zod schema so the shape is\n // validated even when the SDK itself is the one building it. If the\n // schema ever drifts from what buildPageViewMetadata emits, the parse\n // throws here and the event is dropped rather than being sent malformed.\n return pageViewMetadataSchema.parse({\n page: {\n title: document.title || null,\n path: window.location.pathname,\n search: window.location.search,\n hash: window.location.hash,\n },\n referrer:\n referrerOverride !== undefined ? referrerOverride : document.referrer || null,\n viewport: { w: window.innerWidth, h: window.innerHeight },\n });\n}\n\n/**\n * Fire a `page_view` event through the low-level client.\n *\n * Used internally by automatic page view triggers and bfcache restore\n * handling.\n */\nexport function fireManualPageView(client: TrackingClient): void {\n if (typeof window === 'undefined')\n return;\n\n const currentHref = window.location.href;\n const previousFiredUrl = getLastFiredUrl();\n\n // Internal SPA referrer: the previous URL we fired a page_view for in this\n // tab. Only counts when it differs from the current URL (a same-URL re-fire\n // — e.g. React StrictMode double-mount in dev, or a bfcache restore to the\n // same page — should not produce a self-referencing referrer).\n const internalReferrer =\n previousFiredUrl !== null && previousFiredUrl !== currentHref\n ? previousFiredUrl\n : null;\n\n // External referrer: where the user came from before loading the app.\n // Used as the fallback for the very first page_view of the session.\n const externalReferrer =\n typeof document !== 'undefined' ? document.referrer || null : null;\n\n const referrer = internalReferrer ?? externalReferrer;\n\n const metadata = buildPageViewMetadata(referrer);\n client.trackEvent({\n eventType: 'page_view',\n pageUrl: currentHref,\n metadata: metadata as unknown as Record<string, unknown> | null,\n });\n\n // Only advance the in-session pointer when the URL actually changed, so a\n // same-URL re-fire never poisons the next event's referrer.\n if (currentHref !== previousFiredUrl) {\n setLastFiredUrl(currentHref);\n }\n}\n\n/**\n * Attach a back-forward cache restore listener in isolation. Used by\n * surfaces that have their own SPA detection (e.g. the Next.js provider\n * uses `usePathname`) but still want to capture bfcache restores, which\n * don't go through any router state change.\n *\n * Returns a detach function that removes the listener.\n */\nexport function attachBfcacheRestore(client: TrackingClient): () => void {\n if (typeof window === 'undefined') return () => {};\n\n function handlePageShow(event: PageTransitionEvent): void {\n if (!event.persisted) return;\n fireManualPageView(client);\n }\n\n window.addEventListener('pageshow', handlePageShow);\n return () => {\n window.removeEventListener('pageshow', handlePageShow);\n };\n}\n\nexport interface AttachAutoPageViewOptions {\n /** Skip the initial fire on attach (useful when the host already fired one). */\n skipInitial?: boolean;\n}\n\n/**\n * Attach automatic page view tracking for a browser SPA.\n *\n * Patches history navigation, listens to `popstate`, fires the initial\n * page view unless disabled, and returns a detach function.\n */\nexport function attachAutoPageView(\n client: TrackingClient,\n options: AttachAutoPageViewOptions = {},\n): () => void {\n if (typeof window === 'undefined' || typeof history === 'undefined') {\n return () => {};\n }\n\n let lastPath = window.location.pathname + window.location.search;\n\n function maybeFire(): void {\n const current = window.location.pathname + window.location.search;\n if (current === lastPath)\n return;\n lastPath = current;\n fireManualPageView(client);\n }\n\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n function patchedPushState(this: History, ...args: Parameters<History['pushState']>): void {\n originalPushState(...args);\n // Defer one tick so the URL is settled before we read it.\n setTimeout(maybeFire, 0);\n }\n function patchedReplaceState(this: History, ...args: Parameters<History['replaceState']>): void {\n originalReplaceState(...args);\n setTimeout(maybeFire, 0);\n }\n\n // bfcache restore: when the user clicks an external link and hits the\n // back button, the browser can restore the page from its back-forward\n // cache without re-running any JS lifecycles. `popstate` does NOT fire\n // for bfcache restores, but `pageshow` does, with event.persisted === true.\n // When that happens we fire a fresh page_view so the session reflects\n // the re-entry.\n function handlePageShow(event: PageTransitionEvent): void {\n if (!event.persisted) return;\n fireManualPageView(client);\n }\n\n history.pushState = patchedPushState as History['pushState'];\n history.replaceState = patchedReplaceState as History['replaceState'];\n window.addEventListener('popstate', maybeFire);\n window.addEventListener('pageshow', handlePageShow);\n\n if (!options.skipInitial)\n fireManualPageView(client);\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener('popstate', maybeFire);\n window.removeEventListener('pageshow', handlePageShow);\n };\n}\n","// Builds the metadata payload for the sdk_heartbeat event. Called once per\n// new session to report the SDK version and registered trigger configuration\n// back to the Aranova API.\n\nimport type { SdkHeartbeatMetadata } from './events/sdk-heartbeat';\nimport type { TriggerRegistryConfig } from './events/registry';\nimport type { TrackingInstallSurface } from './types';\n\n/**\n * Recursively serialize a value into a JSON-safe form. Converts RegExp\n * instances to their `.source` string so the trigger config can survive\n * `JSON.stringify` without losing information.\n */\nfunction serializeValue(value: unknown): unknown {\n if (value instanceof RegExp) return value.source;\n if (Array.isArray(value)) return value.map(serializeValue);\n if (value !== null && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n out[k] = serializeValue(v);\n }\n return out;\n }\n return value;\n}\n\n/**\n * Build metadata for the SDK-internal `sdk_heartbeat` event.\n *\n * The heartbeat reports package version, install surface, registered trigger\n * names, and non-empty trigger config so the dashboard can show site coverage.\n */\nexport function buildHeartbeatMetadata(\n surface: TrackingInstallSurface,\n sdkVersion: string | null,\n packageName: string | null,\n triggers: TriggerRegistryConfig | null,\n): SdkHeartbeatMetadata {\n const automaticNames = triggers\n ? Object.keys(triggers.automatic)\n : [];\n const manualNames = triggers?.manual\n ? Object.keys(triggers.manual)\n : [];\n\n // Build trigger_config: the init params per event, with RegExp values\n // converted to strings. Skip events with empty config objects.\n let triggerConfig: Record<string, Record<string, unknown>> | null = null;\n if (triggers) {\n const cfg: Record<string, Record<string, unknown>> = {};\n for (const [name, config] of Object.entries(triggers.automatic)) {\n const serialized = serializeValue(config) as Record<string, unknown>;\n if (Object.keys(serialized).length > 0) {\n cfg[name] = serialized;\n }\n }\n if (triggers.manual) {\n for (const [name, config] of Object.entries(triggers.manual)) {\n if (config === undefined) continue;\n const serialized = serializeValue(config) as Record<string, unknown>;\n if (Object.keys(serialized).length > 0) {\n cfg[name] = serialized;\n }\n }\n }\n if (Object.keys(cfg).length > 0) {\n triggerConfig = cfg;\n }\n }\n\n return {\n sdk_version: sdkVersion ?? 'unknown',\n package_name: packageName,\n surface,\n triggers: {\n automatic: automaticNames,\n manual: manualNames,\n },\n trigger_config: triggerConfig,\n };\n}\n","// Tracking ingest client. Queues events, debounce-flushes them to the\n// /tracking/events endpoint, and degrades silently on errors so a broken\n// network never breaks the host site.\n\nimport { getConsentState } from './consent';\nimport { resetPageViewState } from './page-view';\nimport {\n captureTrackingParamsFromLocation,\n createEmptyTrackingParams,\n getCookieValueFromDocument,\n getTrackingParamsFromCookieReader,\n} from './tracking';\nimport type { TriggerRegistryConfig } from './events/registry';\nimport { buildHeartbeatMetadata } from './heartbeat';\nimport { getOrRotateSessionId, getVisitorId } from './session';\nimport type {\n TrackingClientContext,\n TrackingEventCreatePayload,\n TrackingInstallSurface,\n TrackingParams,\n TrackingSessionUpsertPayload,\n} from './types';\n\n/**\n * Default debounce window before queued events are flushed.\n */\nexport const DEFAULT_FLUSH_INTERVAL_MS = 2000;\n\n/**\n * Default queue size that triggers an immediate flush.\n */\nexport const DEFAULT_MAX_QUEUE_SIZE = 10;\n\n/**\n * Hard server-side maximum event count per request body.\n */\nexport const HARD_MAX_BATCH = 50;\n\n/**\n * Header used to authenticate public tracking ingest requests.\n */\nexport const API_KEY_HEADER = 'X-Aranova-Api-Key';\n\n/**\n * Configuration for the low-level ingest client.\n *\n * Framework packages usually create this for you through `createTracking()`.\n */\nexport interface TrackingClientConfig {\n /** Public tracking API key issued for the business. */\n apiKey: string;\n /** Tracking endpoint base URL, usually ending in `/tracking`. */\n endpoint: string;\n /** SDK surface creating this client. */\n surface: TrackingInstallSurface;\n /** Package version reported in session context and heartbeat metadata. */\n sdkVersion?: string;\n /** Package name reported in session context and heartbeat metadata. */\n packageName?: string;\n /** Trigger registry so the heartbeat can report registered events. */\n triggers?: TriggerRegistryConfig;\n /** Override the default 2s debounce window. */\n flushIntervalMs?: number;\n /** Override the default 10-event batch trigger. */\n maxQueueSize?: number;\n /** When true, swallow nothing — useful for tests. */\n debug?: boolean;\n}\n\n/**\n * Input accepted by the low-level stringly-typed client.\n *\n * Prefer the typed `trackEvent(eventName, metadata)` facade exposed by\n * `useTracking()` in React/Next integrations.\n */\nexport interface TrackEventInput {\n /** Event name to enqueue. */\n eventType: string;\n /** URL associated with the event. Defaults to the current page URL. */\n pageUrl?: string | null;\n /** Event-specific metadata. */\n metadata?: Record<string, unknown> | null;\n /** Timestamp override. Defaults to queue time. */\n occurredAt?: Date | string | null;\n}\n\ninterface QueuedEvent {\n event_type: string;\n page_url: string | null;\n metadata: Record<string, unknown> | null;\n occurred_at: string | null;\n}\n\n/**\n * Low-level tracking client responsible for queueing and flushing events.\n */\nexport interface TrackingClient {\n /** Enqueue an event for batched delivery. */\n trackEvent: (input: TrackEventInput) => void;\n /** Flush queued events immediately. */\n flush: () => Promise<void>;\n /** Return the current rolling session id. */\n getSessionId: () => string;\n /** Return the persistent visitor id. */\n getVisitorId: () => string;\n /** Remove timers/listeners and prevent future flushes. */\n destroy: () => void;\n}\n\ninterface IngestRequestBody {\n session: TrackingSessionUpsertPayload;\n events: Array<{\n event_type: string;\n page_url: string | null;\n metadata: Record<string, unknown> | null;\n occurred_at: string | null;\n }>;\n}\n\nfunction buildContext(\n surface: TrackingInstallSurface,\n sdkVersion: string | null,\n packageName: string | null,\n): TrackingClientContext {\n return {\n surface,\n sdk_version: sdkVersion,\n package_name: packageName,\n site_origin: typeof window === 'undefined' ? null : window.location.origin,\n page_title: typeof document === 'undefined' ? null : document.title || null,\n referrer: typeof document === 'undefined' ? null : document.referrer || null,\n };\n}\n\nfunction readTrackingParams(): TrackingParams {\n if (typeof window === 'undefined')\n return createEmptyTrackingParams();\n // Capture from URL on every read so the first event in a session reflects the\n // landing-page params even if the cookie helper hasn't run yet.\n try {\n captureTrackingParamsFromLocation();\n } catch {\n // ignore\n }\n return getTrackingParamsFromCookieReader(getCookieValueFromDocument);\n}\n\nfunction consentSnapshot(): Record<string, unknown> | null {\n try {\n return { state: getConsentState() };\n } catch {\n return null;\n }\n}\n\nasync function postWithFetch(url: string, body: string, apiKey: string, keepalive: boolean): Promise<void> {\n if (typeof fetch !== 'function')\n return;\n try {\n await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n [API_KEY_HEADER]: apiKey,\n },\n body,\n keepalive,\n // CORS is open on the tracking endpoint; never send cookies.\n credentials: 'omit',\n mode: 'cors',\n });\n } catch {\n // fire-and-forget — never throw to the host site\n }\n}\n\nlet globalClient: TrackingClient | null = null;\nlet globalClientKey: string | null = null;\n\nfunction clientConfigKey(config: TrackingClientConfig): string {\n return `${config.apiKey}@${config.endpoint}#${config.surface}`;\n}\n\n/**\n * Return a page-level singleton tracking client. Creating the client anew on\n * every component mount is wrong — React StrictMode double-mounts dev-only,\n * and destroying+recreating the client between the cleanup and re-run strips\n * away the pushState patch that SPA auto page view relies on. A singleton\n * survives all of that: the client lives for the entire page, and providers\n * just attach/detach auto page view against it.\n *\n * If `apiKey` / `endpoint` / `surface` change between calls, the previous\n * singleton is destroyed and a new one replaces it. This covers hot-config\n * changes without leaking state.\n */\nexport function getOrCreateTrackingClient(config: TrackingClientConfig): TrackingClient {\n const key = clientConfigKey(config);\n if (globalClient !== null && globalClientKey === key) {\n return globalClient;\n }\n if (globalClient !== null) {\n globalClient.destroy();\n }\n globalClient = createTrackingClient(config);\n globalClientKey = key;\n return globalClient;\n}\n\n/**\n * Tear down the singleton if any. Primarily an escape hatch for tests where\n * each test should see a fresh client; production code rarely needs this.\n * Also clears the in-session SPA referrer so the next test starts with a\n * fresh referrer chain.\n */\nexport function resetGlobalTrackingClient(): void {\n if (globalClient !== null) {\n globalClient.destroy();\n }\n globalClient = null;\n globalClientKey = null;\n resetPageViewState();\n}\n\n/**\n * Create a low-level ingest client.\n *\n * The client queues events, debounces network flushes, sends an SDK heartbeat\n * once per new session, and swallows network errors so analytics never break\n * the host site.\n */\nexport function createTrackingClient(config: TrackingClientConfig): TrackingClient {\n const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n const maxQueueSize = Math.min(config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE, HARD_MAX_BATCH);\n const sdkVersion = config.sdkVersion ?? null;\n const packageName = config.packageName ?? null;\n const endpointBase = config.endpoint.replace(/\\/$/, '');\n const eventsUrl = `${endpointBase}/events`;\n\n let queue: QueuedEvent[] = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n let firstPage: string | null = null;\n let destroyed = false;\n\n // Initialize identity early so the first POST has stable values.\n const visitorId = getVisitorId();\n const initialSession = getOrRotateSessionId();\n let sessionId = initialSession.id;\n\n if (typeof window !== 'undefined')\n firstPage = window.location.href;\n\n // Queue an sdk_heartbeat event at the start of every new session.\n function enqueueHeartbeat(): void {\n const metadata = buildHeartbeatMetadata(\n config.surface,\n sdkVersion,\n packageName,\n config.triggers ?? null,\n );\n queue.push({\n event_type: 'sdk_heartbeat',\n page_url: typeof window === 'undefined' ? null : window.location.href,\n metadata: metadata as unknown as Record<string, unknown>,\n occurred_at: new Date().toISOString(),\n });\n }\n\n if (initialSession.isNew) {\n enqueueHeartbeat();\n }\n\n function buildSessionPayload(): TrackingSessionUpsertPayload {\n const rotated = getOrRotateSessionId();\n if (rotated.isNew && rotated.id !== sessionId) {\n // Session rotated mid-page (idle > 30min then user returned).\n enqueueHeartbeat();\n }\n sessionId = rotated.id;\n const params = readTrackingParams();\n const context = buildContext(config.surface, sdkVersion, packageName);\n return {\n session_id: sessionId,\n visitor_id: visitorId,\n gclid: params.gclid,\n fbclid: params.fbclid,\n utm_source: params.utm_source,\n utm_medium: params.utm_medium,\n utm_campaign: params.utm_campaign,\n utm_term: params.utm_term,\n utm_content: params.utm_content,\n first_page: firstPage,\n consent_state: consentSnapshot(),\n context,\n };\n }\n\n function scheduleFlush(): void {\n if (flushTimer !== null || destroyed)\n return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n void flush();\n }, flushIntervalMs);\n }\n\n function clearScheduledFlush(): void {\n if (flushTimer !== null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n }\n\n async function flush(): Promise<void> {\n if (queue.length === 0)\n return;\n\n const events = queue.slice(0, HARD_MAX_BATCH);\n queue = queue.slice(events.length);\n clearScheduledFlush();\n\n const body: IngestRequestBody = {\n session: buildSessionPayload(),\n events,\n };\n\n const serialized = JSON.stringify(body);\n\n // Prefer fetch + keepalive for better error visibility. sendBeacon is the\n // pagehide fallback path because it survives navigation away.\n await postWithFetch(eventsUrl, serialized, config.apiKey, false);\n }\n\n function trackEvent(input: TrackEventInput): void {\n if (destroyed)\n return;\n if (!input || typeof input.eventType !== 'string' || input.eventType.length === 0)\n return;\n\n const occurredAt =\n input.occurredAt instanceof Date\n ? input.occurredAt.toISOString()\n : typeof input.occurredAt === 'string'\n ? input.occurredAt\n : new Date().toISOString();\n\n queue.push({\n event_type: input.eventType,\n page_url:\n input.pageUrl ?? (typeof window === 'undefined' ? null : window.location.href),\n metadata: input.metadata ?? null,\n occurred_at: occurredAt,\n });\n\n if (queue.length >= maxQueueSize) {\n void flush();\n } else {\n scheduleFlush();\n }\n }\n\n function flushOnUnload(): void {\n if (queue.length === 0)\n return;\n const events = queue.slice(0, HARD_MAX_BATCH);\n queue = queue.slice(events.length);\n clearScheduledFlush();\n\n const body: IngestRequestBody = {\n session: buildSessionPayload(),\n events,\n };\n const serialized = JSON.stringify(body);\n void postWithFetch(eventsUrl, serialized, config.apiKey, true);\n }\n\n if (typeof window !== 'undefined') {\n window.addEventListener('pagehide', flushOnUnload);\n window.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') flushOnUnload();\n });\n }\n\n return {\n trackEvent,\n flush,\n getSessionId: () => sessionId,\n getVisitorId: () => visitorId,\n destroy: () => {\n destroyed = true;\n // Fire any pending events through the keepalive path before tearing\n // down. Critical for React StrictMode in dev, where the provider's\n // first mount is immediately unmounted and its 2s debounce would\n // otherwise drop the initial page_view on the floor. Uses fetch\n // keepalive so the request survives the component tearing down.\n if (queue.length > 0) {\n flushOnUnload();\n }\n clearScheduledFlush();\n queue = [];\n if (typeof window !== 'undefined') {\n window.removeEventListener('pagehide', flushOnUnload);\n }\n },\n };\n}\n","import { z } from 'zod';\n\n/**\n * Metadata for a manually fired `cta_click` event.\n *\n * Use this for non-phone calls to action such as directions, appointment\n * buttons, downloads, or external booking links.\n */\nexport const ctaClickMetadataSchema = z\n .object({\n cta_name: z.string(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n section: z.string().nullable().optional(),\n destination_url: z.string().nullable().optional(),\n })\n .strict();\n\nexport type CtaClickMetadata = z.infer<typeof ctaClickMetadataSchema>;\n\n/**\n * Registration config for `cta_click`.\n *\n * This event is manual-only and currently has no registration options.\n */\nexport const ctaClickConfigSchema = z.object({}).strict();\nexport type CtaClickConfig = z.infer<typeof ctaClickConfigSchema>;\n","import { z } from 'zod';\n\n/**\n * Registered trigger names reported by the SDK heartbeat.\n */\nexport const sdkHeartbeatTriggersSchema = z.object({\n automatic: z.array(z.string()),\n manual: z.array(z.string()),\n}).strict();\n\n/**\n * Metadata for the SDK-internal `sdk_heartbeat` event.\n *\n * The SDK fires this once per new session so the dashboard can show which SDK\n * version, install surface, and trigger registry a client site is running.\n * Consumers do not manually register or fire this event.\n */\nexport const sdkHeartbeatMetadataSchema = z\n .object({\n sdk_version: z.string(),\n package_name: z.string().nullable(),\n surface: z.enum(['next', 'react', 'script']),\n triggers: sdkHeartbeatTriggersSchema,\n trigger_config: z.record(z.string(), z.record(z.string(), z.unknown())).nullable().optional(),\n })\n .strict();\n\nexport type SdkHeartbeatMetadata = z.infer<typeof sdkHeartbeatMetadataSchema>;\n\n/**\n * Internal registration config for `sdk_heartbeat`.\n *\n * This event has no consumer-facing options.\n */\nexport const sdkHeartbeatConfigSchema = z.object({}).strict();\nexport type SdkHeartbeatConfig = z.infer<typeof sdkHeartbeatConfigSchema>;\n","import { z } from 'zod';\n\n/**\n * Metadata for the automatic `form_start` event.\n *\n * The SDK emits this once per form when the visitor first focuses a field.\n */\nexport const formStartMetadataSchema = z\n .object({\n form: z\n .object({\n id: z.string(),\n action: z.string().nullable(),\n })\n .strict(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type FormStartMetadata = z.infer<typeof formStartMetadataSchema>;\n\n/**\n * Registration config for automatic `form_start`.\n *\n * Use `selector` to narrow which forms can trigger the event. When omitted,\n * the SDK observes all `<form>` elements.\n */\nexport const formStartConfigSchema = z\n .object({\n selector: z.string().optional(),\n })\n .strict();\n\nexport type FormStartConfig = z.infer<typeof formStartConfigSchema>;\n","import { z } from 'zod';\n\n// form_submit is manual — the SDK never auto-fires this. Consumer code calls\n// `tracking.trackEvent('form_submit', { form, page })` from their own submit\n// handler. Registering it enables the type-level permission; omitting it\n// turns manual calls into a compile error.\n//\n// The optional `fields` array captures submitted form field metadata and JSON\n// values. Consumers explicitly build the fields array themselves so they\n// control exactly what is sent.\n\n/**\n * JSON-serializable value accepted by `form_submit.fields[].value`.\n *\n * This intentionally excludes `undefined`, functions, symbols, `Date`\n * instances, and non-finite numbers. Values are stored in PostgreSQL JSONB, so\n * consumers should send only data that has a stable JSON representation.\n */\nexport type JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nexport const jsonValueSchema: z.ZodType<JsonValue> = z.lazy(() =>\n z.union([\n z.string(),\n z.number().finite(),\n z.boolean(),\n z.null(),\n z.array(jsonValueSchema),\n z.record(jsonValueSchema),\n ]),\n);\n\n/**\n * Metadata for a manually fired `form_submit` event.\n *\n * Register the event with `manual: { form_submit: {} }`, then call\n * `trackEvent('form_submit', metadata)` from the host site's submit handler.\n *\n * `fields` is optional. If present, each field value must be JSON-serializable\n * and should be explicitly allowlisted by the integration. Do not send names,\n * emails, visitor phone numbers, addresses, payment data, medical details,\n * passwords, file contents, or free-text messages.\n *\n * @example\n * ```ts\n * tracking.trackEvent('form_submit', {\n * form: {\n * id: 'lead-form',\n * action: '/api/lead',\n * fields: [\n * {\n * name: 'service_interest',\n * type: 'select',\n * label: 'Service interest',\n * value: 'teeth_whitening',\n * },\n * ],\n * },\n * page: { path: window.location.pathname },\n * });\n * ```\n */\nexport const formSubmitMetadataSchema = z\n .object({\n form: z\n .object({\n id: z.string(),\n action: z.string().nullable(),\n fields: z\n .array(\n z\n .object({\n name: z.string(),\n type: z.string(),\n label: z.string().nullable(),\n value: jsonValueSchema,\n })\n .strict(),\n )\n .optional(),\n })\n .strict(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type FormSubmitMetadata = z.infer<typeof formSubmitMetadataSchema>;\n\n/**\n * Registration config for `form_submit`.\n *\n * This event is manual-only and currently has no registration options. The\n * empty object enables typed `trackEvent('form_submit', ...)` calls.\n */\nexport const formSubmitConfigSchema = z.object({}).strict();\nexport type FormSubmitConfig = z.infer<typeof formSubmitConfigSchema>;\n","import { z } from 'zod';\n\n/**\n * Metadata for the automatic `multi_page_session` event.\n *\n * Fired when the visitor reaches the configured distinct-page threshold in a\n * single tracking session.\n */\nexport const multiPageSessionMetadataSchema = z\n .object({\n page_count: z.number().int().min(2),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type MultiPageSessionMetadata = z.infer<typeof multiPageSessionMetadataSchema>;\n\n/**\n * Registration config for automatic `multi_page_session`.\n */\nexport const multiPageSessionConfigSchema = z\n .object({\n pageThreshold: z.number().int().min(2),\n })\n .strict();\n\nexport type MultiPageSessionConfig = z.infer<typeof multiPageSessionConfigSchema>;\n","import { z } from 'zod';\n\n/**\n * Metadata for a manually fired `phone_click` event.\n *\n * `phone_number` should be the business phone number from the clicked `tel:`\n * link, not a visitor-entered phone number. `section` can distinguish header,\n * footer, hero, or contact-page links.\n */\nexport const phoneClickMetadataSchema = z\n .object({\n phone_number: z.string(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n section: z.string().nullable().optional(),\n })\n .strict();\n\nexport type PhoneClickMetadata = z.infer<typeof phoneClickMetadataSchema>;\n\n/**\n * Registration config for `phone_click`.\n *\n * This event is manual-only and currently has no registration options.\n */\nexport const phoneClickConfigSchema = z.object({}).strict();\nexport type PhoneClickConfig = z.infer<typeof phoneClickConfigSchema>;\n","import { z } from 'zod';\n\n/**\n * Metadata for the automatic `scroll_depth` event.\n *\n * Fired once per configured threshold per page.\n */\nexport const scrollDepthMetadataSchema = z\n .object({\n depth_percent: z.number().int().min(1).max(100),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type ScrollDepthMetadata = z.infer<typeof scrollDepthMetadataSchema>;\n\n/**\n * Registration config for automatic `scroll_depth`.\n *\n * `thresholds` are integer percentages from 1 to 100.\n */\nexport const scrollDepthConfigSchema = z\n .object({\n thresholds: z.array(z.number().int().min(1).max(100)).min(1),\n })\n .strict();\n\nexport type ScrollDepthConfig = z.infer<typeof scrollDepthConfigSchema>;\n","import { z } from 'zod';\n\n/**\n * Canonical page intent names supported by `specific_page_visit`.\n */\nexport const SPECIFIC_PAGE_NAMES = [\n 'contact_page',\n 'about_page',\n 'services_page',\n 'booking_page',\n 'location_page',\n 'pricing_page',\n 'faq_page',\n 'testimonials_page',\n] as const;\n\nexport type SpecificPageName = (typeof SPECIFIC_PAGE_NAMES)[number];\n\nexport const specificPageNameSchema = z.enum(SPECIFIC_PAGE_NAMES);\n\n/**\n * Metadata for the automatic `specific_page_visit` event.\n *\n * The SDK emits this when the current pathname matches one of the configured\n * named page patterns.\n */\nexport const specificPageVisitMetadataSchema = z\n .object({\n page_name: specificPageNameSchema,\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type SpecificPageVisitMetadata = z.infer<typeof specificPageVisitMetadataSchema>;\n\n/**\n * Registration config for automatic `specific_page_visit`.\n *\n * Each page entry pairs a semantic `name` with a `RegExp` that matches the\n * pathname. Use this instead of hard-coding path regexes downstream.\n */\nexport const specificPageVisitConfigSchema = z\n .object({\n pages: z\n .array(\n z\n .object({\n name: specificPageNameSchema,\n pathPattern: z.custom<RegExp>((value) => value instanceof RegExp, {\n message: 'pathPattern must be a RegExp',\n }),\n })\n .strict(),\n )\n .min(1),\n })\n .strict();\n\nexport type SpecificPageVisitConfig = z.infer<typeof specificPageVisitConfigSchema>;\n","import { z } from 'zod';\n\n/**\n * Metadata for the automatic `time_on_site` event.\n *\n * The SDK starts a visibility-aware timer and fires once when visible\n * engagement crosses the configured threshold.\n */\nexport const timeOnSiteMetadataSchema = z\n .object({\n duration_ms: z.number().int().nonnegative(),\n page: z\n .object({\n path: z.string(),\n })\n .strict(),\n })\n .strict();\n\nexport type TimeOnSiteMetadata = z.infer<typeof timeOnSiteMetadataSchema>;\n\n/**\n * Registration config for automatic `time_on_site`.\n */\nexport const timeOnSiteConfigSchema = z\n .object({\n thresholdSeconds: z.number().int().positive(),\n })\n .strict();\nexport type TimeOnSiteConfig = z.infer<typeof timeOnSiteConfigSchema>;\n","import type { z } from 'zod';\n\nimport {\n ctaClickConfigSchema,\n ctaClickMetadataSchema,\n type CtaClickConfig,\n type CtaClickMetadata,\n} from './cta-click';\nimport {\n sdkHeartbeatConfigSchema,\n sdkHeartbeatMetadataSchema,\n type SdkHeartbeatConfig,\n type SdkHeartbeatMetadata,\n} from './sdk-heartbeat';\nimport {\n formStartConfigSchema,\n formStartMetadataSchema,\n type FormStartConfig,\n type FormStartMetadata,\n} from './form-start';\nimport {\n formSubmitConfigSchema,\n formSubmitMetadataSchema,\n type FormSubmitConfig,\n type FormSubmitMetadata,\n} from './form-submit';\nimport {\n multiPageSessionConfigSchema,\n multiPageSessionMetadataSchema,\n type MultiPageSessionConfig,\n type MultiPageSessionMetadata,\n} from './multi-page-session';\nimport {\n pageViewConfigSchema,\n pageViewMetadataSchema,\n type PageViewConfig,\n type PageViewMetadata,\n} from './page-view';\nimport {\n phoneClickConfigSchema,\n phoneClickMetadataSchema,\n type PhoneClickConfig,\n type PhoneClickMetadata,\n} from './phone-click';\nimport {\n scrollDepthConfigSchema,\n scrollDepthMetadataSchema,\n type ScrollDepthConfig,\n type ScrollDepthMetadata,\n} from './scroll-depth';\nimport {\n specificPageVisitConfigSchema,\n specificPageVisitMetadataSchema,\n type SpecificPageVisitConfig,\n type SpecificPageVisitMetadata,\n} from './specific-page-visit';\nimport {\n timeOnSiteConfigSchema,\n timeOnSiteMetadataSchema,\n type TimeOnSiteConfig,\n type TimeOnSiteMetadata,\n} from './time-on-site';\n\n// Event kind — automatic events are fired by the SDK itself when their\n// client-side signal fires (page_view on navigation, time_on_site on timer,\n// etc). Manual events are only fireable via explicit consumer code.\nexport type EventKind = 'automatic' | 'manual';\n\n// The registry is the single client-side source of truth for \"what events\n// exist, what shape does their metadata take, and what kind are they\". The\n// backend mirror lives in apps/api/src/schemas/tracking_events.py and is\n// kept in sync via the drift test in apps/api/tests/test_event_schema_drift.py.\nexport const EVENT_REGISTRY = {\n // --- automatic triggers ---\n page_view: {\n kind: 'automatic',\n metadataSchema: pageViewMetadataSchema,\n configSchema: pageViewConfigSchema,\n },\n time_on_site: {\n kind: 'automatic',\n metadataSchema: timeOnSiteMetadataSchema,\n configSchema: timeOnSiteConfigSchema,\n },\n specific_page_visit: {\n kind: 'automatic',\n metadataSchema: specificPageVisitMetadataSchema,\n configSchema: specificPageVisitConfigSchema,\n },\n scroll_depth: {\n kind: 'automatic',\n metadataSchema: scrollDepthMetadataSchema,\n configSchema: scrollDepthConfigSchema,\n },\n multi_page_session: {\n kind: 'automatic',\n metadataSchema: multiPageSessionMetadataSchema,\n configSchema: multiPageSessionConfigSchema,\n },\n form_start: {\n kind: 'automatic',\n metadataSchema: formStartMetadataSchema,\n configSchema: formStartConfigSchema,\n },\n // --- SDK-internal automatic (not consumer-configurable) ---\n sdk_heartbeat: {\n kind: 'automatic',\n metadataSchema: sdkHeartbeatMetadataSchema,\n configSchema: sdkHeartbeatConfigSchema,\n },\n // --- manual triggers ---\n form_submit: {\n kind: 'manual',\n metadataSchema: formSubmitMetadataSchema,\n configSchema: formSubmitConfigSchema,\n },\n phone_click: {\n kind: 'manual',\n metadataSchema: phoneClickMetadataSchema,\n configSchema: phoneClickConfigSchema,\n },\n cta_click: {\n kind: 'manual',\n metadataSchema: ctaClickMetadataSchema,\n configSchema: ctaClickConfigSchema,\n },\n} as const;\n\n/**\n * Name of any event known to the tracking SDK.\n */\nexport type EventName = keyof typeof EVENT_REGISTRY;\n\n/**\n * Event names that are fired by the SDK when their configured signal occurs.\n *\n * Automatic events are not accepted by the typed `trackEvent()` API.\n */\nexport type AutomaticEventName = {\n [K in EventName]: (typeof EVENT_REGISTRY)[K]['kind'] extends 'automatic' ? K : never;\n}[EventName];\n\n/**\n * Event names that consumer code can fire manually after registering them.\n */\nexport type ManualEventName = {\n [K in EventName]: (typeof EVENT_REGISTRY)[K]['kind'] extends 'manual' ? K : never;\n}[EventName];\n\n// Map an event name to its explicit metadata / config TS type. We pull the\n// inferred types directly from the per-event files rather than deriving\n// via `z.infer<typeof EVENT_REGISTRY[K]['metadataSchema']>` so that error\n// messages name the event-specific type (PageViewMetadata, not an\n// anonymous zod inference).\ntype MetadataByName = {\n page_view: PageViewMetadata;\n time_on_site: TimeOnSiteMetadata;\n specific_page_visit: SpecificPageVisitMetadata;\n scroll_depth: ScrollDepthMetadata;\n multi_page_session: MultiPageSessionMetadata;\n form_start: FormStartMetadata;\n sdk_heartbeat: SdkHeartbeatMetadata;\n form_submit: FormSubmitMetadata;\n phone_click: PhoneClickMetadata;\n cta_click: CtaClickMetadata;\n};\n\ntype ConfigByName = {\n page_view: PageViewConfig;\n time_on_site: TimeOnSiteConfig;\n specific_page_visit: SpecificPageVisitConfig;\n scroll_depth: ScrollDepthConfig;\n multi_page_session: MultiPageSessionConfig;\n form_start: FormStartConfig;\n sdk_heartbeat: SdkHeartbeatConfig;\n form_submit: FormSubmitConfig;\n phone_click: PhoneClickConfig;\n cta_click: CtaClickConfig;\n};\n\n/**\n * Metadata payload type for a specific tracking event.\n *\n * @example\n * ```ts\n * type SubmitMetadata = EventMetadata<'form_submit'>;\n * ```\n */\nexport type EventMetadata<K extends EventName> = MetadataByName[K];\n\n/**\n * Trigger registration config type for a specific tracking event.\n */\nexport type EventConfig<K extends EventName> = ConfigByName[K];\n\n// Runtime constant arrays for iteration at consumer / factory time.\n/**\n * Runtime list of automatic event names.\n */\nexport const ALL_AUTOMATIC_EVENT_NAMES: readonly AutomaticEventName[] = (\n Object.entries(EVENT_REGISTRY) as Array<[EventName, (typeof EVENT_REGISTRY)[EventName]]>\n)\n .filter(([, def]) => def.kind === 'automatic')\n .map(([name]) => name) as AutomaticEventName[];\n\n/**\n * Runtime list of manual event names.\n */\nexport const ALL_MANUAL_EVENT_NAMES: readonly ManualEventName[] = (\n Object.entries(EVENT_REGISTRY) as Array<[EventName, (typeof EVENT_REGISTRY)[EventName]]>\n)\n .filter(([, def]) => def.kind === 'manual')\n .map(([name]) => name) as ManualEventName[];\n\n/**\n * Trigger registry passed to `createTracking({ triggers })`.\n *\n * `automatic.page_view` is required because every install should capture page\n * views. Other automatic events are opt-in. Manual events must be registered\n * here before the typed client accepts `trackEvent()` calls for them.\n *\n * @example\n * ```ts\n * createTracking({\n * apiKey,\n * endpoint,\n * triggers: {\n * automatic: {\n * page_view: {},\n * time_on_site: { thresholdSeconds: 60 },\n * },\n * manual: {\n * form_submit: {},\n * phone_click: {},\n * },\n * },\n * });\n * ```\n */\nexport type TriggerRegistryConfig = {\n automatic: {\n page_view: EventConfig<'page_view'>;\n } & Partial<{\n time_on_site: EventConfig<'time_on_site'>;\n specific_page_visit: EventConfig<'specific_page_visit'>;\n scroll_depth: EventConfig<'scroll_depth'>;\n multi_page_session: EventConfig<'multi_page_session'>;\n form_start: EventConfig<'form_start'>;\n }>;\n manual?: Partial<{\n form_submit: EventConfig<'form_submit'>;\n phone_click: EventConfig<'phone_click'>;\n cta_click: EventConfig<'cta_click'>;\n }>;\n};\n\n/**\n * Manual event names registered in a concrete trigger registry.\n *\n * Used by `TypedTrackingClient` so `trackEvent()` only accepts events the\n * consumer explicitly enabled.\n */\nexport type RegisteredManualEvents<TRegistry extends TriggerRegistryConfig> =\n Extract<keyof NonNullable<TRegistry['manual']>, ManualEventName>;\n\n/**\n * Automatic event names registered in a concrete trigger registry.\n */\nexport type RegisteredAutomaticEvents<TRegistry extends TriggerRegistryConfig> =\n Extract<keyof TRegistry['automatic'], AutomaticEventName>;\n\n/**\n * Discriminated union of valid manual tracking calls for a registry.\n */\nexport type TrackableEvent<TRegistry extends TriggerRegistryConfig> = {\n [K in RegisteredManualEvents<TRegistry>]: {\n eventType: K;\n metadata: EventMetadata<K>;\n };\n}[RegisteredManualEvents<TRegistry>];\n\n// Runtime helper: look up a schema pair by name. Cast through `unknown`\n// because the registry is `as const` and TS loses the specific schema type\n// when indexing via a dynamic key.\nexport function getEventDefinition(name: EventName): {\n kind: EventKind;\n metadataSchema: z.ZodTypeAny;\n configSchema: z.ZodTypeAny;\n} {\n return EVENT_REGISTRY[name] as unknown as {\n kind: EventKind;\n metadataSchema: z.ZodTypeAny;\n configSchema: z.ZodTypeAny;\n };\n}\n","// Typed facade over the low-level TrackingClient.\n//\n// The raw TrackingClient in ingest.ts is non-generic and accepts a\n// stringly-typed `TrackEventInput`. The typed facade wraps it to add:\n//\n// 1. A discriminated `trackEvent(eventType, metadata, options?)` signature\n// that only accepts manually-fireable events the consumer registered\n// via `createTracking({ triggers: { manual: { ... } } })`.\n// 2. Optional runtime validation that parses the metadata through the\n// event's Zod schema before forwarding to the raw client. Off by\n// default so prod pays zero cost, on when the factory is given\n// `debug: true`.\n//\n// Automatic events (page_view, time_on_site, specific_page_visit, etc.) are NOT\n// exposed through the typed facade — they're fired by the SDK internally\n// via the raw client, so consumer code that tries `trackEvent('page_view')`\n// is a compile error.\n\nimport { getEventDefinition } from './events/registry';\nimport type {\n EventMetadata,\n RegisteredManualEvents,\n TriggerRegistryConfig,\n} from './events/registry';\nimport type { TrackingClient } from './ingest';\n\nexport interface TypedTrackEventOptions {\n /**\n * Override the page URL associated with this event.\n *\n * Omit this for normal browser usage; the SDK captures `window.location.href`.\n */\n pageUrl?: string | null;\n /**\n * Override the event timestamp.\n *\n * Defaults to the time the event is queued. Accepts a `Date` or ISO string.\n */\n occurredAt?: Date | string | null;\n}\n\n/**\n * Typed tracking client returned by `useTracking()`.\n *\n * The accepted event names and metadata shapes are narrowed from the concrete\n * trigger registry supplied to `createTracking()`.\n */\nexport interface TypedTrackingClient<TRegistry extends TriggerRegistryConfig> {\n /**\n * Fire a manually-registered event. The event name must be present in\n * the registry's `manual` map and the metadata must match that event's\n * canonical Zod-derived shape.\n */\n trackEvent<K extends RegisteredManualEvents<TRegistry>>(\n eventType: K,\n metadata: EventMetadata<K>,\n options?: TypedTrackEventOptions,\n ): void;\n\n /**\n * Immediately flush queued events to the ingest endpoint.\n *\n * Normal consumers rarely need this because the SDK flushes on a debounce,\n * when the queue reaches the batch threshold, and on `pagehide`.\n */\n flush(): Promise<void>;\n /**\n * Return the current rolling session id.\n */\n getSessionId(): string;\n /**\n * Return the persistent visitor id for this browser profile.\n */\n getVisitorId(): string;\n}\n\n/**\n * Options for `createTypedClient()`.\n */\nexport interface CreateTypedClientOptions {\n /**\n * When true, every `trackEvent` call runs the metadata through the Zod\n * schema via `.parse()` before forwarding. Parse failures throw loudly,\n * which is what you want in dev. Prod should leave this off so a single\n * analytics event can never take down the host site.\n */\n debug?: boolean;\n}\n\n/**\n * Wrap a low-level `TrackingClient` with registry-aware TypeScript types.\n *\n * Framework packages call this internally; application code usually accesses\n * the result through the scoped `useTracking()` hook returned by\n * `createTracking()`.\n */\nexport function createTypedClient<TRegistry extends TriggerRegistryConfig>(\n raw: TrackingClient,\n _registry: TRegistry,\n options: CreateTypedClientOptions = {},\n): TypedTrackingClient<TRegistry> {\n const debug = options.debug ?? false;\n\n return {\n trackEvent<K extends RegisteredManualEvents<TRegistry>>(\n eventType: K,\n metadata: EventMetadata<K>,\n opts?: TypedTrackEventOptions,\n ): void {\n // Runtime validation in dev only. Since `trackEvent` is typed at the\n // caller site, the TS compiler already guarantees the metadata shape\n // for well-behaved consumers — this parse() catches runtime bugs in\n // SDK-internal code that bypasses the types (e.g. dynamic payloads\n // passed from a JS-only consumer, or a string coming from localStorage).\n if (debug) {\n const def = getEventDefinition(eventType);\n def.metadataSchema.parse(metadata);\n }\n\n raw.trackEvent({\n eventType: eventType as string,\n metadata: metadata as unknown as Record<string, unknown> | null,\n pageUrl: opts?.pageUrl ?? null,\n occurredAt: opts?.occurredAt ?? null,\n });\n },\n\n flush: raw.flush.bind(raw),\n getSessionId: raw.getSessionId.bind(raw),\n getVisitorId: raw.getVisitorId.bind(raw),\n };\n}\n","// time_on_site automatic trigger.\n//\n// Fires a single `time_on_site` event once the user has been actively\n// engaged with the tab for at least `thresholdSeconds`. The timer is\n// visibility-aware — when the tab goes hidden (other tab focused, app\n// backgrounded) we pause the counter, and resume when it comes back. This\n// stops the \"user opened the tab in the background and walked away\"\n// scenario from artificially inflating engagement.\n//\n// The event fires at most once per page load.\n\nimport type { TimeOnSiteConfig } from '../events/time-on-site';\nimport type { TrackingClient } from '../ingest';\n\n/**\n * Attach the automatic `time_on_site` trigger.\n *\n * Starts a visibility-aware timer and returns a detach function that clears\n * timers/listeners.\n */\nexport function attachTimeOnSite(\n client: TrackingClient,\n config: TimeOnSiteConfig,\n): () => void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return () => {};\n }\n\n const thresholdMs = config.thresholdSeconds * 1000;\n let accumulatedMs = 0;\n let activeSince: number | null = document.visibilityState === 'visible' ? Date.now() : null;\n let timer: ReturnType<typeof setTimeout> | null = null;\n let fired = false;\n\n function fire(): void {\n if (fired) return;\n fired = true;\n client.trackEvent({\n eventType: 'time_on_site',\n metadata: {\n duration_ms: thresholdMs,\n page: { path: window.location.pathname },\n } as unknown as Record<string, unknown>,\n pageUrl: window.location.href,\n occurredAt: null,\n });\n }\n\n function scheduleNext(): void {\n if (fired || activeSince === null) return;\n const remaining = thresholdMs - accumulatedMs;\n if (remaining <= 0) {\n fire();\n return;\n }\n timer = setTimeout(fire, remaining);\n }\n\n function clearTimer(): void {\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n }\n\n function onVisibilityChange(): void {\n if (fired) return;\n if (document.visibilityState === 'hidden') {\n // Pause — drain the current interval into accumulatedMs.\n if (activeSince !== null) {\n accumulatedMs += Date.now() - activeSince;\n activeSince = null;\n }\n clearTimer();\n } else {\n // Resume.\n activeSince = Date.now();\n scheduleNext();\n }\n }\n\n document.addEventListener('visibilitychange', onVisibilityChange);\n scheduleNext();\n\n return () => {\n clearTimer();\n document.removeEventListener('visibilitychange', onVisibilityChange);\n };\n}\n","// specific_page_visit automatic trigger.\n//\n// Fires whenever the current path matches one of the consumer-supplied\n// patterns. Each pattern has a strongly typed page_name from the\n// SpecificPageName union. Dedupes so that repeated navigations to the same\n// (page_name, path) pair only fire once per page lifecycle.\n//\n// Replaces the old contact_page_visit trigger with multi-pattern support.\n\nimport type { SpecificPageVisitConfig } from '../events/specific-page-visit';\nimport type { TrackingClient } from '../ingest';\n\n/**\n * Attach the automatic `specific_page_visit` trigger.\n *\n * Watches navigation changes and fires when the current pathname matches a\n * configured named page pattern.\n */\nexport function attachSpecificPageVisit(\n client: TrackingClient,\n config: SpecificPageVisitConfig,\n): () => void {\n if (typeof window === 'undefined' || typeof history === 'undefined') {\n return () => {};\n }\n\n const { pages } = config;\n const firedSet = new Set<string>();\n\n function check(): void {\n const path = window.location.pathname;\n for (const { name, pathPattern } of pages) {\n pathPattern.lastIndex = 0; // Reset in case consumer passed /g or /y flag\n if (!pathPattern.test(path)) continue;\n const key = `${name}:${path}`;\n if (firedSet.has(key)) continue;\n firedSet.add(key);\n client.trackEvent({\n eventType: 'specific_page_visit',\n metadata: { page_name: name, page: { path } } as unknown as Record<string, unknown>,\n pageUrl: window.location.href,\n occurredAt: null,\n });\n }\n }\n\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n function patchedPushState(this: History, ...args: Parameters<History['pushState']>): void {\n originalPushState(...args);\n setTimeout(check, 0);\n }\n function patchedReplaceState(this: History, ...args: Parameters<History['replaceState']>): void {\n originalReplaceState(...args);\n setTimeout(check, 0);\n }\n\n history.pushState = patchedPushState as History['pushState'];\n history.replaceState = patchedReplaceState as History['replaceState'];\n window.addEventListener('popstate', check);\n\n // Fire once on attach in case the current path already matches.\n check();\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener('popstate', check);\n };\n}\n","// scroll_depth automatic trigger.\n//\n// Installs a requestAnimationFrame-throttled scroll listener and fires once\n// per configured threshold per page. Resets the fired set on SPA navigation\n// when the pathname actually changes (pushState / popstate). replaceState\n// is ignored since it's commonly used for query param updates without a\n// real page transition.\n\nimport type { ScrollDepthConfig } from '../events/scroll-depth';\nimport type { TrackingClient } from '../ingest';\n\n/**\n * Attach the automatic `scroll_depth` trigger.\n *\n * Installs a throttled scroll listener, fires each configured threshold once\n * per page, and returns a detach function.\n */\nexport function attachScrollDepth(\n client: TrackingClient,\n config: ScrollDepthConfig,\n): () => void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return () => {};\n }\n\n const thresholds = new Set(config.thresholds);\n let firedForPath = new Set<number>();\n let currentPath = window.location.pathname;\n let rafId: number | null = null;\n\n function getScrollPercent(): number {\n const doc = document.documentElement;\n const scrollTop = window.scrollY || doc.scrollTop;\n const scrollHeight = doc.scrollHeight;\n const clientHeight = doc.clientHeight;\n if (scrollHeight <= clientHeight) return 100;\n return Math.round(((scrollTop + clientHeight) / scrollHeight) * 100);\n }\n\n function checkThresholds(): void {\n const percent = getScrollPercent();\n for (const threshold of thresholds) {\n if (percent >= threshold && !firedForPath.has(threshold)) {\n firedForPath.add(threshold);\n client.trackEvent({\n eventType: 'scroll_depth',\n metadata: {\n depth_percent: threshold,\n page: { path: currentPath },\n } as unknown as Record<string, unknown>,\n pageUrl: window.location.href,\n occurredAt: null,\n });\n }\n }\n }\n\n function onScroll(): void {\n if (rafId !== null) return;\n rafId = requestAnimationFrame(() => {\n rafId = null;\n checkThresholds();\n });\n }\n\n function resetIfPathChanged(): void {\n const newPath = window.location.pathname;\n if (newPath === currentPath) return;\n currentPath = newPath;\n firedForPath = new Set();\n setTimeout(checkThresholds, 0);\n }\n\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n function patchedPushState(this: History, ...args: Parameters<History['pushState']>): void {\n originalPushState(...args);\n setTimeout(resetIfPathChanged, 0);\n }\n function patchedReplaceState(this: History, ...args: Parameters<History['replaceState']>): void {\n originalReplaceState(...args);\n // Only reset if pathname actually changed — replaceState is commonly\n // used for query param updates that shouldn't re-trigger thresholds.\n setTimeout(resetIfPathChanged, 0);\n }\n\n history.pushState = patchedPushState as History['pushState'];\n history.replaceState = patchedReplaceState as History['replaceState'];\n window.addEventListener('popstate', resetIfPathChanged);\n window.addEventListener('scroll', onScroll, { passive: true });\n\n // Initial check — handles short pages that don't require scrolling.\n setTimeout(checkThresholds, 0);\n\n return () => {\n if (rafId !== null) cancelAnimationFrame(rafId);\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener('popstate', resetIfPathChanged);\n window.removeEventListener('scroll', onScroll);\n };\n}\n","// multi_page_session automatic trigger.\n//\n// Tracks distinct page paths visited in the current session via sessionStorage.\n// Fires once when the count reaches the configured threshold. Resets when\n// the session ID changes (30-min idle rotation).\n\nimport type { MultiPageSessionConfig } from '../events/multi-page-session';\nimport type { TrackingClient } from '../ingest';\nimport { getOrRotateSessionId } from '../session';\n\nconst STORAGE_KEY = 'aranova_tracking_mps_paths';\nconst SESSION_KEY = 'aranova_tracking_mps_session';\nconst FIRED_KEY = 'aranova_tracking_mps_fired';\n\nfunction getSessionStorage(): Storage | null {\n try {\n return typeof window !== 'undefined' ? window.sessionStorage : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Attach the automatic `multi_page_session` trigger.\n *\n * Tracks distinct paths in sessionStorage and fires once when the configured\n * page threshold is reached.\n */\nexport function attachMultiPageSession(\n client: TrackingClient,\n config: MultiPageSessionConfig,\n): () => void {\n if (typeof window === 'undefined' || typeof history === 'undefined') {\n return () => {};\n }\n\n const storage = getSessionStorage();\n if (!storage) return () => {};\n\n const { pageThreshold } = config;\n let lastCheckedPath = '';\n\n function getDistinctPaths(): Set<string> {\n try {\n const raw = storage!.getItem(STORAGE_KEY);\n return raw ? new Set(JSON.parse(raw) as string[]) : new Set();\n } catch {\n return new Set();\n }\n }\n\n function saveDistinctPaths(paths: Set<string>): void {\n try {\n storage!.setItem(STORAGE_KEY, JSON.stringify([...paths]));\n } catch {\n // sessionStorage full — degrade gracefully.\n }\n }\n\n function resetIfSessionChanged(): void {\n const currentSession = getOrRotateSessionId().id;\n const storedSession = storage!.getItem(SESSION_KEY);\n if (storedSession !== currentSession) {\n storage!.setItem(SESSION_KEY, currentSession);\n storage!.removeItem(STORAGE_KEY);\n storage!.removeItem(FIRED_KEY);\n }\n }\n\n function hasFired(): boolean {\n return storage!.getItem(FIRED_KEY) === '1';\n }\n\n function check(): void {\n // Skip if pathname hasn't changed — replaceState is commonly used for\n // query param updates and shouldn't trigger parse/serialize overhead.\n const currentPath = window.location.pathname;\n if (currentPath === lastCheckedPath) return;\n lastCheckedPath = currentPath;\n\n resetIfSessionChanged();\n\n if (hasFired()) return;\n\n const paths = getDistinctPaths();\n paths.add(currentPath);\n saveDistinctPaths(paths);\n\n if (paths.size >= pageThreshold) {\n storage!.setItem(FIRED_KEY, '1');\n client.trackEvent({\n eventType: 'multi_page_session',\n metadata: {\n page_count: paths.size,\n page: { path: window.location.pathname },\n } as unknown as Record<string, unknown>,\n pageUrl: window.location.href,\n occurredAt: null,\n });\n }\n }\n\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n function patchedPushState(this: History, ...args: Parameters<History['pushState']>): void {\n originalPushState(...args);\n setTimeout(check, 0);\n }\n function patchedReplaceState(this: History, ...args: Parameters<History['replaceState']>): void {\n originalReplaceState(...args);\n setTimeout(check, 0);\n }\n\n history.pushState = patchedPushState as History['pushState'];\n history.replaceState = patchedReplaceState as History['replaceState'];\n window.addEventListener('popstate', check);\n\n // Fire once on attach for the initial page.\n check();\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener('popstate', check);\n };\n}\n","// form_start automatic trigger.\n//\n// Uses focusin event delegation on document to detect the first field\n// interaction inside a <form>. Fires once per form per page lifecycle.\n// Dedupes via a Set keyed on form id/action. Resets on SPA navigation.\n\nimport type { FormStartConfig } from '../events/form-start';\nimport type { TrackingClient } from '../ingest';\n\n/**\n * Attach the automatic `form_start` trigger.\n *\n * Uses `focusin` event delegation to detect the first interaction with each\n * matching form and returns a detach function.\n */\nexport function attachFormStart(\n client: TrackingClient,\n config: FormStartConfig,\n): () => void {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return () => {};\n }\n\n const selector = config.selector ?? 'form';\n let firedForms = new Set<string>();\n let currentPath = window.location.pathname;\n\n function getFormKey(form: HTMLFormElement): string {\n if (form.id) return `id:${form.id}`;\n // Use getAttribute — the DOM property always returns a resolved URL,\n // never \"\" or null, so it can't distinguish \"no action\" from \"action=current page\".\n const explicitAction = form.getAttribute('action');\n if (explicitAction) return `action:${explicitAction}`;\n const forms = Array.from(document.querySelectorAll(selector));\n return `index:${forms.indexOf(form)}`;\n }\n\n function onFocusIn(event: FocusEvent): void {\n const target = event.target;\n if (!(target instanceof HTMLElement)) return;\n\n // Walk up to the nearest matching <form>.\n const form = target.closest(selector) as HTMLFormElement | null;\n if (!form || form.tagName !== 'FORM') return;\n\n const key = getFormKey(form);\n if (firedForms.has(key)) return;\n firedForms.add(key);\n\n client.trackEvent({\n eventType: 'form_start',\n metadata: {\n form: {\n id: form.id || '',\n action: form.getAttribute('action') ?? null,\n },\n page: { path: window.location.pathname },\n } as unknown as Record<string, unknown>,\n pageUrl: window.location.href,\n occurredAt: null,\n });\n }\n\n function resetIfPathChanged(): void {\n const newPath = window.location.pathname;\n if (newPath === currentPath) return;\n currentPath = newPath;\n firedForms = new Set();\n }\n\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n function patchedPushState(this: History, ...args: Parameters<History['pushState']>): void {\n originalPushState(...args);\n setTimeout(resetIfPathChanged, 0);\n }\n function patchedReplaceState(this: History, ...args: Parameters<History['replaceState']>): void {\n originalReplaceState(...args);\n setTimeout(resetIfPathChanged, 0);\n }\n\n history.pushState = patchedPushState as History['pushState'];\n history.replaceState = patchedReplaceState as History['replaceState'];\n window.addEventListener('popstate', resetIfPathChanged);\n document.addEventListener('focusin', onFocusIn);\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener('popstate', resetIfPathChanged);\n document.removeEventListener('focusin', onFocusIn);\n };\n}\n","'use client';\n\nimport { useEffect } from 'react';\n\nimport { bootstrapGoogleAdsTracking } from '../../tracking-core/src/index';\n\nexport interface GoogleAdsTrackingProps {\n /**\n * Google Ads tag id, for example `AW-123456789`.\n */\n gtagId: string;\n}\n\n/**\n * Client component that loads Google Ads gtag and restores stored consent.\n *\n * Render once near the application root when the client site runs paid Google\n * Ads. The component renders nothing.\n */\nexport function GoogleAdsTracking({ gtagId }: GoogleAdsTrackingProps) {\n useEffect(() => {\n bootstrapGoogleAdsTracking(gtagId);\n }, [gtagId]);\n\n return null;\n}\n","'use client';\n\nimport { useEffect, useState } from 'react';\n\nimport { getConsentState } from '../../tracking-core/src/index';\nimport {\n createEmptyTrackingParams,\n getCookieValueFromDocument,\n getTrackingParamsFromCookieReader,\n type ConsentState,\n type TrackingParams,\n} from '../../tracking-core/src/index';\n\n/**\n * Read the captured Google Ads click id from first-party cookies.\n *\n * Returns `null` during SSR and before the client has mounted.\n */\nexport function useGclid(): string | null {\n const [gclid, setGclid] = useState<string | null>(null);\n\n useEffect(() => {\n setGclid(getCookieValueFromDocument('gclid'));\n }, []);\n\n return gclid;\n}\n\n/**\n * Read all captured attribution parameters from first-party cookies.\n *\n * Values are loaded after mount, so the initial render returns all `null`s.\n */\nexport function useTrackingParams(): TrackingParams {\n const [trackingParams, setTrackingParams] = useState<TrackingParams>(createEmptyTrackingParams());\n\n useEffect(() => {\n setTrackingParams(getTrackingParamsFromCookieReader(getCookieValueFromDocument));\n }, []);\n\n return trackingParams;\n}\n\n/**\n * Read the current visitor consent state and update when another tab changes\n * the stored value.\n */\nexport function useConsentState(): ConsentState {\n const [consentState, setConsentStateValue] = useState<ConsentState>('pending');\n\n useEffect(() => {\n setConsentStateValue(getConsentState());\n\n const handleStorage = (event: StorageEvent): void => {\n if (event.key === 'consent_state')\n setConsentStateValue(getConsentState());\n };\n\n window.addEventListener('storage', handleStorage);\n\n return () => {\n window.removeEventListener('storage', handleStorage);\n };\n }, []);\n\n return consentState;\n}\n","'use client';\n\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n type ReactNode,\n} from 'react';\n\nimport { version } from '../package.json';\nimport {\n attachAutoPageView,\n attachBfcacheRestore,\n attachFormStart,\n attachMultiPageSession,\n attachScrollDepth,\n attachSpecificPageVisit,\n attachTimeOnSite,\n bootstrapGoogleAdsTracking,\n createTypedClient,\n getOrCreateTrackingClient,\n type TriggerRegistryConfig,\n type TypedTrackingClient,\n} from '../../tracking-core/src/index';\n\nexport interface CreateTrackingOptions<TRegistry extends TriggerRegistryConfig> {\n /**\n * Public tracking API key issued for this business.\n *\n * This key is safe to expose in browser code. Abuse is bounded by the\n * server-side origin allowlist and rate limits.\n */\n apiKey: string;\n /**\n * Tracking endpoint base URL, usually ending in `/tracking`.\n *\n * The client posts events to `${endpoint}/events`.\n */\n endpoint: string;\n /**\n * Trigger registry. Determines which events the SDK fires automatically\n * and which ones the consumer can fire manually via `trackEvent()`.\n * `automatic.page_view` is required — every tracking install needs it.\n */\n triggers: TRegistry;\n /**\n * When true, the typed client validates every `trackEvent()` metadata\n * payload through the Zod schema before forwarding. Errors are thrown\n * loudly. Leave off in prod; turn on in dev to catch shape bugs early.\n */\n debug?: boolean;\n}\n\nexport interface TrackingProviderProps {\n /**\n * Optional Google Ads tag id, for example `AW-123456789`.\n *\n * If omitted, no gtag script is loaded by the provider.\n */\n gtagId?: string;\n /**\n * Application tree that should have access to the scoped tracking client.\n */\n children: ReactNode;\n}\n\nexport interface CreateTrackingResult<TRegistry extends TriggerRegistryConfig> {\n /**\n * Provider component that initializes the page-level tracking client.\n *\n * Mount this once near the root of the React tree.\n */\n TrackingProvider: (props: TrackingProviderProps) => ReactNode;\n /**\n * Hook that returns the registry-typed tracking client.\n *\n * Import this hook from your local tracking module, not directly from the\n * package root, so TypeScript preserves your trigger registry.\n */\n useTracking: () => TypedTrackingClient<TRegistry>;\n}\n\n/**\n * Create a scoped `TrackingProvider` and `useTracking()` hook for a specific\n * trigger registry. Call this once at app startup (e.g. in a shared\n * `lib/tracking.ts` file) and import the returned `TrackingProvider` /\n * `useTracking` from that module, not from `@aranova/tracking-react`\n * directly. This lets TypeScript thread the registry type through every\n * consumer so `trackEvent()` autocompletes + rejects unregistered events.\n *\n * Example:\n *\n * ```ts\n * // src/lib/tracking.ts\n * import { createTracking } from '@aranova/tracking-react';\n *\n * export const { TrackingProvider, useTracking } = createTracking({\n * apiKey: import.meta.env.VITE_ARANOVA_TRACKING_API_KEY,\n * endpoint: import.meta.env.VITE_ARANOVA_TRACKING_ENDPOINT,\n * triggers: {\n * automatic: {\n * page_view: {},\n * time_on_site: { thresholdSeconds: 60 },\n * },\n * manual: {\n * form_submit: {},\n * },\n * },\n * });\n * ```\n */\n// No-op client returned when apiKey/endpoint are missing. Every method\n// is a silent no-op so the host app continues running without tracking.\nconst NOOP_CLIENT: TypedTrackingClient<TriggerRegistryConfig> = {\n trackEvent: () => {},\n flush: async () => {},\n getSessionId: () => '',\n getVisitorId: () => '',\n};\n\nexport function createTracking<TRegistry extends TriggerRegistryConfig>(\n options: CreateTrackingOptions<TRegistry>,\n): CreateTrackingResult<TRegistry> {\n const { apiKey, endpoint, triggers, debug } = options;\n\n // Graceful degradation: if credentials are missing, warn once and return\n // a no-op provider + hook. The app keeps running — tracking is simply\n // disabled. This lets clients deploy without tracking env vars set (e.g.\n // preview environments, local dev without a seeded key) without crashing\n // on startup.\n if (!apiKey || !endpoint) {\n if (apiKey || endpoint) {\n // eslint-disable-next-line no-console\n console.warn(\n '[AranovaTracking] createTracking() requires both `apiKey` and `endpoint`. ' +\n 'Tracking is disabled for this session.',\n );\n }\n\n const noopTyped = NOOP_CLIENT as unknown as TypedTrackingClient<TRegistry>;\n return {\n TrackingProvider: ({ children }: TrackingProviderProps) => children,\n useTracking: () => noopTyped,\n };\n }\n\n const TrackingContext = createContext<TypedTrackingClient<TRegistry> | null>(null);\n\n function TrackingProvider({ gtagId, children }: TrackingProviderProps): ReactNode {\n // Page-level singleton raw client. Surviving strict-mode unmount +\n // remount is handled by getOrCreateTrackingClient itself.\n const client = useMemo(\n () =>\n createTypedClient<TRegistry>(\n getOrCreateTrackingClient({\n apiKey,\n endpoint,\n surface: 'react',\n packageName: '@aranova/tracking-react',\n sdkVersion: version,\n triggers,\n debug,\n }),\n triggers,\n { debug },\n ),\n [],\n );\n\n useEffect(() => {\n if (!gtagId) return;\n bootstrapGoogleAdsTracking(gtagId);\n }, [gtagId]);\n\n // Attach the automatic triggers declared in the registry. Each\n // returns its own detach function; we call them all in reverse order\n // on cleanup so layered pushState patches restore cleanly.\n useEffect(() => {\n const detachers: Array<() => void> = [];\n const rawClient = getOrCreateTrackingClient({\n apiKey,\n endpoint,\n surface: 'react',\n packageName: '@aranova/tracking-react',\n triggers,\n debug,\n });\n\n // page_view is always present (required in the type).\n detachers.push(attachAutoPageView(rawClient));\n detachers.push(attachBfcacheRestore(rawClient));\n\n const timeOnSite = triggers.automatic.time_on_site;\n if (timeOnSite) {\n detachers.push(attachTimeOnSite(rawClient, timeOnSite));\n }\n\n const specificPageVisit = triggers.automatic.specific_page_visit;\n if (specificPageVisit) {\n detachers.push(attachSpecificPageVisit(rawClient, specificPageVisit));\n }\n\n const scrollDepth = triggers.automatic.scroll_depth;\n if (scrollDepth) {\n detachers.push(attachScrollDepth(rawClient, scrollDepth));\n }\n\n const multiPageSession = triggers.automatic.multi_page_session;\n if (multiPageSession) {\n detachers.push(attachMultiPageSession(rawClient, multiPageSession));\n }\n\n const formStart = triggers.automatic.form_start;\n if (formStart) {\n detachers.push(attachFormStart(rawClient, formStart));\n }\n\n // IMPORTANT: Detach in reverse (LIFO) order. Multiple triggers patch\n // history.pushState by capturing the current value at attach time and\n // restoring it on detach. This only composes correctly when the last\n // attached trigger is the first to detach — otherwise stale function\n // references are restored and earlier triggers' patches are lost.\n return () => {\n for (let i = detachers.length - 1; i >= 0; i--) {\n detachers[i]();\n }\n };\n }, []);\n\n return <TrackingContext.Provider value={client}>{children}</TrackingContext.Provider>;\n }\n\n function useTracking(): TypedTrackingClient<TRegistry> {\n const client = useContext(TrackingContext);\n if (client === null) {\n throw new Error(\n 'useTracking must be called inside a <TrackingProvider> returned by createTracking()',\n );\n }\n return client;\n }\n\n return { TrackingProvider, useTracking };\n}\n","{\n \"name\": \"@aranova/tracking-react\",\n \"version\": \"0.6.1\",\n \"private\": false,\n \"description\": \"React tracking and consent utilities for Aranova client sites\",\n \"files\": [\n \"dist\",\n \"README.md\"\n ],\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.mjs\",\n \"types\": \"./dist/index.d.ts\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/AranovaIO/aranova_internal.git\",\n \"directory\": \"packages/tracking-react\"\n },\n \"homepage\": \"https://github.com/AranovaIO/aranova_internal/tree/master/packages/tracking-react\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.mjs\",\n \"require\": \"./dist/index.js\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"test\": \"vitest\",\n \"test:run\": \"vitest run\",\n \"prepublishOnly\": \"npm run build\"\n },\n \"peerDependencies\": {\n \"react\": \">=18.0.0\"\n },\n \"dependencies\": {\n \"zod\": \"^3.24.1\"\n },\n \"devDependencies\": {\n \"@types/react\": \"^19.2.2\",\n \"@types/react-dom\": \"^19.2.2\",\n \"jsdom\": \"^29.0.1\",\n \"react\": \"^19.2.0\",\n \"react-dom\": \"^19.2.0\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^5.7.3\",\n \"vitest\": \"^4.1.4\"\n },\n \"publishConfig\": {\n \"registry\": \"https://registry.npmjs.org\",\n \"access\": \"public\"\n }\n}\n"],"mappings":"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","names":["z","z","z","z","z","z","z","z","z","useEffect","useEffect","useEffect","useState","useState","useEffect","useEffect","jsx","useEffect"]}