@aranova/tracking-react 0.9.0 → 0.10.0

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","../../tracking-core/src/resources/sales/errors.ts","../../tracking-core/src/resources/sales/transport.ts","../../tracking-core/src/resources/sales/client.ts","../../tracking-core/src/resources/services.ts","../../tracking-core/src/resources/sales/money.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 TrackingEnvironment,\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 environment?: TrackingEnvironment;\n activeGtagIds?: Record<string, 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 environment: input.environment ?? 'production',\n active_gtag_ids: input.activeGtagIds ?? 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';\nimport type { GtagEnvironmentMap } from './types';\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 * Pattern matching valid Google Ads / GA4 tag IDs.\n *\n * Accepted formats: `AW-123456789`, `G-XXXXXXXXXX`, `GT-XXXXXXX`, `DC-XXXXXXX`.\n * IDs that don't match are silently dropped to prevent script injection when\n * interpolated into inline `<script>` content.\n */\nconst GTAG_ID_PATTERN = /^[A-Z]{1,3}-[A-Za-z0-9_-]+$/;\n\nexport function isValidGtagId(id: string): boolean {\n return GTAG_ID_PATTERN.test(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 if (!isValidGtagId(gtagId)) return '';\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 if (!isValidGtagId(gtagId))\n return;\n\n applyDefaultConsentState();\n loadGtagScript(gtagId);\n initializeGtag(gtagId);\n restoreStoredConsent();\n}\n\n/**\n * Load and initialize Google Ads tracking for ALL labelled gtag IDs.\n *\n * Every ID in the map gets a `gtag('config', ...)` call — gtag natively\n * supports multiple configured tags on the same page. The script loader\n * only runs once (for the first ID); subsequent IDs reuse the shared\n * `dataLayer`.\n */\nexport function bootstrapMultipleGtags(gtagIds: GtagEnvironmentMap): void {\n if (typeof window === 'undefined' || typeof document === 'undefined')\n return;\n\n const ids = Object.values(gtagIds).filter(isValidGtagId);\n if (ids.length === 0)\n return;\n\n applyDefaultConsentState();\n loadGtagScript(ids[0]);\n\n // Call gtag('js') once, then gtag('config') per ID. initializeGtag\n // calls both, but gtag('js') should only fire once per page load.\n const gtag = ensureGtagFunction();\n gtag('js', new Date());\n for (const id of ids) {\n gtag('config', id);\n }\n\n restoreStoredConsent();\n}\n\n/**\n * Create an inline script that initializes gtag for multiple Google Ads tag ids.\n *\n * Used by the Next.js `GoogleAdsTracking` component when rendering `<Script>`\n * tags for a multi-gtag configuration.\n */\nexport function createGtagInitScriptMulti(gtagIds: string[]): string {\n const safeIds = gtagIds.filter(isValidGtagId);\n const configs = safeIds.map(id => `gtag('config', '${id}');`).join('\\n');\n return `\nwindow.dataLayer = window.dataLayer || [];\nfunction gtag(){dataLayer.push(arguments);}\nwindow.gtag = gtag;\ngtag('js', new Date());\n${configs}\n`.trim();\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 gtagIds: Record<string, string> | null = 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 configured_gtag_ids: gtagIds,\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 TrackingEnvironment,\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 * Identity headers stamped on every ingest request. Duplicate fields already\n * present in `session.context` but survive body-parse failures so the backend\n * can attribute 422s to the offending SDK install.\n */\nexport const SDK_VERSION_HEADER = 'X-Aranova-Sdk-Version';\nexport const SDK_PACKAGE_HEADER = 'X-Aranova-Sdk-Package';\nexport const SDK_SURFACE_HEADER = 'X-Aranova-Sdk-Surface';\nexport const SDK_ENVIRONMENT_HEADER = 'X-Aranova-Sdk-Environment';\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 /** Deployment environment label reported in session context. */\n environment?: TrackingEnvironment;\n /** All active gtag IDs, keyed by label. Included in session context. */\n activeGtagIds?: Record<string, string>;\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 environment: TrackingEnvironment,\n activeGtagIds: Record<string, 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 environment,\n active_gtag_ids: activeGtagIds,\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\ninterface IdentityHeaders {\n sdkVersion: string;\n packageName: string;\n surface: string;\n environment: string;\n}\n\nasync function postWithFetch(\n url: string,\n body: string,\n apiKey: string,\n identity: IdentityHeaders,\n keepalive: boolean,\n): 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 [SDK_VERSION_HEADER]: identity.sdkVersion,\n [SDK_PACKAGE_HEADER]: identity.packageName,\n [SDK_SURFACE_HEADER]: identity.surface,\n [SDK_ENVIRONMENT_HEADER]: identity.environment,\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 // Default to 'production' so the wire payload always carries a valid enum\n // value. Sending null would fail the backend's strict enum validation.\n const environment: TrackingEnvironment = config.environment ?? 'production';\n const activeGtagIds = config.activeGtagIds ?? null;\n const endpointBase = config.endpoint.replace(/\\/$/, '');\n const eventsUrl = `${endpointBase}/events`;\n const identityHeaders: IdentityHeaders = {\n sdkVersion: sdkVersion ?? '',\n packageName: packageName ?? '',\n surface: config.surface,\n environment,\n };\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 activeGtagIds,\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, environment, activeGtagIds);\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, identityHeaders, 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, identityHeaders, 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 configured_gtag_ids: z.record(z.string(), z.string()).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","/**\n * Error thrown by the sales client (`createSalesClient`) on a\n * non-2xx response. Unlike the fire-and-forget event queue (which swallows\n * failures), a sale is a transaction the caller must be able to react to.\n */\nexport class AranovaApiError extends Error {\n readonly status: number;\n readonly code: string | undefined;\n readonly requestId: string | undefined;\n\n constructor(message: string, options: { status: number; code?: string; requestId?: string }) {\n super(message);\n this.name = 'AranovaApiError';\n this.status = options.status;\n this.code = options.code;\n this.requestId = options.requestId;\n }\n}\n","import {\n API_KEY_HEADER,\n SDK_ENVIRONMENT_HEADER,\n SDK_PACKAGE_HEADER,\n SDK_SURFACE_HEADER,\n SDK_VERSION_HEADER,\n} from '../../ingest';\nimport { AranovaApiError } from './errors';\n\n/** Shared config for every sales HTTP helper. */\nexport interface SalesTransportConfig {\n /** Public (`aranv_pk_…`) or secret (`aranv_sk_…`) API key. */\n apiKey: string;\n /**\n * Base tracking endpoint, e.g. `https://aranovainternal-production.up.railway.app/tracking`.\n * The `/sales` path is appended by the helpers.\n */\n endpoint: string;\n /** Optional SDK identity headers (mirrors the event ingest client). */\n sdkVersion?: string;\n packageName?: string;\n surface?: string;\n environment?: string;\n}\n\nfunction identityHeaders(config: SalesTransportConfig): Record<string, string> {\n const headers: Record<string, string> = { [API_KEY_HEADER]: config.apiKey };\n if (config.sdkVersion) headers[SDK_VERSION_HEADER] = config.sdkVersion;\n if (config.packageName) headers[SDK_PACKAGE_HEADER] = config.packageName;\n if (config.surface) headers[SDK_SURFACE_HEADER] = config.surface;\n if (config.environment) headers[SDK_ENVIRONMENT_HEADER] = config.environment;\n return headers;\n}\n\nfunction joinUrl(endpoint: string, path: string): string {\n return `${endpoint.replace(/\\/$/, '')}${path}`;\n}\n\n/**\n * Single awaited request used by every sales helper. Unlike the event queue,\n * this surfaces failures: any non-2xx rejects with an {@link AranovaApiError}.\n * Returns `undefined` for 204 No Content.\n */\nexport async function salesRequest<T>(\n config: SalesTransportConfig,\n method: string,\n path: string,\n body?: unknown,\n): Promise<T> {\n const headers = identityHeaders(config);\n if (body !== undefined) headers['Content-Type'] = 'application/json';\n\n const response = await fetch(joinUrl(config.endpoint, path), {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n\n if (!response.ok) {\n let detail: string | undefined;\n let code: string | undefined;\n try {\n const parsed: unknown = await response.json();\n if (parsed && typeof parsed === 'object') {\n const record = parsed as Record<string, unknown>;\n if (typeof record.detail === 'string') detail = record.detail;\n if (typeof record.code === 'string') code = record.code;\n }\n } catch {\n // non-JSON error body — fall back to status text\n }\n throw new AranovaApiError(detail ?? response.statusText ?? 'Request failed', {\n status: response.status,\n code,\n requestId: response.headers.get('x-request-id') ?? undefined,\n });\n }\n\n if (response.status === 204) return undefined as T;\n return (await response.json()) as T;\n}\n","import type {\n Sale,\n SaleCursorPage,\n SaleInput,\n SaleListQuery,\n SaleUpdateInput,\n SupportedCurrency,\n} from './schema';\nimport { salesRequest, type SalesTransportConfig } from './transport';\n\n/** Config for {@link createSalesClient}. */\nexport interface SalesClientConfig extends SalesTransportConfig {\n /** Applied when an individual `record()` call omits `currency`. */\n defaultCurrency?: SupportedCurrency;\n}\n\n/**\n * One isomorphic sales client — what a key may *do* is enforced by the backend,\n * not by hiding methods. A **public** key (`aranv_pk_…`) may `record` (the\n * backend rejects reads/CRUD from it with a `403`); a **secret** key\n * (`aranv_sk_…`), used **server-side only**, gets full read/list/update/delete.\n * Never ship a secret key in a browser bundle.\n *\n * Generic over the service-key union `TService`: bind the type emitted by\n * `@aranova/tracking-cli gen` for compile-time-checked `service` values.\n */\nexport interface SalesClient<TService extends string = string> {\n record(\n input: Omit<SaleInput, 'currency' | 'occurred_at' | 'service'> & {\n service?: TService | null;\n currency?: SupportedCurrency;\n occurred_at?: string;\n },\n ): Promise<Sale>;\n list(query?: SaleListQuery): Promise<SaleCursorPage>;\n get(id: string): Promise<Sale>;\n update(\n id: string,\n patch: Omit<SaleUpdateInput, 'service'> & { service?: TService | null },\n ): Promise<Sale>;\n delete(id: string): Promise<void>;\n}\n\nexport function createSalesClient<TService extends string = string>(\n config: SalesClientConfig,\n): SalesClient<TService> {\n return {\n async record(input) {\n const currency = input.currency ?? config.defaultCurrency;\n if (!currency) {\n throw new Error(\n 'record: `currency` is required (pass it on the sale or set config.defaultCurrency)',\n );\n }\n const body: SaleInput = {\n ...input,\n currency,\n occurred_at: input.occurred_at ?? new Date().toISOString(),\n };\n return salesRequest<Sale>(config, 'POST', '/sales', body);\n },\n\n async list(query) {\n // Pagination travels at the top level; everything else is a filter.\n // Sort is fixed at occurred_at DESC by the backend (keyset cursor).\n const { cursor, limit, ...filters } = query ?? {};\n return salesRequest<SaleCursorPage>(config, 'POST', '/sales/query', {\n filters,\n ...(limit !== undefined ? { limit } : {}),\n ...(cursor !== undefined ? { cursor } : {}),\n });\n },\n\n async get(id) {\n return salesRequest<Sale>(config, 'GET', `/sales/${id}`);\n },\n\n async update(id, patch) {\n return salesRequest<Sale>(config, 'PATCH', `/sales/${id}`, patch);\n },\n\n async delete(id) {\n await salesRequest<void>(config, 'DELETE', `/sales/${id}`);\n },\n };\n}\n","import { salesRequest, type SalesTransportConfig } from './sales/transport';\n\n/** A business's active service, as returned by `GET /tracking/services`. */\nexport interface PublicServiceItem {\n key: string;\n label: string;\n}\n\n/**\n * Fetch the caller's business's active service taxonomy. Accepts a public or\n * secret key (the taxonomy is low-sensitivity category names). Powers the\n * `@aranova/tracking-cli gen` codegen.\n */\nexport async function fetchServices(config: SalesTransportConfig): Promise<PublicServiceItem[]> {\n return salesRequest<PublicServiceItem[]>(config, 'GET', '/services');\n}\n\n/**\n * Render the generated TypeScript module: a typed `as const` union of the\n * business's service keys. Keys are sorted so re-running `gen` produces no\n * spurious diffs.\n *\n * KEEP IN SYNC with the byte-identical twin in\n * `packages/tracking-cli/src/gen.ts::renderServicesModule`. The CLI cannot import\n * this (tracking-core is private/unpublished), so the two are independent copies;\n * the emitted format is a consumer-pinned contract. Each side's test asserts the\n * exact output for a fixed input — change both together.\n */\nexport function renderServicesModule(items: PublicServiceItem[]): string {\n const keys = [...items.map((item) => item.key)].sort();\n const literal =\n keys.length === 0 ? '[]' : `[\\n${keys.map((k) => ` ${JSON.stringify(k)},`).join('\\n')}\\n]`;\n return `// AUTO-GENERATED by \\`@aranova/tracking-cli gen\\` — do not edit by hand.\n// Re-run the command to refresh after changing services in the dashboard.\n\nexport const ARANOVA_SERVICES = ${literal} as const;\n\nexport type AranovaService = (typeof ARANOVA_SERVICES)[number];\n`;\n}\n","import type { SupportedCurrency } from './schema';\n\n/**\n * Money conversion helpers.\n *\n * The API speaks **integer minor units (cents)** exclusively. These helpers move\n * between a human-facing major amount (dollars) and cents, and format cents for\n * display — so a consumer always has both representations without float math.\n */\n\n// Minor-unit exponent per supported currency (USD/CAD = 2 decimal places).\nconst MINOR_UNIT_EXPONENT: Record<SupportedCurrency, number> = {\n USD: 2,\n CAD: 2,\n};\n\nfunction exponentFor(currency: SupportedCurrency): number {\n return MINOR_UNIT_EXPONENT[currency] ?? 2;\n}\n\n/**\n * Convert a major amount (dollars `250.5`) to integer minor units (`25050`).\n *\n * Convenience only — the wire is always integer cents. Uses float multiply +\n * `Math.round`, so values that aren't exactly representable in binary float\n * (e.g. `1.005`) can round to the neighbouring cent. If you already hold an\n * exact cents integer, pass it straight through and skip this helper.\n */\nexport function toMinor(amount: number, currency: SupportedCurrency): number {\n return Math.round(amount * 10 ** exponentFor(currency));\n}\n\n/** Convert integer minor units (`25050`) to a major amount (`250.5`). */\nexport function fromMinor(cents: number, currency: SupportedCurrency): number {\n return cents / 10 ** exponentFor(currency);\n}\n\n/**\n * Format integer minor units as a localized currency string (e.g. `\"$250.50\"`).\n * Uses the built-in `Intl.NumberFormat` — no extra dependency.\n */\nexport function formatMoney(\n cents: number,\n currency: SupportedCurrency,\n locale?: string,\n): string {\n return new Intl.NumberFormat(locale, { style: 'currency', currency }).format(\n fromMinor(cents, currency),\n );\n}\n","'use client';\n\nimport { useEffect, useMemo } from 'react';\n\nimport {\n bootstrapGoogleAdsTracking,\n bootstrapMultipleGtags,\n type GtagEnvironmentMap,\n} from '../../tracking-core/src/index';\n\n/**\n * Props for the Google Ads tracking component.\n *\n * Accepts either a single `gtagId` (legacy) or a labelled `gtagIds` map\n * where ALL entries are loaded simultaneously via `gtag('config', ...)`.\n */\nexport type GoogleAdsTrackingProps =\n | { gtagId: string; gtagIds?: undefined }\n | { gtagId?: undefined; gtagIds: GtagEnvironmentMap };\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(props: GoogleAdsTrackingProps) {\n const { gtagId, gtagIds } = props;\n\n // Stabilize object reference so the effect doesn't re-fire when the\n // parent passes an inline object literal on every render.\n const gtagIdsKey = useMemo(\n () => (gtagIds ? JSON.stringify(gtagIds) : ''),\n [gtagIds],\n );\n\n useEffect(() => {\n if (gtagIds && Object.keys(gtagIds).length > 0) {\n bootstrapMultipleGtags(gtagIds);\n } else if (gtagId) {\n bootstrapGoogleAdsTracking(gtagId);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- gtagIdsKey is the stable proxy for gtagIds\n }, [gtagId, gtagIdsKey]);\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 bootstrapMultipleGtags,\n createTypedClient,\n getOrCreateTrackingClient,\n type GtagEnvironmentMap,\n type TrackingEnvironment,\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 * Deployment environment label reported in session context.\n *\n * Does not affect which gtag IDs are loaded — all configured IDs are\n * loaded simultaneously. This value is purely for event tagging so the\n * dashboard can filter by environment.\n */\n environment?: TrackingEnvironment;\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 and `gtagIds` is also omitted, no gtag script is loaded.\n */\n gtagId?: string;\n /**\n * Labelled map of Google Ads tag IDs. ALL are loaded simultaneously.\n * When provided, `gtagId` is ignored.\n */\n gtagIds?: GtagEnvironmentMap;\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, environment, 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, gtagIds, children }: TrackingProviderProps): ReactNode {\n // Resolve gtag IDs map for context and heartbeat.\n const resolvedGtagIds = gtagIds ?? (gtagId ? { default: gtagId } : undefined);\n\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 environment,\n activeGtagIds: resolvedGtagIds,\n debug,\n }),\n triggers,\n { debug },\n ),\n [],\n );\n\n // Stabilize object reference so the effect doesn't re-fire when the\n // parent passes an inline object literal on every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const gtagIdsKey = useMemo(() => (gtagIds ? JSON.stringify(gtagIds) : ''), [gtagIds]);\n\n useEffect(() => {\n if (gtagIds && Object.keys(gtagIds).length > 0) {\n bootstrapMultipleGtags(gtagIds);\n } else if (gtagId) {\n bootstrapGoogleAdsTracking(gtagId);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- gtagIdsKey is the stable proxy for gtagIds\n }, [gtagId, gtagIdsKey]);\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 environment,\n activeGtagIds: resolvedGtagIds,\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.9.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":["identityHeaders","z","z","z","z","z","z","z","z","z","useEffect","useEffect","useEffect","useState","useState","useEffect","useEffect","useMemo","jsx","useMemo","useEffect"]}
1
+ {"version":3,"sources":["../src/ConsentBanner.tsx","../src/hooks.ts","../../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","../../tracking-core/src/resources/sales/errors.ts","../../tracking-core/src/resources/sales/transport.ts","../../tracking-core/src/resources/sales/client.ts","../../tracking-core/src/resources/services.ts","../../tracking-core/src/resources/sales/money.ts","../src/GoogleAdsTracking.tsx","../src/factory.tsx","../package.json"],"sourcesContent":["'use client';\n\nimport { useEffect, useState, type CSSProperties, type ReactNode } from 'react';\n\nimport { useConsent } from './hooks';\n\n/**\n * Default non-blocking consent banner.\n *\n * Renders only while consent is `pending`; collapses to `null` once the\n * visitor has chosen.\n *\n * **Styling is intentionally self-contained** — inline styles, zero CSS\n * dependencies, no Tailwind required at the consumer. The Tailwind-based\n * banner shipped before 0.9.1 rendered as transparent in any consumer that\n * didn't configure their content array to scan\n * `node_modules/@aranova/tracking-react/dist/**`; this version sidesteps that\n * class of bug entirely.\n *\n * For a fully bespoke banner, skip this component and use {@link useConsent}\n * directly to drive your own UI.\n *\n * @example\n * // Drop-in default\n * <ConsentBanner />\n *\n * @example\n * // Customized\n * <ConsentBanner\n * message=\"We use cookies to learn which ads drive bookings.\"\n * acceptLabel=\"Sounds good\"\n * declineLabel=\"No thanks\"\n * policyHref=\"/privacy\"\n * policyLabel=\"Privacy policy\"\n * theme=\"dark\"\n * onAccept={() => track('consent_accepted')}\n * onDecline={() => track('consent_declined')}\n * />\n */\nexport interface ConsentBannerProps {\n /** Body text. Defaults to the standard cookies-for-ad-performance message. */\n message?: ReactNode;\n /** Optional bold title above the body text. */\n title?: ReactNode;\n /** Label for the accept button. Default: `\"Accept\"`. */\n acceptLabel?: string;\n /** Label for the decline button. Default: `\"Decline\"`. */\n declineLabel?: string;\n /** Optional link inline with the message (e.g. to a privacy policy). */\n policyHref?: string;\n /** Visible text for {@link policyHref}. Default: `\"Learn more\"`. */\n policyLabel?: string;\n /**\n * Fires after the consent state is persisted + propagated to gtag. Useful\n * for emitting your own analytics event on the choice.\n */\n onAccept?: () => void;\n onDecline?: () => void;\n /** Where the banner docks. Default: `\"bottom\"`. */\n position?: 'top' | 'bottom';\n /**\n * Visual theme. `\"auto\"` follows `prefers-color-scheme`. Default: `\"light\"`.\n */\n theme?: 'light' | 'dark' | 'auto';\n /** Class added to the outer wrapper for additional styling hooks. */\n className?: string;\n /** Inline style overrides applied to the outer wrapper after the defaults. */\n style?: CSSProperties;\n}\n\ninterface ThemeTokens {\n background: string;\n border: string;\n text: string;\n mutedText: string;\n acceptBg: string;\n acceptText: string;\n declineBg: string;\n declineText: string;\n declineBorder: string;\n shadow: string;\n linkColor: string;\n}\n\nconst LIGHT_THEME: ThemeTokens = {\n background: '#ffffff',\n border: '#e5e7eb',\n text: '#111827',\n mutedText: '#4b5563',\n acceptBg: '#111827',\n acceptText: '#ffffff',\n declineBg: 'transparent',\n declineText: '#111827',\n declineBorder: '#d1d5db',\n shadow: '0 -4px 16px -2px rgba(15, 23, 42, 0.08), 0 -2px 6px -1px rgba(15, 23, 42, 0.04)',\n linkColor: '#1f2937',\n};\n\nconst DARK_THEME: ThemeTokens = {\n background: '#0f172a',\n border: '#1e293b',\n text: '#f1f5f9',\n mutedText: '#cbd5e1',\n acceptBg: '#f1f5f9',\n acceptText: '#0f172a',\n declineBg: 'transparent',\n declineText: '#f1f5f9',\n declineBorder: '#334155',\n shadow: '0 -4px 16px -2px rgba(0, 0, 0, 0.5), 0 -2px 6px -1px rgba(0, 0, 0, 0.3)',\n linkColor: '#e2e8f0',\n};\n\nfunction useResolvedTheme(theme: ConsentBannerProps['theme']): ThemeTokens {\n const [prefersDark, setPrefersDark] = useState<boolean>(false);\n\n useEffect(() => {\n if (theme !== 'auto' || typeof window === 'undefined' || !window.matchMedia) return;\n const mql = window.matchMedia('(prefers-color-scheme: dark)');\n setPrefersDark(mql.matches);\n const onChange = (e: MediaQueryListEvent): void => setPrefersDark(e.matches);\n mql.addEventListener('change', onChange);\n return () => mql.removeEventListener('change', onChange);\n }, [theme]);\n\n if (theme === 'dark') return DARK_THEME;\n if (theme === 'auto' && prefersDark) return DARK_THEME;\n return LIGHT_THEME;\n}\n\nconst DEFAULT_MESSAGE =\n 'We use cookies to understand ad performance and improve how our marketing works across visits. You can accept or decline this tracking.';\n\nexport function ConsentBanner({\n message,\n title,\n acceptLabel = 'Accept',\n declineLabel = 'Decline',\n policyHref,\n policyLabel = 'Learn more',\n onAccept,\n onDecline,\n position = 'bottom',\n theme = 'light',\n className,\n style,\n}: ConsentBannerProps = {}): ReactNode {\n const { isPending, accept, decline } = useConsent();\n const tokens = useResolvedTheme(theme);\n const [hasMounted, setHasMounted] = useState(false);\n\n useEffect(() => {\n setHasMounted(true);\n }, []);\n\n if (!hasMounted || !isPending) return null;\n\n const wrapperStyle: CSSProperties = {\n position: 'fixed',\n left: 0,\n right: 0,\n [position]: 0,\n zIndex: 2147483640,\n background: tokens.background,\n color: tokens.text,\n borderTop: position === 'bottom' ? `1px solid ${tokens.border}` : 'none',\n borderBottom: position === 'top' ? `1px solid ${tokens.border}` : 'none',\n boxShadow: tokens.shadow,\n padding: '16px 20px',\n boxSizing: 'border-box',\n animation: `${ANIMATION_NAME}-${position} 200ms ease-out`,\n fontFamily:\n 'system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n ...style,\n };\n\n const innerStyle: CSSProperties = {\n maxWidth: 1100,\n margin: '0 auto',\n display: 'flex',\n flexWrap: 'wrap',\n gap: 16,\n alignItems: 'center',\n justifyContent: 'space-between',\n };\n\n const messageStyle: CSSProperties = {\n flex: '1 1 320px',\n margin: 0,\n fontSize: 14,\n lineHeight: 1.5,\n color: tokens.mutedText,\n };\n\n const titleStyle: CSSProperties = {\n margin: '0 0 4px 0',\n fontSize: 14,\n fontWeight: 600,\n color: tokens.text,\n };\n\n const actionsStyle: CSSProperties = {\n display: 'flex',\n gap: 8,\n flexShrink: 0,\n };\n\n const buttonBase: CSSProperties = {\n appearance: 'none',\n fontFamily: 'inherit',\n fontSize: 14,\n fontWeight: 500,\n padding: '8px 16px',\n borderRadius: 6,\n cursor: 'pointer',\n border: '1px solid transparent',\n transition: 'opacity 120ms ease',\n };\n\n const declineStyle: CSSProperties = {\n ...buttonBase,\n background: tokens.declineBg,\n color: tokens.declineText,\n borderColor: tokens.declineBorder,\n };\n\n const acceptStyle: CSSProperties = {\n ...buttonBase,\n background: tokens.acceptBg,\n color: tokens.acceptText,\n };\n\n const linkStyle: CSSProperties = {\n color: tokens.linkColor,\n textDecoration: 'underline',\n textUnderlineOffset: 2,\n };\n\n const handleAccept = (): void => {\n accept();\n onAccept?.();\n };\n\n const handleDecline = (): void => {\n decline();\n onDecline?.();\n };\n\n return (\n <>\n <style>{ANIMATION_KEYFRAMES}</style>\n <div\n role=\"dialog\"\n aria-live=\"polite\"\n aria-label=\"Cookie consent\"\n className={className}\n style={wrapperStyle}\n >\n <div style={innerStyle}>\n <div style={{ flex: '1 1 320px' }}>\n {title ? <p style={titleStyle}>{title}</p> : null}\n <p style={messageStyle}>\n {message ?? DEFAULT_MESSAGE}\n {policyHref ? (\n <>\n {' '}\n <a href={policyHref} style={linkStyle}>\n {policyLabel}\n </a>\n </>\n ) : null}\n </p>\n </div>\n <div style={actionsStyle}>\n <button type=\"button\" onClick={handleDecline} style={declineStyle}>\n {declineLabel}\n </button>\n <button type=\"button\" onClick={handleAccept} style={acceptStyle}>\n {acceptLabel}\n </button>\n </div>\n </div>\n </div>\n </>\n );\n}\n\n// Single injected stylesheet for the slide-in animation. Keyframes can't be\n// expressed in inline `style`, so this is the one CSS dependency. Scoped to\n// the banner's own animation-name to avoid colliding with consumer styles.\nconst ANIMATION_NAME = 'aranova-consent-banner';\nconst ANIMATION_KEYFRAMES = `\n@keyframes ${ANIMATION_NAME}-bottom {\n from { transform: translateY(100%); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n}\n@keyframes ${ANIMATION_NAME}-top {\n from { transform: translateY(-100%); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n}\n`;\n","'use client';\n\nimport { useCallback, useEffect, useState } from 'react';\n\nimport {\n CONSENT_STATE_KEY,\n getConsentState,\n resetConsent,\n setConsentState,\n} 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 *\n * Prefer {@link useConsent} for new code — it returns the same state plus\n * the `accept` / `decline` / `reset` actions a custom consent UI needs.\n * `useConsentState` is kept as a convenience for callers that only need to\n * read.\n */\nexport function useConsentState(): ConsentState {\n return useConsent().state;\n}\n\n/**\n * The headless consent surface — state + actions in one hook.\n *\n * Build a fully-custom banner without losing the gtag-sync, localStorage\n * persistence, or cross-tab propagation:\n *\n * ```tsx\n * const { state, accept, decline, reset, isPending } = useConsent();\n *\n * if (!isPending) {\n * return <button onClick={reset}>Cookie preferences</button>;\n * }\n * return (\n * <MyBannerStyling>\n * <button onClick={decline}>No thanks</button>\n * <button onClick={accept}>Sure</button>\n * </MyBannerStyling>\n * );\n * ```\n *\n * The boolean helpers (`isPending` / `isGranted` / `isDenied`) are equivalent\n * to comparing `state` directly — they're there for readability at call sites.\n */\nexport interface UseConsentResult {\n state: ConsentState;\n isPending: boolean;\n isGranted: boolean;\n isDenied: boolean;\n accept: () => void;\n decline: () => void;\n reset: () => void;\n}\n\nexport function useConsent(): UseConsentResult {\n const [state, setState] = useState<ConsentState>('pending');\n\n useEffect(() => {\n setState(getConsentState());\n\n const handleStorage = (event: StorageEvent): void => {\n if (event.key === CONSENT_STATE_KEY) setState(getConsentState());\n };\n\n window.addEventListener('storage', handleStorage);\n return () => window.removeEventListener('storage', handleStorage);\n }, []);\n\n const accept = useCallback((): void => {\n setConsentState('granted');\n setState('granted');\n }, []);\n\n const decline = useCallback((): void => {\n setConsentState('denied');\n setState('denied');\n }, []);\n\n const reset = useCallback((): void => {\n resetConsent();\n setState('pending');\n }, []);\n\n return {\n state,\n isPending: state === 'pending',\n isGranted: state === 'granted',\n isDenied: state === 'denied',\n accept,\n decline,\n reset,\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 * Clear the stored consent choice so the banner re-appears on next render.\n *\n * Power a \"Cookie preferences\" link in a footer so visitors can change their\n * mind without losing access to your site:\n *\n * ```tsx\n * const { reset } = useConsent();\n * <button onClick={reset}>Cookie preferences</button>\n * ```\n *\n * Does NOT push an `update` to gtag — there's nothing to update because the\n * visitor hasn't chosen anything yet. The next `setConsentState()` call will\n * sync gtag once they re-choose.\n */\nexport function resetConsent(): void {\n if (typeof window === 'undefined')\n return;\n\n window.localStorage.removeItem(CONSENT_STATE_KEY);\n window.localStorage.removeItem(CONSENT_TIMESTAMP_KEY);\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 TrackingEnvironment,\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 environment?: TrackingEnvironment;\n activeGtagIds?: Record<string, 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 environment: input.environment ?? 'production',\n active_gtag_ids: input.activeGtagIds ?? 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';\nimport type { GtagEnvironmentMap } from './types';\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 * Pattern matching valid Google Ads / GA4 tag IDs.\n *\n * Accepted formats: `AW-123456789`, `G-XXXXXXXXXX`, `GT-XXXXXXX`, `DC-XXXXXXX`.\n * IDs that don't match are silently dropped to prevent script injection when\n * interpolated into inline `<script>` content.\n */\nconst GTAG_ID_PATTERN = /^[A-Z]{1,3}-[A-Za-z0-9_-]+$/;\n\nexport function isValidGtagId(id: string): boolean {\n return GTAG_ID_PATTERN.test(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 if (!isValidGtagId(gtagId)) return '';\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 if (!isValidGtagId(gtagId))\n return;\n\n applyDefaultConsentState();\n loadGtagScript(gtagId);\n initializeGtag(gtagId);\n restoreStoredConsent();\n}\n\n/**\n * Load and initialize Google Ads tracking for ALL labelled gtag IDs.\n *\n * Every ID in the map gets a `gtag('config', ...)` call — gtag natively\n * supports multiple configured tags on the same page. The script loader\n * only runs once (for the first ID); subsequent IDs reuse the shared\n * `dataLayer`.\n */\nexport function bootstrapMultipleGtags(gtagIds: GtagEnvironmentMap): void {\n if (typeof window === 'undefined' || typeof document === 'undefined')\n return;\n\n const ids = Object.values(gtagIds).filter(isValidGtagId);\n if (ids.length === 0)\n return;\n\n applyDefaultConsentState();\n loadGtagScript(ids[0]);\n\n // Call gtag('js') once, then gtag('config') per ID. initializeGtag\n // calls both, but gtag('js') should only fire once per page load.\n const gtag = ensureGtagFunction();\n gtag('js', new Date());\n for (const id of ids) {\n gtag('config', id);\n }\n\n restoreStoredConsent();\n}\n\n/**\n * Create an inline script that initializes gtag for multiple Google Ads tag ids.\n *\n * Used by the Next.js `GoogleAdsTracking` component when rendering `<Script>`\n * tags for a multi-gtag configuration.\n */\nexport function createGtagInitScriptMulti(gtagIds: string[]): string {\n const safeIds = gtagIds.filter(isValidGtagId);\n const configs = safeIds.map(id => `gtag('config', '${id}');`).join('\\n');\n return `\nwindow.dataLayer = window.dataLayer || [];\nfunction gtag(){dataLayer.push(arguments);}\nwindow.gtag = gtag;\ngtag('js', new Date());\n${configs}\n`.trim();\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 gtagIds: Record<string, string> | null = 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 configured_gtag_ids: gtagIds,\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 TrackingEnvironment,\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 * Identity headers stamped on every ingest request. Duplicate fields already\n * present in `session.context` but survive body-parse failures so the backend\n * can attribute 422s to the offending SDK install.\n */\nexport const SDK_VERSION_HEADER = 'X-Aranova-Sdk-Version';\nexport const SDK_PACKAGE_HEADER = 'X-Aranova-Sdk-Package';\nexport const SDK_SURFACE_HEADER = 'X-Aranova-Sdk-Surface';\nexport const SDK_ENVIRONMENT_HEADER = 'X-Aranova-Sdk-Environment';\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 /** Deployment environment label reported in session context. */\n environment?: TrackingEnvironment;\n /** All active gtag IDs, keyed by label. Included in session context. */\n activeGtagIds?: Record<string, string>;\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 environment: TrackingEnvironment,\n activeGtagIds: Record<string, 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 environment,\n active_gtag_ids: activeGtagIds,\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\ninterface IdentityHeaders {\n sdkVersion: string;\n packageName: string;\n surface: string;\n environment: string;\n}\n\nasync function postWithFetch(\n url: string,\n body: string,\n apiKey: string,\n identity: IdentityHeaders,\n keepalive: boolean,\n): 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 [SDK_VERSION_HEADER]: identity.sdkVersion,\n [SDK_PACKAGE_HEADER]: identity.packageName,\n [SDK_SURFACE_HEADER]: identity.surface,\n [SDK_ENVIRONMENT_HEADER]: identity.environment,\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 // Default to 'production' so the wire payload always carries a valid enum\n // value. Sending null would fail the backend's strict enum validation.\n const environment: TrackingEnvironment = config.environment ?? 'production';\n const activeGtagIds = config.activeGtagIds ?? null;\n const endpointBase = config.endpoint.replace(/\\/$/, '');\n const eventsUrl = `${endpointBase}/events`;\n const identityHeaders: IdentityHeaders = {\n sdkVersion: sdkVersion ?? '',\n packageName: packageName ?? '',\n surface: config.surface,\n environment,\n };\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 activeGtagIds,\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, environment, activeGtagIds);\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, identityHeaders, 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, identityHeaders, 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 configured_gtag_ids: z.record(z.string(), z.string()).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","/**\n * Error thrown by the sales client (`createSalesClient`) on a\n * non-2xx response. Unlike the fire-and-forget event queue (which swallows\n * failures), a sale is a transaction the caller must be able to react to.\n */\nexport class AranovaApiError extends Error {\n readonly status: number;\n readonly code: string | undefined;\n readonly requestId: string | undefined;\n\n constructor(message: string, options: { status: number; code?: string; requestId?: string }) {\n super(message);\n this.name = 'AranovaApiError';\n this.status = options.status;\n this.code = options.code;\n this.requestId = options.requestId;\n }\n}\n","import {\n API_KEY_HEADER,\n SDK_ENVIRONMENT_HEADER,\n SDK_PACKAGE_HEADER,\n SDK_SURFACE_HEADER,\n SDK_VERSION_HEADER,\n} from '../../ingest';\nimport { AranovaApiError } from './errors';\n\n/** Shared config for every sales HTTP helper. */\nexport interface SalesTransportConfig {\n /** Public (`aranv_pk_…`) or secret (`aranv_sk_…`) API key. */\n apiKey: string;\n /**\n * Base tracking endpoint, e.g. `https://aranovainternal-production.up.railway.app/tracking`.\n * The `/sales` path is appended by the helpers.\n */\n endpoint: string;\n /** Optional SDK identity headers (mirrors the event ingest client). */\n sdkVersion?: string;\n packageName?: string;\n surface?: string;\n environment?: string;\n}\n\nfunction identityHeaders(config: SalesTransportConfig): Record<string, string> {\n const headers: Record<string, string> = { [API_KEY_HEADER]: config.apiKey };\n if (config.sdkVersion) headers[SDK_VERSION_HEADER] = config.sdkVersion;\n if (config.packageName) headers[SDK_PACKAGE_HEADER] = config.packageName;\n if (config.surface) headers[SDK_SURFACE_HEADER] = config.surface;\n if (config.environment) headers[SDK_ENVIRONMENT_HEADER] = config.environment;\n return headers;\n}\n\nfunction joinUrl(endpoint: string, path: string): string {\n return `${endpoint.replace(/\\/$/, '')}${path}`;\n}\n\n/**\n * Single awaited request used by every sales helper. Unlike the event queue,\n * this surfaces failures: any non-2xx rejects with an {@link AranovaApiError}.\n * Returns `undefined` for 204 No Content.\n */\nexport async function salesRequest<T>(\n config: SalesTransportConfig,\n method: string,\n path: string,\n body?: unknown,\n): Promise<T> {\n const headers = identityHeaders(config);\n if (body !== undefined) headers['Content-Type'] = 'application/json';\n\n const response = await fetch(joinUrl(config.endpoint, path), {\n method,\n headers,\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n\n if (!response.ok) {\n let detail: string | undefined;\n let code: string | undefined;\n try {\n const parsed: unknown = await response.json();\n if (parsed && typeof parsed === 'object') {\n const record = parsed as Record<string, unknown>;\n if (typeof record.detail === 'string') detail = record.detail;\n if (typeof record.code === 'string') code = record.code;\n }\n } catch {\n // non-JSON error body — fall back to status text\n }\n throw new AranovaApiError(detail ?? response.statusText ?? 'Request failed', {\n status: response.status,\n code,\n requestId: response.headers.get('x-request-id') ?? undefined,\n });\n }\n\n if (response.status === 204) return undefined as T;\n return (await response.json()) as T;\n}\n","import type {\n Sale,\n SaleCursorPage,\n SaleInput,\n SaleListQuery,\n SaleSummary,\n SaleSummaryQuery,\n SaleUpdateInput,\n SupportedCurrency,\n} from './schema';\nimport { salesRequest, type SalesTransportConfig } from './transport';\n\n/** Config for {@link createSalesClient}. */\nexport interface SalesClientConfig extends SalesTransportConfig {\n /** Applied when an individual `record()` call omits `currency`. */\n defaultCurrency?: SupportedCurrency;\n}\n\n/**\n * One isomorphic sales client — what a key may *do* is enforced by the backend,\n * not by hiding methods. A **public** key (`aranv_pk_…`) may `record` (the\n * backend rejects reads/CRUD from it with a `403`); a **secret** key\n * (`aranv_sk_…`), used **server-side only**, gets full read/list/update/delete.\n * Never ship a secret key in a browser bundle.\n *\n * Generic over the service-key union `TService`: bind the type emitted by\n * `@aranova/tracking-cli gen` for compile-time-checked `service` values.\n */\nexport interface SalesClient<TService extends string = string> {\n record(\n input: Omit<SaleInput, 'currency' | 'occurred_at' | 'service'> & {\n service?: TService | null;\n currency?: SupportedCurrency;\n occurred_at?: string;\n },\n ): Promise<Sale>;\n list(query?: SaleListQuery): Promise<SaleCursorPage>;\n /**\n * Currency-grouped aggregations for the key's business (revenue per currency,\n * AOV, distinct customers, by-service / by-currency / by-category breakdowns,\n * and a revenue/count trend). Secret key only — a public key gets a `403`.\n */\n summary(query: SaleSummaryQuery): Promise<SaleSummary>;\n get(id: string): Promise<Sale>;\n update(\n id: string,\n patch: Omit<SaleUpdateInput, 'service'> & { service?: TService | null },\n ): Promise<Sale>;\n delete(id: string): Promise<void>;\n}\n\nexport function createSalesClient<TService extends string = string>(\n config: SalesClientConfig,\n): SalesClient<TService> {\n return {\n async record(input) {\n const currency = input.currency ?? config.defaultCurrency;\n if (!currency) {\n throw new Error(\n 'record: `currency` is required (pass it on the sale or set config.defaultCurrency)',\n );\n }\n const body: SaleInput = {\n ...input,\n currency,\n occurred_at: input.occurred_at ?? new Date().toISOString(),\n };\n return salesRequest<Sale>(config, 'POST', '/sales', body);\n },\n\n async list(query) {\n // Pagination travels at the top level; everything else is a filter.\n // Sort is fixed at occurred_at DESC by the backend (keyset cursor).\n const { cursor, limit, ...filters } = query ?? {};\n return salesRequest<SaleCursorPage>(config, 'POST', '/sales/query', {\n filters,\n ...(limit !== undefined ? { limit } : {}),\n ...(cursor !== undefined ? { cursor } : {}),\n });\n },\n\n async summary(query) {\n // Mirrors list(): pagination-free, range + options at the top level, the\n // rest of the shape is filters. Same POST-body convention as /sales/query.\n const { range, include_categories, include_deleted_services, top_n, ...filters } = query;\n return salesRequest<SaleSummary>(config, 'POST', '/sales/summary', {\n filters,\n range,\n ...(include_categories !== undefined ? { include_categories } : {}),\n ...(include_deleted_services !== undefined ? { include_deleted_services } : {}),\n ...(top_n !== undefined ? { top_n } : {}),\n });\n },\n\n async get(id) {\n return salesRequest<Sale>(config, 'GET', `/sales/${id}`);\n },\n\n async update(id, patch) {\n return salesRequest<Sale>(config, 'PATCH', `/sales/${id}`, patch);\n },\n\n async delete(id) {\n await salesRequest<void>(config, 'DELETE', `/sales/${id}`);\n },\n };\n}\n","import { salesRequest, type SalesTransportConfig } from './sales/transport';\n\n/** A business's active service, as returned by `GET /tracking/services`. */\nexport interface PublicServiceItem {\n key: string;\n label: string;\n}\n\n/**\n * Fetch the caller's business's active service taxonomy. Accepts a public or\n * secret key (the taxonomy is low-sensitivity category names). Powers the\n * `@aranova/tracking-cli gen` codegen.\n */\nexport async function fetchServices(config: SalesTransportConfig): Promise<PublicServiceItem[]> {\n return salesRequest<PublicServiceItem[]>(config, 'GET', '/services');\n}\n\n/**\n * Render the generated TypeScript module: an `as const` array of `{ key, label }`\n * items (sorted by key → stable diffs), the `AranovaService` key union, and a\n * `ARANOVA_SERVICE_LABELS` lookup typed against that union.\n *\n * KEEP IN SYNC with the byte-identical twin in\n * `packages/tracking-cli/src/gen.ts::renderServicesModule`. The CLI cannot import\n * this (tracking-core is private/unpublished), so the two are independent copies;\n * the emitted format is a consumer-pinned contract. Each side's test asserts the\n * exact output for a fixed input — change both together.\n */\nexport function renderServicesModule(items: PublicServiceItem[]): string {\n const sorted = [...items].sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));\n\n const itemsLiteral =\n sorted.length === 0\n ? '[]'\n : `[\\n${sorted\n .map((i) => ` { key: ${JSON.stringify(i.key)}, label: ${JSON.stringify(i.label)} },`)\n .join('\\n')}\\n]`;\n\n const labelsLiteral =\n sorted.length === 0\n ? '{}'\n : `{\\n${sorted\n .map((i) => ` ${JSON.stringify(i.key)}: ${JSON.stringify(i.label)},`)\n .join('\\n')}\\n}`;\n\n return `// AUTO-GENERATED by \\`@aranova/tracking-cli gen\\` — do not edit by hand.\n// Re-run the command to refresh after changing services in the dashboard.\n\nexport const ARANOVA_SERVICES = ${itemsLiteral} as const;\n\nexport type AranovaService = (typeof ARANOVA_SERVICES)[number][\"key\"];\n\nexport const ARANOVA_SERVICE_LABELS: Record<AranovaService, string> = ${labelsLiteral};\n`;\n}\n","import type { SupportedCurrency } from './schema';\n\n/**\n * Money conversion helpers.\n *\n * The API speaks **integer minor units (cents)** exclusively. These helpers move\n * between a human-facing major amount (dollars) and cents, and format cents for\n * display — so a consumer always has both representations without float math.\n */\n\n// Minor-unit exponent per supported currency (USD/CAD = 2 decimal places).\nconst MINOR_UNIT_EXPONENT: Record<SupportedCurrency, number> = {\n USD: 2,\n CAD: 2,\n};\n\nfunction exponentFor(currency: SupportedCurrency): number {\n return MINOR_UNIT_EXPONENT[currency] ?? 2;\n}\n\n/**\n * Convert a major amount (dollars `250.5`) to integer minor units (`25050`).\n *\n * Convenience only — the wire is always integer cents. Uses float multiply +\n * `Math.round`, so values that aren't exactly representable in binary float\n * (e.g. `1.005`) can round to the neighbouring cent. If you already hold an\n * exact cents integer, pass it straight through and skip this helper.\n */\nexport function toMinor(amount: number, currency: SupportedCurrency): number {\n return Math.round(amount * 10 ** exponentFor(currency));\n}\n\n/** Convert integer minor units (`25050`) to a major amount (`250.5`). */\nexport function fromMinor(cents: number, currency: SupportedCurrency): number {\n return cents / 10 ** exponentFor(currency);\n}\n\n/**\n * Format integer minor units as a localized currency string (e.g. `\"$250.50\"`).\n * Uses the built-in `Intl.NumberFormat` — no extra dependency.\n */\nexport function formatMoney(\n cents: number,\n currency: SupportedCurrency,\n locale?: string,\n): string {\n return new Intl.NumberFormat(locale, { style: 'currency', currency }).format(\n fromMinor(cents, currency),\n );\n}\n","'use client';\n\nimport { useEffect, useMemo } from 'react';\n\nimport {\n bootstrapGoogleAdsTracking,\n bootstrapMultipleGtags,\n type GtagEnvironmentMap,\n} from '../../tracking-core/src/index';\n\n/**\n * Props for the Google Ads tracking component.\n *\n * Accepts either a single `gtagId` (legacy) or a labelled `gtagIds` map\n * where ALL entries are loaded simultaneously via `gtag('config', ...)`.\n */\nexport type GoogleAdsTrackingProps =\n | { gtagId: string; gtagIds?: undefined }\n | { gtagId?: undefined; gtagIds: GtagEnvironmentMap };\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(props: GoogleAdsTrackingProps) {\n const { gtagId, gtagIds } = props;\n\n // Stabilize object reference so the effect doesn't re-fire when the\n // parent passes an inline object literal on every render.\n const gtagIdsKey = useMemo(\n () => (gtagIds ? JSON.stringify(gtagIds) : ''),\n [gtagIds],\n );\n\n useEffect(() => {\n if (gtagIds && Object.keys(gtagIds).length > 0) {\n bootstrapMultipleGtags(gtagIds);\n } else if (gtagId) {\n bootstrapGoogleAdsTracking(gtagId);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- gtagIdsKey is the stable proxy for gtagIds\n }, [gtagId, gtagIdsKey]);\n\n return null;\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 bootstrapMultipleGtags,\n createTypedClient,\n getOrCreateTrackingClient,\n type GtagEnvironmentMap,\n type TrackingEnvironment,\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 * Deployment environment label reported in session context.\n *\n * Does not affect which gtag IDs are loaded — all configured IDs are\n * loaded simultaneously. This value is purely for event tagging so the\n * dashboard can filter by environment.\n */\n environment?: TrackingEnvironment;\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 and `gtagIds` is also omitted, no gtag script is loaded.\n */\n gtagId?: string;\n /**\n * Labelled map of Google Ads tag IDs. ALL are loaded simultaneously.\n * When provided, `gtagId` is ignored.\n */\n gtagIds?: GtagEnvironmentMap;\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, environment, 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, gtagIds, children }: TrackingProviderProps): ReactNode {\n // Resolve gtag IDs map for context and heartbeat.\n const resolvedGtagIds = gtagIds ?? (gtagId ? { default: gtagId } : undefined);\n\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 environment,\n activeGtagIds: resolvedGtagIds,\n debug,\n }),\n triggers,\n { debug },\n ),\n [],\n );\n\n // Stabilize object reference so the effect doesn't re-fire when the\n // parent passes an inline object literal on every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const gtagIdsKey = useMemo(() => (gtagIds ? JSON.stringify(gtagIds) : ''), [gtagIds]);\n\n useEffect(() => {\n if (gtagIds && Object.keys(gtagIds).length > 0) {\n bootstrapMultipleGtags(gtagIds);\n } else if (gtagId) {\n bootstrapGoogleAdsTracking(gtagId);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- gtagIdsKey is the stable proxy for gtagIds\n }, [gtagId, gtagIdsKey]);\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 environment,\n activeGtagIds: resolvedGtagIds,\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.10.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":["useEffect","useState","identityHeaders","z","z","z","z","z","z","z","z","z","useState","useEffect","useEffect","useEffect","useEffect","useMemo","jsx","useMemo","useEffect"]}