@aranova/tracking-react 1.18.0 → 1.20.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.
- package/dist/{bookings-CVvpj_8K.d.mts → bookings-BpLjL5w1.d.mts} +25 -1
- package/dist/{bookings-CCGpXhYE.d.ts → bookings-CSDPoT-I.d.ts} +25 -1
- package/dist/client.d.mts +121 -4
- package/dist/client.d.ts +121 -4
- package/dist/client.js +1685 -31
- package/dist/client.js.map +1 -1
- package/dist/client.mjs +1687 -31
- package/dist/client.mjs.map +1 -1
- package/dist/index.d.mts +15 -39
- package/dist/index.d.ts +15 -39
- package/dist/index.js +178 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +176 -6
- package/dist/index.mjs.map +1 -1
- package/dist/{ingest-typed-CoCjTqJs.d.mts → ingest-typed-BEFQd3WM.d.mts} +200 -2
- package/dist/{ingest-typed-C3XAjHlH.d.ts → ingest-typed-DChvvgwd.d.ts} +200 -2
- package/dist/{sales-Dlc_umAV.d.ts → sales-CM_dxsf8.d.ts} +1 -1
- package/dist/{sales-B1ONR-Yi.d.mts → sales-agx4zLC9.d.mts} +1 -1
- package/dist/sales.d.mts +2 -2
- package/dist/sales.d.ts +2 -2
- package/dist/sales.js +115 -5
- package/dist/sales.js.map +1 -1
- package/dist/sales.mjs +115 -5
- package/dist/sales.mjs.map +1 -1
- package/package.json +1 -1
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/ConsentBanner.tsx","../src/hooks.ts","../../tracking-core/src/phone.ts","../../tracking-core/src/user-data.ts","../../tracking-core/src/consent.ts","../../tracking-core/src/capabilities.ts","../../tracking-core/src/cross-site-link.ts","../../tracking-core/src/tracking.ts","../../tracking-core/src/gtag.ts","../../tracking-core/src/fbq.ts","../../tracking-core/src/landing.ts","../../tracking-core/src/uuid.ts","../../tracking-core/src/session.ts","../../tracking-core/src/resources/conversion-firing.ts","../../tracking-core/src/resources/conversion-config.ts","../../tracking-core/src/resources/automatic-transaction.ts","../../tracking-core/src/resources/automatic-trigger.ts","../../tracking-core/src/resources/sales/money.ts","../../tracking-core/src/resources/tracking-config-runtime.ts","../../../contracts/autoleap-booking-links.json","../../tracking-core/src/integrations-public.ts","../../tracking-core/src/resources/conversion-autofire.ts","../../tracking-core/src/events/semantics.ts","../../tracking-core/src/page-view.ts","../../tracking-core/src/events/page-view.ts","../../tracking-core/src/heartbeat.ts","../../tracking-core/src/ingest.ts","../../tracking-core/src/ingest-typed.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-details.ts","../../tracking-core/src/events/form-submit.ts","../../tracking-core/src/events/form-step.ts","../../tracking-core/src/events/multi-page-session.ts","../../tracking-core/src/events/page-exit.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/site-link.ts","../../tracking-core/src/events/time-on-site.ts","../../tracking-core/src/events/registry.ts","../../tracking-core/src/form-submit-salvage.ts","../../tracking-core/src/triggers/time-on-site.ts","../../tracking-core/src/triggers/specific-page-visit.ts","../../tracking-core/src/triggers/navigation.ts","../../tracking-core/src/triggers/scroll-measurement.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/triggers/page-exit.ts","../../tracking-core/src/triggers/cta-click-capture.ts","../../tracking-core/src/triggers/site-link-capture.ts","../../tracking-core/src/triggers/phone-click-capture.ts","../../tracking-core/src/lead-forms/lead-forms.gen.ts","../../tracking-core/src/lead-forms/submit.ts","../../tracking-core/src/resources/sales/bookings.ts","../src/AdPlatformTracking.tsx","../src/GoogleAdsTracking.tsx","../src/factory.tsx","../package.json","../../tracking-core/src/phone-react.tsx","../../tracking-core/src/booking-react.tsx"],"sourcesContent":["\"use client\";\n// The React surface of @aranova/tracking-react, behind a real client boundary.\n//\n// Same build constraint as tracking-next: tsup runs with `splitting: false`,\n// so esbuild inlines non-entry modules into whatever entry imports them and\n// DROPS their `\"use client\"` directive. Only a directive leading an ENTRY file\n// survives. This file is that entry; it is marked external under its own\n// package specifier (see tsup.config.ts) so nothing inlines it back, and\n// index.ts re-exports from it.\n//\n// Consumers keep importing from the package root. The root entry itself stays\n// directive-free so its React-free helpers remain usable from server code.\n//\n// package.json needs a `typesVersions` entry for this subpath, not just an\n// `exports` one: node10 type resolution ignores `exports`, and the root\n// index.d.ts re-exports from this specifier — so without it, attw fails the\n// root entrypoint with an unresolvable internal import. (`blog-client` needs\n// no such entry: its specifier only appears in blog-server.d.ts, which node10\n// cannot reach at all, so it is never analyzed.)\n//\n// Keep the directive on line 1.\n\nexport { ConsentBanner, type ConsentBannerProps } from \"./ConsentBanner\";\nexport { AdPlatformTracking, type AdPlatformTrackingProps } from \"./AdPlatformTracking\";\nexport { GoogleAdsTracking, type GoogleAdsTrackingProps } from \"./GoogleAdsTracking\";\nexport {\n useConsent,\n useConsentState,\n useCookiePreferences,\n useGclid,\n useTrackingParams,\n type UseConsentResult,\n type UseCookiePreferencesOptions,\n type UseCookiePreferencesResult,\n} from \"./hooks\";\nexport {\n createTracking,\n type CreateTrackingOptions,\n type CreateTrackingResult,\n type TrackingProviderProps,\n} from \"./factory\";\nexport { PhoneField, usePhoneConfig, usePhoneField } from \"./phone\";\nexport type {\n PhoneFieldApi,\n PhoneFieldProps,\n PhoneInputProps,\n UsePhoneFieldOptions,\n} from \"./phone\";\nexport {\n BookingHandoffLink,\n useBookingHandoffUrl,\n useBookingIntegration,\n useBookingSlots,\n useBookingSubmit,\n} from \"./booking\";\nexport type {\n BookedAttempt,\n BookingHandoffLinkProps,\n BookingHandoffOptions,\n BookingSubmitState,\n BookingSubmitStatus,\n} from \"./booking\";\n// Typed lead-form submit (ADR-0024): here, not on the root, so it shares the tracking\n// client's module state — the factory's settings, the capability registry, the config runtime.\nexport { createLeadFormSubmit } from \"../../tracking-core/src/lead-forms/submit\";\n","\"use client\";\n\nimport { useEffect, useState, type CSSProperties, type ReactNode } from \"react\";\n\nimport { useConsent } from \"./hooks\";\n\n/**\n * Legacy opt-in-era consent banner.\n *\n * @deprecated PERMANENTLY INERT since consent v2 (opt-out model): it renders\n * only while consent is `pending`, and the effective state is never `pending`\n * anymore, so this component always returns `null`. Tracking is on by default;\n * replace the banner with a footer \"cookie preferences\" control built on\n * {@link useCookiePreferences} (see the package README). Kept exported so\n * existing integrations keep compiling; scheduled for removal.\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\n/**\n * @deprecated Permanently inert since consent v2 — always renders `null`\n * because the effective consent state is never `pending`. Use a footer\n * control built on {@link useCookiePreferences} instead. See\n * {@link ConsentBannerProps} for details.\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_EXPIRES_AT_KEY,\n CONSENT_STATE_KEY,\n CONSENT_TIMESTAMP_KEY,\n createEmptyTrackingParams,\n getConsentChoice,\n getCookieValueFromDocument,\n getTrackingParamsFromCookieReader,\n onConsentChange,\n optIn,\n optOut,\n registerCapability,\n resetConsent,\n type ConsentChoice,\n type ConsentChoiceState,\n type ConsentSource,\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/** Options for {@link useCookiePreferences}. */\nexport interface UseCookiePreferencesOptions {\n /** Days an explicit decline is honored. Defaults to 90. */\n declineTtlDays?: number;\n}\n\n/**\n * The headless cookie-preferences surface returned by\n * {@link useCookiePreferences}.\n */\nexport interface UseCookiePreferencesResult {\n /** Effective consent — `granted` unless an unexpired explicit decline exists. */\n state: ConsentChoiceState;\n /** `default` = no valid explicit choice stored; `explicit` = visitor chose. */\n source: ConsentSource;\n /** True when the visitor has made no (valid, unexpired) explicit choice. */\n isDefault: boolean;\n isGranted: boolean;\n isDenied: boolean;\n /** ISO timestamp of the explicit choice; null for the default state. */\n updatedAt: string | null;\n /** ISO expiry of an unexpired decline; null otherwise. */\n expiresAt: string | null;\n /** Explicitly opt out of ad tracking (honored for 90 days by default). */\n optOut: () => void;\n /** Explicitly opt in (never expires). */\n optIn: () => void;\n /** Clear the explicit choice — back to default-granted. */\n reset: () => void;\n}\n\nconst DEFAULT_CHOICE: ConsentChoice = {\n state: \"granted\",\n source: \"default\",\n updatedAt: null,\n expiresAt: null,\n};\n\n/**\n * Headless cookie-preferences hook for the opt-out consent model (consent v2).\n *\n * Tracking is ON by default; this hook is how each client site wires its own\n * footer \"Cookie preferences\" control (button, dialog, toggle — the packages\n * ship no consent UI). State stays in sync with actions from other components\n * in the same tab (via `onConsentChange`) and other tabs (via `storage`\n * events).\n *\n * ```tsx\n * function CookiePreferences() {\n * const { isDenied, optOut, optIn } = useCookiePreferences();\n * return isDenied ? (\n * <button onClick={optIn}>Enable ad measurement</button>\n * ) : (\n * <button onClick={optOut}>Opt out of ad measurement</button>\n * );\n * }\n * ```\n */\nexport function useCookiePreferences(\n options?: UseCookiePreferencesOptions,\n): UseCookiePreferencesResult {\n // This hook is the only way a site can offer the mandatory footer opt-out\n // control, so its use is the signal that the control actually shipped.\n registerCapability(\"consent_controls\");\n // SSR/pre-mount value = the effective default; synced from storage on mount.\n const [choice, setChoice] = useState<ConsentChoice>(DEFAULT_CHOICE);\n const ttlDays = options?.declineTtlDays;\n\n useEffect(() => {\n const sync = (): void => setChoice(getConsentChoice());\n sync();\n\n // Cross-tab: storage events (key === null means storage.clear()). The\n // timestamp key matters too — a re-affirmed explicit choice in another tab\n // writes the same state value (no event for it), only a new timestamp.\n const handleStorage = (event: StorageEvent): void => {\n if (\n event.key === null ||\n event.key === CONSENT_STATE_KEY ||\n event.key === CONSENT_EXPIRES_AT_KEY ||\n event.key === CONSENT_TIMESTAMP_KEY\n )\n sync();\n };\n window.addEventListener(\"storage\", handleStorage);\n\n // Same-tab: another component's optIn/optOut/reset.\n const unsubscribe = onConsentChange(sync);\n\n return () => {\n window.removeEventListener(\"storage\", handleStorage);\n unsubscribe();\n };\n }, []);\n\n // State updates flow back through onConsentChange — actions never setState.\n const optOutAction = useCallback((): void => {\n optOut(ttlDays != null ? { declineTtlDays: ttlDays } : undefined);\n }, [ttlDays]);\n\n const optInAction = useCallback((): void => {\n optIn();\n }, []);\n\n const reset = useCallback((): void => {\n resetConsent();\n }, []);\n\n return {\n state: choice.state,\n source: choice.source,\n isDefault: choice.source === \"default\",\n isGranted: choice.state === \"granted\",\n isDenied: choice.state === \"denied\",\n updatedAt: choice.updatedAt,\n expiresAt: choice.expiresAt,\n optOut: optOutAction,\n optIn: optInAction,\n reset,\n };\n}\n\n/**\n * Read the current visitor consent state.\n *\n * @deprecated Since consent v2 (opt-out model) the state is never `pending`.\n * Use {@link useCookiePreferences} — it exposes the effective state plus\n * `source` so you can tell a default grant from an explicit one.\n */\nexport function useConsentState(): ConsentState {\n return useConsent().state;\n}\n\n/**\n * Result shape of the deprecated {@link useConsent} hook.\n *\n * @deprecated Use {@link UseCookiePreferencesResult} via\n * {@link useCookiePreferences}. `isPending` is always `false` since consent v2.\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\n/**\n * Legacy opt-in-era consent hook.\n *\n * @deprecated Since consent v2 tracking defaults ON (opt-out model): the state\n * is never `pending`, so banner UIs gated on `isPending` never render. Use\n * {@link useCookiePreferences} for footer \"cookie preferences\" controls.\n * `accept` / `decline` still work and map to `optIn` / `optOut`.\n */\nexport function useConsent(): UseConsentResult {\n const {\n state,\n isGranted,\n isDenied,\n optIn: accept,\n optOut: decline,\n reset,\n } = useCookiePreferences();\n\n return {\n state,\n isPending: false,\n isGranted,\n isDenied,\n accept,\n decline,\n reset,\n };\n}\n","// Framework-agnostic phone-number utilities — isomorphic (browser + Node/RSC),\n// zero React. Bundled `libphonenumber-js` (standard metadata) so clients add no\n// dependency. The transmitted value is ALWAYS E.164; display is the only knob.\n\nimport { AsYouType, parsePhoneNumberFromString, type CountryCode } from \"libphonenumber-js\";\n\nexport type { CountryCode };\n\n/**\n * How a phone number is shown in the UI. The transmitted value is always E.164\n * and is deliberately NOT part of this — only the display format is configurable.\n * A function form covers the long tail (`(parsed) => string`).\n */\nexport type PhoneDisplayFormat =\n | \"national\"\n | \"international\"\n | \"e164\"\n | ((parsed: ParsedPhone) => string);\n\nexport interface ParsedPhone {\n /** E.164 (`\"+14165550199\"`) or `null` when the input isn't a valid number. This is what gets transmitted. */\n e164: string | null;\n /** National display form (`\"(416) 555-0199\"`); empty string when unparseable. */\n national: string;\n /** International display form (`\"+1 416 555 0199\"`); empty string when unparseable. */\n international: string;\n /** ISO-3166 country resolved by libphonenumber, or `null`. */\n country: CountryCode | null;\n isValid: boolean;\n}\n\n/** Region assumed for numbers typed without a country code. */\nexport const DEFAULT_PHONE_COUNTRY: CountryCode = \"CA\";\n\n/** Parse a raw/display string into every representation at once (one parse → display + wire never drift). */\nexport function parsePhone(raw: string, country?: CountryCode): ParsedPhone {\n const region = country ?? DEFAULT_PHONE_COUNTRY;\n const parsed = parsePhoneNumberFromString(raw ?? \"\", region);\n if (!parsed) {\n return { e164: null, national: \"\", international: \"\", country: region, isValid: false };\n }\n const isValid = parsed.isValid();\n return {\n // E.164 is only surfaced for a *valid* number — a possible-but-invalid input\n // (e.g. too few digits) still parses but must not be transmitted.\n e164: isValid ? parsed.number : null,\n national: parsed.formatNational(),\n international: parsed.formatInternational(),\n country: parsed.country ?? region,\n isValid,\n };\n}\n\n/** Normalize any raw/display value to E.164, or `null` if it isn't a valid number. */\nexport function toE164(raw: string, country?: CountryCode): string | null {\n return parsePhone(raw, country).e164;\n}\n\n/** Format a value for display. Defaults to `'national'`. Never affects the wire value. */\nexport function formatPhone(\n value: string,\n format: PhoneDisplayFormat = \"national\",\n country?: CountryCode,\n): string {\n const parsed = parsePhone(value, country);\n if (typeof format === \"function\") return format(parsed);\n switch (format) {\n case \"international\":\n return parsed.international || value;\n case \"e164\":\n return parsed.e164 ?? value;\n case \"national\":\n default:\n return parsed.national || value;\n }\n}\n\n/** Live, incremental formatting for an `<input>` as the user types (`AsYouType`). */\nexport function formatPhoneAsTyped(raw: string, country?: CountryCode): string {\n return new AsYouType(country ?? DEFAULT_PHONE_COUNTRY).input(raw ?? \"\");\n}\n","// Enhanced-conversions user_data bridge (Google Ads \"user-provided data\").\n//\n// Why this exists: Google's automatic user-provided-data capture demonstrably\n// misses phone fields on client sites (verified live: a filled tel input never\n// produced a `pn` hit parameter), and our offline sale uploads are phone-hash\n// keyed — without a phone↔click pairing built at conversion time, none of them\n// can match. This module makes the SDK the deterministic capture path: it\n// extracts the visitor's own email/phone from `form_submit` fields (or an\n// explicit override / a recorded sale's customer fields), normalizes them\n// (E.164 phone, lowercased email), and sets them as PLAINTEXT\n// `gtag('set','user_data', …)` immediately before a conversion fires. gtag.js\n// normalizes further and SHA-256 hashes IN THE BROWSER — plaintext never\n// leaves the page, and we never own Google's hashing contract.\n//\n// Privacy posture: the stash is module-level memory only — never persisted to\n// any storage, never added to our own ingest payloads (the form fields were\n// already part of the site's `form_submit` metadata; we only read them).\n// Consent gating happens at the egress in `fireConversionWithConsent` (the\n// same gate every conversion already passes through); an explicit consent\n// denial additionally clears the stash and nulls gtag's page state (see\n// consent.ts). This module must NOT import consent.ts — consent.ts imports us\n// for that clearing hook, and the reverse edge would be a cycle.\n\nimport { toE164, type CountryCode } from \"./phone\";\n\n/** Raw identifiers a caller may hand us (normalized before use). */\nexport interface ConversionUserData {\n email?: string | null;\n phone?: string | null;\n}\n\n/** Normalized identifiers as gtag's `user_data` expects them. */\nexport interface NormalizedUserData {\n email: string | null;\n /** E.164 (`\"+16477836797\"`) — the format Google's parser reliably accepts. */\n phoneNumber: string | null;\n}\n\nconst EMAIL_SHAPE = /^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/;\nconst EMAIL_NAME_HINT = /e[-_]?mail/i;\n// `\\b` won't do: underscores are word chars, so `customer_phone` would miss.\n// The leading guard keeps bare \"tel\" from matching e.g. \"hotel\".\nconst PHONE_NAME_HINT = /(^|[^a-z])(phone|tel|mobile|cell)/i;\n\n// Page-lifetime, memory-only. Freshest non-null value wins per identifier.\nlet stash: NormalizedUserData = { email: null, phoneNumber: null };\n\n/**\n * Trim + lowercase + shape-check. Deliberately NO gmail dot/plus\n * canonicalization — gtag.js applies Google's own normalization before\n * hashing, and owning that contract here would be drift waiting to happen.\n */\nexport function normalizeEmail(raw: unknown): string | null {\n if (typeof raw !== \"string\") return null;\n const cleaned = raw.trim().toLowerCase();\n return EMAIL_SHAPE.test(cleaned) ? cleaned : null;\n}\n\ninterface FieldLike {\n name?: unknown;\n type?: unknown;\n label?: unknown;\n value?: unknown;\n}\n\nfunction fieldText(field: FieldLike, key: \"name\" | \"type\" | \"label\"): string {\n const value = field[key];\n return typeof value === \"string\" ? value : \"\";\n}\n\n/**\n * Deterministic extraction from `form_submit` `fields[]`: for each identifier,\n * a typed field (`type=\"email\"` / `type=\"tel\"`) wins over a name/label match\n * (`user_email`, `customer_phone`, …), in document order. A field whose value\n * fails normalization does NOT consume the slot — scanning continues, so one\n * malformed phone field can't mask a later valid one. Never throws.\n */\nexport function extractUserDataFromFormFields(\n fields: unknown,\n country?: CountryCode,\n): NormalizedUserData {\n const result: NormalizedUserData = { email: null, phoneNumber: null };\n if (!Array.isArray(fields)) return result;\n const passes: Array<(field: FieldLike, hint: RegExp, type: string) => boolean> = [\n (field, _hint, type) => fieldText(field, \"type\").toLowerCase() === type,\n (field, hint) => hint.test(fieldText(field, \"name\")) || hint.test(fieldText(field, \"label\")),\n ];\n for (const matches of passes) {\n for (const raw of fields) {\n if (raw === null || typeof raw !== \"object\") continue;\n const field = raw as FieldLike;\n if (typeof field.value !== \"string\" || field.value.length === 0) continue;\n if (result.email === null && matches(field, EMAIL_NAME_HINT, \"email\")) {\n result.email = normalizeEmail(field.value);\n }\n if (result.phoneNumber === null && matches(field, PHONE_NAME_HINT, \"tel\")) {\n try {\n result.phoneNumber = toE164(field.value, country);\n } catch {\n // libphonenumber choked on garbage — leave the slot open\n }\n }\n }\n if (result.email !== null && result.phoneNumber !== null) break;\n }\n return result;\n}\n\n/** Normalize + merge into the stash (non-null values win over existing). */\nexport function stashUserData(data: ConversionUserData, country?: CountryCode): void {\n const email = normalizeEmail(data.email);\n let phoneNumber: string | null = null;\n if (typeof data.phone === \"string\" && data.phone.length > 0) {\n try {\n phoneNumber = toE164(data.phone, country);\n } catch {\n phoneNumber = null;\n }\n }\n stash = {\n email: email ?? stash.email,\n phoneNumber: phoneNumber ?? stash.phoneNumber,\n };\n}\n\n/** Extract from `form_submit` fields and merge into the stash. Never throws. */\nexport function stashUserDataFromFormFields(fields: unknown, country?: CountryCode): void {\n try {\n const extracted = extractUserDataFromFormFields(fields, country);\n stash = {\n email: extracted.email ?? stash.email,\n phoneNumber: extracted.phoneNumber ?? stash.phoneNumber,\n };\n } catch {\n // extraction must never break event tracking\n }\n}\n\n/** Snapshot of the current stash (copy — mutations don't leak back). */\nexport function getStashedUserData(): NormalizedUserData {\n return { ...stash };\n}\n\n/** Drop everything (consent denial, tests). */\nexport function clearStashedUserData(): void {\n stash = { email: null, phoneNumber: null };\n}\n\n/**\n * Set gtag's per-page `user_data` right before a conversion fires: explicit\n * values win per-key over the stash; an empty merge sets nothing at all.\n * Browser-gated and throw-proof — the conversion must fire regardless.\n * NOT consent-checked here: the single caller (`fireConversionWithConsent`)\n * has already returned on denial before reaching this.\n *\n * Returns whether a `gtag('set','user_data', …)` call was made.\n */\nexport function applyUserDataForConversion(\n explicit?: ConversionUserData | null,\n country?: CountryCode,\n): boolean {\n if (typeof window === \"undefined\" || typeof window.gtag !== \"function\") return false;\n let email = stash.email;\n let phoneNumber = stash.phoneNumber;\n if (explicit) {\n const normalizedEmail = normalizeEmail(explicit.email);\n if (normalizedEmail) email = normalizedEmail;\n if (typeof explicit.phone === \"string\" && explicit.phone.length > 0) {\n try {\n phoneNumber = toE164(explicit.phone, country) ?? phoneNumber;\n } catch {\n // keep the stashed phone\n }\n }\n }\n if (email === null && phoneNumber === null) return false;\n try {\n window.gtag(\"set\", \"user_data\", {\n ...(email !== null ? { email } : {}),\n ...(phoneNumber !== null ? { phone_number: phoneNumber } : {}),\n });\n return true;\n } catch {\n return false;\n }\n}\n","import type { ConsentState } from \"./types\";\nimport { clearStashedUserData } from \"./user-data\";\n\n/**\n * localStorage key for the visitor's explicit 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 * localStorage key for the ISO timestamp when an explicit decline expires.\n *\n * Written alongside a `denied` choice; absent for grants (they never expire).\n * Computed at WRITE time so every reader — including the inline\n * `beforeInteractive` scripts that can't import this module — only has to\n * compare a stored ISO string against the clock.\n */\nexport const CONSENT_EXPIRES_AT_KEY = \"consent_expires_at\";\n\n/**\n * How long an explicit decline is honored before the visitor reverts to the\n * default-granted state. Explicit grants never expire.\n */\nexport const DEFAULT_DECLINE_TTL_DAYS = 90;\n\nconst DAY_MS = 86_400_000;\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/** Effective consent state — the opt-out model has no \"pending\". */\nexport type ConsentChoiceState = \"granted\" | \"denied\";\n\n/**\n * Where the effective state came from: `explicit` when the visitor made a\n * stored, still-valid choice; `default` otherwise (no choice, expired decline,\n * storage blocked, SSR).\n */\nexport type ConsentSource = \"default\" | \"explicit\";\n\n/**\n * The effective consent decision plus its provenance.\n */\nexport interface ConsentChoice {\n state: ConsentChoiceState;\n source: ConsentSource;\n /** ISO timestamp of the explicit choice; null for the default state. */\n updatedAt: string | null;\n /** ISO expiry of an unexpired decline; null for grants and the default state. */\n expiresAt: string | null;\n}\n\n/** Options for explicit consent writes. */\nexport interface SetConsentOptions {\n /** Days an explicit decline is honored. Defaults to {@link DEFAULT_DECLINE_TTL_DAYS}. */\n declineTtlDays?: number;\n}\n\nconst DEFAULT_CHOICE: ConsentChoice = {\n state: \"granted\",\n source: \"default\",\n updatedAt: null,\n expiresAt: null,\n};\n\ntype ConsentChangeListener = (choice: ConsentChoice) => void;\nconst changeListeners = new Set<ConsentChangeListener>();\n\n/**\n * Subscribe to consent changes (opt-in, opt-out, reset) made in THIS tab.\n * Cross-tab changes surface via the browser's `storage` event instead.\n * Returns an unsubscribe function.\n */\nexport function onConsentChange(listener: ConsentChangeListener): () => void {\n changeListeners.add(listener);\n return () => {\n changeListeners.delete(listener);\n };\n}\n\n// Takes the intended choice rather than re-reading storage: when localStorage\n// is blocked, a re-read would report default-granted right after an opt-out\n// click even though the live gtag/fbq revoke DID apply for this page load.\nfunction notifyConsentChanged(choice: ConsentChoice): void {\n for (const listener of changeListeners) {\n try {\n listener(choice);\n } catch {\n // a listener must never break the consent flow\n }\n }\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 * Resolve the visitor's effective consent (opt-out model).\n *\n * Default is GRANTED. Only a stored, unexpired explicit decline yields\n * `denied`. SSR, blocked storage, and expired declines all resolve to the\n * default. A legacy decline stored by the opt-in-era SDK (no expiry key) stays\n * denied and gets a fresh 90-day expiry backfilled, keeping this reader\n * consistent with the inline scripts (which treat a missing expiry as denied).\n */\nexport function getConsentChoice(): ConsentChoice {\n if (typeof window === \"undefined\") return DEFAULT_CHOICE;\n\n try {\n const stored = window.localStorage.getItem(CONSENT_STATE_KEY);\n const updatedAt = window.localStorage.getItem(CONSENT_TIMESTAMP_KEY);\n\n if (stored === \"granted\")\n return { state: \"granted\", source: \"explicit\", updatedAt, expiresAt: null };\n\n if (stored === \"denied\") {\n let expiresAt = window.localStorage.getItem(CONSENT_EXPIRES_AT_KEY);\n if (!expiresAt) {\n expiresAt = new Date(Date.now() + DEFAULT_DECLINE_TTL_DAYS * DAY_MS).toISOString();\n try {\n window.localStorage.setItem(CONSENT_EXPIRES_AT_KEY, expiresAt);\n } catch {\n // backfill is best-effort; the decline is still honored this read\n }\n }\n // A garbled expiry parses to NaN, the comparison is false, and the decline\n // stays honored (fail closed on the decliner's side). Only a parseable\n // expiry in the past reverts to the default.\n if (!(Date.parse(expiresAt) <= Date.now()))\n return { state: \"denied\", source: \"explicit\", updatedAt, expiresAt };\n }\n } catch {\n // localStorage blocked (sandboxed iframe / storage disabled) — default\n }\n\n return DEFAULT_CHOICE;\n}\n\n/**\n * Read the visitor's effective consent state.\n *\n * Opt-out model: returns `granted` unless a stored, unexpired explicit decline\n * exists. Never returns `pending` — that value survives in {@link ConsentState}\n * only so pre-v2 call sites keep compiling.\n */\nexport function getConsentState(): ConsentState {\n return getConsentChoice().state;\n}\n\nfunction pushConsentToPlatforms(state: GtagConsentValue): void {\n if (typeof window === \"undefined\") return;\n\n if (typeof window.gtag === \"function\")\n window.gtag(\"consent\", \"update\", buildConsentPayload(state));\n\n // Mirror to the Meta Pixel so one control gates both ad platforms.\n if (typeof window.fbq === \"function\")\n window.fbq(\"consent\", state === \"granted\" ? \"grant\" : \"revoke\");\n}\n\n/**\n * Persist an explicit visitor consent choice and push it live to Google\n * Consent Mode and the Meta Pixel.\n *\n * Declines expire after {@link DEFAULT_DECLINE_TTL_DAYS} days (override via\n * `options.declineTtlDays`); grants never expire.\n */\nexport function setConsentState(state: GtagConsentValue, options?: SetConsentOptions): void {\n if (typeof window === \"undefined\") return;\n\n // Compute the full intended choice BEFORE touching storage so (a) a garbage\n // TTL can't throw mid-write (state written, expiry not), and (b) listeners\n // get the visitor's actual choice even when storage is blocked.\n const requestedTtl = options?.declineTtlDays;\n const ttlDays =\n typeof requestedTtl === \"number\" && Number.isFinite(requestedTtl) && requestedTtl > 0\n ? requestedTtl\n : DEFAULT_DECLINE_TTL_DAYS;\n const updatedAt = new Date().toISOString();\n const expiresAt =\n state === \"denied\" ? new Date(Date.now() + ttlDays * DAY_MS).toISOString() : null;\n\n try {\n window.localStorage.setItem(CONSENT_STATE_KEY, state);\n window.localStorage.setItem(CONSENT_TIMESTAMP_KEY, updatedAt);\n if (expiresAt !== null) {\n window.localStorage.setItem(CONSENT_EXPIRES_AT_KEY, expiresAt);\n } else {\n window.localStorage.removeItem(CONSENT_EXPIRES_AT_KEY);\n }\n } catch {\n // localStorage blocked — still push the live consent update below so the\n // visitor's click takes effect for this page load even if it can't persist\n }\n\n pushConsentToPlatforms(state);\n if (state === \"denied\") {\n // A mid-session revoke also scrubs enhanced-conversions page state: drop\n // the stashed identifiers and best-effort null gtag's user_data.\n clearStashedUserData();\n try {\n if (typeof window.gtag === \"function\") window.gtag(\"set\", \"user_data\", null);\n } catch {\n // scrubbing is best-effort; the consent update above already gates fires\n }\n }\n notifyConsentChanged({ state, source: \"explicit\", updatedAt, expiresAt });\n}\n\n/**\n * Explicitly opt the visitor in to ad tracking (never expires).\n */\nexport function optIn(): void {\n setConsentState(\"granted\");\n}\n\n/**\n * Explicitly opt the visitor out of ad tracking for\n * {@link DEFAULT_DECLINE_TTL_DAYS} days (override via `options.declineTtlDays`).\n *\n * This is the primitive a client site's footer \"cookie preferences\" control\n * should call — the packages ship no consent UI of their own.\n */\nexport function optOut(options?: SetConsentOptions): void {\n setConsentState(\"denied\", options);\n}\n\n/**\n * Clear the stored explicit choice, returning the visitor to the\n * default-granted state, and push that state live to gtag + Meta.\n *\n * Power a \"Cookie preferences\" reset in a footer:\n *\n * ```tsx\n * const { reset } = useCookiePreferences();\n * <button onClick={reset}>Reset cookie preferences</button>\n * ```\n */\nexport function resetConsent(): void {\n if (typeof window === \"undefined\") return;\n\n try {\n window.localStorage.removeItem(CONSENT_STATE_KEY);\n window.localStorage.removeItem(CONSENT_TIMESTAMP_KEY);\n window.localStorage.removeItem(CONSENT_EXPIRES_AT_KEY);\n } catch {\n // localStorage blocked — nothing to clear\n }\n\n pushConsentToPlatforms(\"granted\");\n notifyConsentChanged(DEFAULT_CHOICE);\n}\n\n/**\n * Re-apply a stored explicit consent choice to Google Consent Mode + Meta.\n *\n * @deprecated The v2 consent-default path ({@link applyDefaultConsentState} /\n * `createConsentDefaultScript`) already applies the effective state at\n * bootstrap, making this redundant. Pushes platform updates directly — it must\n * NOT route through {@link setConsentState}, which would renew the decline's\n * 90-day expiry on every page load.\n */\nexport function restoreStoredConsent(): ConsentState {\n const choice = getConsentChoice();\n\n if (choice.source === \"explicit\") pushConsentToPlatforms(choice.state);\n\n return choice.state;\n}\n\n/**\n * Inline-JS fragment that resolves the effective consent value into\n * `var aranovaConsent = 'granted' | 'denied'`.\n *\n * Single source of the expiry logic for every inline `<script>` builder (gtag\n * and Meta), so the runtime reader and the inline readers can't drift. Must\n * stay dependency-free ES5 and never throw (localStorage access throws in\n * sandboxed iframes).\n */\nexport function createEffectiveConsentSnippet(): string {\n // IIFE keeps the temporaries off the page's global scope — only\n // `aranovaConsent` (the value consumed by the rest of the script) leaks.\n return `\nvar aranovaConsent = (function () {\n try {\n if (window.localStorage.getItem('${CONSENT_STATE_KEY}') === 'denied') {\n var exp = window.localStorage.getItem('${CONSENT_EXPIRES_AT_KEY}');\n var expMs = exp ? Date.parse(exp) : NaN;\n if (!(expMs <= Date.now())) return 'denied';\n }\n } catch (e) {}\n return 'granted';\n})();\n`.trim();\n}\n","// Capability self-report registry.\n//\n// Every SDK construct registers itself here at construction time, and the\n// registered set rides out on `TrackingClientContext.capabilities`, which the\n// backend persists on the session row. The dashboard derives what a client site\n// ACTUALLY has installed from those sessions, rather than trusting an operator\n// checkbox — see contracts/tracking-capabilities.json, which pins this id list\n// against the backend enum and the dashboard type union.\n//\n// Browser-only by design: a server-side client (calendar bookings) emits no\n// session, and its artifact is better evidence than a self-report anyway.\n//\n// This module deliberately imports nothing: it is the one thing every other\n// resource can import without adding an edge that could close a cycle.\n\n/**\n * Every capability an install can report. Pinned by\n * `contracts/tracking-capabilities.json` — adding one here without a catalog\n * entry fails the drift test.\n */\nexport const TRACKING_CAPABILITIES = [\n \"ad_tags_google\",\n \"ad_tags_meta\",\n \"base_tracking\",\n \"blog_rendering\",\n \"booking_handoff\",\n \"booking_integration\",\n \"calendar_read\",\n \"calendar_write\",\n \"consent_controls\",\n \"conversion_goals_auto\",\n \"conversion_goals_manual\",\n \"cross_site_linker\",\n \"cta_click_capture\",\n \"form_capture\",\n \"phone_click_capture\",\n \"phone_fields\",\n \"typed_form_submit\",\n] as const;\n\nexport type TrackingCapability = (typeof TRACKING_CAPABILITIES)[number];\n\n/**\n * DOM attribute a server-rendered surface stamps to report itself.\n *\n * Blog rendering never calls the Aranova API — it reads the CDN directly — so\n * neither the ingest payload nor the request-header path can see it. The\n * renderer stamps this attribute instead and the browser context collector\n * picks it up on the next batch.\n */\nexport const CAPABILITY_DOM_ATTRIBUTE = \"data-aranova-capability\";\n\nconst registered = new Set<TrackingCapability>();\n\n/**\n * Record that a capability is present in this runtime.\n *\n * Idempotent and side-effect free — safe to call on every construction,\n * including React strict-mode double mounts.\n */\nexport function registerCapability(capability: TrackingCapability): void {\n registered.add(capability);\n}\n\n/**\n * Capabilities registered so far in this runtime, sorted for a stable wire\n * value (so the backend can hash the array to skip redundant writes).\n *\n * Also collects `data-aranova-capability` markers from the DOM, which is how\n * server-rendered surfaces that never call our API report themselves.\n */\nexport function getRegisteredCapabilities(): TrackingCapability[] {\n const all = new Set<TrackingCapability>(registered);\n for (const marker of readDomMarkers()) all.add(marker);\n return Array.from(all).sort();\n}\n\nfunction readDomMarkers(): TrackingCapability[] {\n if (typeof document === \"undefined\") return [];\n const known = new Set<string>(TRACKING_CAPABILITIES);\n const found: TrackingCapability[] = [];\n // Unknown values are dropped rather than forwarded: the backend rejects ids\n // outside its enum, and a stray attribute on a client's page must never be\n // able to fail an otherwise-valid ingest batch.\n for (const node of document.querySelectorAll(`[${CAPABILITY_DOM_ATTRIBUTE}]`)) {\n const value = node.getAttribute(CAPABILITY_DOM_ATTRIBUTE);\n if (value && known.has(value)) found.push(value as TrackingCapability);\n }\n return found;\n}\n\n/** Test-only: drop everything registered so far. */\nexport function resetRegisteredCapabilitiesForTests(): void {\n registered.clear();\n}\n","// Cross-site session linkage: the origin site's SDK appends an `_arv` identity\n// token to links pointing at a sibling hostname; the destination consumes it\n// before minting ids. Sessions are LINKED (`origin_*`), never shared. The token\n// never reaches the backend — only its decoded fields do.\n\nimport * as z from \"zod/mini\";\n\nimport { getConsentChoice } from \"./consent\";\nimport { FBC_COOKIE, getFbcCookie } from \"./fbq\";\nimport { adoptVisitorId, getOrRotateSessionId, getVisitorId } from \"./session\";\nimport { persistCookieValue, readCookieValue, setTrackingCookie } from \"./tracking\";\n\n/** Query parameter the token rides in. */\nexport const LINK_PARAM = \"_arv\";\n\n/** Token version byte; bump only when a key's meaning changes. */\nexport const LINK_TOKEN_VERSION = \"1\";\n\n/** A token older than this is treated as a shared/bookmarked URL, not a hop. */\nexport const LINK_TOKEN_TTL_MS = 5 * 60 * 1000;\n\n// Clock skew tolerance: a token stamped slightly in the future is still fresh.\nconst FUTURE_SKEW_MS = 60 * 1000;\n\n/** Carried click ids the token transports: short wire key → attribution param. */\nconst CARRIED_KEYS = {\n g: \"gclid\",\n w: \"wbraid\",\n gb: \"gbraid\",\n f: \"fbclid\",\n y: \"ylpcid\",\n} as const;\n\ntype CarriedWireKey = keyof typeof CARRIED_KEYS;\ntype CarriedParamKey = (typeof CARRIED_KEYS)[CarriedWireKey];\nconst CARRIED_ENTRIES = Object.entries(CARRIED_KEYS) as Array<[CarriedWireKey, CarriedParamKey]>;\n\nconst identifier = z.string().check(z.minLength(1), z.maxLength(128));\nconst optionalIdentifier = z.optional(identifier);\n\n/** Decoded token payload — the single schema every reader and writer shares.\n * `z.object`, not `strictObject`: an unknown key from a newer writer is dropped\n * rather than failing the whole token, which is what makes a mixed fleet safe. */\nexport const linkTokenSchema = z.object({\n v: identifier,\n s: identifier,\n // Epoch ms in base36.\n t: z.string().check(z.regex(/^[0-9a-z]{1,12}$/)),\n g: optionalIdentifier,\n w: optionalIdentifier,\n gb: optionalIdentifier,\n f: optionalIdentifier,\n fc: optionalIdentifier,\n y: optionalIdentifier,\n});\n\nexport type LinkTokenPayload = z.infer<typeof linkTokenSchema>;\n\n/** Typed result of consuming a valid, fresh token. */\nexport interface OriginLink {\n visitorId: string;\n sessionId: string;\n /** Epoch ms the origin site stamped the link. */\n linkedAt: number;\n carried: Partial<Record<CarriedParamKey, string>>;\n fbc: string | null;\n}\n\n/** `stale` = older than the TTL (a shared or bookmarked URL); `invalid` = unreadable,\n * unknown version, or stamped beyond the clock-skew tolerance. */\nexport type InboundLinkOutcome = \"linked\" | \"stale\" | \"invalid\";\n\nexport interface InboundLinkResult {\n outcome: InboundLinkOutcome;\n link: OriginLink | null;\n /** True when this browser had no visitor id and took the origin's. */\n adoptedVisitor: boolean;\n}\n\nfunction toBase64Url(input: string): string {\n const bytes = new TextEncoder().encode(input);\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nfunction fromBase64Url(input: string): string | null {\n try {\n const padded = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const binary = atob(padded + \"=\".repeat((4 - (padded.length % 4)) % 4));\n const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));\n return new TextDecoder().decode(bytes);\n } catch {\n return null;\n }\n}\n\n/** Encode a payload into the `_arv` value. */\nexport function encodeLinkToken(payload: LinkTokenPayload): string {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(payload)) {\n if (typeof value === \"string\" && value.length > 0) params.set(key, value);\n }\n return `${LINK_TOKEN_VERSION}.${toBase64Url(params.toString())}`;\n}\n\n/** Decode an `_arv` value; an unknown version or a bad payload is an undecorated URL. */\nexport function decodeLinkToken(raw: string): LinkTokenPayload | null {\n const dot = raw.indexOf(\".\");\n if (dot <= 0 || raw.slice(0, dot) !== LINK_TOKEN_VERSION) return null;\n const decoded = fromBase64Url(raw.slice(dot + 1));\n if (decoded === null) return null;\n const params = new URLSearchParams(decoded);\n const candidate: Record<string, string> = {};\n params.forEach((value, key) => {\n candidate[key] = value;\n });\n const result = linkTokenSchema.safeParse(candidate);\n return result.success ? result.data : null;\n}\n\nfunction payloadToLink(payload: LinkTokenPayload): OriginLink {\n const carried: Partial<Record<CarriedParamKey, string>> = {};\n for (const [wire, param] of CARRIED_ENTRIES) {\n const value = payload[wire];\n if (value) carried[param] = value;\n }\n return {\n visitorId: payload.v,\n sessionId: payload.s,\n linkedAt: Number.parseInt(payload.t, 36),\n carried,\n fbc: payload.fc ?? null,\n };\n}\n\nfunction stripWww(hostname: string): string {\n return hostname.toLowerCase().replace(/^www\\./, \"\");\n}\n\n/** True when `hostname` is `sibling` or a subdomain of it (www-insensitive). */\nexport function isSiblingHost(hostname: string, sibling: string): boolean {\n const host = stripWww(hostname);\n const target = stripWww(sibling);\n return host === target || host.endsWith(`.${target}`);\n}\n\n/** One outbound hop: the sibling this link reaches, and the URL to navigate to. */\nexport interface CrossSiteLink {\n /** The roster hostname matched. */\n hostname: string;\n /** `url` with a fresh token, or `url` unchanged under an explicit consent decline. */\n decorated: string;\n}\n\n/**\n * Resolve a link to a sibling site and decorate it in the same pass — the\n * caller learns \"this is a hop\" independently of whether the URL changed.\n * An explicit decline still resolves (the hop is ordinary analytics) but\n * carries no token, so no identity crosses. Own host and non-http(s) never qualify.\n */\nexport function resolveCrossSiteLink(\n url: string,\n linkedHostnames: readonly string[],\n now: number = Date.now(),\n): CrossSiteLink | null {\n if (typeof window === \"undefined\" || linkedHostnames.length === 0) return null;\n let target: URL;\n try {\n target = new URL(url, window.location.href);\n } catch {\n return null;\n }\n if (target.protocol !== \"http:\" && target.protocol !== \"https:\") return null;\n const here = window.location.hostname;\n if (here && isSiblingHost(target.hostname, here)) return null;\n const hostname = linkedHostnames.find((sibling) => isSiblingHost(target.hostname, sibling));\n if (hostname === undefined) return null;\n const consent = getConsentChoice();\n if (consent.state === \"denied\" && consent.source === \"explicit\") {\n return { hostname, decorated: url };\n }\n target.searchParams.set(LINK_PARAM, buildOutboundLinkToken(now));\n return { hostname, decorated: target.href };\n}\n\n/** Remove the token from a URL string. Returns the input untouched when unparseable. */\nexport function stripLinkParam(url: string): string {\n try {\n const parsed = new URL(url, typeof window === \"undefined\" ? undefined : window.location.href);\n if (!parsed.searchParams.has(LINK_PARAM)) return url;\n parsed.searchParams.delete(LINK_PARAM);\n return parsed.href;\n } catch {\n return url;\n }\n}\n\n/** Remove the token from a raw `?a=b` search string. */\nexport function stripLinkParamFromSearch(search: string): string {\n if (!search.includes(LINK_PARAM)) return search;\n const params = new URLSearchParams(search);\n if (!params.has(LINK_PARAM)) return search;\n params.delete(LINK_PARAM);\n const rest = params.toString();\n return rest.length > 0 ? `?${rest}` : \"\";\n}\n\nfunction readCarried(): Partial<Record<CarriedWireKey, string>> {\n const out: Partial<Record<CarriedWireKey, string>> = {};\n for (const [wire, param] of CARRIED_ENTRIES) {\n const value = readCookieValue(param);\n if (value) out[wire] = value;\n }\n return out;\n}\n\n/** A fresh token from this browser's identity; the session rotates if idle (a click is activity). */\nexport function buildOutboundLinkToken(now: number = Date.now()): string {\n const fbc = getFbcCookie();\n return encodeLinkToken({\n v: getVisitorId(),\n s: getOrRotateSessionId(now).id,\n t: now.toString(36),\n ...readCarried(),\n ...(fbc ? { fc: fbc } : {}),\n });\n}\n\n/** Append (or replace) the token on a link to a sibling site; every other URL passes through. */\nexport function decorateCrossSiteUrl(\n url: string,\n linkedHostnames: readonly string[],\n now: number = Date.now(),\n): string {\n return resolveCrossSiteLink(url, linkedHostnames, now)?.decorated ?? url;\n}\n\n/**\n * Consume an inbound token; must run before this origin mints identity. The\n * visitor id and carried click ids fill in only where this origin has none.\n */\nexport function consumeInboundLink(\n href: string = typeof window === \"undefined\" ? \"\" : window.location.href,\n now: number = Date.now(),\n): InboundLinkResult | null {\n if (typeof window === \"undefined\" || !href) return null;\n let raw: string | null;\n try {\n raw = new URL(href, window.location.href).searchParams.get(LINK_PARAM);\n } catch {\n return null;\n }\n if (!raw) return null;\n\n const payload = decodeLinkToken(raw);\n if (payload === null) return { outcome: \"invalid\", link: null, adoptedVisitor: false };\n const link = payloadToLink(payload);\n const age = now - link.linkedAt;\n if (!Number.isFinite(age) || age < -FUTURE_SKEW_MS) {\n return { outcome: \"invalid\", link: null, adoptedVisitor: false };\n }\n if (age > LINK_TOKEN_TTL_MS) {\n return { outcome: \"stale\", link: null, adoptedVisitor: false };\n }\n\n const adoptedVisitor = adoptVisitorId(link.visitorId);\n for (const [param, value] of Object.entries(link.carried) as Array<[CarriedParamKey, string]>) {\n if (!readCookieValue(param)) setTrackingCookie(param, value);\n }\n // A Pixel-set `_fbc` on this origin is canonical; the origin's only fills a gap.\n if (link.fbc && !getFbcCookie()) persistCookieValue(FBC_COOKIE, link.fbc);\n\n return { outcome: \"linked\", link, adoptedVisitor };\n}\n\n/** Strip the token from the address bar. `null` state, or Next's patched `replaceState` short-circuits. */\nexport function stripLinkParamFromLocation(): void {\n if (typeof window === \"undefined\" || typeof history === \"undefined\") return;\n const clean = stripLinkParam(window.location.href);\n if (clean === window.location.href) return;\n try {\n history.replaceState(null, \"\", clean);\n } catch {\n // never break the host site\n }\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 // Google's iOS/Safari replacement click IDs — issued when privacy features\n // withhold gclid (wbraid: web-to-web, gbraid: app-to-web). First-class\n // citizens: captured, persisted, and attributed exactly like gclid.\n \"wbraid\",\n \"gbraid\",\n // Yelp Ads click id (40 hex chars), appended to ad-click URLs.\n \"ylpcid\",\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 wbraid: null,\n gbraid: null,\n ylpcid: 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 * localStorage fallback prefix. Meta/Facebook in-app browsers (Instagram / FB\n * webviews) frequently block `document.cookie`; when a cookie write can't be\n * confirmed we mirror the value here so attribution (and `_fbc`) survives the\n * session and subsequent in-webview reloads instead of being silently lost.\n */\nconst FALLBACK_STORAGE_PREFIX = \"_aranova_track_\";\n\nfunction fallbackKey(name: string): string {\n return `${FALLBACK_STORAGE_PREFIX}${name}`;\n}\n\n/**\n * Persist a cookie value AND mirror it to localStorage. Generic over the cookie\n * name so it serves the attribution params AND the Meta `_fbc` cookie. Never\n * throws.\n *\n * The localStorage mirror is written UNCONDITIONALLY — it is NOT gated on a\n * cookie read-back. Meta/Instagram in-app webviews partition cookies: the write\n * is visible to a same-page read (so a verify-after-write would falsely pass)\n * yet is silently dropped on the next navigation/reload. Gating the fallback on\n * that read-back would leave the value unrecoverable in exactly the case it\n * exists for; a stale same-name cookie from a prior visit would also mask a\n * blocked write. So we always write both. {@link readCookieValue} prefers the\n * cookie when present, which keeps the mirror harmless when cookies work and\n * load-bearing when they don't survive navigation.\n */\nexport function persistCookieValue(\n name: string,\n value: string,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): void {\n const encoded = encodeURIComponent(value);\n if (typeof document !== \"undefined\") {\n try {\n document.cookie = `${name}=${encoded}; Max-Age=${maxAgeSeconds}; Path=/; SameSite=Lax`;\n } catch {\n // cookie blocked — the localStorage mirror below is the fallback\n }\n }\n if (typeof window !== \"undefined\") {\n try {\n window.localStorage.setItem(fallbackKey(name), encoded);\n } catch {\n // both blocked — give up silently, never break the host site\n }\n }\n}\n\n/**\n * Read a cookie value, falling back to the localStorage mirror written by\n * {@link persistCookieValue} when the cookie is absent (blocked webview).\n */\nexport function readCookieValue(name: string): string | null {\n if (typeof document !== \"undefined\") {\n const cookies = document.cookie ? document.cookie.split(\"; \") : [];\n const match = cookies.find((cookie) => cookie.startsWith(`${name}=`));\n if (match) {\n const [, rawValue = \"\"] = match.split(\"=\");\n return normalizeTrackingCookieValue(decodeURIComponent(rawValue));\n }\n }\n if (typeof window !== \"undefined\") {\n try {\n const stored = window.localStorage.getItem(fallbackKey(name));\n if (stored) return normalizeTrackingCookieValue(decodeURIComponent(stored));\n } catch {\n // ignore\n }\n }\n return null;\n}\n\n/**\n * Read a tracking cookie from `document.cookie` (or the localStorage fallback).\n *\n * Returns `null` during SSR or when neither source has the value.\n */\nexport function getCookieValueFromDocument(key: TrackingParamKey): string | null {\n return readCookieValue(key);\n}\n\n/**\n * Persist one attribution value as a first-party cookie (localStorage fallback\n * when cookies are blocked).\n */\nexport function setTrackingCookie(\n key: TrackingParamKey,\n value: string,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): void {\n persistCookieValue(key, value, maxAgeSeconds);\n}\n\n/**\n * Merge two attribution param sets, preferring `primary`'s non-null values and\n * filling the gaps from `fallback`. Used to combine the cookie-backed read with\n * the synchronous init-time snapshot so a value captured before an SPA router\n * stripped the URL (or before a webview blocked the cookie) still wins.\n */\nexport function mergeTrackingParams(\n primary: TrackingParams,\n fallback: TrackingParams,\n): TrackingParams {\n return TRACKING_PARAM_KEYS.reduce<TrackingParams>((merged, key) => {\n merged[key] = primary[key] ?? fallback[key];\n return merged;\n }, createEmptyTrackingParams());\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 =\n 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","// This module loads the Google tag (gtag.js), applies Consent Mode, and fires\n// `gtag('config', …)` (remarketing) plus real-time `gtag('event','conversion')`\n// via `fireGtagConversion` (GAP28). Consent v2 is OPT-OUT: the effective state\n// is granted unless the visitor stored an explicit, unexpired decline, so tags\n// run from first paint for the default visitor.\n// See docs/google-ads-deployment/gtags-and-conversion-tracking.md.\n\nimport { buildConsentPayload, createEffectiveConsentSnippet, getConsentState } 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 the visitor's EFFECTIVE\n * consent as the Consent Mode default (opt-out model).\n *\n * Resolves granted/denied synchronously from localStorage in one shot — a\n * decliner never gets a granted window and the default visitor never gets a\n * denied one, so no `wait_for_update` and no follow-up restore script is\n * needed. Run it `beforeInteractive` so the default lands before gtag.js.\n */\nexport function createConsentDefaultScript(): string {\n return `\nwindow.dataLayer = window.dataLayer || [];\nfunction gtag(){dataLayer.push(arguments);}\nwindow.gtag = gtag;\n${createEffectiveConsentSnippet()}\ngtag('consent', 'default', {\n ad_storage: aranovaConsent,\n ad_user_data: aranovaConsent,\n ad_personalization: aranovaConsent,\n analytics_storage: aranovaConsent\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 pushes the effective consent as an update.\n *\n * @deprecated Redundant since consent v2 — `createConsentDefaultScript()`\n * already resolves the effective state (including decline expiry) into the\n * Consent Mode default, so no restore pass is needed. Kept expiry-aware for\n * integrations that still render it.\n */\nexport function createConsentRestoreScript(): string {\n return `\n${createEffectiveConsentSnippet()}\nif (window.gtag) {\n window.gtag('consent', 'update', {\n ad_storage: aranovaConsent,\n ad_user_data: aranovaConsent,\n ad_personalization: aranovaConsent,\n analytics_storage: aranovaConsent\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\") return window.gtag;\n\n // CRITICAL: push the `arguments` object, NOT a rest-param array. gtag.js only\n // treats the canonical `dataLayer.push(arguments)` form as a command — a plain\n // array pushed to the dataLayer is ignored. With the array form, every\n // gtag('config' | 'consent' | 'event') call silently no-ops: the account never\n // registers, consent mode never applies, and conversions never fire (verified\n // live — the array form sends zero network hits; the arguments form sends the\n // pagead/conversion ping). Mirror Google's canonical inline snippet exactly.\n function gtag(): void {\n // eslint-disable-next-line prefer-rest-params\n window.dataLayer?.push(arguments);\n }\n window.gtag = gtag;\n\n return window.gtag;\n}\n\nconst SEND_TO_RE = /^AW-[A-Za-z0-9]+\\/[A-Za-z0-9_-]+$/;\n\n/** A gtag `send_to` target, e.g. `AW-123456789/AbC-D_efg`. */\nexport function isValidSendTo(sendTo: string): boolean {\n return SEND_TO_RE.test(sendTo);\n}\n\nexport interface GtagConversionInput {\n /** `AW-<id>/<label>` — the conversion action's firing target. */\n sendTo: string;\n /** Conversion value in MAJOR units; omit to let Google apply the action's default. */\n value?: number | null;\n currency?: string | null;\n transactionId?: string | null;\n}\n\n/**\n * Fire a real-time on-site conversion — `gtag('event','conversion',{ send_to, … })`\n * (GAP28). Browser-gated and never throws: `record()` is isomorphic and runs server-side\n * under a secret key where `window`/`gtag` are absent, so this no-ops there. Consent\n * gating + de-dup are the caller's responsibility (see `fireConversionWithConsent`).\n * Returns whether the event was actually pushed.\n */\nexport function fireGtagConversion(input: GtagConversionInput): boolean {\n if (typeof window === \"undefined\" || typeof window.gtag !== \"function\") return false;\n if (!isValidSendTo(input.sendTo)) return false;\n const params: Record<string, unknown> = { send_to: input.sendTo };\n if (input.value != null) params.value = input.value;\n if (input.currency) params.currency = input.currency;\n if (input.transactionId) params.transaction_id = input.transactionId;\n try {\n window.gtag(\"event\", \"conversion\", params);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Apply the visitor's EFFECTIVE consent as the Consent Mode default\n * (opt-out model): granted unless a stored, unexpired decline exists.\n *\n * Reading via `getConsentState()` also backfills the expiry key for\n * legacy opt-in-era declines, keeping later inline reads consistent.\n */\nexport function applyDefaultConsentState(): void {\n const gtag = ensureGtagFunction();\n gtag(\n \"consent\",\n \"default\",\n buildConsentPayload(getConsentState() === \"denied\" ? \"denied\" : \"granted\"),\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\") return;\n\n const marker = getScriptMarker(\"gtag-loader\");\n const existingScript = document.querySelector<HTMLScriptElement>(\n `script[${TRACKING_SCRIPT_ATTRIBUTE}=\"${marker}\"]`,\n );\n if (existingScript) 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.append(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 current effective consent to gtag as an update.\n *\n * @deprecated Redundant since consent v2 — `applyDefaultConsentState()`\n * already applies the effective state at bootstrap. Pushes directly (no\n * storage writes), so it never renews a decline's expiry.\n */\nexport function restoreConsentState(): void {\n if (typeof window === \"undefined\") 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\") return;\n if (!isValidGtagId(gtagId)) return;\n\n applyDefaultConsentState();\n loadGtagScript(gtagId);\n initializeGtag(gtagId);\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\") return;\n\n const ids = Object.values(gtagIds).filter(\n (id): id is string => typeof id === \"string\" && isValidGtagId(id),\n );\n if (ids.length === 0) 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\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","// The Meta Pixel (`fbq`) loader — the Facebook analogue of `gtag.ts`. It loads\n// fbevents.js and fires `fbq('init', …)` + `fbq('track','PageView')` to build Meta\n// remarketing / custom audiences (and let Meta optimize delivery + attribute).\n// Consent v2 is OPT-OUT: the pixel runs from first paint unless the visitor\n// stored an explicit, unexpired decline. This is NOT our in-house analytics\n// (that's the event ingest pipeline); it's the ad-platform tag, exactly like the\n// Google tag is for Google Ads.\n//\n// It also forms the Conversions-API-canonical `_fbc` cookie from the landing\n// `fbclid` (and reads the Pixel-set `_fbp`) so a future server-side Meta\n// Conversions API writeback has everything it needs.\n\nimport { createEffectiveConsentSnippet, getConsentState } from \"./consent\";\nimport { persistCookieValue, readCookieValue, TRACKING_COOKIE_MAX_AGE_SECONDS } from \"./tracking\";\nimport { TRACKING_SCRIPT_ATTRIBUTE } from \"./gtag\";\nimport type { MetaPixelEnvironmentMap } from \"./types\";\n\n/** Meta Pixel script host. */\nexport const FB_EVENTS_SCRIPT_HOST = \"https://connect.facebook.net/en_US/fbevents.js\";\n\n/** Cookie names Meta uses for the Conversions API. */\nexport const FBC_COOKIE = \"_fbc\";\nexport const FBP_COOKIE = \"_fbp\";\n\n/**\n * Valid Meta Pixel ID: a 15-16 digit number. IDs that don't match are dropped to\n * prevent script injection when interpolated into inline `<script>` content\n * (mirrors GTAG_ID_PATTERN).\n */\nconst META_PIXEL_ID_PATTERN = /^\\d{15,16}$/;\n\nexport function isValidMetaPixelId(id: string): boolean {\n return META_PIXEL_ID_PATTERN.test(id);\n}\n\n// ---------------------------------------------------------------------------\n// _fbc / _fbp cookie formation\n// ---------------------------------------------------------------------------\n\n/**\n * subdomainIndex for the `_fbc` value: `com` → 0, `example.com` → 1,\n * `www.example.com` → 2 (dot-label count minus one).\n */\nexport function computeFbSubdomainIndex(hostname: string): number {\n const labels = hostname.split(\".\").filter(Boolean);\n return Math.max(0, labels.length - 1);\n}\n\n/**\n * Build the CAPI-canonical `_fbc` value: `fb.<subdomainIndex>.<creationMs>.<fbclid>`.\n * Never hashed.\n */\nexport function buildFbc(fbclid: string, now: number, hostname?: string): string {\n const host = hostname ?? (typeof window === \"undefined\" ? \"\" : window.location.hostname);\n return `fb.${computeFbSubdomainIndex(host)}.${now}.${fbclid}`;\n}\n\n/** Current `_fbc` cookie (or localStorage fallback), if any. */\nexport function getFbcCookie(): string | null {\n return readCookieValue(FBC_COOKIE);\n}\n\n/** Current `_fbp` cookie — set by the Meta Pixel; we only ever read it. */\nexport function getFbpCookie(): string | null {\n return readCookieValue(FBP_COOKIE);\n}\n\nfunction readFbclidFromUrl(): string | null {\n if (typeof window === \"undefined\") return null;\n try {\n const value = new URL(window.location.href).searchParams.get(\"fbclid\");\n return value && value.trim().length > 0 ? value : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Form and persist `_fbc` from the landing `fbclid` when the Pixel hasn't already\n * set it. Reads the fbclid from the URL first, then the captured attribution\n * cookie. No-ops when there's no fbclid or `_fbc` already exists. `_fbp` is\n * Pixel-only — never written here.\n */\nexport function captureFbc(now = typeof Date === \"undefined\" ? 0 : Date.now()): void {\n if (typeof window === \"undefined\") return;\n // A Pixel-set `_fbc` is canonical — don't overwrite it.\n if (getFbcCookie()) return;\n const fbclid = readFbclidFromUrl() ?? readCookieValue(\"fbclid\");\n if (!fbclid) return;\n persistCookieValue(FBC_COOKIE, buildFbc(fbclid, now), TRACKING_COOKIE_MAX_AGE_SECONDS);\n}\n\n// ---------------------------------------------------------------------------\n// fbq loader (mirrors gtag.ts)\n// ---------------------------------------------------------------------------\n\ntype FbqStub = ((...args: unknown[]) => void) & {\n callMethod?: (...args: unknown[]) => void;\n queue: unknown[][];\n push: unknown;\n loaded: boolean;\n version: string;\n};\n\nfunction getScriptMarker(id: string): string {\n return `aranova-${id}`;\n}\n\n/**\n * Ensure `window.fbq` exists (the standard fbevents bootstrap stub) and return\n * it. Safe to call repeatedly — it only initializes once.\n */\nexport function ensureFbqFunction(): NonNullable<Window[\"fbq\"]> {\n const w = window as Window & { fbq?: FbqStub; _fbq?: FbqStub };\n if (typeof w.fbq === \"function\") return w.fbq;\n\n const fbq = function (this: unknown, ...args: unknown[]) {\n if (fbq.callMethod) fbq.callMethod.apply(fbq, args);\n else fbq.queue.push(args);\n } as FbqStub;\n\n fbq.push = fbq;\n fbq.loaded = true;\n fbq.version = \"2.0\";\n fbq.queue = [];\n\n w.fbq = fbq;\n if (!w._fbq) w._fbq = fbq;\n return fbq;\n}\n\n/**\n * Apply the visitor's EFFECTIVE consent to the Meta Pixel (opt-out model):\n * grant unless a stored, unexpired decline exists.\n */\nexport function applyDefaultMetaConsentState(): void {\n ensureFbqFunction()(\"consent\", getConsentState() === \"denied\" ? \"revoke\" : \"grant\");\n}\n\n/** Load fbevents.js once. */\nexport function loadFbeventsScript(): void {\n if (typeof document === \"undefined\") return;\n const marker = getScriptMarker(\"fbq-loader\");\n const existing = document.querySelector<HTMLScriptElement>(\n `script[${TRACKING_SCRIPT_ATTRIBUTE}=\"${marker}\"]`,\n );\n if (existing) return;\n\n const script = document.createElement(\"script\");\n script.async = true;\n script.src = FB_EVENTS_SCRIPT_HOST;\n script.setAttribute(TRACKING_SCRIPT_ATTRIBUTE, marker);\n document.head.append(script);\n}\n\n/** `fbq('init', id)` + the initial PageView. */\nexport function initializeMetaPixel(pixelId: string): void {\n const fbq = ensureFbqFunction();\n fbq(\"init\", pixelId);\n fbq(\"track\", \"PageView\");\n}\n\n/**\n * Push the current effective consent to fbq.\n *\n * @deprecated Redundant since consent v2 — `applyDefaultMetaConsentState()`\n * already applies the effective state at bootstrap. Pushes directly (no\n * storage writes), so it never renews a decline's expiry.\n */\nexport function restoreMetaConsentState(): void {\n if (typeof window === \"undefined\") return;\n const state = getConsentState();\n if (state === \"granted\") window.fbq?.(\"consent\", \"grant\");\n else if (state === \"denied\") window.fbq?.(\"consent\", \"revoke\");\n}\n\n/**\n * Load + initialize one Meta Pixel with Consent Mode support, and form `_fbc`.\n */\nexport function bootstrapMetaPixel(pixelId: string): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return;\n if (!isValidMetaPixelId(pixelId)) return;\n\n applyDefaultMetaConsentState();\n loadFbeventsScript();\n initializeMetaPixel(pixelId);\n captureFbc();\n}\n\n/**\n * Load + initialize ALL labelled Meta Pixel IDs (script loads once; `fbq('init')`\n * fires per id — the Pixel supports multiple pixels on one page).\n */\nexport function bootstrapMultiplePixels(pixelIds: MetaPixelEnvironmentMap): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return;\n const ids = Object.values(pixelIds).filter(\n (id): id is string => typeof id === \"string\" && isValidMetaPixelId(id),\n );\n if (ids.length === 0) return;\n\n applyDefaultMetaConsentState();\n loadFbeventsScript();\n const fbq = ensureFbqFunction();\n for (const id of ids) {\n fbq(\"init\", id);\n }\n fbq(\"track\", \"PageView\");\n captureFbc();\n}\n\n// ---------------------------------------------------------------------------\n// Inline <script> builders for the Next.js <Script> path (mirror gtag.ts)\n// ---------------------------------------------------------------------------\n\n/** The standard fbevents stub as an inline string. */\nfunction fbqStubScript(): string {\n return `\n!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?\nn.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;\nn.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;\nt.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,\ndocument,'script','${FB_EVENTS_SCRIPT_HOST}');`.trim();\n}\n\n/**\n * Inline script that loads the fbevents stub and applies the visitor's\n * EFFECTIVE consent (opt-out model) before any `init`/`PageView` fires.\n */\nexport function createMetaConsentDefaultScript(): string {\n return `${fbqStubScript()}\n${createEffectiveConsentSnippet()}\nfbq('consent', aranovaConsent === 'denied' ? 'revoke' : 'grant');`;\n}\n\n/** Inline script that inits one pixel + PageView. */\nexport function createMetaPixelInitScript(pixelId: string): string {\n if (!isValidMetaPixelId(pixelId)) return \"\";\n return `fbq('init', '${pixelId}');\\nfbq('track', 'PageView');`;\n}\n\n/** Inline script that inits multiple pixels + a single PageView. */\nexport function createMetaPixelInitScriptMulti(pixelIds: string[]): string {\n const inits = pixelIds.filter(isValidMetaPixelId).map((id) => `fbq('init', '${id}');`);\n if (inits.length === 0) return \"\";\n return `${inits.join(\"\\n\")}\\nfbq('track', 'PageView');`;\n}\n\n/**\n * Inline script that pushes the effective consent into fbq.\n *\n * @deprecated Redundant since consent v2 — `createMetaConsentDefaultScript()`\n * already applies the effective state (including decline expiry) before the\n * pixel initializes. Kept expiry-aware for integrations that still render it.\n */\nexport function createMetaConsentRestoreScript(): string {\n return `\n${createEffectiveConsentSnippet()}\nif (window.fbq) {\n window.fbq('consent', aranovaConsent === 'denied' ? 'revoke' : 'grant');\n}`.trim();\n}\n","// Session-scoped LANDING attribution (ADR-016).\n//\n// The carried attribution channel (tracking.ts) persists gclid/fbclid/utm_*\n// for 90 days so journey stitching and conversion writeback keep working\n// across sessions. This module answers a different question — \"what did THIS\n// session's landing URL actually carry?\" — so the dashboard's source label is\n// never poisoned by a click from an earlier session.\n//\n// One localStorage record, keyed to the current session id and overwritten\n// whenever a new session is minted: self-cleaning, O(1) storage, survives\n// in-webview reloads (localStorage, not cookies — Meta/IG webviews silently\n// drop cookie writes, the exact failure mode the carried channel already\n// engineers around). An in-memory mirror covers storage-blocked environments.\n\nimport { getTrackingQueryValues, TRACKING_PARAM_KEYS, type TrackingParamKey } from \"./tracking\";\n\n/**\n * localStorage key for the landing-params record (same `_aranova_track_`\n * family as the carried-channel cookie mirrors).\n */\nexport const LANDING_STORAGE_KEY = \"_aranova_track_landing\";\n\n/**\n * Serialized landing record: the params present in the landing URL of the\n * session identified by `session_id`.\n */\nexport interface StoredLandingRecord {\n session_id: string;\n params: Partial<Record<TrackingParamKey, string>>;\n}\n\nlet memoryRecord: StoredLandingRecord | null = null;\n\nfunction sanitizeParams(value: unknown): Partial<Record<TrackingParamKey, string>> {\n if (typeof value !== \"object\" || value === null) return {};\n const source = value as Record<string, unknown>;\n return TRACKING_PARAM_KEYS.reduce<Partial<Record<TrackingParamKey, string>>>((params, key) => {\n const entry = source[key];\n if (typeof entry === \"string\" && entry.length > 0) params[key] = entry;\n return params;\n }, {});\n}\n\nfunction readStoredRecord(): StoredLandingRecord | null {\n try {\n const raw = window.localStorage.getItem(LANDING_STORAGE_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw) as Partial<StoredLandingRecord>;\n if (typeof parsed.session_id !== \"string\" || parsed.session_id.length === 0) return null;\n return { session_id: parsed.session_id, params: sanitizeParams(parsed.params) };\n } catch {\n return null;\n }\n}\n\nfunction writeRecord(record: StoredLandingRecord): void {\n memoryRecord = record;\n try {\n window.localStorage.setItem(LANDING_STORAGE_KEY, JSON.stringify(record));\n } catch {\n // Storage blocked — the in-memory mirror above still serves this page.\n }\n}\n\nfunction captureFromUrl(url?: string): Partial<Record<TrackingParamKey, string>> {\n try {\n const resolved = new URL(url ?? window.location.href, window.location.origin);\n return getTrackingQueryValues(resolved.searchParams);\n } catch {\n return {};\n }\n}\n\n/**\n * Return the landing params for `sessionId`, capturing them from the current\n * URL when no record for that session exists yet.\n *\n * First-wins: a record whose `session_id` matches is returned as-is (a second\n * tab's URL never clobbers the session's true landing). A missing or\n * mismatched record means a new session just started — the current URL IS that\n * session's landing, so it is captured and persisted. Read-only with respect\n * to the carried channel: never touches cookies. Never throws; returns `{}`\n * during SSR.\n */\nexport function getOrCaptureLandingParams(\n sessionId: string,\n url?: string,\n): Partial<Record<TrackingParamKey, string>> {\n if (typeof window === \"undefined\") return {};\n\n const stored = readStoredRecord();\n if (stored && stored.session_id === sessionId) {\n memoryRecord = stored;\n return stored.params;\n }\n if (memoryRecord && memoryRecord.session_id === sessionId) {\n return memoryRecord.params;\n }\n\n const record: StoredLandingRecord = { session_id: sessionId, params: captureFromUrl(url) };\n writeRecord(record);\n return record.params;\n}\n\n/**\n * The 10 landing wire fields, null-filled. Key PRESENCE tells the backend the\n * landing is explicitly known; when it is NOT known the keys must be omitted\n * entirely so the backend falls back to parsing first_page.\n */\nexport interface LandingPayloadFields {\n landing_gclid: string | null;\n landing_wbraid: string | null;\n landing_gbraid: string | null;\n landing_ylpcid: string | null;\n landing_fbclid: string | null;\n landing_utm_source: string | null;\n landing_utm_medium: string | null;\n landing_utm_campaign: string | null;\n landing_utm_term: string | null;\n landing_utm_content: string | null;\n}\n\n/**\n * Build the landing portion of a session payload — the single place the\n * \"present iff known\" wire contract is enforced. Returns `{}` (omit the keys)\n * when there is no landing knowledge: during SSR the session's landing URL is\n * simply not observable, and sending explicit nulls would wrongly tell the\n * backend \"this session landed with no params\", suppressing its fallback.\n */\nexport function buildLandingPayloadFields(\n sessionId: string,\n override?: Partial<Record<TrackingParamKey, string>>,\n): Partial<LandingPayloadFields> {\n const params =\n override ?? (typeof window === \"undefined\" ? null : getOrCaptureLandingParams(sessionId));\n if (params === null) return {};\n return {\n landing_gclid: params.gclid ?? null,\n landing_wbraid: params.wbraid ?? null,\n landing_gbraid: params.gbraid ?? null,\n landing_ylpcid: params.ylpcid ?? null,\n landing_fbclid: params.fbclid ?? null,\n landing_utm_source: params.utm_source ?? null,\n landing_utm_medium: params.utm_medium ?? null,\n landing_utm_campaign: params.utm_campaign ?? null,\n landing_utm_term: params.utm_term ?? null,\n landing_utm_content: params.utm_content ?? null,\n };\n}\n\n/**\n * Drop the landing record (localStorage + memory). Paired with\n * `resetTrackingIdentity()`; also used by tests.\n */\nexport function clearLandingRecord(): void {\n memoryRecord = null;\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.removeItem(LANDING_STORAGE_KEY);\n } catch {\n // ignored\n }\n}\n","/** A UUID v4 from Web Crypto, with a `getRandomValues` (then `Math.random`) fallback. */\nexport function uuidV4(): string {\n const cryptoApi = globalThis.crypto;\n if (typeof cryptoApi?.randomUUID === \"function\") return cryptoApi.randomUUID();\n const bytes = new Uint8Array(16);\n if (typeof cryptoApi?.getRandomValues === \"function\") cryptoApi.getRandomValues(bytes);\n else for (let i = 0; i < 16; i += 1) bytes[i] = Math.floor(Math.random() * 256);\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\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\nimport { clearLandingRecord } from \"./landing\";\nimport { uuidV4 } from \"./uuid\";\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 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\") return uuidV4();\n\n const existing = readLocalStorage(VISITOR_STORAGE_KEY);\n if (existing && existing.length > 0) return existing;\n\n const fresh = uuidV4();\n writeLocalStorage(VISITOR_STORAGE_KEY, fresh);\n return fresh;\n}\n\n/** Adopt `visitorId` only when this browser has none — an existing id is never orphaned. */\nexport function adoptVisitorId(visitorId: string): boolean {\n if (typeof window === \"undefined\" || visitorId.length === 0) return false;\n const existing = readLocalStorage(VISITOR_STORAGE_KEY);\n if (existing && existing.length > 0) return false;\n writeLocalStorage(VISITOR_STORAGE_KEY, visitorId);\n return readLocalStorage(VISITOR_STORAGE_KEY) === visitorId;\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\") return { id: uuidV4(), 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: uuidV4(), last_event_at: now };\n writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(fresh));\n return { id: fresh.id, isNew: true };\n}\n\n/** This browser's visitor id, read without creating one; `null` before tracking has run. */\nexport function peekVisitorId(): string | null {\n if (typeof window === \"undefined\") return null;\n const existing = readLocalStorage(VISITOR_STORAGE_KEY);\n return existing && existing.length > 0 ? existing : null;\n}\n\n/**\n * The live session id, read without refreshing or rotating it; `null` once the idle window\n * has lapsed, since the next event would start a new session anyway.\n */\nexport function peekSessionId(now: number = Date.now()): string | null {\n if (typeof window === \"undefined\") return null;\n const raw = readLocalStorage(SESSION_STORAGE_KEY);\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as Partial<StoredSession>;\n if (typeof parsed.id !== \"string\" || typeof parsed.last_event_at !== \"number\") return null;\n return now - parsed.last_event_at <= SESSION_IDLE_MS ? parsed.id : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Clear visitor and session identity from localStorage, including the\n * session-scoped landing record (a landing must never outlive its session).\n *\n * Intended for tests, debugging, and explicit user reset flows.\n */\nexport function resetTrackingIdentity(): void {\n clearLandingRecord();\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.removeItem(VISITOR_STORAGE_KEY);\n window.localStorage.removeItem(SESSION_STORAGE_KEY);\n } catch {\n // ignored\n }\n}\n","// Consent-gated, de-duped orchestration around the raw `fireGtagConversion` (GAP28).\n//\n// Consent v2 (opt-out model): fire unless the visitor's effective consent is an\n// explicit, unexpired decline. There is no \"pending\" state and therefore no\n// queue — the default visitor converts in real time.\n//\n// `fireGtagConversion` is itself browser-gated and never throws, so this is safe to call\n// from the isomorphic sales `record()` — server-side it simply no-ops.\n\nimport { getConsentState } from \"../consent\";\nimport { fireGtagConversion, isValidSendTo, type GtagConversionInput } from \"../gtag\";\nimport { applyUserDataForConversion, type ConversionUserData } from \"../user-data\";\n\nconst DEDUP_PREFIX = \"_aranova_conv_\";\n\nexport type ConversionFireOutcome = \"fired\" | \"denied\" | \"duplicate\" | \"invalid\" | \"retryable\";\n\nfunction dedupKey(input: GtagConversionInput): string {\n return `${DEDUP_PREFIX}${input.transactionId ?? \"\"}:${input.sendTo}`;\n}\n\n// Has this (transaction_id, send_to) already fired? Read-only — the key is written ONLY\n// after a successful fire (see `fireOnce`), so a fire that never happened (e.g. gtag not\n// ready yet) is never suppressed on retry. Best-effort: a blocked sessionStorage reads as\n// \"not fired\" so the conversion still gets a chance.\nfunction alreadyFired(input: GtagConversionInput): boolean {\n if (!input.transactionId || typeof window === \"undefined\") return false;\n try {\n return window.sessionStorage.getItem(dedupKey(input)) !== null;\n } catch {\n return false;\n }\n}\n\nfunction markFired(input: GtagConversionInput): void {\n if (!input.transactionId || typeof window === \"undefined\") return;\n try {\n window.sessionStorage.setItem(dedupKey(input), \"1\");\n } catch {\n // sessionStorage unavailable — Google's counting_type is the authoritative backstop\n }\n}\n\n/**\n * Fire a conversion through the consent gate + de-dup guard.\n *\n * Enhanced conversions: right before the fire (past the consent gate AND the\n * de-dup guard — a duplicate that won't emit an event must not mutate gtag's\n * page-level user_data either), gtag's `user_data` is set from the explicit\n * `options.userData` merged over the form-submit stash — this is the single\n * choke point all firing paths (recordSale, trackConversion, autofire) flow\n * through, so every conversion hit carries the visitor's hashed identifiers\n * when any are known. Re-setting before each fire is idempotent (gtag scopes\n * user_data to the page), so a later autofire on the same page matches too.\n * The de-dup key is written only AFTER a successful fire, so a fire that\n * never happened (gtag not ready) is never suppressed on retry.\n */\nexport function fireConversionWithConsent(\n input: GtagConversionInput,\n options?: { userData?: ConversionUserData | null },\n): ConversionFireOutcome {\n if (getConsentState() === \"denied\") return \"denied\";\n if (alreadyFired(input)) return \"duplicate\";\n if (!isValidSendTo(input.sendTo)) return \"invalid\";\n try {\n applyUserDataForConversion(options?.userData);\n if (!fireGtagConversion(input)) return \"retryable\";\n markFired(input);\n return \"fired\";\n } catch {\n return \"retryable\";\n }\n}\n\n/**\n * Flush conversions queued while consent was pending.\n *\n * @deprecated No-op since consent v2 — there is no \"pending\" state, so nothing\n * is ever queued. Kept exported so pre-v2 integrations keep compiling.\n */\nexport function flushPendingConversions(): void {\n // intentionally empty\n}\n","// The client tracking config the SDK reads at runtime (GAP28 / ENG-46).\n//\n// The browser fetches the per-business object published to R2/CDN. This module is a\n// TOLERANT reader — it ignores unknown fields, defaults every field, and never throws — so\n// the backend can add fields freely (additive-only within the `v1` schema major) without\n// breaking an older SDK, and a newer object never breaks an older reader. See\n// docs/tracking-package/conversion-config-schema.md for the contract.\n\nexport interface ServiceFiring {\n send_to: string;\n /** Action's configured default value (cents); the SDK fires it only when a sale has no amount. */\n value_cents?: number | null;\n currency?: string | null;\n}\n\n/** Serializable on-page trigger (owned by tracking-core/src/events/trigger-spec.ts). The shape\n * varies by `event_type`; only the parameter for that type is set. */\nexport interface ConfigTriggerSpec {\n event_type: string;\n threshold_percent?: number;\n threshold_seconds?: number;\n page_threshold?: number;\n page_name?: string;\n}\n\n/** One unified conversion goal: a revenue `sale` or an on-page `event`. */\nexport interface ConversionGoal {\n key: string;\n label?: string;\n kind: \"sale\" | \"event\";\n /** Structured fire-when for event-goals; null for sales. */\n trigger: ConfigTriggerSpec | null;\n firing: ServiceFiring | null;\n}\n\nexport interface ConversionConfig {\n schema_version: number;\n config_version: number;\n business_id?: string;\n customer_id?: string | null;\n environment?: string;\n google_tracking_state: \"active\" | \"disabled\";\n gtag_ids: Record<string, string>;\n meta_pixel_ids: Record<string, string>;\n /** LEGACY: sale-goals only (for pre-unified-goal readers). */\n services: Array<{\n key: string;\n label?: string;\n firing: ServiceFiring | null;\n }>;\n /** Unified goal list (sales + on-page events). Superset of `services`. */\n goals: ConversionGoal[];\n /** Sibling hostnames of this site (roster minus self); the cross-site linker's targets. */\n linked_hostnames?: string[];\n /** Third-party integrations the site renders UI for; a missing provider means none. */\n integrations?: ConversionIntegrations;\n /** The lead-form contract (ADR-0024); null from an API that predates it. */\n lead_forms?: LeadFormsConfig | null;\n}\n\nexport interface LeadFormsConfig {\n /** The registry version the object was published under. */\n registry_version: number;\n /** One per form kind a bound consumer needs something from; an absent kind needs nothing. */\n contracts: LeadFormContract[];\n}\n\n/** What one kind of form must collect for this business, derived server-side. */\nexport interface LeadFormContract {\n form_kind: string;\n requires: LeadFormRequirement[];\n /** Each group is satisfied by any one of its roles. */\n requires_any: Array<{ roles: string[]; waived: boolean }>;\n options: Array<{ role: string; options: LeadFormContractOption[] }>;\n /** Echo on `form_submit` as `form.contract_hash`. */\n contract_hash: string;\n}\n\nexport interface LeadFormRequirement {\n role: string;\n /** Required only when `role` holds one of `any_of`; never nested. */\n required_when: { role: string; any_of: string[] } | null;\n waived: boolean;\n}\n\nexport interface LeadFormContractOption {\n key: string;\n label: string;\n is_non_answer: boolean;\n /** Dropped from the form; still accepted from a cached config. */\n deprecated: boolean;\n}\n\n/** Bookable start hours on one weekday, shop wall time (8 = 08:00); both empty = closed. */\nexport interface BookingDayWindows {\n morning: number[];\n afternoon: number[];\n}\n\nexport interface BookingRulesConfig {\n min_days_ahead: number;\n max_days_ahead: number;\n /** Keyed `monday` … `sunday`. */\n hours: Record<string, BookingDayWindows>;\n /** `YYYY-MM-DD` shop dates the portal refuses although the weekday is open. */\n closed_dates: string[];\n /** `YYYY-MM-DD` shop dates whose start hours replace their weekday's; never also closed. */\n special_hours: Record<string, BookingDayWindows>;\n vehicle_required: boolean;\n /** False from an API older than the deferred vehicle. */\n vehicle_deferred: boolean;\n /** Empty when the shop hides the drop-off question. */\n drop_off_options: string[];\n /**\n * `YYYY-MM-DD` shop dates → live bookings that day (synced appointments, pending requests,\n * website bookings not yet synced), today to the end of the window; days with none are left\n * out. Empty from an API older than the counts. The site decides when a day is full.\n */\n booked_counts: Record<string, number>;\n}\n\n/** One service choice on the site's booking form; the shop system's ids stay server-side. */\nexport interface BookingOptionConfig {\n /** What the site posts in `services`. */\n key: string;\n label: string;\n /** The shop's service category; options arrive grouped, each category in one run. */\n group: string | null;\n /** Optional helper text under the label. */\n description: string | null;\n}\n\n/** Where a booking went; decided by the API. A real customer always gets `live`. */\nexport const BOOKING_MODES = [\"live\", \"test\", \"simulated\"] as const;\n/**\n * `live`: the shop's real queue. `test`: a real request on the shop's test lead source, never\n * counted. `simulated`: never sent; nothing reached the shop or the customer.\n */\nexport type BookingMode = (typeof BOOKING_MODES)[number];\n\nfunction isBookingMode(value: unknown): value is BookingMode {\n return (BOOKING_MODES as readonly unknown[]).includes(value);\n}\n\n/** `integrations.autoleap.booking` — never carries a secret. */\nexport interface AutoLeapBookingConfig {\n /** False: show only `fallback_url`. The site never blocks the customer on it. */\n enabled: boolean;\n /** The shop's own booking page; always present. */\n fallback_url: string;\n timezone: string;\n /** Snapshot the rules came from; null before the first one. */\n config_version: number | null;\n /** `enabled` and at least one option mapped: the site may post a booking. */\n accepts_bookings: boolean;\n rules: BookingRulesConfig | null;\n options: BookingOptionConfig[];\n /** Development objects only: `test` or `simulated`, so a dev page can say so up front. */\n mode?: BookingMode;\n}\n\nexport interface ConversionIntegrations {\n autoleap?: { booking: AutoLeapBookingConfig };\n}\n\nexport interface ConversionConfigStore {\n /** Firing config for a goal/service key, or null when it doesn't fire on-site. */\n getFiring(key: string): ServiceFiring | null;\n /** The full goal for a key, or null when unknown. */\n getGoal(key: string): ConversionGoal | null;\n /** Every adopted goal (sales + events). */\n listGoals(): ConversionGoal[];\n /** Sibling hostnames for the cross-site linker; empty until a config resolves. */\n linkedHostnames(): readonly string[];\n /** The currently adopted config (baked / cached fallback until the fetch lands). */\n current(): ConversionConfig | null;\n /** True once a config is adopted (seeded synchronously from cache/baked, or fetched). */\n isReady(): boolean;\n /**\n * Run `listener` when a config first becomes available — immediately if already\n * ready, otherwise on the first adopt. Lets auto-fire replay automatic events that\n * occurred before the async CDN fetch resolved. Returns an unsubscribe fn.\n */\n onResolve(listener: () => void): () => void;\n /** Force a background revalidate against the CDN object. */\n revalidate(): Promise<void>;\n}\n\nfunction isStringMap(value: unknown): value is Record<string, string> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n Object.values(value).every((v) => typeof v === \"string\")\n );\n}\n\nfunction parseFiring(value: unknown): ServiceFiring | null {\n if (!value || typeof value !== \"object\") return null;\n const f = value as Record<string, unknown>;\n if (typeof f.send_to !== \"string\") return null;\n return {\n send_to: f.send_to,\n value_cents: typeof f.value_cents === \"number\" ? f.value_cents : null,\n currency: typeof f.currency === \"string\" ? f.currency : null,\n };\n}\n\nfunction isNumberList(value: unknown): value is number[] {\n return Array.isArray(value) && value.every((v) => typeof v === \"number\");\n}\n\nfunction isStringList(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((v) => typeof v === \"string\");\n}\n\nfunction parseWindowsMap(value: unknown): Record<string, BookingDayWindows> {\n const windowsByKey: Record<string, BookingDayWindows> = {};\n if (!value || typeof value !== \"object\") return windowsByKey;\n for (const [key, windows] of Object.entries(value as Record<string, unknown>)) {\n if (!windows || typeof windows !== \"object\") continue;\n const w = windows as Record<string, unknown>;\n windowsByKey[key] = {\n morning: isNumberList(w.morning) ? w.morning : [],\n afternoon: isNumberList(w.afternoon) ? w.afternoon : [],\n };\n }\n return windowsByKey;\n}\n\nfunction parseBookingRules(value: unknown): BookingRulesConfig | null {\n if (!value || typeof value !== \"object\") return null;\n const r = value as Record<string, unknown>;\n if (typeof r.min_days_ahead !== \"number\" || typeof r.max_days_ahead !== \"number\") return null;\n if (!r.hours || typeof r.hours !== \"object\") return null;\n const hours = parseWindowsMap(r.hours);\n return {\n min_days_ahead: r.min_days_ahead,\n max_days_ahead: r.max_days_ahead,\n hours,\n closed_dates: isStringList(r.closed_dates) ? r.closed_dates : [],\n special_hours: parseWindowsMap(r.special_hours),\n vehicle_required: r.vehicle_required === true,\n vehicle_deferred: r.vehicle_deferred === true,\n drop_off_options: isStringList(r.drop_off_options) ? r.drop_off_options : [],\n booked_counts: parseCounts(r.booked_counts),\n };\n}\n\n/** `{date: n}` with only non-negative integer counts kept; anything else is `{}`. */\nfunction parseCounts(value: unknown): Record<string, number> {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return {};\n const counts: Record<string, number> = {};\n for (const [date, count] of Object.entries(value as Record<string, unknown>)) {\n if (typeof count === \"number\" && Number.isInteger(count) && count >= 0) counts[date] = count;\n }\n return counts;\n}\n\nfunction parseBookingOption(value: unknown): BookingOptionConfig[] {\n if (!value || typeof value !== \"object\") return [];\n const o = value as Record<string, unknown>;\n if (typeof o.key !== \"string\" || !o.key || typeof o.label !== \"string\") return [];\n return [\n {\n key: o.key,\n label: o.label,\n group: typeof o.group === \"string\" && o.group ? o.group : null,\n description: typeof o.description === \"string\" && o.description ? o.description : null,\n },\n ];\n}\n\nfunction parseIntegrations(value: unknown): ConversionIntegrations {\n if (!value || typeof value !== \"object\") return {};\n const autoleap = (value as Record<string, unknown>).autoleap;\n if (!autoleap || typeof autoleap !== \"object\") return {};\n const booking = (autoleap as Record<string, unknown>).booking;\n if (!booking || typeof booking !== \"object\") return {};\n const b = booking as Record<string, unknown>;\n // Without a fallback there is nothing safe to render, so the block is unusable.\n if (typeof b.fallback_url !== \"string\" || !b.fallback_url) return {};\n return {\n autoleap: {\n booking: {\n enabled: b.enabled === true,\n fallback_url: b.fallback_url,\n timezone: typeof b.timezone === \"string\" ? b.timezone : \"UTC\",\n config_version: typeof b.config_version === \"number\" ? b.config_version : null,\n accepts_bookings: b.accepts_bookings === true,\n rules: parseBookingRules(b.rules),\n options: Array.isArray(b.options) ? b.options.flatMap(parseBookingOption) : [],\n ...(isBookingMode(b.mode) ? { mode: b.mode } : {}),\n },\n },\n };\n}\n\nfunction parseTrigger(value: unknown): ConfigTriggerSpec | null {\n if (!value || typeof value !== \"object\") return null;\n const t = value as Record<string, unknown>;\n if (typeof t.event_type !== \"string\") return null;\n const spec: ConfigTriggerSpec = { event_type: t.event_type };\n if (typeof t.threshold_percent === \"number\") spec.threshold_percent = t.threshold_percent;\n if (typeof t.threshold_seconds === \"number\") spec.threshold_seconds = t.threshold_seconds;\n if (typeof t.page_threshold === \"number\") spec.page_threshold = t.page_threshold;\n if (typeof t.page_name === \"string\") spec.page_name = t.page_name;\n return spec;\n}\n\n/** Tolerant parse of a raw config object — never throws; returns null only when unusable. */\nexport function parseConversionConfig(raw: unknown): ConversionConfig | null {\n if (!raw || typeof raw !== \"object\") return null;\n const obj = raw as Record<string, unknown>;\n const servicesRaw = Array.isArray(obj.services) ? obj.services : [];\n const services = servicesRaw.flatMap((entry) => {\n if (!entry || typeof entry !== \"object\") return [];\n const s = entry as Record<string, unknown>;\n if (typeof s.key !== \"string\") return [];\n return [\n {\n key: s.key,\n label: typeof s.label === \"string\" ? s.label : undefined,\n firing: parseFiring(s.firing),\n },\n ];\n });\n const goalsRaw = Array.isArray(obj.goals) ? obj.goals : null;\n // Back-compat: an object from a pre-unified-goal backend has no `goals` — derive them from\n // the legacy `services` (all sale-goals) so the store always has a unified list.\n const goals: ConversionGoal[] = goalsRaw\n ? goalsRaw.flatMap((entry) => {\n if (!entry || typeof entry !== \"object\") return [];\n const g = entry as Record<string, unknown>;\n if (typeof g.key !== \"string\") return [];\n return [\n {\n key: g.key,\n label: typeof g.label === \"string\" ? g.label : undefined,\n kind: g.kind === \"event\" ? \"event\" : \"sale\",\n trigger: parseTrigger(g.trigger),\n firing: parseFiring(g.firing),\n },\n ];\n })\n : services.map((s) => ({\n key: s.key,\n label: s.label,\n kind: \"sale\" as const,\n trigger: null,\n firing: s.firing,\n }));\n return {\n schema_version: typeof obj.schema_version === \"number\" ? obj.schema_version : 1,\n config_version: typeof obj.config_version === \"number\" ? obj.config_version : 0,\n business_id: typeof obj.business_id === \"string\" ? obj.business_id : undefined,\n customer_id: typeof obj.customer_id === \"string\" ? obj.customer_id : null,\n environment: typeof obj.environment === \"string\" ? obj.environment : undefined,\n google_tracking_state: obj.google_tracking_state === \"active\" ? \"active\" : \"disabled\",\n gtag_ids: isStringMap(obj.gtag_ids) ? obj.gtag_ids : {},\n meta_pixel_ids: isStringMap(obj.meta_pixel_ids) ? obj.meta_pixel_ids : {},\n services,\n goals,\n linked_hostnames: Array.isArray(obj.linked_hostnames)\n ? obj.linked_hostnames.filter((h): h is string => typeof h === \"string\" && h.length > 0)\n : [],\n integrations: parseIntegrations(obj.integrations),\n lead_forms: parseLeadForms(obj.lead_forms),\n };\n}\n\nfunction parseLeadForms(value: unknown): LeadFormsConfig | null {\n if (!isRecord(value) || typeof value.registry_version !== \"number\") return null;\n return {\n registry_version: value.registry_version,\n contracts: records(value.contracts).flatMap((c) =>\n typeof c.form_kind === \"string\" && typeof c.contract_hash === \"string\"\n ? [\n {\n form_kind: c.form_kind,\n requires: records(c.requires).flatMap((r) =>\n typeof r.role === \"string\"\n ? [\n {\n role: r.role,\n required_when: parseCondition(r.required_when),\n waived: r.waived === true,\n },\n ]\n : [],\n ),\n requires_any: records(c.requires_any).map((g) => ({\n roles: strings(g.roles),\n waived: g.waived === true,\n })),\n options: records(c.options).flatMap((o) =>\n typeof o.role === \"string\"\n ? [{ role: o.role, options: records(o.options).flatMap(parseContractOption) }]\n : [],\n ),\n contract_hash: c.contract_hash,\n },\n ]\n : [],\n ),\n };\n}\n\nfunction parseCondition(value: unknown): LeadFormRequirement[\"required_when\"] {\n if (!isRecord(value) || typeof value.role !== \"string\") return null;\n return { role: value.role, any_of: strings(value.any_of) };\n}\n\nfunction parseContractOption(value: Record<string, unknown>): LeadFormContractOption[] {\n if (typeof value.key !== \"string\" || typeof value.label !== \"string\") return [];\n return [\n {\n key: value.key,\n label: value.label,\n is_non_answer: value.is_non_answer === true,\n deprecated: value.deprecated === true,\n },\n ];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction records(value: unknown): Array<Record<string, unknown>> {\n return Array.isArray(value) ? value.filter(isRecord) : [];\n}\n\nfunction strings(value: unknown): string[] {\n return Array.isArray(value) ? value.filter((v): v is string => typeof v === \"string\") : [];\n}\n\n// --- stale-while-revalidate cache (sessionStorage) -----------------------------------------\n\nconst CACHE_PREFIX = \"_aranova_cfg_\";\n\ninterface CachedEntry {\n etag: string | null;\n config: ConversionConfig;\n}\n\nfunction cacheKey(url: string): string {\n return `${CACHE_PREFIX}${url}`;\n}\n\nfunction readCache(url: string): CachedEntry | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = window.sessionStorage.getItem(cacheKey(url));\n if (!raw) return null;\n const parsed = JSON.parse(raw) as { etag?: unknown; config?: unknown };\n const config = parseConversionConfig(parsed.config);\n if (!config) return null;\n return {\n etag: typeof parsed.etag === \"string\" ? parsed.etag : null,\n config,\n };\n } catch {\n return null;\n }\n}\n\nfunction writeCache(url: string, entry: CachedEntry): void {\n if (typeof window === \"undefined\") return;\n try {\n window.sessionStorage.setItem(cacheKey(url), JSON.stringify(entry));\n } catch {\n // sessionStorage unavailable — runtime adoption still works, just not across inits\n }\n}\n\nexport interface ResolveConversionConfigOptions {\n /** Full URL of the per-business CDN object. */\n cdnUrl: string;\n /** Offline-correct fallback (e.g. the CLI-baked snapshot). */\n baked?: ConversionConfig | null;\n /** Injectable for tests / non-global-fetch runtimes. */\n fetchImpl?: typeof fetch;\n}\n\n/**\n * Resolve the config with stale-while-revalidate: seed synchronously from the\n * sessionStorage cache (or the baked fallback), then conditionally re-fetch the CDN object\n * with `If-None-Match`. A fetched object is adopted only if its `config_version` is strictly\n * greater than what's cached, so a reordered edge copy can't downgrade fresher state.\n * Non-blocking and browser-only; a failed fetch leaves the seed in place.\n */\nexport function resolveConversionConfig(\n options: ResolveConversionConfigOptions,\n): ConversionConfigStore {\n const cached = readCache(options.cdnUrl);\n let current: ConversionConfig | null = cached?.config ?? options.baked ?? null;\n let etag: string | null = cached?.etag ?? null;\n const goalsByKey = new Map<string, ConversionGoal>();\n const resolveListeners = new Set<() => void>();\n\n function rebuildIndex(): void {\n goalsByKey.clear();\n for (const goal of current?.goals ?? []) {\n goalsByKey.set(goal.key, goal);\n }\n }\n\n function notifyResolved(): void {\n // One-shot: only the FIRST adoption matters for replay. Snapshot then clear so a\n // listener that re-subscribes inside its callback isn't double-run.\n const listeners = [...resolveListeners];\n resolveListeners.clear();\n // Isolate each listener: the set is already cleared, so one throwing subscriber\n // must not drop the others' buffered replays. Swallow (best-effort) to match the\n // SDK's policy of never surfacing tracking errors to the host page.\n for (const listener of listeners) {\n try {\n listener();\n } catch {\n // never let one subscriber break conversion replay\n }\n }\n }\n\n function adopt(next: ConversionConfig | null, nextEtag: string | null): void {\n if (!next) return;\n if (current && next.config_version <= current.config_version) return;\n const wasEmpty = current === null;\n current = next;\n etag = nextEtag;\n rebuildIndex();\n writeCache(options.cdnUrl, { etag, config: next });\n if (wasEmpty) notifyResolved();\n }\n\n async function revalidate(): Promise<void> {\n if (typeof window === \"undefined\") return;\n try {\n const doFetch = options.fetchImpl ?? globalThis.fetch;\n if (!doFetch) return;\n const headers: Record<string, string> = {};\n if (etag) headers[\"If-None-Match\"] = etag;\n const response = await doFetch(options.cdnUrl, {\n method: \"GET\",\n headers,\n });\n if (response.status === 304 || !response.ok) return;\n adopt(parseConversionConfig(await response.json()), response.headers.get(\"ETag\"));\n } catch {\n // best-effort; the cached / baked fallback stays in place\n }\n }\n\n rebuildIndex();\n void revalidate();\n\n return {\n getFiring: (key) => goalsByKey.get(key)?.firing ?? null,\n getGoal: (key) => goalsByKey.get(key) ?? null,\n listGoals: () => [...goalsByKey.values()],\n linkedHostnames: () => current?.linked_hostnames ?? [],\n current: () => current,\n isReady: () => current !== null,\n onResolve: (listener) => {\n if (current !== null) {\n listener();\n return () => {};\n }\n resolveListeners.add(listener);\n return () => resolveListeners.delete(listener);\n },\n revalidate,\n };\n}\n","const STORAGE_KEY = \"_aranova_auto_txn_map\";\nconst transactionIds = new Map<string, string>();\nlet legacyCounter = 0;\n\ninterface StoredTransactionMap {\n sessionId: string;\n entries: Record<string, string>;\n}\n\nfunction scopeKey(sessionId: string, goalKey: string, path: string): string {\n return JSON.stringify([sessionId, goalKey, path]);\n}\n\nfunction randomId(): string {\n const cryptoApi = globalThis.crypto;\n if (typeof cryptoApi?.randomUUID === \"function\") return cryptoApi.randomUUID();\n if (typeof cryptoApi?.getRandomValues === \"function\") {\n const bytes = cryptoApi.getRandomValues(new Uint8Array(16));\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n }\n // Ancient-browser fallback. Keep fixed-length opaque shape; modern browsers\n // always use Web Crypto above.\n legacyCounter = (legacyCounter + 1) % 0x1_0000_0000;\n const timestamp = Date.now().toString(16).padStart(12, \"0\");\n const counter = legacyCounter.toString(16).padStart(8, \"0\");\n const random = Math.floor(Math.random() * 0xffffffffffff)\n .toString(16)\n .padStart(12, \"0\");\n return `${timestamp}${counter}${random}`.slice(0, 32);\n}\n\nfunction isValidTransactionId(value: unknown): value is string {\n return (\n typeof value === \"string\" &&\n /^auto:(?:[0-9a-f]{32}|[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$/i.test(\n value,\n ) &&\n value.length <= 64\n );\n}\n\nfunction readStoredMap(sessionId: string): StoredTransactionMap {\n if (typeof window === \"undefined\") return { sessionId, entries: {} };\n try {\n const raw = window.localStorage.getItem(STORAGE_KEY);\n if (!raw) return { sessionId, entries: {} };\n const parsed = JSON.parse(raw) as Partial<StoredTransactionMap>;\n if (\n parsed.sessionId !== sessionId ||\n !parsed.entries ||\n typeof parsed.entries !== \"object\" ||\n Array.isArray(parsed.entries)\n ) {\n return { sessionId, entries: {} };\n }\n return { sessionId, entries: parsed.entries };\n } catch {\n return { sessionId, entries: {} };\n }\n}\n\nfunction writeStoredMap(stored: StoredTransactionMap): void {\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.setItem(STORAGE_KEY, JSON.stringify(stored));\n } catch {\n // Storage unavailable; page-lifetime stability is still preserved.\n }\n}\n\n/**\n * Return one opaque, bounded Google transaction ID per session/goal/path.\n *\n * The raw session id and URL path stay in first-party local storage and are\n * never sent to Google. The one-current-session map preserves deduplication\n * across tabs while discarding prior-session mappings.\n */\nexport function getAutomaticTransactionId(\n sessionId: string,\n goalKey: string,\n path: string,\n): string {\n const key = scopeKey(sessionId, goalKey, path);\n const existing = transactionIds.get(key);\n if (existing) return existing;\n\n const stored = readStoredMap(sessionId);\n const storedId = stored.entries[key];\n if (isValidTransactionId(storedId)) {\n transactionIds.set(key, storedId);\n return storedId;\n }\n\n const transactionId = `auto:${randomId()}`;\n transactionIds.set(key, transactionId);\n // Re-read before write so another tab that won the race remains authoritative.\n const latest = readStoredMap(sessionId);\n const concurrentId = latest.entries[key];\n if (isValidTransactionId(concurrentId)) {\n transactionIds.set(key, concurrentId);\n return concurrentId;\n }\n latest.entries[key] = transactionId;\n writeStoredMap(latest);\n return transactionId;\n}\n\nexport function resetAutomaticTransactionIdsForTests(): void {\n transactionIds.clear();\n legacyCounter = 0;\n}\n","import type { ConversionGoal } from \"./conversion-config\";\n\nfunction hasNumberField<K extends string>(\n metadata: Readonly<Record<string, unknown>>,\n key: K,\n): metadata is Readonly<Record<string, unknown> & Record<K, number>> {\n return typeof metadata[key] === \"number\";\n}\n\nfunction hasStringField<K extends string>(\n metadata: Readonly<Record<string, unknown>>,\n key: K,\n): metadata is Readonly<Record<string, unknown> & Record<K, string>> {\n return typeof metadata[key] === \"string\";\n}\n\n/** Shared automatic-goal matcher used by every SDK runtime. */\nexport function automaticThresholdMet(\n goal: ConversionGoal,\n eventType: string,\n metadata: Readonly<Record<string, unknown>>,\n): boolean {\n const trigger = goal.trigger;\n if (!trigger || trigger.event_type !== eventType) return false;\n switch (eventType) {\n case \"scroll_depth\":\n return (\n hasNumberField(metadata, \"depth_percent\") &&\n trigger.threshold_percent != null &&\n metadata.depth_percent >= trigger.threshold_percent\n );\n case \"time_on_site\":\n return (\n hasNumberField(metadata, \"duration_ms\") &&\n trigger.threshold_seconds != null &&\n metadata.duration_ms >= trigger.threshold_seconds * 1000\n );\n case \"multi_page_session\":\n return (\n hasNumberField(metadata, \"page_count\") &&\n trigger.page_threshold != null &&\n metadata.page_count >= trigger.page_threshold\n );\n case \"specific_page_visit\":\n return hasStringField(metadata, \"page_name\") && metadata.page_name === trigger.page_name;\n case \"page_view\":\n case \"form_start\":\n case \"phone_click\":\n return true;\n default:\n return false;\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(cents: number, currency: SupportedCurrency, locale?: string): string {\n return new Intl.NumberFormat(locale, { style: \"currency\", currency }).format(\n fromMinor(cents, currency),\n );\n}\n\n// Hoisted to `resources/intl/date` so the calendar resource shares the same\n// binding; still public API via `sales-public.ts`.\nexport { formatDateInTz } from \"../intl/date\";\n","import { stripLinkParam } from \"../cross-site-link\";\nimport { applyDefaultConsentState, isValidGtagId, loadGtagScript } from \"../gtag\";\nimport {\n parseConversionConfig,\n type ConversionConfig,\n type ConversionGoal,\n type ConversionIntegrations,\n type LeadFormsConfig,\n} from \"./conversion-config\";\nimport { fireConversionWithConsent } from \"./conversion-firing\";\nimport { getAutomaticTransactionId } from \"./automatic-transaction\";\nimport { automaticThresholdMet } from \"./automatic-trigger\";\nimport { fromMinor } from \"./sales/money\";\nimport type { SupportedCurrency } from \"./sales/schema\";\n\n/**\n * Where a business's published tracking config lives.\n *\n * `businessId` + `environment` are the stable identity; the ORIGIN it is fetched\n * from is deployment-specific (production CDN vs. a local object store), which is\n * why `cdnBaseUrl` exists. Codegen bakes only the identity, so one committed\n * generated file works in every environment and only an env var changes.\n *\n * `cdnUrl` remains supported and WINS when present — every client repo generated\n * before `cdnBaseUrl` passes one, and those must keep working untouched.\n */\nexport interface TrackingConfigReference {\n /**\n * Fully-resolved object URL. Optional: omit it and pass `cdnBaseUrl` (or rely\n * on the production default) to have it composed from the identity below.\n */\n cdnUrl?: string;\n /**\n * Origin (optionally with a path prefix) the config object is served from, e.g.\n * `https://demos.aranova.io` in production or\n * `http://localhost:9100/aranova-demos` against a local MinIO. Ignored when\n * `cdnUrl` is set; blank or omitted falls back to {@link DEFAULT_CDN_BASE_URL}.\n * `tracking-cli gen` wires this to an env var so retargeting is config, not code.\n */\n cdnBaseUrl?: string;\n businessId: string;\n /**\n * The site whose config object to load (ENG-284). Selects the per-site\n * `tracking-config/v1/sites/` key; omitted (older generated files) resolves\n * the legacy per-business key, which is the primary site's config.\n */\n siteId?: string;\n environment: \"production\" | \"test\";\n}\n\n/** Production object-storage origin — the default when nothing overrides it. */\nexport const DEFAULT_CDN_BASE_URL = \"https://demos.aranova.io\";\n\n/**\n * Object key for a config, mirroring the backend's\n * `core/storage.py::tracking_config_key` (per-site) and its legacy per-business\n * key. The backend stays the authority: it\n * publishes at this key and returns the absolute URL, which is what the CLI bakes\n * as the legacy `cdnUrl`. This is the composition path for references that carry\n * identity instead of a URL — the `v1` prefix is the schema MAJOR and must move in\n * lockstep with the backend if it is ever cut to `v2`.\n */\nfunction trackingConfigKey(ref: TrackingConfigReference): string {\n if (ref.siteId) return `tracking-config/v1/sites/${ref.siteId}-${ref.environment}.json`;\n return `tracking-config/v1/${ref.businessId}-${ref.environment}.json`;\n}\n\n/**\n * The URL a reference resolves to. An explicit `cdnUrl` wins (back-compat);\n * otherwise compose from `cdnBaseUrl` (or the production default).\n *\n * A blank/whitespace `cdnBaseUrl` is treated as UNSET rather than composed: the\n * generated module reads it from an env var, and a var that is present-but-empty\n * (a stray `NEXT_PUBLIC_ARANOVA_CDN_BASE_URL=` line) would otherwise produce\n * `/tracking-config/...` — a same-origin request to the client's own site.\n */\nexport function resolveTrackingConfigUrl(ref: TrackingConfigReference): string {\n if (ref.cdnUrl) return ref.cdnUrl;\n const configured = ref.cdnBaseUrl?.trim();\n const base = (configured || DEFAULT_CDN_BASE_URL).replace(/\\/+$/, \"\");\n return `${base}/${trackingConfigKey(ref)}`;\n}\n\ntype RuntimeState = \"unconfirmed\" | \"active\" | \"tombstone\";\n\ninterface CachedEntry {\n etag: string | null;\n config: ConversionConfig;\n}\n\ninterface QueuedConversion {\n key: string;\n value?: number | null;\n currency?: string | null;\n transactionId?: string | null;\n}\n\nexport type TrackingConfigConversionOptions = Omit<QueuedConversion, \"key\">;\n\ninterface QueuedPageView {\n href: string;\n title: string | null;\n referrer: string | null;\n}\n\ninterface QueuedAutomaticEvent {\n eventType: string;\n metadata: Record<string, unknown>;\n transactionPath: string;\n transactionScope: string;\n}\n\nconst CACHE_PREFIX = \"_aranova_cfg_runtime_\";\nconst AUTHORITY_TTL_MS = 60_000;\n// Queues are bounded rather than cleared while authority is unconfirmed: the\n// config can appear at any time (a first publish, or R2 recovering), and the\n// most recent items are still worth firing when it does. Unbounded, a site whose\n// config 404s accumulates an entry per page view for the life of the tab.\nconst MAX_QUEUE_LENGTH = 50;\n// While unconfirmed, revalidation is spaced by a growing delay instead of riding\n// every queued item. Without it a missing config produces one no-cache request\n// per page view, forever. `focus` / `visibilitychange` still revalidate, so\n// recovery stays prompt once the object exists.\nconst RETRY_BASE_MS = 5_000;\nconst RETRY_MAX_MS = 5 * 60_000;\n\nconst runtimes = new Map<string, TrackingConfigRuntime>();\nconst configuredIds = new Set<string>();\nlet scriptLoad: Promise<void> | null = null;\nlet jsInitialized = false;\n\nfunction cacheKey(url: string): string {\n return `${CACHE_PREFIX}${url}`;\n}\n\n/** Append, dropping the oldest entry once the cap is reached. */\nfunction pushBounded<T>(queue: T[], item: T): void {\n queue.push(item);\n if (queue.length > MAX_QUEUE_LENGTH) queue.splice(0, queue.length - MAX_QUEUE_LENGTH);\n}\n\nfunction readCache(url: string): CachedEntry | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = window.sessionStorage.getItem(cacheKey(url));\n if (!raw) return null;\n const parsed = JSON.parse(raw) as { etag?: unknown; config?: unknown };\n const config = parseConversionConfig(parsed.config);\n if (!config) return null;\n return {\n etag: typeof parsed.etag === \"string\" ? parsed.etag : null,\n config,\n };\n } catch {\n return null;\n }\n}\n\nfunction writeCache(url: string, entry: CachedEntry): void {\n if (typeof window === \"undefined\") return;\n try {\n window.sessionStorage.setItem(cacheKey(url), JSON.stringify(entry));\n } catch {\n // sessionStorage unavailable — runtime still works, just without a conditional candidate\n }\n}\n\nfunction isTombstone(config: ConversionConfig): boolean {\n return config.google_tracking_state === \"disabled\" || Object.keys(config.gtag_ids).length === 0;\n}\n\nfunction validateConfig(config: ConversionConfig, ref: TrackingConfigReference): boolean {\n return (\n config.business_id === ref.businessId &&\n config.environment === ref.environment &&\n (config.google_tracking_state === \"active\" || config.google_tracking_state === \"disabled\")\n );\n}\n\nfunction pageViewSnapshot(): QueuedPageView | null {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return null;\n return {\n href: stripLinkParam(window.location.href),\n title: document.title || null,\n referrer: document.referrer || null,\n };\n}\n\nfunction ensureScript(gtagId: string): Promise<void> {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return Promise.resolve();\n applyDefaultConsentState();\n loadGtagScript(gtagId);\n if (scriptLoad) return scriptLoad;\n scriptLoad = new Promise((resolve) => {\n const script = document.querySelector<HTMLScriptElement>(\n 'script[data-aranova-tracking=\"aranova-gtag-loader\"]',\n );\n if (!script) {\n resolve();\n return;\n }\n if ((script as HTMLScriptElement & { dataset: DOMStringMap }).dataset.loaded === \"true\") {\n resolve();\n return;\n }\n window.setTimeout(resolve, 0);\n script.addEventListener(\n \"load\",\n () => {\n script.dataset.loaded = \"true\";\n resolve();\n },\n { once: true },\n );\n script.addEventListener(\"error\", () => resolve(), { once: true });\n });\n return scriptLoad;\n}\n\nexport class TrackingConfigRuntime {\n private readonly fetchImpl: typeof fetch;\n /** Resolved config URL — see the constructor for why it is computed once. */\n private readonly url: string;\n private current: ConversionConfig | null = null;\n private etag: string | null = null;\n private stateValue: RuntimeState = \"unconfirmed\";\n private confirmedAt = 0;\n private authorityGeneration = 0;\n private inFlight: Promise<void> | null = null;\n private flushInFlight: Promise<void> | null = null;\n private flushRequested = false;\n private retryTimer: number | null = null;\n private started = false;\n /** Consecutive failed authority attempts — drives the revalidate backoff. */\n private authorityFailures = 0;\n /** Epoch ms before which `ensureAuthority` must not issue another request. */\n private nextAuthorityAttemptAt = 0;\n private readonly conversionQueue: QueuedConversion[] = [];\n private readonly automaticQueue: QueuedAutomaticEvent[] = [];\n private readonly pageQueue: QueuedPageView[] = [];\n private readonly listeners = new Set<() => void>();\n\n constructor(\n readonly ref: TrackingConfigReference,\n fetchImpl: typeof fetch = globalThis.fetch,\n ) {\n // Native browser fetch requires Window/WorkerGlobalScope as its receiver.\n // Calling an unbound native fetch stored on `this` throws \"Illegal invocation\".\n this.fetchImpl = fetchImpl.bind(globalThis);\n // Resolved once: the cache key, the fetch target, and the write-back key must\n // all be the same string, or a reference that composes its URL would read a\n // cache entry it never writes.\n this.url = resolveTrackingConfigUrl(ref);\n const cached = readCache(this.url);\n this.current = cached?.config ?? null;\n this.etag = cached?.etag ?? null;\n this.start();\n }\n\n /** The URL this runtime actually fetches (composed or explicit). */\n configUrl(): string {\n return this.url;\n }\n\n /**\n * Explicitly start authority resolution and Google-tag bootstrap.\n *\n * Idempotent so framework effects can call it after hydration without\n * depending on constructor timing.\n */\n start(): void {\n if (this.started) {\n void this.flush();\n return;\n }\n this.started = true;\n void this.revalidate();\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"visible\") void this.revalidate();\n });\n window.addEventListener(\"focus\", () => void this.revalidate());\n }\n }\n\n state(): RuntimeState {\n return this.stateValue;\n }\n\n config(): ConversionConfig | null {\n return this.stateValue === \"active\" || this.stateValue === \"tombstone\" ? this.current : null;\n }\n\n __unsafeExpireAuthorityForTests(): void {\n this.confirmedAt = Number.NEGATIVE_INFINITY;\n this.nextAuthorityAttemptAt = 0;\n }\n\n /** Queue depths — asserted by tests to pin the bound. */\n __queueDepthsForTests(): { conversions: number; automatic: number; pages: number } {\n return {\n conversions: this.conversionQueue.length,\n automatic: this.automaticQueue.length,\n pages: this.pageQueue.length,\n };\n }\n\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n async ensureAuthority(): Promise<boolean> {\n if (this.stateValue !== \"unconfirmed\" && Date.now() - this.confirmedAt < AUTHORITY_TTL_MS) {\n return true;\n }\n // Backoff applies only to this implicit path (driven by queued work). An\n // explicit `revalidate()` — the visibility/focus listeners, or a caller —\n // always goes to the network so recovery is never delayed by the timer.\n if (Date.now() < this.nextAuthorityAttemptAt) return false;\n await this.revalidateAuthority();\n return this.stateValue !== \"unconfirmed\" && Date.now() - this.confirmedAt < AUTHORITY_TTL_MS;\n }\n\n async revalidate(): Promise<void> {\n this.nextAuthorityAttemptAt = 0;\n await this.revalidateAuthority();\n await this.flush();\n }\n\n private async revalidateAuthority(): Promise<void> {\n if (typeof window === \"undefined\" || !this.fetchImpl) return;\n if (this.inFlight) return this.inFlight;\n this.inFlight = this.revalidateNow().finally(() => {\n this.inFlight = null;\n });\n return this.inFlight;\n }\n\n queuePageView(snapshot: QueuedPageView | null = pageViewSnapshot()): void {\n if (!snapshot) return;\n pushBounded(this.pageQueue, snapshot);\n void this.flush();\n }\n\n fireConversion(key: string, options?: TrackingConfigConversionOptions): void {\n pushBounded(this.conversionQueue, { key, ...options });\n void this.flush();\n }\n\n queueAutomaticEvent(\n eventType: string,\n metadata: Record<string, unknown>,\n transactionPath: string,\n transactionScope: string,\n ): void {\n pushBounded(this.automaticQueue, {\n eventType,\n metadata,\n transactionPath,\n transactionScope,\n });\n void this.flush();\n }\n\n listGoals(): ConversionGoal[] {\n return this.current?.goals ?? [];\n }\n\n /** Sibling hostnames for the linker — read from the cached object even while unconfirmed. */\n linkedHostnames(): readonly string[] {\n return this.current?.linked_hostnames ?? [];\n }\n\n /**\n * Integration blocks, from the cached object even while unconfirmed: the booking fallback\n * link is safe to show stale. Same reference until the next adopt, for `useSyncExternalStore`.\n */\n integrations(): ConversionIntegrations | null {\n return this.current?.integrations ?? null;\n }\n\n /** The lead-form contracts, from the cached object like `integrations()`. */\n leadForms(): LeadFormsConfig | null {\n return this.current?.lead_forms ?? null;\n }\n\n private async revalidateNow(): Promise<void> {\n try {\n const headers: Record<string, string> = {};\n if (this.etag) headers[\"If-None-Match\"] = this.etag;\n const response = await this.fetchImpl(this.url, {\n method: \"GET\",\n headers,\n cache: \"no-cache\",\n });\n if (response.status === 304 && this.current && validateConfig(this.current, this.ref)) {\n this.confirm(this.current, this.etag);\n return;\n }\n if (!response.ok) {\n this.expireAuthority();\n return;\n }\n const next = parseConversionConfig(await response.json());\n if (!next || !validateConfig(next, this.ref)) {\n this.expireAuthority();\n return;\n }\n if (this.current && next.config_version < this.current.config_version) {\n this.expireAuthority();\n return;\n }\n this.confirm(next, response.headers.get(\"ETag\"));\n } catch {\n this.expireAuthority();\n }\n }\n\n private expireAuthority(): void {\n this.authorityGeneration += 1;\n if (this.stateValue !== \"unconfirmed\") this.stateValue = \"unconfirmed\";\n // Exponential, capped. Queued work keeps accumulating (bounded), so the\n // config appearing later still fires the most recent items.\n this.authorityFailures += 1;\n const delay = Math.min(RETRY_MAX_MS, RETRY_BASE_MS * 2 ** (this.authorityFailures - 1));\n this.nextAuthorityAttemptAt = Date.now() + delay;\n }\n\n private confirm(config: ConversionConfig, etag: string | null): void {\n this.authorityGeneration += 1;\n this.current = config;\n this.etag = etag;\n this.confirmedAt = Date.now();\n this.authorityFailures = 0;\n this.nextAuthorityAttemptAt = 0;\n this.stateValue = isTombstone(config) ? \"tombstone\" : \"active\";\n writeCache(this.url, { etag, config });\n if (this.stateValue === \"tombstone\") {\n if (this.retryTimer !== null) {\n window.clearTimeout(this.retryTimer);\n this.retryTimer = null;\n }\n this.conversionQueue.length = 0;\n this.automaticQueue.length = 0;\n this.pageQueue.length = 0;\n }\n for (const listener of this.listeners) listener();\n }\n\n private async flush(): Promise<void> {\n if (this.flushInFlight) {\n this.flushRequested = true;\n return this.flushInFlight;\n }\n this.flushRequested = false;\n this.flushInFlight = this.flushNow().finally(() => {\n this.flushInFlight = null;\n if (this.flushRequested && this.stateValue === \"active\") void this.flush();\n });\n return this.flushInFlight;\n }\n\n private scheduleRetry(): void {\n if (this.retryTimer !== null || typeof window === \"undefined\") return;\n this.retryTimer = window.setTimeout(() => {\n this.retryTimer = null;\n void this.flush();\n }, 1000);\n }\n\n private async flushNow(): Promise<void> {\n if (!(await this.ensureAuthority())) return;\n if (this.stateValue !== \"active\" || !this.current) return;\n const config = this.current;\n const generation = this.authorityGeneration;\n const ids = Object.values(config.gtag_ids).filter(\n (id): id is string => typeof id === \"string\" && isValidGtagId(id),\n );\n if (ids.length === 0) return;\n await ensureScript(ids[0]!);\n if (\n this.authorityGeneration !== generation ||\n this.stateValue !== \"active\" ||\n this.current !== config\n ) {\n return;\n }\n const gtag = window.gtag;\n if (typeof gtag !== \"function\") return;\n try {\n if (!jsInitialized) {\n gtag(\"js\", new Date());\n jsInitialized = true;\n }\n for (const id of ids) {\n if (configuredIds.has(id)) continue;\n gtag(\"config\", id, { send_page_view: false });\n configuredIds.add(id);\n }\n } catch {\n this.scheduleRetry();\n return;\n }\n while (this.pageQueue.length > 0) {\n const page = this.pageQueue[0]!;\n try {\n gtag(\"event\", \"page_view\", {\n page_location: page.href,\n page_title: page.title ?? undefined,\n page_referrer: page.referrer ?? undefined,\n });\n this.pageQueue.shift();\n } catch {\n this.scheduleRetry();\n return;\n }\n }\n while (this.automaticQueue.length > 0) {\n const event = this.automaticQueue[0]!;\n for (const goal of config.goals) {\n if (goal.kind !== \"event\" || !goal.firing) continue;\n if (!automaticThresholdMet(goal, event.eventType, event.metadata)) continue;\n pushBounded(this.conversionQueue, {\n key: goal.key,\n transactionId: getAutomaticTransactionId(\n event.transactionScope,\n goal.key,\n event.transactionPath,\n ),\n });\n }\n this.automaticQueue.shift();\n }\n while (this.conversionQueue.length > 0) {\n const item = this.conversionQueue[0]!;\n const goal = config.goals.find((g) => g.key === item.key);\n const firing = goal?.firing;\n if (!firing) {\n this.conversionQueue.shift();\n continue;\n }\n const currency = item.currency ?? firing.currency ?? null;\n const value =\n item.value ??\n (firing.value_cents != null && currency\n ? fromMinor(firing.value_cents, currency as SupportedCurrency)\n : null);\n const outcome = fireConversionWithConsent({\n sendTo: firing.send_to,\n value,\n currency,\n transactionId: item.transactionId ?? null,\n });\n if (outcome === \"retryable\") {\n this.scheduleRetry();\n return;\n }\n this.conversionQueue.shift();\n }\n }\n}\n\nexport function getTrackingConfigRuntime(\n ref: TrackingConfigReference,\n fetchImpl?: typeof fetch,\n): TrackingConfigRuntime {\n // Keyed on the RESOLVED url: `{cdnUrl}` and an equivalent\n // `{cdnBaseUrl,businessId,environment}` name the same object and must share one\n // runtime, or each would keep its own authority state and gtag bootstrap.\n const key = `${resolveTrackingConfigUrl(ref)}|${ref.businessId}|${ref.environment}`;\n const existing = runtimes.get(key);\n if (existing) return existing;\n const runtime = new TrackingConfigRuntime(ref, fetchImpl ?? globalThis.fetch);\n runtimes.set(key, runtime);\n return runtime;\n}\n\nexport function resetTrackingConfigRuntimesForTests(): void {\n runtimes.clear();\n configuredIds.clear();\n scriptLoad = null;\n jsInitialized = false;\n}\n","{\n \"hosts\": [\"myalp.io\"]\n}\n","// Provider-agnostic booking read over the published config (`integrations.*.booking`).\n// React-free: the hooks and the link live in `booking-react.tsx`.\n\nimport bookingLinks from \"../../../contracts/autoleap-booking-links.json\";\nimport { parsePhone } from \"./phone\";\nimport type {\n BookingDayWindows,\n BookingMode,\n BookingRulesConfig,\n ConversionIntegrations,\n} from \"./resources/conversion-config\";\n\n/** Hosts a handoff link may point at: the backend's short-link allowlist, from one contract. */\nconst HANDOFF_HOSTS = new Set<string>(bookingLinks.hosts);\n\nexport type BookingProvider = \"autoleap\";\n\n/**\n * An option key the live config offered. Branded so a contract-typed booking accepts it beside the\n * generated keys: a key newer than the site's generated file still books (reported as behind).\n */\nexport type BookingOptionKey = string & { readonly __bookingOptionKey: true };\n\n/** One service choice on the booking form, by the site's own key. */\nexport interface BookingOption {\n key: BookingOptionKey;\n label: string;\n /** The shop's service category, for a heading over its choices; null when it has none. */\n group: string | null;\n /** Optional helper text to show under the label. */\n description: string | null;\n}\n\n/** One heading on the form and the choices under it; `group` null holds the ungrouped ones. */\nexport interface BookingOptionGroup {\n group: string | null;\n options: BookingOption[];\n}\n\n/** The booking rules in camelCase, all in the shop's wall time. */\nexport interface BookingRules {\n minDaysAhead: number;\n maxDaysAhead: number;\n /** Start hours per weekday, `monday` … `sunday`; both lists empty = closed. */\n hours: Record<string, BookingDayWindows>;\n /** `YYYY-MM-DD` shop dates with no bookings although the weekday is open. */\n closedDates: string[];\n /** `YYYY-MM-DD` shop dates whose start hours replace their weekday's. */\n specialHours: Record<string, BookingDayWindows>;\n vehicleRequired: boolean;\n /** The vehicle may come after booking: send it with `bookings.updateBooking`. */\n vehicleDeferred: boolean;\n /** `waitForIt` / `dropOff`; empty when the shop hides the question. */\n dropOffOptions: string[];\n /** `YYYY-MM-DD` shop dates → live bookings already held that day; days with none absent. */\n bookedCounts: Record<string, number>;\n}\n\nexport interface BookingIntegration {\n provider: BookingProvider;\n /** False: render only the handoff link. Never block the customer on it. */\n enabled: boolean;\n /** The shop's own booking page. */\n fallbackUrl: string;\n /** True when the site may post a booking: `enabled`, rules captured and options mapped. */\n acceptsBookings: boolean;\n /** The shop's IANA timezone; every date and hour here is its wall time. */\n timezone: string;\n /** Sent back with each booking; a stale one is refused and the config refetched. */\n configVersion: number | null;\n rules: BookingRules | null;\n options: BookingOption[];\n /**\n * Where a booking from this page goes: `live` in production; on a development config `test`\n * (the shop's test lead source) or `simulated` (never sent). Show a banner when not `live`.\n */\n mode: BookingMode;\n}\n\n/** What the visitor already typed, carried onto the shop's booking page. */\nexport interface BookingHandoffPrefill {\n /** A single full-name field; split on the first space when `firstName` is not given. */\n name?: string | null;\n firstName?: string | null;\n lastName?: string | null;\n /** Any format; sent as national digits (no country code), as the shop's portal stores it. */\n phone?: string | null;\n email?: string | null;\n}\n\n/** The enabled-or-fallback booking provider in a config's integrations, if any. */\nexport function getBookingIntegration(\n integrations: ConversionIntegrations | null | undefined,\n): BookingIntegration | null {\n const booking = integrations?.autoleap?.booking;\n if (!booking) return null;\n const rules = booking.rules ? camelRules(booking.rules) : null;\n return {\n provider: \"autoleap\",\n enabled: booking.enabled,\n fallbackUrl: booking.fallback_url,\n acceptsBookings: booking.accepts_bookings && rules !== null && booking.config_version !== null,\n timezone: booking.timezone,\n configVersion: booking.config_version,\n rules,\n options: booking.options.map((option) => ({\n key: option.key as BookingOptionKey,\n label: option.label,\n group: option.group,\n description: option.description,\n })),\n mode: booking.mode ?? \"live\",\n };\n}\n\nfunction camelRules(rules: BookingRulesConfig): BookingRules {\n return {\n minDaysAhead: rules.min_days_ahead,\n maxDaysAhead: rules.max_days_ahead,\n hours: rules.hours,\n closedDates: rules.closed_dates,\n specialHours: rules.special_hours,\n vehicleRequired: rules.vehicle_required,\n vehicleDeferred: rules.vehicle_deferred,\n dropOffOptions: rules.drop_off_options,\n bookedCounts: rules.booked_counts ?? {},\n };\n}\n\nconst WEEKDAYS = [\"sunday\", \"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\", \"saturday\"];\n\n/** One bookable day: its start hours split as the shop's portal shows them. */\nexport interface BookableDay {\n /** `YYYY-MM-DD`, shop-local. */\n date: string;\n /** `monday` … `sunday`. */\n weekday: string;\n /** Start hours before noon (8 = 08:00). */\n morning: number[];\n /** Start hours from noon on. */\n afternoon: number[];\n /**\n * Live bookings the shop already holds that day (0 when none). Never filters a day out:\n * a site with a daily cap marks the day full itself.\n */\n booked: number;\n}\n\n/** Today's date in `timeZone` as `YYYY-MM-DD`; the server judges notice by the same date. */\nexport function shopToday(timeZone: string, now: Date = new Date()): string {\n const parts = new Intl.DateTimeFormat(\"en-CA\", {\n timeZone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n }).formatToParts(now);\n const part = (type: string) => parts.find((p) => p.type === type)?.value ?? \"\";\n return `${part(\"year\")}-${part(\"month\")}-${part(\"day\")}`;\n}\n\nfunction addDays(isoDate: string, days: number): string {\n const [year, month, day] = isoDate.split(\"-\").map(Number);\n const date = new Date(Date.UTC(year, month - 1, day + days));\n return date.toISOString().slice(0, 10);\n}\n\n/**\n * Every day a customer may book, from the minimum notice to the end of the window, with its\n * start hours. Exactly what the server accepts: closed dates removed, special hours applied.\n */\nexport function bookableSlots(\n rules: BookingRules,\n { timeZone, now = new Date() }: { timeZone: string; now?: Date },\n): BookableDay[] {\n const today = shopToday(timeZone, now);\n const days: BookableDay[] = [];\n const closed = new Set(rules.closedDates);\n for (let offset = rules.minDaysAhead; offset <= rules.maxDaysAhead; offset += 1) {\n const date = addDays(today, offset);\n if (closed.has(date)) continue;\n const weekday = WEEKDAYS[new Date(`${date}T00:00:00Z`).getUTCDay()];\n const windows = rules.specialHours[date] ?? rules.hours[weekday];\n if (!windows || (windows.morning.length === 0 && windows.afternoon.length === 0)) continue;\n days.push({\n date,\n weekday,\n morning: [...windows.morning],\n afternoon: [...windows.afternoon],\n booked: rules.bookedCounts?.[date] ?? 0,\n });\n }\n return days;\n}\n\n/**\n * The handoff URL with the visitor's details prefilled, or null when `fallbackUrl` is not an\n * https link on the shop's booking host (a config value never becomes an arbitrary redirect).\n */\nexport function buildBookingHandoffUrl(\n fallbackUrl: string,\n prefill: BookingHandoffPrefill = {},\n): string | null {\n let url: URL;\n try {\n url = new URL(fallbackUrl);\n } catch {\n return null;\n }\n if (\n url.protocol !== \"https:\" ||\n !HANDOFF_HOSTS.has(url.hostname) ||\n url.username ||\n url.password ||\n url.port\n ) {\n return null;\n }\n const [firstName, lastName] = prefill.firstName?.trim()\n ? [prefill.firstName, prefill.lastName]\n : splitFullName(prefill.name);\n const values: Array<[string, string | null | undefined]> = [\n [\"firstName\", firstName?.trim()],\n [\"lastName\", lastName?.trim()],\n [\"phone\", prefill.phone ? nationalDigits(prefill.phone) : \"\"],\n [\"email\", prefill.email?.trim()],\n ];\n for (const [key, value] of values) {\n if (value) url.searchParams.set(key, value);\n }\n return url.toString();\n}\n\nfunction splitFullName(name: string | null | undefined): [string, string] {\n const [first = \"\", ...rest] = (name ?? \"\").trim().split(/\\s+/);\n return [first, rest.join(\" \")];\n}\n\n/** \"+1 (416) 555-0100\" → \"4165550100\"; an unparseable value keeps its raw digits. */\nfunction nationalDigits(phone: string): string {\n const parsed = parsePhone(phone);\n return (parsed.isValid ? parsed.national : phone).replace(/\\D/g, \"\");\n}\n\n/**\n * The options as headed sections, in published order: one per category, choices in order\n * under it. The API saves them grouped, so a category is never split; this keeps the first run.\n */\nexport function groupBookingOptions(options: readonly BookingOption[]): BookingOptionGroup[] {\n const groups = new Map<string | null, BookingOption[]>();\n for (const option of options) {\n const members = groups.get(option.group);\n if (members) members.push(option);\n else groups.set(option.group, [option]);\n }\n return [...groups].map(([group, members]) => ({ group, options: members }));\n}\n","// Auto-fire on-page conversions for AUTOMATIC event-goals (GAP28 / unified-goal ADR).\n//\n// The SDK's automatic detectors (scroll_depth, time_on_site, multi_page_session,\n// specific_page_visit, page_view, form_start) already emit analytics events via\n// `client.trackEvent`. This layer observes those events and ALSO fires a\n// `gtag('event','conversion')` when a published EVENT-goal's trigger threshold is met — so the\n// consumer writes ZERO conversion code for automatic goals (the config drives everything).\n//\n// `phone_click` is the one manual event we ALSO auto-fire: a `tel:` tap (captured by\n// `attachPhoneClickCapture`) is an unambiguous on-site conversion, so a linked phone_click\n// event-goal fires with zero consumer code (see thresholdMet). The other manual event-goals\n// (form_submit/cta_click) are NOT auto-fired — the consumer fires them via `trackConversion`.\n// Sales fire via `recordSale`.\n\nimport type { TrackingClient, TrackEventInput } from \"../ingest\";\nimport { getAutomaticTransactionId } from \"./automatic-transaction\";\nimport { automaticThresholdMet } from \"./automatic-trigger\";\nimport type { ConversionConfigStore } from \"./conversion-config\";\nimport { fireConversionWithConsent } from \"./conversion-firing\";\nimport { fromMinor } from \"./sales/money\";\nimport type { SupportedCurrency } from \"./sales/schema\";\nimport type { TrackingConfigRuntime } from \"./tracking-config-runtime\";\n\nfunction currentPath(): string {\n return typeof window === \"undefined\" ? \"\" : window.location.pathname;\n}\n\nexport interface ConversionAutoFire {\n /** Fire any matching automatic event-goal's conversion for a just-emitted detector event. */\n onAutomaticEvent(\n eventType: string,\n metadata: Record<string, unknown>,\n transactionScope: string,\n ): void;\n}\n\n// Bound the pre-config buffer so a page that never resolves its config (offline, bad\n// URL) can't grow it without limit.\nconst MAX_BUFFERED_EVENTS = 50;\n\n/** Build the auto-fire matcher bound to a config store. */\nexport function createConversionAutoFire(\n store:\n | Pick<ConversionConfigStore, \"listGoals\" | \"isReady\" | \"onResolve\">\n | Pick<TrackingConfigRuntime, \"listGoals\" | \"fireConversion\" | \"queueAutomaticEvent\">,\n): ConversionAutoFire {\n // Detectors fire synchronously, but the CDN config resolves async. A detector can emit\n // before a cold-cache fetch lands (an early real scroll, a fast time_on_site threshold,\n // the initial page_view) and won't re-emit on the same page. Buffer events that arrive\n // before the config is ready and replay them on the first resolve, so an early trigger\n // still converts (a beat late) instead of being silently dropped.\n const pending: Array<{\n eventType: string;\n metadata: Record<string, unknown>;\n transactionScope: string;\n }> = [];\n let subscribed = false;\n\n function fireMatching(\n eventType: string,\n metadata: Record<string, unknown>,\n transactionScope: string,\n ): void {\n for (const goal of store.listGoals()) {\n if (goal.kind !== \"event\" || !goal.firing) continue;\n if (!automaticThresholdMet(goal, eventType, metadata)) continue;\n const transactionId = getAutomaticTransactionId(transactionScope, goal.key, currentPath());\n if (\"queueAutomaticEvent\" in store) {\n store.fireConversion(goal.key, {\n transactionId,\n });\n continue;\n }\n const firing = goal.firing;\n const cents = firing.value_cents ?? null;\n const currency = firing.currency ?? null;\n fireConversionWithConsent({\n sendTo: firing.send_to,\n value: cents != null && currency ? fromMinor(cents, currency as SupportedCurrency) : null,\n currency,\n transactionId,\n });\n }\n }\n\n return {\n onAutomaticEvent(eventType, metadata, transactionScope) {\n if (\"queueAutomaticEvent\" in store) {\n store.queueAutomaticEvent(eventType, metadata, currentPath(), transactionScope);\n return;\n }\n if (store.isReady()) {\n fireMatching(eventType, metadata, transactionScope);\n return;\n }\n // Config still loading — buffer (bounded) and replay on the first resolve.\n if (pending.length < MAX_BUFFERED_EVENTS) {\n pending.push({ eventType, metadata, transactionScope });\n }\n if (!subscribed) {\n subscribed = true;\n store.onResolve(() => {\n const buffered = pending.splice(0);\n for (const event of buffered) {\n fireMatching(event.eventType, event.metadata, event.transactionScope);\n }\n });\n }\n },\n };\n}\n\n/**\n * Wrap a {@link TrackingClient} so every automatic event it tracks ALSO drives conversion\n * auto-fire. Pass the wrapped client to the `attach*` detectors. The auto-fire is best-effort\n * and never breaks ingest.\n */\nexport function withConversionAutoFire(\n client: TrackingClient,\n autoFire: ConversionAutoFire,\n): TrackingClient {\n return {\n ...client,\n trackEvent: (input: TrackEventInput) => {\n client.trackEvent(input);\n try {\n autoFire.onAutomaticEvent(input.eventType, input.metadata ?? {}, client.getSessionId());\n } catch {\n // never let conversion firing break analytics ingest\n }\n },\n };\n}\n","export type EventOutcomeRole = \"lead\" | \"engagement\" | \"navigation\" | \"diagnostic\";\nexport type EventCategory = \"page\" | \"engagement\" | \"lead\" | \"commerce\" | \"system\";\nexport type EventClientVisibility = \"simple\" | \"detailed\" | \"hidden\";\n\nexport interface EventSemantics {\n label: string;\n category: EventCategory;\n outcomeRole: EventOutcomeRole;\n clientVisibility: EventClientVisibility;\n}\n\n/**\n * Lightweight runtime event metadata. This module intentionally imports no\n * Zod schemas so dashboards and framework adapters can consume it without\n * pulling the validation registry into their bundles.\n */\nexport const EVENT_SEMANTICS = {\n page_view: {\n label: \"Page view\",\n category: \"page\",\n outcomeRole: \"navigation\",\n clientVisibility: \"simple\",\n },\n time_on_site: {\n label: \"Time on site\",\n category: \"engagement\",\n outcomeRole: \"engagement\",\n clientVisibility: \"detailed\",\n },\n specific_page_visit: {\n label: \"Key page visit\",\n category: \"engagement\",\n outcomeRole: \"engagement\",\n clientVisibility: \"detailed\",\n },\n scroll_depth: {\n label: \"Scroll depth\",\n category: \"engagement\",\n outcomeRole: \"engagement\",\n clientVisibility: \"detailed\",\n },\n multi_page_session: {\n label: \"Multi-page session\",\n category: \"engagement\",\n outcomeRole: \"engagement\",\n clientVisibility: \"detailed\",\n },\n form_start: {\n label: \"Form started\",\n category: \"engagement\",\n outcomeRole: \"engagement\",\n clientVisibility: \"simple\",\n },\n sdk_heartbeat: {\n label: \"SDK heartbeat\",\n category: \"system\",\n outcomeRole: \"diagnostic\",\n clientVisibility: \"hidden\",\n },\n page_exit: {\n label: \"Page exit\",\n category: \"engagement\",\n outcomeRole: \"diagnostic\",\n clientVisibility: \"detailed\",\n },\n form_step: {\n label: \"Form step\",\n category: \"engagement\",\n outcomeRole: \"engagement\",\n clientVisibility: \"detailed\",\n },\n // Adds to the submit it names; counting it as a lead would double every lead.\n form_details: {\n label: \"Form details added\",\n category: \"lead\",\n outcomeRole: \"engagement\",\n clientVisibility: \"detailed\",\n },\n form_submit: {\n label: \"Form submitted\",\n category: \"lead\",\n outcomeRole: \"lead\",\n clientVisibility: \"simple\",\n },\n phone_click: {\n label: \"Phone click\",\n category: \"lead\",\n outcomeRole: \"lead\",\n clientVisibility: \"simple\",\n },\n cta_click: {\n label: \"CTA click\",\n category: \"engagement\",\n outcomeRole: \"engagement\",\n clientVisibility: \"simple\",\n },\n site_link_click: {\n label: \"Left for sibling site\",\n category: \"page\",\n outcomeRole: \"navigation\",\n clientVisibility: \"detailed\",\n },\n site_link_arrival: {\n label: \"Arrived from sibling site\",\n category: \"page\",\n outcomeRole: \"navigation\",\n clientVisibility: \"simple\",\n },\n} as const satisfies Record<string, EventSemantics>;\n\nexport type EventName = keyof typeof EVENT_SEMANTICS;\n\nexport function getEventSemantics(eventName: string): EventSemantics | null {\n return eventName in EVENT_SEMANTICS ? EVENT_SEMANTICS[eventName as EventName] : null;\n}\n","// Auto page_view emission: on attach, on pushState/replaceState/popstate, and on\n// bfcache restore, all through one per-route gate. The Next provider drives the\n// gate from `usePathname` instead of patching history.\n\nimport { parse } from \"zod/mini\";\n\nimport { stripLinkParamFromSearch } from \"./cross-site-link\";\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 autoPageViewSlot().key = null;\n}\n\n// Last auto-emitted route, per document. On `globalThis`, not module state, so a\n// second copy of the bundle (script tag loaded twice) shares it.\nconst AUTO_PAGE_VIEW_SLOT = Symbol.for(\"aranova.tracking.autoPageView\");\n\ninterface AutoPageViewSlot {\n key: string | null;\n}\n\nfunction autoPageViewSlot(): AutoPageViewSlot {\n const scope = globalThis as { [AUTO_PAGE_VIEW_SLOT]?: AutoPageViewSlot };\n scope[AUTO_PAGE_VIEW_SLOT] ??= { key: null };\n return scope[AUTO_PAGE_VIEW_SLOT];\n}\n\n/**\n * Canonical route identity for page_view dedupe: path plus query, without the\n * cross-site token and with an empty query spelled one way (`\"\"`, never `\"?\"`).\n */\nexport function pageViewRouteKey(pathname: string, search: string): string {\n const query = stripLinkParamFromSearch(search);\n return query.length > 1 ? `${pathname}${query}` : pathname;\n}\n\nexport interface FireAutoPageViewOptions {\n /** Emit even for the route already emitted (a bfcache restore is a real re-entry). */\n force?: boolean;\n}\n\n/**\n * Emit the automatic `page_view` unless this route was the last one emitted; returns whether it did.\n *\n * The single gate for every automatic emitter, so extra providers, remounts, re-inits or a\n * second script copy cannot double-count one navigation (ENG-358).\n */\nexport function fireAutoPageView(\n client: TrackingClient,\n options: FireAutoPageViewOptions = {},\n): boolean {\n if (typeof window === \"undefined\") return false;\n const key = pageViewRouteKey(window.location.pathname, window.location.search);\n const slot = autoPageViewSlot();\n if (!options.force && slot.key === key) return false;\n slot.key = key;\n fireManualPageView(client);\n return true;\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\") 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 parse(pageViewMetadataSchema, {\n page: {\n title: document.title || null,\n path: window.location.pathname,\n search: stripLinkParamFromSearch(window.location.search),\n hash: window.location.hash,\n },\n referrer: 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, unconditionally.\n *\n * Automatic emitters go through `fireAutoPageView`, which dedupes per route.\n */\nexport function fireManualPageView(client: TrackingClient): void {\n if (typeof window === \"undefined\") 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 ? previousFiredUrl : 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 = 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 fireAutoPageView(client, { force: true });\n }\n\n window.addEventListener(\"pageshow\", handlePageShow);\n return () => {\n window.removeEventListener(\"pageshow\", handlePageShow);\n };\n}\n\n/**\n * Attach automatic page view tracking for a browser SPA.\n *\n * Fires for the current route, then on every history navigation and bfcache\n * restore; `fireAutoPageView` drops same-route repeats. Returns a detacher.\n */\nexport function attachAutoPageView(client: TrackingClient): () => void {\n if (typeof window === \"undefined\" || typeof history === \"undefined\") {\n return () => {};\n }\n\n function fireForCurrentRoute(): void {\n fireAutoPageView(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(fireForCurrentRoute, 0);\n }\n function patchedReplaceState(this: History, ...args: Parameters<History[\"replaceState\"]>): void {\n originalReplaceState(...args);\n setTimeout(fireForCurrentRoute, 0);\n }\n\n history.pushState = patchedPushState as History[\"pushState\"];\n history.replaceState = patchedReplaceState as History[\"replaceState\"];\n window.addEventListener(\"popstate\", fireForCurrentRoute);\n // popstate does not fire on a bfcache restore; pageshow(persisted) does.\n const detachBfcacheRestore = attachBfcacheRestore(client);\n\n fireForCurrentRoute();\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener(\"popstate\", fireForCurrentRoute);\n detachBfcacheRestore();\n };\n}\n","import * as z from \"zod/mini\";\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.strictObject({\n page: z.strictObject({\n title: z.nullable(z.string()),\n path: z.string(),\n search: z.string(),\n hash: z.string(),\n }),\n referrer: z.nullable(z.string()),\n // Optional + nullable — 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.optional(\n z.nullable(\n z.strictObject({\n w: z.number(),\n h: z.number(),\n }),\n ),\n ),\n});\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.strictObject({});\nexport type PageViewConfig = z.infer<typeof pageViewConfigSchema>;\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, non-empty trigger config, and which site's config object the SDK\n * loads (`config_site_id`; null on a legacy per-business reference).\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 configSiteId: string | null = null,\n): SdkHeartbeatMetadata {\n const automaticNames = triggers ? Object.keys(triggers.automatic) : [];\n const manualNames = triggers?.manual ? Object.keys(triggers.manual) : [];\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 config_site_id: configSiteId,\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 { getRegisteredCapabilities } from \"./capabilities\";\nimport { getConsentChoice, getConsentState } from \"./consent\";\nimport { consumeInboundLink, stripLinkParam, type OriginLink } from \"./cross-site-link\";\nimport { getEventSemantics } from \"./events/semantics\";\nimport { resetPageViewState } from \"./page-view\";\nimport type { PhoneConfig } from \"./phone-field\";\nimport { captureFbc, getFbcCookie, getFbpCookie } from \"./fbq\";\nimport {\n captureTrackingParamsFromLocation,\n createEmptyTrackingParams,\n getCookieValueFromDocument,\n getTrackingParamsFromCookieReader,\n mergeTrackingParams,\n} from \"./tracking\";\nimport type { TriggerRegistryConfig } from \"./events/registry\";\nimport { buildHeartbeatMetadata } from \"./heartbeat\";\nimport { buildLandingPayloadFields, getOrCaptureLandingParams } from \"./landing\";\nimport { getOrRotateSessionId, getVisitorId } from \"./session\";\nimport { stashUserDataFromFormFields } from \"./user-data\";\nimport type {\n TrackingClientContext,\n TrackingEnvironment,\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 * Ceiling on the exponential backoff between failed delivery attempts.\n */\nexport const MAX_RETRY_BACKOFF_MS = 60_000;\n\n/**\n * Cap on events held in the durable retry buffer. Oldest batches are dropped\n * first once this is exceeded — an unbounded buffer would eventually blow the\n * localStorage quota and take the whole SDK down with it.\n */\nexport const MAX_BUFFERED_EVENTS = 200;\n\n/**\n * localStorage key prefix for the durable retry buffer. Versioned so a future\n * shape change can't be misread as the current one.\n */\nexport const RETRY_BUFFER_KEY_PREFIX = \"aranova_tracking_pending_v1\";\n\n/**\n * What to do with a batch after an attempted delivery.\n *\n * `retry` covers the transport failing and the server saying \"later\" (408, 429,\n * 5xx). `drop` covers a permanent rejection — a 422 from a malformed payload\n * will never succeed, and retrying it forever would wedge every batch behind it.\n */\ntype DeliveryOutcome = \"ok\" | \"retry\" | \"drop\";\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 /** `siteId` of the tracking-config reference in use; reported on the heartbeat. */\n configSiteId?: string | null;\n /** When true, swallow nothing — useful for tests. */\n debug?: boolean;\n /** Phone-field config, carried for parity; the React hook reads it via context. */\n phone?: PhoneConfig;\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 (fetch, non-keepalive). */\n flush: () => Promise<void>;\n /**\n * Flush queued events through the keepalive transport so the request\n * survives document unload. Use from `pagehide`/`visibilitychange:hidden`\n * handlers — a plain `flush()` there is aborted by the browser on unload.\n */\n flushBeacon: () => 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 // Rebuilt per flush (this runs inside the payload builder), so a client\n // constructed later on a deeper route still gets reported.\n capabilities: getRegisteredCapabilities(),\n };\n}\n\nfunction readTrackingParams(): TrackingParams {\n if (typeof window === \"undefined\") 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 // Effective consent + provenance so the dashboard can tell default-granted\n // (opt-out model, no interaction) apart from an explicit choice.\n const choice = getConsentChoice();\n return {\n state: choice.state,\n source: choice.source,\n updated_at: choice.updatedAt,\n expires_at: choice.expiresAt,\n };\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<DeliveryOutcome> {\n // No fetch at all (a non-browser runtime): there is nothing to retry against,\n // and scheduling one would spin a timer forever. Give up on the batch.\n if (typeof fetch !== \"function\") return \"drop\";\n try {\n const response = 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 if (response.ok) return \"ok\";\n if (response.status === 408 || response.status === 429 || response.status >= 500) {\n return \"retry\";\n }\n // 401/403/422 and friends: the payload or the key is wrong, and will still\n // be wrong in 30 seconds.\n return \"drop\";\n } catch {\n // Network-level failure (offline, DNS, CORS preflight, aborted). Never\n // throws to the host site; the batch stays buffered for the next attempt.\n return \"retry\";\n }\n}\n\n/**\n * A batch awaiting delivery. Each one carries its OWN session snapshot: a batch\n * can outlive the session that produced it (buffered across a reload), and\n * re-sending it under whatever session is current would silently misattribute\n * those events.\n */\ninterface PendingBatch {\n session: TrackingSessionUpsertPayload;\n events: QueuedEvent[];\n}\n\nfunction retryBufferKey(apiKey: string, endpoint: string): string {\n return `${RETRY_BUFFER_KEY_PREFIX}:${apiKey}:${endpoint}`;\n}\n\nfunction readPendingBatches(key: string): PendingBatch[] {\n if (typeof window === \"undefined\") return [];\n try {\n const raw = window.localStorage.getItem(key);\n if (!raw) return [];\n const parsed: unknown = JSON.parse(raw);\n if (!Array.isArray(parsed)) return [];\n return parsed.filter(\n (entry): entry is PendingBatch =>\n typeof entry === \"object\" &&\n entry !== null &&\n \"session\" in entry &&\n Array.isArray((entry as PendingBatch).events),\n );\n } catch {\n // Storage blocked, or a corrupt/foreign value — start clean rather than\n // letting a bad read break the host site.\n return [];\n }\n}\n\n/**\n * Mirror the buffer to storage unconditionally.\n *\n * Deliberately no read-back verification: under partitioned storage a write can\n * succeed for this document and still not be visible to a verifying read, so a\n * verify-gate false-negatives exactly where durability matters most.\n */\nfunction writePendingBatches(key: string, batches: PendingBatch[]): void {\n if (typeof window === \"undefined\") return;\n try {\n if (batches.length === 0) window.localStorage.removeItem(key);\n else window.localStorage.setItem(key, JSON.stringify(batches));\n } catch {\n // Quota exceeded or storage blocked — in-memory retry still works.\n }\n}\n\n/** Drop the oldest batches until the buffer is back under the event cap. */\nfunction trimToBufferCap(batches: PendingBatch[]): PendingBatch[] {\n let total = batches.reduce((sum, batch) => sum + batch.events.length, 0);\n const trimmed = batches.slice();\n while (total > MAX_BUFFERED_EVENTS && trimmed.length > 1) {\n const dropped = trimmed.shift();\n total -= dropped ? dropped.events.length : 0;\n }\n return trimmed;\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 // A config swap is a genuinely new client, not a remount of this one — the old client's\n // route already latched the page_view gate, which would otherwise suppress the new client's.\n resetPageViewState();\n }\n globalClient = createTrackingClient(config);\n globalClientKey = key;\n return globalClient;\n}\n\n// Global/delegated captures (document/window listeners: page_exit, scroll_depth,\n// time_on_site, form_start, cta_click, phone_click, bfcache restore) must attach ONCE\n// per page-singleton client — NOT once per React provider mount. A page can mount several\n// <TrackingProvider>s (a supported island pattern: a global provider for page-level\n// triggers plus per-component providers so `useSearchParams()` doesn't opt the whole tree\n// out of static rendering). Since the client is a singleton but each provider runs its own\n// attach effect, N mounts would stack N document listeners and every interaction would emit\n// its event N times. We ref-count attaches against the singleton client's identity: the\n// first mount attaches, later mounts are no-ops, and the last unmount detaches.\ninterface ClientCaptureEntry {\n detach: () => void;\n refCount: number;\n}\nconst clientCaptureRegistry = new WeakMap<TrackingClient, ClientCaptureEntry>();\n\n/**\n * Attach a set of global/delegated captures against a singleton `client` exactly once,\n * ref-counted across provider mounts. `build` performs the actual `document`/`window`\n * listener attachment and returns a single detacher for all of them; it runs only on the\n * first mount for a given client. The returned release decrements the ref-count and runs\n * `build`'s detacher when the last holder releases. Release is idempotent — React\n * StrictMode invokes an effect's cleanup twice in dev, and a double release must not\n * double-decrement.\n */\nexport function attachClientCapturesOnce(\n client: TrackingClient,\n build: () => () => void,\n): () => void {\n let entry = clientCaptureRegistry.get(client);\n if (entry === undefined) {\n entry = { detach: build(), refCount: 0 };\n clientCaptureRegistry.set(client, entry);\n }\n entry.refCount += 1;\n\n let released = false;\n return () => {\n if (released) return;\n released = true;\n const current = clientCaptureRegistry.get(client);\n if (current === undefined) return;\n current.refCount -= 1;\n if (current.refCount <= 0) {\n current.detach();\n clientCaptureRegistry.delete(client);\n }\n };\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 // Force-detach any captures still attached to this client so their document/window\n // listeners can't leak into the next test even if a provider didn't unmount.\n const entry = clientCaptureRegistry.get(globalClient);\n if (entry !== undefined) {\n entry.detach();\n clientCaptureRegistry.delete(globalClient);\n }\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 // Batches that have been handed to the network at least once and not yet\n // acknowledged. Mirrored to localStorage so a reload — or a deploy window that\n // 5xxs every request — costs latency instead of data.\n const bufferKey = retryBufferKey(config.apiKey, endpointBase);\n let pending: PendingBatch[] = readPendingBatches(bufferKey);\n // Flushes are serialized through one chain. Batches are no longer removed from\n // the buffer before the response arrives, so two overlapping flushes would\n // send the same batch twice. Chaining (rather than a \"busy\" flag that returns\n // early) keeps `await flush()` meaning \"flushed\" for the caller.\n let flushChain: Promise<void> = Promise.resolve();\n let retryAttempt = 0;\n let firstPage: string | null = null;\n // Attribution captured synchronously at client creation (before any SPA router\n // can strip the query string). Merged into every session payload as a fallback\n // so a blocked-cookie webview or a stripped URL still attributes.\n let initialParams = createEmptyTrackingParams();\n let destroyed = false;\n\n // Consumed BEFORE identity is minted: it may supply the visitor id itself.\n let inboundLink: ReturnType<typeof consumeInboundLink> = null;\n if (typeof window !== \"undefined\") {\n try {\n inboundLink = consumeInboundLink();\n } catch {\n // never break the host site\n }\n }\n // The sibling-site session this page load arrived from; cleared on rotation.\n let originLink: OriginLink | null = inboundLink?.link ?? null;\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 = stripLinkParam(window.location.href);\n try {\n initialParams = captureTrackingParamsFromLocation();\n } catch {\n // ignore — never break the host site\n }\n // Pin this session's LANDING params now, before any SPA router strips the\n // query string (reuses the stored record when the session already has one).\n getOrCaptureLandingParams(sessionId);\n try {\n captureFbc();\n } catch {\n // ignore\n }\n }\n\n /** The live page URL as stored — never carrying the transport token. */\n function currentPageUrl(): string | null {\n return typeof window === \"undefined\" ? null : stripLinkParam(window.location.href);\n }\n\n // SDK-internal events are queued at construction, before `trackEvent` exists.\n function enqueueInternal(\n eventType: string,\n metadata: Record<string, unknown>,\n pageUrl: string | null,\n ): void {\n queue.push({\n event_type: eventType,\n page_url: pageUrl,\n metadata,\n occurred_at: new Date().toISOString(),\n });\n }\n\n // Re-read per call: a session can rotate mid-page, and the heartbeat reports\n // the page the visitor is on then, not the one the client was built on.\n function enqueueHeartbeat(): void {\n enqueueInternal(\n \"sdk_heartbeat\",\n buildHeartbeatMetadata(\n config.surface,\n sdkVersion,\n packageName,\n config.triggers ?? null,\n activeGtagIds,\n config.configSiteId ?? null,\n ),\n currentPageUrl(),\n );\n }\n\n if (initialSession.isNew) {\n enqueueHeartbeat();\n }\n\n // A rejected token is reported too: the backend never sees one, only this verdict.\n if (inboundLink !== null && typeof window !== \"undefined\") {\n const link = inboundLink.link;\n enqueueInternal(\n \"site_link_arrival\",\n {\n outcome: inboundLink.outcome,\n origin_session_id: link?.sessionId ?? null,\n origin_visitor_id: link?.visitorId ?? null,\n transit_ms: link ? Math.max(0, Date.now() - link.linkedAt) : null,\n adopted_visitor: inboundLink.adoptedVisitor,\n page: { path: window.location.pathname },\n },\n firstPage,\n );\n scheduleFlush();\n }\n\n // Anything left buffered by a previous page load goes out on the normal\n // debounce, ahead of whatever this page produces.\n if (pending.length > 0) {\n scheduleFlush();\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 originLink = null;\n }\n sessionId = rotated.id;\n // Prefer the live cookie/localStorage read; fall back to the init-time\n // snapshot (covers a webview that blocked the cookie AND a router that\n // already stripped the landing URL by flush time).\n const params = mergeTrackingParams(readTrackingParams(), initialParams);\n const context = buildContext(\n config.surface,\n sdkVersion,\n packageName,\n environment,\n activeGtagIds,\n );\n return {\n session_id: sessionId,\n visitor_id: visitorId,\n gclid: params.gclid,\n wbraid: params.wbraid,\n gbraid: params.gbraid,\n ylpcid: params.ylpcid,\n fbclid: params.fbclid,\n fbc: getFbcCookie(),\n fbp: getFbpCookie(),\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 // Landing params for the CURRENT session id — captured on the spot when\n // the session just rotated (the current URL is the rotated session's\n // landing), reused from the stored record otherwise. Keys are omitted\n // entirely when the landing isn't observable (SSR).\n ...buildLandingPayloadFields(sessionId),\n ...(originLink\n ? {\n origin_session_id: originLink.sessionId,\n origin_visitor_id: originLink.visitorId,\n origin_linked_at: new Date(originLink.linkedAt).toISOString(),\n }\n : {}),\n first_page: firstPage,\n consent_state: consentSnapshot(),\n context,\n };\n }\n\n function scheduleFlush(delayMs: number = flushIntervalMs): void {\n if (flushTimer !== null || destroyed) return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n void flush();\n }, delayMs);\n }\n\n function clearScheduledFlush(): void {\n if (flushTimer !== null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n }\n\n /** Move everything currently queued into the durable buffer. */\n function bufferQueued(): void {\n if (queue.length === 0) return;\n const events = queue.slice(0, HARD_MAX_BATCH);\n queue = queue.slice(events.length);\n pending = trimToBufferCap([...pending, { session: buildSessionPayload(), events }]);\n writePendingBatches(bufferKey, pending);\n }\n\n /**\n * Drain the durable buffer, oldest batch first.\n *\n * A batch leaves the buffer only on `ok` (acknowledged) or `drop` (permanently\n * rejected). Anything else keeps it, so a 5xx window costs latency, not data.\n * At-least-once: the backend drops a re-sent event by `tracking.events.dedupe_key`.\n */\n function flush(): Promise<void> {\n // `.catch` keeps one unexpected throw from poisoning every later flush.\n flushChain = flushChain.then(runFlush).catch(() => {});\n return flushChain;\n }\n\n async function runFlush(): Promise<void> {\n if (destroyed) return;\n clearScheduledFlush();\n bufferQueued();\n if (pending.length === 0) return;\n\n try {\n while (pending.length > 0) {\n const batch = pending[0]!;\n const body: IngestRequestBody = { session: batch.session, events: batch.events };\n // Plain fetch on a live page so the outcome is observable; only the unload\n // path needs keepalive, which outlives the document but can't be observed.\n const outcome = await postWithFetch(\n eventsUrl,\n JSON.stringify(body),\n config.apiKey,\n identityHeaders,\n false,\n );\n // Destroyed mid-send: the successor client owns the shared buffer now.\n if (destroyed) return;\n if (outcome === \"retry\") {\n retryAttempt += 1;\n return;\n }\n // By identity, not position: an unload during the await may already have\n // taken this batch, and `pending[0]` is then one that was never sent.\n pending = pending.filter((entry) => entry !== batch);\n writePendingBatches(bufferKey, pending);\n retryAttempt = 0;\n }\n } finally {\n if (pending.length > 0) {\n const backoff = Math.min(flushIntervalMs * 2 ** retryAttempt, MAX_RETRY_BACKOFF_MS);\n scheduleFlush(backoff);\n } else if (queue.length > 0) {\n // `bufferQueued` takes at most HARD_MAX_BATCH per pass, so a burst\n // larger than one batch still has events waiting.\n scheduleFlush();\n }\n }\n }\n\n function trackEvent(input: TrackEventInput): void {\n if (destroyed) return;\n if (!input || typeof input.eventType !== \"string\" || input.eventType.length === 0) return;\n\n // Enhanced conversions: a form submit is the one moment the visitor's own\n // email/phone pass through the SDK — stash them (memory only) so the\n // conversion that fires next carries user_data. NOT done for phone_click:\n // its metadata holds the BUSINESS's number, not the visitor's. An explicit\n // consent decline also skips the stash entirely — egress is already gated,\n // but a decliner's identifiers shouldn't sit in page memory either.\n if (input.eventType === \"form_submit\" && getConsentState() !== \"denied\") {\n try {\n const fields = (input.metadata as { form?: { fields?: unknown } } | null)?.form?.fields;\n if (fields) stashUserDataFromFormFields(fields, config.phone?.defaultCountry);\n } catch {\n // user-data capture must never break ingest\n }\n }\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 // A caller's href still carries `_arv` until the surface strips it after hydration.\n page_url: input.pageUrl != null ? stripLinkParam(input.pageUrl) : currentPageUrl(),\n metadata: input.metadata ?? null,\n occurred_at: occurredAt,\n });\n\n // A lead (or `form_details`, tapped just before the tab closes) is sent while the\n // page is alive: a hide or leave usually follows, and the unload path can't retry.\n if (\n queue.length >= maxQueueSize ||\n input.eventType === \"form_details\" ||\n getEventSemantics(input.eventType)?.outcomeRole === \"lead\"\n ) {\n void flush();\n } else {\n scheduleFlush();\n }\n }\n\n /**\n * Last-gasp keepalive send on pagehide/visibilitychange.\n *\n * Its outcome is unobservable, so the batch leaves the buffer optimistically;\n * re-sending one `runFlush` has in flight is intended (unload may abort that).\n */\n function flushOnUnload(): void {\n if (destroyed) return;\n bufferQueued();\n clearScheduledFlush();\n if (pending.length === 0) return;\n\n const batch = pending[0]!;\n pending = pending.slice(1);\n writePendingBatches(bufferKey, pending);\n\n const body: IngestRequestBody = { session: batch.session, events: batch.events };\n void postWithFetch(eventsUrl, JSON.stringify(body), config.apiKey, identityHeaders, true);\n }\n\n function onVisibilityChange(): void {\n if (document.visibilityState === \"hidden\") flushOnUnload();\n }\n\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"pagehide\", flushOnUnload);\n window.addEventListener(\"visibilitychange\", onVisibilityChange);\n }\n\n return {\n trackEvent,\n flush,\n flushBeacon: flushOnUnload,\n getSessionId: () => sessionId,\n getVisitorId: () => visitorId,\n destroy: () => {\n if (destroyed) return;\n // Keepalive-send what's queued, or StrictMode's dev unmount drops the first page_view.\n if (queue.length > 0) {\n flushOnUnload();\n }\n // Only after that final send: from here the successor owns the shared buffer.\n destroyed = true;\n clearScheduledFlush();\n queue = [];\n if (typeof window !== \"undefined\") {\n window.removeEventListener(\"pagehide\", flushOnUnload);\n window.removeEventListener(\"visibilitychange\", onVisibilityChange);\n }\n },\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. Runtime validation of the metadata against the event's Zod schema.\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 { parse, safeParse } from \"zod/mini\";\n\nimport { registerCapability } from \"./capabilities\";\nimport { getEventDefinition } from \"./events/registry\";\nimport type {\n EventMetadata,\n RegisteredManualEvents,\n TriggerRegistryConfig,\n} from \"./events/registry\";\nimport type { TrackingClient } from \"./ingest\";\nimport { salvageFormSubmit } from \"./form-submit-salvage\";\nimport type { TrackingConfigReference } from \"./resources/tracking-config-runtime\";\nimport type { TrackingEnvironment } from \"./types\";\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, metadata that fails its Zod schema throws, which is what you\n * want in dev. Otherwise the event is dropped with a console warning, so an\n * analytics bug never takes down the host site — except `form_submit`, which\n * is salvaged and sent tagged `invalid_shape`, because it is a lead.\n */\n debug?: boolean;\n /** The factory's environment; the lead-form ladder is loud outside production. */\n environment?: TrackingEnvironment;\n /** The factory's config reference; the lead-form ladder reads live contracts from it. */\n trackingConfig?: TrackingConfigReference;\n}\n\n// Keyed by client so `createLeadFormSubmit(useTracking(), …)` inherits the factory's settings.\nconst clientSettings = new WeakMap<object, CreateTypedClientOptions>();\n\n/** The options a typed client was created with; empty for any other object. */\nexport function typedClientSettings(client: object): CreateTypedClientOptions {\n return clientSettings.get(client) ?? {};\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 // Reported from the registry, not from the first firing: a site whose form\n // gets a handful of submissions a month is still fully wired, and keying the\n // capability on a real submit would let the presence window mark it removed\n // between them. One registration site covers next/react/browser.\n if (registry.manual?.form_submit) registerCapability(\"form_capture\");\n\n const client: TypedTrackingClient<TRegistry> = {\n trackEvent<K extends RegisteredManualEvents<TRegistry>>(\n eventType: K,\n metadata: EventMetadata<K>,\n opts?: TypedTrackEventOptions,\n ): void {\n // The API 422s the whole batch on one invalid event, so drop just this one.\n const def = getEventDefinition(eventType);\n let sent: unknown = metadata;\n if (debug) {\n parse(def.metadataSchema, metadata);\n } else if (!safeParse(def.metadataSchema, metadata).success) {\n sent = eventType === \"form_submit\" ? salvageFormSubmit(metadata) : null;\n // eslint-disable-next-line no-console\n console.warn(\n sent\n ? `[AranovaTracking] trackEvent: metadata for \"form_submit\" failed schema validation; sent salvaged`\n : `[AranovaTracking] trackEvent: metadata for \"${eventType}\" failed schema validation; event dropped`,\n );\n if (!sent) return;\n }\n\n raw.trackEvent({\n eventType: eventType as string,\n metadata: sent 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 clientSettings.set(client, options);\n return client;\n}\n","import * as z from \"zod/mini\";\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.strictObject({\n cta_name: z.string(),\n page: z.strictObject({\n path: z.string(),\n }),\n section: z.optional(z.nullable(z.string())),\n destination_url: z.optional(z.nullable(z.string())),\n // Set by auto-capture (and available to manual callers): the link target\n // and a short element descriptor (tag#id) for tying clicks to specific UI.\n href: z.optional(z.nullable(z.string())),\n element: z.optional(z.nullable(z.string())),\n});\n\nexport type CtaClickMetadata = z.infer<typeof ctaClickMetadataSchema>;\n\n/**\n * Registration config for `cta_click`.\n *\n * The event stays manually fireable; `autoCapture` additionally attaches a\n * delegated click listener that fires it for any element matching `selector`\n * (default `[data-aranova-cta]`) — tag your CTAs, get analytics for free.\n */\nexport const ctaClickConfigSchema = z.strictObject({\n autoCapture: z.optional(\n z.strictObject({\n selector: z.optional(z.string()),\n }),\n ),\n});\nexport type CtaClickConfig = z.infer<typeof ctaClickConfigSchema>;\n","import * as z from \"zod/mini\";\n\n/**\n * Registered trigger names reported by the SDK heartbeat.\n */\nexport const sdkHeartbeatTriggersSchema = z.strictObject({\n automatic: z.array(z.string()),\n manual: z.array(z.string()),\n});\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.strictObject({\n sdk_version: z.string(),\n package_name: z.nullable(z.string()),\n surface: z.enum([\"next\", \"react\", \"script\"]),\n triggers: sdkHeartbeatTriggersSchema,\n trigger_config: z.optional(z.nullable(z.record(z.string(), z.record(z.string(), z.unknown())))),\n configured_gtag_ids: z.optional(z.nullable(z.record(z.string(), z.string()))),\n // Site whose config object the SDK resolves; null on a legacy business ref.\n config_site_id: z.optional(z.nullable(z.uuid())),\n});\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.strictObject({});\nexport type SdkHeartbeatConfig = z.infer<typeof sdkHeartbeatConfigSchema>;\n","import * as z from \"zod/mini\";\n\n/**\n * Metadata for the automatic `form_start` event.\n *\n * The SDK emits this once per form when the visitor first focuses or taps\n * anything inside it.\n */\nexport const formStartMetadataSchema = z.strictObject({\n form: z.strictObject({\n id: z.string(),\n action: z.nullable(z.string()),\n }),\n page: z.strictObject({\n path: z.string(),\n }),\n});\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.strictObject({\n selector: z.optional(z.string()),\n});\n\nexport type FormStartConfig = z.infer<typeof formStartConfigSchema>;\n","import * as z from \"zod/mini\";\n\nimport {\n contractHashSchema,\n contractNameSchema,\n contractVersionSchema,\n formContractViolationsSchema,\n formExtraSchema,\n formFieldSchema,\n type JsonValue,\n} from \"./form-submit\";\n\n// In scope so the bundled .d.ts names JsonValue locally, not from a bare `src` module.\nexport type { JsonValue };\n\n/**\n * A manual `form_details`: optional answers the API folds into the `form_submit` whose\n * `submission_id` it names (add-only, never a lead). Built by `leadForms.addDetails()`.\n */\nexport const formDetailsMetadataSchema = z.strictObject({\n form: z.strictObject({\n id: z.string(),\n submission_id: z.uuidv4(),\n kind: z.optional(z.nullable(contractNameSchema)),\n fields: z.array(formFieldSchema),\n contract_version: contractVersionSchema,\n contract_hash: contractHashSchema,\n extra: formExtraSchema,\n contract_violations: formContractViolationsSchema,\n }),\n page: z.strictObject({\n path: z.string(),\n }),\n});\n\nexport type FormDetailsMetadata = z.infer<typeof formDetailsMetadataSchema>;\n\n/**\n * Registration config for `form_details`.\n *\n * Manual-only with no options; the empty object enables typed\n * `trackEvent('form_details', ...)` calls.\n */\nexport const formDetailsConfigSchema = z.strictObject({});\nexport type FormDetailsConfig = z.infer<typeof formDetailsConfigSchema>;\n","import * as z from \"zod/mini\";\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\n// `.meta({ id })` names the emitted definition: recursion forces Zod to emit a\n// `$ref`, and without an id it would be positional (`__schema0`) and churn the\n// committed snapshot whenever emission order changed.\nexport const jsonValueSchema: z.ZodMiniType<JsonValue> = z\n .lazy(() =>\n z.union([\n z.string(),\n z.number(),\n z.boolean(),\n z.null(),\n z.array(jsonValueSchema),\n z.record(z.string(), jsonValueSchema),\n ]),\n )\n .register(z.globalRegistry, { id: \"JsonSerializableValue\" });\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 value must be JSON-serializable. Client\n * integrations may intentionally capture raw submitted lead data (including name,\n * email, phone, address, selections, and free text) for first-party analytics and\n * lead operations. Submitted file data is supported after conversion to a\n * JSON-serializable representation; upload larger files separately and send a storage\n * reference. Never include credentials, authentication tokens, payment-card or bank\n * secrets, or private keys.\n *\n * No byte limit is enforced on this metadata, here or on the server — `fields` and its\n * values are unbounded. Keep inlined payloads small anyway: a submit fired just before\n * navigation flushes over `keepalive`, which browsers cap near 64 KB and drop silently.\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 */\n// ADR-0024 lead-form contract keys, all optional; a field's `role` picks how the API renders its\n// value. Kind and role are pattern-checked, not enum-checked, on the wire; the registry's types\n// in `lead-forms.gen.ts` are where a site gets the strict ones.\nexport const contractNameSchema = z\n .string()\n .check(z.regex(/^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?$/));\n// A role, or an any-of group's roles joined by `|` (the API's `form_contract_violations.role`).\nconst violationRole = z\n .string()\n .check(\n z.maxLength(200),\n z.regex(\n /^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?(\\|[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?)*$/,\n ),\n );\n\n/** Most violations one submit reports; the SDK truncates past it. */\nexport const MAX_CONTRACT_VIOLATIONS = 50;\n\n/** What the SDK's ladder found wrong with a submit it sent anyway (ADR-0024 §6). */\nexport const contractViolationSchema = z.strictObject({\n role: z.nullable(violationRole),\n rule: z.string().check(z.regex(/^[a-z][a-z_]{0,47}$/)),\n});\nexport type FormContractViolation = z.infer<typeof contractViolationSchema>;\n\n/** One answered field, shared by `form_submit` and `form_details`. */\nexport const formFieldSchema = z.strictObject({\n name: z.string(),\n type: z.string(),\n label: z.nullable(z.string()),\n value: jsonValueSchema,\n role: z.optional(z.nullable(contractNameSchema)),\n});\n\n/** Questions with no registry role, shared by `form_submit` and `form_details`. */\nexport const formExtraSchema = z.optional(\n z.nullable(\n z\n .array(\n z.strictObject({\n key: z.string().check(z.maxLength(64)),\n label: z.string().check(z.maxLength(200)),\n value: jsonValueSchema,\n }),\n )\n .check(z.maxLength(20)),\n ),\n);\n\nexport const contractVersionSchema = z.optional(z.nullable(z.int().check(z.gte(1))));\nexport const contractHashSchema = z.optional(z.nullable(z.string().check(z.maxLength(64))));\n\nexport const formContractViolationsSchema = z.optional(\n z.nullable(z.array(contractViolationSchema).check(z.maxLength(MAX_CONTRACT_VIOLATIONS))),\n);\n\nexport const formSubmitMetadataSchema = z.strictObject({\n form: z.strictObject({\n id: z.string(),\n action: z.nullable(z.string()),\n fields: z.optional(z.array(formFieldSchema)),\n kind: z.optional(z.nullable(contractNameSchema)),\n contract_version: contractVersionSchema,\n contract_hash: contractHashSchema,\n extra: formExtraSchema,\n contract_violations: formContractViolationsSchema,\n // Minted per submit; a later `form_details` names it to add answers to this lead.\n submission_id: z.optional(z.nullable(z.uuidv4())),\n }),\n page: z.strictObject({\n path: z.string(),\n }),\n});\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.strictObject({});\nexport type FormSubmitConfig = z.infer<typeof formSubmitConfigSchema>;\n","import * as z from \"zod/mini\";\n\n/**\n * Metadata for a manually fired `form_step` event.\n *\n * `form.id` must equal `form_start`/`form_submit`'s id. See\n * docs/tracking-package/installing.md#multi-step-forms-form_step for the emit rule and an example.\n */\nexport const formStepMetadataSchema = z.strictObject({\n form: z.strictObject({\n id: z.string(),\n action: z.nullable(z.string()),\n }),\n step: z.strictObject({\n id: z.string().check(z.minLength(1), z.maxLength(64), z.regex(/^[A-Za-z0-9_-]+$/)),\n // 1-based position in the flow the visitor is on right now.\n index: z.int().check(z.gte(1), z.lte(50)),\n // Null when the number of steps depends on earlier answers.\n total: z.nullable(z.int().check(z.gte(1), z.lte(50))),\n }),\n direction: z.enum([\"forward\", \"back\"]),\n page: z.strictObject({\n path: z.string(),\n }),\n});\n\nexport type FormStepMetadata = z.infer<typeof formStepMetadataSchema>;\n\n/**\n * Registration config for `form_step`.\n *\n * Manual-only with no options; the empty object enables typed\n * `trackEvent('form_step', ...)` calls.\n */\nexport const formStepConfigSchema = z.strictObject({});\nexport type FormStepConfig = z.infer<typeof formStepConfigSchema>;\n","import * as z from \"zod/mini\";\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.strictObject({\n page_count: z.int().check(z.gte(2)),\n page: z.strictObject({\n path: z.string(),\n }),\n});\n\nexport type MultiPageSessionMetadata = z.infer<typeof multiPageSessionMetadataSchema>;\n\n/**\n * Registration config for automatic `multi_page_session`.\n */\nexport const multiPageSessionConfigSchema = z.strictObject({\n pageThreshold: z.int().check(z.gte(2)),\n});\n\nexport type MultiPageSessionConfig = z.infer<typeof multiPageSessionConfigSchema>;\n","import * as z from \"zod/mini\";\n\n/**\n * Metadata for the automatic `page_exit` event.\n *\n * Fired when the user leaves a page (SPA navigation away, tab hidden, or\n * pagehide). `dwell_ms` is the ACTIVE (visible) time spent on the page segment\n * being closed — hidden time never counts, matching `time_on_site` semantics.\n * A page revisited after being hidden emits another `page_exit` for the next\n * visible segment, so summing `dwell_ms` per page/session yields total active\n * dwell without double counting.\n */\nexport const pageExitMetadataSchema = z.strictObject({\n dwell_ms: z.int().check(z.gte(0)),\n // null = left without any scroll signal; floor is 0 so a valid 0% is never\n // rejected (a single bad field 422s the whole keepalive beacon batch).\n max_scroll_percent: z.nullable(z.int().check(z.gte(0), z.lte(100))),\n // The gating baseline: the fraction of the page visible at load with\n // zero scrolling — or, for pages that grew after the post-paint snapshot\n // (skeleton/streaming renders), the first-scroll position that\n // established it. null = page had no scrollable range, or the segment\n // ended before the snapshot. Optionality is load-bearing: SDK builds\n // predating this field keep POSTing page_exit without the key —\n // requiring it would 422 whole keepalive beacon batches.\n scroll_baseline_percent: z.optional(z.nullable(z.int().check(z.gte(0), z.lte(100)))),\n page: z.strictObject({\n path: z.string(),\n }),\n});\n\nexport type PageExitMetadata = z.infer<typeof pageExitMetadataSchema>;\n\n/**\n * Registration config for automatic `page_exit`.\n *\n * SDK-internal: attached unconditionally (like `sdk_heartbeat`), so there are\n * no registration options.\n */\nexport const pageExitConfigSchema = z.strictObject({});\nexport type PageExitConfig = z.infer<typeof pageExitConfigSchema>;\n","import * as z from \"zod/mini\";\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.strictObject({\n phone_number: z.string(),\n page: z.strictObject({\n path: z.string(),\n }),\n section: z.optional(z.nullable(z.string())),\n});\n\nexport type PhoneClickMetadata = z.infer<typeof phoneClickMetadataSchema>;\n\n/**\n * Registration config for `phone_click`.\n *\n * The event stays manually fireable; `autoCapture` additionally attaches a\n * delegated click listener that fires it for any `tel:` link matching\n * `selector` (default `a[href^=\"tel:\"]`) — link your phone number, get the\n * analytics event (and, for a linked phone-click goal, the conversion) for free.\n */\nexport const phoneClickConfigSchema = z.strictObject({\n autoCapture: z.optional(\n z.strictObject({\n selector: z.optional(z.string()),\n }),\n ),\n});\nexport type PhoneClickConfig = z.infer<typeof phoneClickConfigSchema>;\n","import * as z from \"zod/mini\";\n\n/**\n * Metadata for the automatic `scroll_depth` event.\n *\n * Fired once per configured threshold per page.\n */\nexport const scrollDepthMetadataSchema = z.strictObject({\n depth_percent: z.int().check(z.gte(1), z.lte(100)),\n page: z.strictObject({\n path: z.string(),\n }),\n});\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.strictObject({\n thresholds: z.array(z.int().check(z.gte(1), z.lte(100))).check(z.minLength(1)),\n});\n\nexport type ScrollDepthConfig = z.infer<typeof scrollDepthConfigSchema>;\n","import * as z from \"zod/mini\";\n\nexport const PAGE_IDENTITY_MAX_LENGTH = 64;\nexport const PAGE_IDENTITY_PATTERN = /^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/;\n\n/** Common page identities offered as presets by operator tooling. */\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 const pageIdentitySchema = z.string().check(\n z.maxLength(PAGE_IDENTITY_MAX_LENGTH),\n z.regex(PAGE_IDENTITY_PATTERN, {\n message:\n \"page identity must be lowercase snake_case, start with a letter, and contain at most 64 characters\",\n }),\n);\n\nexport type PageIdentity = z.infer<typeof pageIdentitySchema>;\n\n/** @deprecated Use PageIdentity. Kept as a compatibility alias for existing consumers. */\nexport type SpecificPageName = PageIdentity;\n\n/** @deprecated Use pageIdentitySchema. Kept for source compatibility. */\nexport const specificPageNameSchema = pageIdentitySchema;\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.strictObject({\n page_name: specificPageNameSchema,\n page: z.strictObject({\n path: z.string(),\n }),\n});\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 stable, validated identity with a `RegExp` that\n * matches the pathname. The identity can use a preset above or a client-specific\n * lowercase snake-case slug.\n */\nexport const specificPageVisitConfigSchema = z.strictObject({\n pages: z\n .array(\n z.strictObject({\n name: specificPageNameSchema,\n pathPattern: z.custom<RegExp>((value) => value instanceof RegExp, {\n message: \"pathPattern must be a RegExp\",\n }),\n }),\n )\n .check(z.minLength(1)),\n});\n\nexport type SpecificPageVisitConfig = z.infer<typeof specificPageVisitConfigSchema>;\n","import * as z from \"zod/mini\";\n\n/** Metadata for `site_link_click`: a decorated hop to a sibling site (`destination_url` sans token). */\nexport const siteLinkClickMetadataSchema = z.strictObject({\n destination_host: z.string(),\n destination_url: z.string(),\n page: z.strictObject({\n path: z.string(),\n }),\n});\n\nexport type SiteLinkClickMetadata = z.infer<typeof siteLinkClickMetadataSchema>;\n\n/** `site_link_click` is SDK-internal; no consumer options. */\nexport const siteLinkClickConfigSchema = z.strictObject({});\nexport type SiteLinkClickConfig = z.infer<typeof siteLinkClickConfigSchema>;\n\n/**\n * Metadata for `site_link_arrival`: this page load carried a linker token.\n * `stale` / `invalid` are reported but never adopted; origin ids only for `linked`.\n */\nexport const siteLinkArrivalMetadataSchema = z.strictObject({\n outcome: z.enum([\"linked\", \"stale\", \"invalid\"]),\n origin_session_id: z.optional(z.nullable(z.string())),\n origin_visitor_id: z.optional(z.nullable(z.string())),\n // Milliseconds between the origin click and this page load.\n transit_ms: z.optional(z.nullable(z.number())),\n // True when this browser had no visitor id and took the origin's.\n adopted_visitor: z.boolean(),\n page: z.strictObject({\n path: z.string(),\n }),\n});\n\nexport type SiteLinkArrivalMetadata = z.infer<typeof siteLinkArrivalMetadataSchema>;\n\n/** `site_link_arrival` is SDK-internal; no consumer options. */\nexport const siteLinkArrivalConfigSchema = z.strictObject({});\nexport type SiteLinkArrivalConfig = z.infer<typeof siteLinkArrivalConfigSchema>;\n","import * as z from \"zod/mini\";\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.strictObject({\n duration_ms: z.int().check(z.nonnegative()),\n page: z.strictObject({\n path: z.string(),\n }),\n});\n\nexport type TimeOnSiteMetadata = z.infer<typeof timeOnSiteMetadataSchema>;\n\n/**\n * Registration config for automatic `time_on_site`.\n */\nexport const timeOnSiteConfigSchema = z.strictObject({\n thresholdSeconds: z.int().check(z.positive()),\n});\nexport type TimeOnSiteConfig = z.infer<typeof timeOnSiteConfigSchema>;\n","// `$ZodType` is the shared base both the classic and the Mini builders\n// implement, so the registry accepts a schema from either.\nimport type { $ZodType } from \"zod/v4/core\";\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 formDetailsConfigSchema,\n formDetailsMetadataSchema,\n type FormDetailsConfig,\n type FormDetailsMetadata,\n} from \"./form-details\";\nimport {\n formStepConfigSchema,\n formStepMetadataSchema,\n type FormStepConfig,\n type FormStepMetadata,\n} from \"./form-step\";\nimport {\n formSubmitConfigSchema,\n formSubmitMetadataSchema,\n type FormSubmitConfig,\n type FormSubmitMetadata,\n type JsonValue,\n} from \"./form-submit\";\nimport {\n multiPageSessionConfigSchema,\n multiPageSessionMetadataSchema,\n type MultiPageSessionConfig,\n type MultiPageSessionMetadata,\n} from \"./multi-page-session\";\nimport {\n pageExitConfigSchema,\n pageExitMetadataSchema,\n type PageExitConfig,\n type PageExitMetadata,\n} from \"./page-exit\";\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 siteLinkArrivalConfigSchema,\n siteLinkArrivalMetadataSchema,\n siteLinkClickConfigSchema,\n siteLinkClickMetadataSchema,\n type SiteLinkArrivalConfig,\n type SiteLinkArrivalMetadata,\n type SiteLinkClickConfig,\n type SiteLinkClickMetadata,\n} from \"./site-link\";\nimport {\n timeOnSiteConfigSchema,\n timeOnSiteMetadataSchema,\n type TimeOnSiteConfig,\n type TimeOnSiteMetadata,\n} from \"./time-on-site\";\nimport { EVENT_SEMANTICS, type EventName, type EventSemantics } from \"./semantics\";\n\nexport { EVENT_SEMANTICS, getEventSemantics } from \"./semantics\";\nexport type {\n EventCategory,\n EventClientVisibility,\n EventName,\n EventOutcomeRole,\n EventSemantics,\n} from \"./semantics\";\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// In scope so the emitted registry type names JsonValue locally; without it the bundled\n// .d.ts imports it from a bare `src` module. `check-dts-runtime.cjs` fails on that.\nexport type { JsonValue };\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 semantics: EVENT_SEMANTICS.page_view,\n metadataSchema: pageViewMetadataSchema,\n configSchema: pageViewConfigSchema,\n },\n time_on_site: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.time_on_site,\n metadataSchema: timeOnSiteMetadataSchema,\n configSchema: timeOnSiteConfigSchema,\n },\n specific_page_visit: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.specific_page_visit,\n metadataSchema: specificPageVisitMetadataSchema,\n configSchema: specificPageVisitConfigSchema,\n },\n scroll_depth: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.scroll_depth,\n metadataSchema: scrollDepthMetadataSchema,\n configSchema: scrollDepthConfigSchema,\n },\n multi_page_session: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.multi_page_session,\n metadataSchema: multiPageSessionMetadataSchema,\n configSchema: multiPageSessionConfigSchema,\n },\n form_start: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.form_start,\n metadataSchema: formStartMetadataSchema,\n configSchema: formStartConfigSchema,\n },\n // --- SDK-internal automatic (not consumer-configurable) ---\n sdk_heartbeat: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.sdk_heartbeat,\n metadataSchema: sdkHeartbeatMetadataSchema,\n configSchema: sdkHeartbeatConfigSchema,\n },\n page_exit: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.page_exit,\n metadataSchema: pageExitMetadataSchema,\n configSchema: pageExitConfigSchema,\n },\n // Cross-site linkage: emitted by the linker (roster comes from the site\n // config, not the trigger registry), so neither is consumer-configurable.\n site_link_click: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.site_link_click,\n metadataSchema: siteLinkClickMetadataSchema,\n configSchema: siteLinkClickConfigSchema,\n },\n site_link_arrival: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.site_link_arrival,\n metadataSchema: siteLinkArrivalMetadataSchema,\n configSchema: siteLinkArrivalConfigSchema,\n },\n // --- manual triggers ---\n form_submit: {\n kind: \"manual\",\n semantics: EVENT_SEMANTICS.form_submit,\n metadataSchema: formSubmitMetadataSchema,\n configSchema: formSubmitConfigSchema,\n },\n form_step: {\n kind: \"manual\",\n semantics: EVENT_SEMANTICS.form_step,\n metadataSchema: formStepMetadataSchema,\n configSchema: formStepConfigSchema,\n },\n form_details: {\n kind: \"manual\",\n semantics: EVENT_SEMANTICS.form_details,\n metadataSchema: formDetailsMetadataSchema,\n configSchema: formDetailsConfigSchema,\n },\n phone_click: {\n kind: \"manual\",\n semantics: EVENT_SEMANTICS.phone_click,\n metadataSchema: phoneClickMetadataSchema,\n configSchema: phoneClickConfigSchema,\n },\n cta_click: {\n kind: \"manual\",\n semantics: EVENT_SEMANTICS.cta_click,\n metadataSchema: ctaClickMetadataSchema,\n configSchema: ctaClickConfigSchema,\n },\n} as const satisfies Record<\n EventName,\n {\n kind: EventKind;\n semantics: EventSemantics;\n metadataSchema: $ZodType;\n configSchema: $ZodType;\n }\n>;\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 page_exit: PageExitMetadata;\n site_link_click: SiteLinkClickMetadata;\n site_link_arrival: SiteLinkArrivalMetadata;\n form_submit: FormSubmitMetadata;\n form_step: FormStepMetadata;\n form_details: FormDetailsMetadata;\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 page_exit: PageExitConfig;\n site_link_click: SiteLinkClickConfig;\n site_link_arrival: SiteLinkArrivalConfig;\n form_submit: FormSubmitConfig;\n form_step: FormStepConfig;\n form_details: FormDetailsConfig;\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 * Events that represent a lead/outcome in first-party tracking projections.\n */\nexport const ALL_LEAD_EVENT_NAMES: readonly EventName[] = (\n Object.entries(EVENT_REGISTRY) as Array<[EventName, (typeof EVENT_REGISTRY)[EventName]]>\n)\n .filter(([, def]) => def.semantics.outcomeRole === \"lead\")\n .map(([name]) => name);\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 form_step: EventConfig<\"form_step\">;\n form_details: EventConfig<\"form_details\">;\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> = Extract<\n keyof NonNullable<TRegistry[\"manual\"]>,\n ManualEventName\n>;\n\n/**\n * Automatic event names registered in a concrete trigger registry.\n */\nexport type RegisteredAutomaticEvents<TRegistry extends TriggerRegistryConfig> = Extract<\n keyof TRegistry[\"automatic\"],\n AutomaticEventName\n>;\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 semantics: EventSemantics;\n metadataSchema: $ZodType;\n configSchema: $ZodType;\n} {\n return EVENT_REGISTRY[name] as unknown as {\n kind: EventKind;\n semantics: EventSemantics;\n metadataSchema: $ZodType;\n configSchema: $ZodType;\n };\n}\n","// A production form_submit that fails its schema is a lead, so it is coerced, not dropped (ADR-0024 §6).\n\nimport { safeParse } from \"zod/mini\";\n\nimport {\n contractNameSchema,\n contractViolationSchema,\n formSubmitMetadataSchema,\n MAX_CONTRACT_VIOLATIONS,\n type FormContractViolation,\n type FormSubmitMetadata,\n type JsonValue,\n} from \"./events/form-submit\";\n\nconst formShape = formSubmitMetadataSchema.shape.form.shape;\nconst INVALID_SHAPE: FormContractViolation = { role: null, rule: \"invalid_shape\" };\n\n/**\n * The closest schema-valid `form_submit` to `metadata`, tagged `invalid_shape`; null when there is\n * no `form` object to salvage, or the coerced event still fails.\n */\nexport function salvageFormSubmit(metadata: unknown): FormSubmitMetadata | null {\n if (!isRecord(metadata) || !isRecord(metadata.form)) return null;\n const form = metadata.form;\n const page = isRecord(metadata.page) ? metadata.page : {};\n const reported = Array.isArray(form.contract_violations)\n ? form.contract_violations.filter((v) => safeParse(contractViolationSchema, v).success)\n : [];\n const salvaged = {\n form: {\n id: typeof form.id === \"string\" ? form.id : String(form.id ?? \"\"),\n action: typeof form.action === \"string\" ? form.action : null,\n fields: Array.isArray(form.fields) ? form.fields.filter(isRecord).map(salvageField) : [],\n ...valid(\"kind\", form.kind),\n ...valid(\"contract_version\", form.contract_version),\n ...valid(\"contract_hash\", form.contract_hash),\n ...valid(\"extra\", form.extra),\n ...valid(\"submission_id\", form.submission_id),\n contract_violations: [...reported, INVALID_SHAPE].slice(-MAX_CONTRACT_VIOLATIONS),\n },\n page: { path: typeof page.path === \"string\" ? page.path : currentPath() },\n };\n const parsed = safeParse(formSubmitMetadataSchema, salvaged);\n return parsed.success ? parsed.data : null;\n}\n\nfunction salvageField(field: Record<string, unknown>) {\n return {\n name: String(field.name ?? \"\"),\n type: String(field.type ?? \"\"),\n label: typeof field.label === \"string\" ? field.label : null,\n value: toJson(field.value),\n ...(typeof field.role === \"string\" && safeParse(contractNameSchema, field.role).success\n ? { role: field.role }\n : {}),\n };\n}\n\nfunction valid<K extends \"kind\" | \"contract_version\" | \"contract_hash\" | \"extra\" | \"submission_id\">(\n key: K,\n value: unknown,\n): Partial<Record<K, unknown>> {\n if (value == null || !safeParse(formShape[key], value).success) return {};\n return { [key]: value } as Partial<Record<K, unknown>>;\n}\n\n/** JSONB-safe: dates as ISO strings, non-finite numbers and unrepresentable values as null. */\nfunction toJson(value: unknown): JsonValue {\n if (value === null || typeof value === \"string\" || typeof value === \"boolean\") return value;\n if (typeof value === \"number\") return Number.isFinite(value) ? value : null;\n if (typeof value === \"bigint\") return value.toString();\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value.toISOString();\n if (Array.isArray(value)) return value.map(toJson);\n if (isRecord(value)) {\n return Object.fromEntries(\n Object.entries(value)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => [k, toJson(v)]),\n );\n }\n return null;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction currentPath(): string {\n return typeof window === \"undefined\" ? \"/\" : window.location.pathname;\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(client: TrackingClient, config: TimeOnSiteConfig): () => 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 validated lowercase snake-case page identity.\n// 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","// Shared SPA-navigation signal for triggers.\n//\n// history.pushState/replaceState can only be observed by monkeypatching, and\n// per-trigger patches don't compose: each trigger captured the originals at\n// attach and blindly restored them at detach, so a non-LIFO detach order\n// stomped the other trigger's patch (it silently stopped seeing SPA\n// navigations). This module installs ONE ref-counted patch: it goes in with\n// the first subscriber and is restored with the last unsubscribe, making\n// detach order irrelevant.\n//\n// Notification timing preserves the previous per-trigger behavior:\n// pushState/replaceState notify via setTimeout(0) (the URL updates\n// synchronously but frameworks render after), popstate notifies\n// synchronously. Listeners are expected to read window.location themselves\n// and pathname-guard (query-only changes are their no-op, not ours).\n\ntype HistoryChangeListener = () => void;\n\nconst listeners = new Set<HistoryChangeListener>();\nlet restorePatch: (() => void) | null = null;\n\nfunction notify(): void {\n // Set iteration tolerates concurrent delete: a listener that unsubscribes\n // itself (or another) mid-notification is simply skipped if not yet visited.\n for (const listener of listeners) listener();\n}\n\nfunction notifyDeferred(): void {\n setTimeout(notify, 0);\n}\n\nfunction installPatch(): void {\n // Keep the exact original references (no .bind) so restore reinstates the\n // identical functions — a bound copy would change identity and stack a new\n // bind wrapper on every install/restore cycle.\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n\n function patchedPushState(this: History, ...args: Parameters<History[\"pushState\"]>): void {\n originalPushState.apply(this, args);\n notifyDeferred();\n }\n function patchedReplaceState(this: History, ...args: Parameters<History[\"replaceState\"]>): void {\n originalReplaceState.apply(this, args);\n notifyDeferred();\n }\n\n history.pushState = patchedPushState as History[\"pushState\"];\n history.replaceState = patchedReplaceState as History[\"replaceState\"];\n window.addEventListener(\"popstate\", notify);\n\n restorePatch = () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener(\"popstate\", notify);\n restorePatch = null;\n };\n}\n\n/**\n * Subscribe to SPA navigations (pushState / replaceState / popstate).\n * Returns an unsubscribe function. Callers must be browser-guarded\n * (`typeof window !== \"undefined\"`) before subscribing.\n */\nexport function onHistoryChange(listener: HistoryChangeListener): () => void {\n if (listeners.size === 0) installPatch();\n listeners.add(listener);\n return () => {\n if (!listeners.delete(listener)) return;\n if (listeners.size === 0) restorePatch?.();\n };\n}\n","// Shared scroll-depth measurement for the scroll_depth and page_exit triggers.\n//\n// The metric is \"percent of page content the bottom of the viewport has\n// passed\": (scrollTop + viewportHeight) / pageHeight. Crucially, a page with\n// no scrollable range (it fits the viewport) measures as `null`, NOT 100 —\n// scroll depth is undefined there, and treating it as 100 is what used to\n// fire every threshold on short pages without any user scrolling.\n\n// Pixels of slack at the true bottom: fractional scroll positions under\n// browser zoom / DPR scaling can leave scrollTop + clientHeight 1-2px short\n// of scrollHeight, which would make 100% unreachable.\nexport const BOTTOM_EPSILON_PX = 2;\n\n/**\n * Current scroll percent of the page, or `null` when the page has no\n * scrollable range (`scrollHeight <= clientHeight`) or the metrics are\n * degenerate. scrollTop is clamped to the scrollable range so iOS\n * rubber-band overscroll can't produce out-of-range values.\n */\nexport function measureScrollPercent(): number | null {\n const root = document.scrollingElement ?? document.documentElement;\n const scrollHeight = root.scrollHeight;\n const clientHeight = root.clientHeight;\n if (scrollHeight <= 0 || clientHeight <= 0) return null;\n // Effectively unscrollable includes pages within the bottom epsilon of the\n // viewport: at scrollTop 0 the epsilon check below would already report 100,\n // turning a 1-2px-taller page into a phantom full read on any stray scroll.\n if (scrollHeight <= clientHeight + BOTTOM_EPSILON_PX) return null;\n const maxTop = scrollHeight - clientHeight;\n const scrollTop = Math.min(Math.max(root.scrollTop, 0), maxTop);\n if (scrollTop + clientHeight >= scrollHeight - BOTTOM_EPSILON_PX) return 100;\n return Math.max(1, Math.min(100, Math.round(((scrollTop + clientHeight) / scrollHeight) * 100)));\n}\n\n/**\n * Snapshot the baseline scroll percent — the fraction of the page already\n * visible with zero scrolling — once the page has actually painted.\n *\n * Double requestAnimationFrame: on attach and especially on SPA navigation\n * the new page's content and the framework's scroll-to-top haven't settled\n * yet when our code runs; measuring after two frames reads the settled\n * layout. Returns a cancel function — call it on detach or when scheduling\n * a replacement snapshot (re-navigation).\n */\nexport function scheduleBaselineSnapshot(\n onSnapshot: (baselinePercent: number | null) => void,\n): () => void {\n let rafId = requestAnimationFrame(() => {\n rafId = requestAnimationFrame(() => {\n onSnapshot(measureScrollPercent());\n });\n });\n return () => cancelAnimationFrame(rafId);\n}\n\nexport interface BaselineGate {\n /** Discard state and schedule a fresh post-paint snapshot (attach / SPA nav). */\n rebaseline(): void;\n /** Cancel any pending snapshot without touching state (call on detach). */\n cancel(): void;\n /**\n * The gating baseline, or null while the snapshot is pending or when the\n * page had no scrollable range. For pages that grow after the snapshot\n * (skeleton/streaming renders) this is the position of the first scroll\n * that found a scrollable page, not the at-load fraction.\n */\n baseline(): number | null;\n /**\n * Sample the current scroll percent through the gate. Returns null when the\n * scroll event must be ignored: the snapshot hasn't landed (SPA\n * scroll-to-top / restoration noise), the page is unscrollable, or this\n * very sample established the lazy baseline (depth counts from the NEXT\n * scroll). A non-null return guarantees baseline() is non-null.\n */\n sample(): number | null;\n}\n\n/**\n * The baseline machinery shared by the scroll_depth and page_exit triggers —\n * one implementation so the two can't drift.\n */\nexport function createBaselineGate(): BaselineGate {\n let baselinePercent: number | null = null;\n let ready = false;\n let cancelSnapshot: (() => void) | null = null;\n\n return {\n rebaseline() {\n cancelSnapshot?.();\n ready = false;\n baselinePercent = null;\n cancelSnapshot = scheduleBaselineSnapshot((b) => {\n baselinePercent = b;\n ready = true;\n });\n },\n cancel() {\n cancelSnapshot?.();\n },\n baseline() {\n return ready ? baselinePercent : null;\n },\n sample() {\n if (!ready) return null;\n if (baselinePercent === null) {\n // Lazy re-baseline: the page wasn't scrollable at snapshot time.\n baselinePercent = measureScrollPercent();\n return null;\n }\n return measureScrollPercent();\n },\n };\n}\n","// scroll_depth automatic trigger.\n//\n// Fires once per configured threshold per page, but only for depth the user\n// actually earned by scrolling:\n//\n// - A baseline percent (the fraction of the page visible with zero\n// scrolling) is snapshotted after the page paints (double-rAF, via the\n// shared BaselineGate). Thresholds at or below the baseline were \"free\"\n// at load and are suppressed for the whole pageview — a page that fits\n// the viewport (baseline null) never fires at all. This scales across\n// viewports automatically: the same page has a lower baseline on a\n// phone, so more thresholds become earnable there.\n// - Thresholds are only evaluated from scroll events, and scroll events\n// are ignored until the baseline snapshot lands — so SPA scroll-to-top /\n// popstate scroll restoration during a navigation can't fire anything.\n// Restoration that lands AFTER the snapshot (browsers defer it on\n// incrementally-rendered pages) is indistinguishable from a user scroll\n// and re-emits threshold events for depth reached on a prior visit to\n// the path; the linked gtag conversions stay deduped (session-scoped\n// per goal+path) and backend evaluation takes the session max, so the\n// cost is a duplicate analytics event, not a duplicate conversion.\n// - Lazy re-baseline: if the page wasn't scrollable at snapshot time\n// (skeleton/streaming render), the first scroll event that finds a\n// scrollable page establishes the baseline instead of firing.\n//\n// Suppression is deliberately permanent per pageview: content that grows\n// after the snapshot can't un-suppress a threshold (conservative — every\n// timing race under-counts rather than false-fires).\n//\n// The fired set resets on SPA navigation when the pathname actually changes\n// (pushState / replaceState / popstate); same-path replaceState (query param\n// updates) is a no-op via the pathname guard.\n\nimport type { ScrollDepthConfig } from \"../events/scroll-depth\";\nimport type { TrackingClient } from \"../ingest\";\nimport { onHistoryChange } from \"./navigation\";\nimport { createBaselineGate } from \"./scroll-measurement\";\n\n/**\n * Attach the automatic `scroll_depth` trigger.\n *\n * Installs a throttled scroll listener, fires each configured threshold once\n * per page (baseline-gated, real scrolls only), and returns a detach function.\n */\nexport function attachScrollDepth(client: TrackingClient, config: ScrollDepthConfig): () => 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 const gate = createBaselineGate();\n\n function checkThresholds(): void {\n const percent = gate.sample();\n if (percent === null) return;\n const baseline = gate.baseline();\n if (baseline === null) return; // unreachable when percent !== null; keeps the types honest\n for (const threshold of thresholds) {\n if (threshold <= baseline) continue; // visible at load — never earnable this pageview\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 gate.rebaseline();\n }\n\n const unsubscribeNav = onHistoryChange(resetIfPathChanged);\n window.addEventListener(\"scroll\", onScroll, { passive: true });\n\n gate.rebaseline();\n\n return () => {\n if (rafId !== null) cancelAnimationFrame(rafId);\n gate.cancel();\n unsubscribeNav();\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// Fires on the first focus, click or tap inside a <form>. A tap is a pointerdown released without\n// a pointercancel or travel past TAP_SLOP_PX: every mobile touch-scroll starts with pointerdown.\n// Once per form per page, keyed on form id/action, reset on SPA navigation.\n\nimport type { FormStartConfig } from \"../events/form-start\";\nimport type { TrackingClient } from \"../ingest\";\n\nconst TAP_SLOP_PX = 10;\n\ninterface PendingTap {\n pointerId: number;\n x: number;\n y: number;\n target: EventTarget | null;\n}\n\n/** Attach the automatic `form_start` trigger; returns a detach function. */\nexport function attachFormStart(client: TrackingClient, config: FormStartConfig): () => 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 let pendingTap: PendingTap | null = null;\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 startFrom(event.target);\n }\n\n function onClick(event: MouseEvent): void {\n if (event.button !== 0) return;\n startFrom(event.target);\n }\n\n function onPointerDown(event: PointerEvent): void {\n // A right-click opens a context menu; it is not the visitor starting the form.\n pendingTap =\n event.button === 0\n ? { pointerId: event.pointerId, x: event.clientX, y: event.clientY, target: event.target }\n : null;\n }\n\n function onPointerCancel(): void {\n pendingTap = null;\n }\n\n function onPointerUp(event: PointerEvent): void {\n const tap = pendingTap;\n pendingTap = null;\n if (!tap || tap.pointerId !== event.pointerId) return;\n if (Math.hypot(event.clientX - tap.x, event.clientY - tap.y) > TAP_SLOP_PX) return;\n // iOS Safari neither focuses a tapped <button> nor clicks a plain element, so the tap counts.\n startFrom(tap.target);\n }\n\n function startFrom(target: EventTarget | null): void {\n // Element, not HTMLElement: a tap can land on an <svg> icon in a button.\n if (!(target instanceof Element)) 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 const passiveCapture = { capture: true, passive: true };\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 document.addEventListener(\"click\", onClick, passiveCapture);\n document.addEventListener(\"pointerdown\", onPointerDown, passiveCapture);\n document.addEventListener(\"pointerup\", onPointerUp, passiveCapture);\n document.addEventListener(\"pointercancel\", onPointerCancel, passiveCapture);\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener(\"popstate\", resetIfPathChanged);\n document.removeEventListener(\"focusin\", onFocusIn);\n document.removeEventListener(\"click\", onClick, { capture: true });\n document.removeEventListener(\"pointerdown\", onPointerDown, { capture: true });\n document.removeEventListener(\"pointerup\", onPointerUp, { capture: true });\n document.removeEventListener(\"pointercancel\", onPointerCancel, { capture: true });\n };\n}\n","// page_exit automatic trigger (SDK-internal — attached unconditionally).\n//\n// Emits one `page_exit` per visible page segment with the ACTIVE dwell time\n// and the max scroll depth reached, on three leave signals:\n// - SPA navigation away (pushState / replaceState / popstate, pathname change)\n// - the tab going hidden (visibilitychange — the reliable mobile signal)\n// - pagehide (tab close / hard navigation)\n//\n// Dwell is visibility-aware like time_on_site: hidden time never counts. When\n// a hidden tab becomes visible again, the accumulator resets and the next\n// leave emits another segment — the backend sums segments per session.\n//\n// CRITICAL: on pagehide/hidden this trigger flushes the client itself via\n// `flushBeacon()` (keepalive transport). The ingest client's own\n// flush-on-pagehide listener is registered at client creation — BEFORE this\n// trigger attaches — so it fires first, sending the queue WITHOUT our\n// just-enqueued page_exit. We must therefore push it ourselves, and a plain\n// `flush()` is aborted by the browser on unload — only the keepalive beacon\n// survives, so `flushBeacon` (not `flush`) is load-bearing here.\n\nimport type { TrackingClient } from \"../ingest\";\nimport { onHistoryChange } from \"./navigation\";\nimport { createBaselineGate } from \"./scroll-measurement\";\n\n// Visible segments shorter than this are noise (no human reads a page for tens\n// of ms) and, load-bearingly, this drops the phantom emit from the\n// pagehide/visibilitychange double-fire. See emitSegment.\nconst MIN_SEGMENT_MS = 50;\n\nexport function attachPageExit(client: TrackingClient): () => void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return () => {};\n }\n\n let currentPath = window.location.pathname;\n let activeSince: number | null = document.visibilityState === \"visible\" ? Date.now() : null;\n let accumulatedMs = 0;\n let maxScrollPercent: number | null = null;\n let rafId: number | null = null;\n // Shared baseline machinery with the scroll_depth trigger: scroll events\n // are gated until the post-paint snapshot lands (SPA scroll-to-top noise\n // can't register as depth), and a page with no scrollable range keeps\n // max_scroll_percent null instead of the old phantom 100.\n const gate = createBaselineGate();\n\n function onScroll(): void {\n if (rafId !== null) return;\n rafId = requestAnimationFrame(() => {\n rafId = null;\n const percent = gate.sample();\n if (percent !== null && (maxScrollPercent === null || percent > maxScrollPercent)) {\n maxScrollPercent = percent;\n }\n });\n }\n\n function settledDwellMs(): number {\n let total = accumulatedMs;\n if (activeSince !== null) {\n total += Date.now() - activeSince;\n }\n return Math.max(0, Math.round(total));\n }\n\n function emitSegment(path: string, flush: boolean): void {\n const dwell = settledDwellMs();\n // A segment shorter than this carries no signal — skip it. This also\n // absorbs the phantom emit when pagehide and visibilitychange:hidden both\n // fire on the same unload: the first emits the real segment and closes it\n // (activeSince = null below), so the second computes ~0ms and is dropped.\n if (dwell < MIN_SEGMENT_MS) return;\n client.trackEvent({\n eventType: \"page_exit\",\n metadata: {\n dwell_ms: dwell,\n max_scroll_percent: maxScrollPercent,\n // Lets the backend tell \"scrolled to the bottom\" apart from \"the page\n // was barely scrollable\". null = unscrollable page or the segment\n // ended before the post-paint snapshot landed. For pages that grew\n // after the snapshot this is the first-scroll position, not the\n // at-load fraction (see BaselineGate.baseline).\n scroll_baseline_percent: gate.baseline(),\n page: { path },\n } as unknown as Record<string, unknown>,\n pageUrl: window.location.href,\n occurredAt: null,\n });\n if (flush) {\n // The client's own pagehide flush already ran without this event — push\n // it through the keepalive beacon so it survives unload.\n client.flushBeacon();\n }\n // Close the segment. A new one only begins on an explicit visible/navigate\n // (both set activeSince themselves) — NOT here: at pagehide the document is\n // often still \"visible\", so restarting a segment would make the following\n // visibilitychange:hidden emit a phantom ~0ms duplicate.\n accumulatedMs = 0;\n activeSince = null;\n }\n\n function onNavigate(): void {\n const newPath = window.location.pathname;\n if (newPath === currentPath) return;\n emitSegment(currentPath, false);\n currentPath = newPath;\n maxScrollPercent = null;\n gate.rebaseline();\n accumulatedMs = 0;\n activeSince = document.visibilityState === \"visible\" ? Date.now() : null;\n }\n\n function onVisibilityChange(): void {\n if (document.visibilityState === \"hidden\") {\n emitSegment(currentPath, true);\n } else {\n // Back from hidden: a fresh segment starts accruing.\n activeSince = Date.now();\n }\n }\n\n function onPageHide(): void {\n emitSegment(currentPath, true);\n }\n\n const unsubscribeNav = onHistoryChange(onNavigate);\n window.addEventListener(\"scroll\", onScroll, { passive: true });\n document.addEventListener(\"visibilitychange\", onVisibilityChange);\n window.addEventListener(\"pagehide\", onPageHide);\n\n gate.rebaseline();\n\n return () => {\n if (rafId !== null) cancelAnimationFrame(rafId);\n gate.cancel();\n unsubscribeNav();\n window.removeEventListener(\"scroll\", onScroll);\n document.removeEventListener(\"visibilitychange\", onVisibilityChange);\n window.removeEventListener(\"pagehide\", onPageHide);\n };\n}\n","// cta_click auto-capture (opt-in via the cta_click registration config).\n//\n// A single delegated click listener fires `cta_click` for any element matching\n// the configured selector (default `[data-aranova-cta]`). The event itself\n// stays `manual` in the registry — consumers can still fire it from code; this\n// trigger just removes the need to hand-instrument every button.\n//\n// CTA identity: the `data-aranova-cta` attribute value when present, else the\n// element's trimmed text (capped). `href` and a short `element` descriptor\n// (tag#id) ride along so the dashboard can tie clicks to concrete UI.\n\nimport { registerCapability } from \"../capabilities\";\nimport type { CtaClickConfig } from \"../events/cta-click\";\nimport type { TrackingClient } from \"../ingest\";\n\nexport const DEFAULT_CTA_SELECTOR = \"[data-aranova-cta]\";\n\nconst CTA_NAME_MAX_LENGTH = 120;\n\nfunction describeElement(el: Element): string {\n const tag = el.tagName.toLowerCase();\n return el.id ? `${tag}#${el.id}` : tag;\n}\n\nfunction resolveCtaName(el: Element): string {\n const explicit = el.getAttribute(\"data-aranova-cta\");\n if (explicit && explicit.trim().length > 0) return explicit.trim();\n const text = (el.textContent ?? \"\").trim().replaceAll(/\\s+/g, \" \");\n if (text.length > 0) return text.slice(0, CTA_NAME_MAX_LENGTH);\n return describeElement(el);\n}\n\n/**\n * Attach the delegated cta_click auto-capture listener. Returns a detach\n * function. No-ops (returns a noop detacher) when `autoCapture` is absent.\n */\nexport function attachCtaClickCapture(client: TrackingClient, config: CtaClickConfig): () => void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return () => {};\n }\n const autoCapture = config.autoCapture;\n if (!autoCapture) {\n return () => {};\n }\n // Registered past the guard, so the capability means \"the listener is\n // attached\", not \"the option was passed\".\n registerCapability(\"cta_click_capture\");\n const selector = autoCapture.selector ?? DEFAULT_CTA_SELECTOR;\n\n function onClick(event: MouseEvent): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n let matched: Element | null = null;\n try {\n matched = target.closest(selector);\n } catch {\n return; // invalid selector — never break the host site\n }\n if (matched === null) return;\n const href =\n matched instanceof HTMLAnchorElement ? matched.href || null : matched.getAttribute(\"href\");\n client.trackEvent({\n eventType: \"cta_click\",\n metadata: {\n cta_name: resolveCtaName(matched),\n page: { path: window.location.pathname },\n section: matched.getAttribute(\"data-aranova-section\"),\n destination_url: href,\n href,\n element: describeElement(matched),\n } as unknown as Record<string, unknown>,\n pageUrl: window.location.href,\n occurredAt: null,\n });\n }\n\n // Capture phase so navigations that stop propagation still get counted.\n document.addEventListener(\"click\", onClick, true);\n return () => {\n document.removeEventListener(\"click\", onClick, true);\n };\n}\n","// Cross-site linker: decorates links to sibling sites (roster read at click time)\n// and emits `site_link_click`. `pointerdown` covers middle-click and drag-to-tab,\n// which never reach `click`; `click` covers keyboard activation. The secondary\n// button is excluded: decorating before a context menu opens would put the token\n// behind \"Copy link address\".\n\nimport { registerCapability } from \"../capabilities\";\nimport { resolveCrossSiteLink, stripLinkParam, type CrossSiteLink } from \"../cross-site-link\";\nimport type { TrackingClient } from \"../ingest\";\n\nexport interface SiteLinkCaptureOptions {\n /** Sibling hostnames of this site; empty until the config resolves. */\n linkedHostnames: () => readonly string[];\n}\n\ninterface Hop {\n anchor: HTMLAnchorElement;\n link: CrossSiteLink;\n}\n\nfunction anchorFrom(target: EventTarget | null): HTMLAnchorElement | null {\n if (!(target instanceof Element)) return null;\n const anchor = target.closest(\"a[href]\");\n // SVG <a> exposes `href` as an SVGAnimatedString; only HTML anchors navigate.\n return anchor instanceof HTMLAnchorElement ? anchor : null;\n}\n\n/** Primary (0) and middle (1) open the link; 2 is the context menu. */\nfunction opensTheLink(event: MouseEvent): boolean {\n return event.button === 0 || event.button === 1;\n}\n\n/** Attach the linker. Returns a detach function; a noop outside the browser. */\nexport function attachSiteLinkCapture(\n client: TrackingClient,\n options: SiteLinkCaptureOptions,\n): () => void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return () => {};\n }\n registerCapability(\"cross_site_linker\");\n\n function hopFrom(event: Event): Hop | null {\n const anchor = anchorFrom(event.target);\n if (anchor === null) return null;\n const link = resolveCrossSiteLink(anchor.href, options.linkedHostnames());\n return link === null ? null : { anchor, link };\n }\n\n function onPointerDown(event: PointerEvent): void {\n if (!opensTheLink(event)) return;\n const hop = hopFrom(event);\n if (hop !== null) hop.anchor.href = hop.link.decorated;\n }\n\n function onClick(event: MouseEvent): void {\n if (!opensTheLink(event)) return;\n const hop = hopFrom(event);\n if (hop === null) return;\n hop.anchor.href = hop.link.decorated;\n client.trackEvent({\n eventType: \"site_link_click\",\n metadata: {\n destination_host: hop.anchor.hostname,\n destination_url: stripLinkParam(hop.anchor.href),\n page: { path: window.location.pathname },\n },\n pageUrl: window.location.href,\n occurredAt: null,\n });\n // The visitor is leaving; the debounce would drop this on the floor.\n client.flushBeacon();\n }\n\n document.addEventListener(\"pointerdown\", onPointerDown, true);\n document.addEventListener(\"click\", onClick, true);\n return () => {\n document.removeEventListener(\"pointerdown\", onPointerDown, true);\n document.removeEventListener(\"click\", onClick, true);\n };\n}\n","// phone_click auto-capture (opt-in via the phone_click registration config).\n//\n// A single delegated click listener fires `phone_click` for any `tel:` link\n// matching the configured selector (default `a[href^=\"tel:\"]`). The event\n// itself stays `manual` in the registry — consumers can still fire it from\n// code; this trigger just removes the need to hand-instrument every call link.\n//\n// Unlike cta_click, a captured phone_click ALSO drives conversion auto-fire: a\n// `tel:` tap is an unambiguous on-site conversion, so `conversion-autofire`\n// fires a linked `phone_click` event-goal (see thresholdMet). The number rides\n// along as E.164 (`toE164`, falling back to the raw href value) so the\n// dashboard can tie clicks to a concrete number.\n//\n// Overlap caveat: the default selectors don't collide (`a[href^=\"tel:\"]` vs\n// cta_click's `[data-aranova-cta]`). If an operator tags a `tel:` link with\n// `data-aranova-cta` AND enables both auto-captures, one tap emits two analytics\n// events — but only phone_click auto-fires a conversion (cta_click is fire-on-\n// demand via trackConversion), so there's no double-count of the conversion.\n\nimport { registerCapability } from \"../capabilities\";\nimport type { PhoneClickConfig, PhoneClickMetadata } from \"../events/phone-click\";\nimport type { TrackingClient } from \"../ingest\";\nimport { toE164 } from \"../phone\";\n\nexport const DEFAULT_TEL_SELECTOR = 'a[href^=\"tel:\"]';\n\n/** decodeURIComponent throws `URIError` on a malformed `%` sequence (e.g. `tel:+1%`),\n * which a `tel:` href can legitimately carry — return the raw value instead of letting\n * it escape the click listener (the module's no-throw contract). */\nfunction safeDecodeURIComponent(value: string): string {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n}\n\n/** The dialable number from a `tel:` href, normalized to E.164 when possible. */\nfunction resolvePhoneNumber(el: Element): string {\n const href = el instanceof HTMLAnchorElement ? el.href : (el.getAttribute(\"href\") ?? \"\");\n // Strip the scheme (and any `;`-suffixed params like `tel:+123;ext=9`) before normalizing.\n const raw = safeDecodeURIComponent(href.replace(/^tel:/i, \"\").split(\";\")[0]).trim();\n return toE164(raw) ?? raw;\n}\n\n/**\n * Attach the delegated phone_click auto-capture listener. Returns a detach\n * function. No-ops (returns a noop detacher) when `autoCapture` is absent.\n */\nexport function attachPhoneClickCapture(\n client: TrackingClient,\n config: PhoneClickConfig,\n): () => void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return () => {};\n }\n const autoCapture = config.autoCapture;\n if (!autoCapture) {\n return () => {};\n }\n // Registered past the guard, so the capability means \"the listener is\n // attached\", not \"the option was passed\".\n registerCapability(\"phone_click_capture\");\n const selector = autoCapture.selector ?? DEFAULT_TEL_SELECTOR;\n\n function onClick(event: MouseEvent): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n let matched: Element | null = null;\n try {\n matched = target.closest(selector);\n } catch {\n return; // invalid selector — never break the host site\n }\n if (matched === null) return;\n // Typed so the emitted shape is checked against the schema (not an opaque cast).\n const metadata: PhoneClickMetadata = {\n phone_number: resolvePhoneNumber(matched),\n page: { path: window.location.pathname },\n section: matched.getAttribute(\"data-aranova-section\"),\n };\n client.trackEvent({\n eventType: \"phone_click\",\n metadata,\n pageUrl: window.location.href,\n occurredAt: null,\n });\n }\n\n // Capture phase so navigations that stop propagation still get counted.\n document.addEventListener(\"click\", onClick, true);\n return () => {\n document.removeEventListener(\"click\", onClick, true);\n };\n}\n","// GENERATED FILE — DO NOT EDIT BY HAND.\n// Regenerate from apps/api with:\n// poetry run python scripts/generate_lead_form_contract.py\n// Source of truth: apps/api/src/services/lead_forms/ (ADR-0024). Pinned by\n// lead-forms.contract.test.ts and test_lead_form_contract.py.\n\nimport * as z from \"zod/mini\";\n\n/** Bump-on-change registry version; travels on every contract-typed form_submit. */\nexport const LEAD_FORM_REGISTRY_VERSION = 2;\n\nexport const LEAD_FORM_KINDS = [\"booking\", \"quote\", \"contact\", \"consult\", \"callback\"] as const;\n/** What a form is for; `form_id` stays the instance handle. */\nexport type LeadFormKind = (typeof LEAD_FORM_KINDS)[number];\n\nexport const LEAD_FORM_ROLES = [\n \"given_name\",\n \"family_name\",\n \"phone\",\n \"email\",\n \"service\",\n \"preferred_date\",\n \"preferred_time\",\n \"note\",\n \"visit\",\n \"intent\",\n \"vehicle\",\n \"drop_off\",\n \"address\",\n \"patient_type\",\n \"matter\",\n \"language\",\n \"products\",\n \"goal\",\n \"business_type\",\n \"lead_volume\",\n \"attribution\",\n] as const;\n/** A role from the registry, or `add_on:<key>` for a yes/no extra with its own label. */\nexport type LeadFormRole = (typeof LEAD_FORM_ROLES)[number] | `add_on:${string}`;\n\nexport const LEAD_FORM_PRIMITIVES = [\n \"string\",\n \"text\",\n \"enum\",\n \"multi_enum\",\n \"bool\",\n \"date\",\n \"time_window\",\n \"phone\",\n \"email\",\n \"address\",\n \"number\",\n] as const;\nexport type LeadFormPrimitive = (typeof LEAD_FORM_PRIMITIVES)[number];\n\n/** The wire schema of each primitive's value. */\n// Pure, so a bundle that only reads the version or the types drops the schemas.\nexport const leadFormPrimitiveSchemas = /* @__PURE__ */ (() =>\n ({\n string: z.string().check(z.maxLength(120)),\n text: z.string().check(z.maxLength(2000)),\n enum: z.string().check(z.regex(/^[a-z0-9-]{1,48}$/)),\n multi_enum: z.array(z.string().check(z.regex(/^[a-z0-9-]{1,48}$/))).check(z.maxLength(10)),\n bool: z.boolean(),\n date: z.string().check(z.regex(/^\\d{4}-\\d{2}-\\d{2}$/)),\n time_window: z.strictObject({\n start: z.string().check(z.regex(/^([01]\\d|2[0-3]):[0-5]\\d$/)),\n end: z.nullable(z.string().check(z.regex(/^([01]\\d|2[0-3]):[0-5]\\d$/))),\n label: z.optional(z.string().check(z.maxLength(60))),\n }),\n phone: z.string().check(z.regex(/^\\+[1-9]\\d{6,14}$/)),\n email: z.string().check(z.maxLength(320), z.regex(/^[^\\s@]+@[^\\s@]+$/)),\n address: z.strictObject({\n line1: z.string().check(z.maxLength(120)),\n line2: z.optional(z.string().check(z.maxLength(120))),\n city: z.optional(z.string().check(z.maxLength(120))),\n region: z.optional(z.string().check(z.maxLength(120))),\n postal_code: z.optional(z.string().check(z.maxLength(120))),\n country: z.optional(z.string().check(z.maxLength(120))),\n }),\n number: z.number(),\n }) as const)();\n\nexport interface LeadFormRoleOption {\n key: string;\n label: string;\n isNonAnswer?: true;\n}\n\nexport interface LeadFormRoleSpec {\n primitive: LeadFormPrimitive;\n smsLabel: string;\n contactIdentity?: true;\n uninteresting?: true;\n options?: readonly LeadFormRoleOption[];\n}\n\n/** Every role's primitive and text label; `add_on` is the template for `add_on:<key>`. */\nexport const LEAD_FORM_ROLE_SPECS = {\n given_name: { primitive: \"string\", smsLabel: \"\", contactIdentity: true },\n family_name: { primitive: \"string\", smsLabel: \"\", contactIdentity: true },\n phone: { primitive: \"phone\", smsLabel: \"\", contactIdentity: true },\n email: { primitive: \"email\", smsLabel: \"\", contactIdentity: true },\n service: { primitive: \"multi_enum\", smsLabel: \"Service\" },\n preferred_date: { primitive: \"date\", smsLabel: \"Date\" },\n preferred_time: { primitive: \"time_window\", smsLabel: \"Time\" },\n note: { primitive: \"text\", smsLabel: \"Note\" },\n visit: {\n primitive: \"enum\",\n smsLabel: \"Visit\",\n options: [\n { key: \"in-shop\", label: \"In shop\" },\n { key: \"in-home\", label: \"In home\" },\n { key: \"remote\", label: \"Remote\" },\n { key: \"on-site\", label: \"On site\" },\n ],\n },\n intent: {\n primitive: \"enum\",\n smsLabel: \"Wants\",\n options: [\n { key: \"book\", label: \"Book\" },\n { key: \"quote\", label: \"Quote\" },\n ],\n },\n vehicle: { primitive: \"string\", smsLabel: \"Vehicle\" },\n drop_off: {\n primitive: \"enum\",\n smsLabel: \"Drop-off\",\n options: [\n { key: \"wait-for-it\", label: \"Waiting\" },\n { key: \"drop-off\", label: \"Dropping off\" },\n ],\n },\n address: { primitive: \"address\", smsLabel: \"Address\" },\n patient_type: { primitive: \"enum\", smsLabel: \"Patient\" },\n matter: { primitive: \"enum\", smsLabel: \"Matter\" },\n language: { primitive: \"enum\", smsLabel: \"Language\" },\n products: { primitive: \"multi_enum\", smsLabel: \"Products\" },\n goal: { primitive: \"enum\", smsLabel: \"Goal\" },\n business_type: { primitive: \"enum\", smsLabel: \"Business\" },\n lead_volume: { primitive: \"enum\", smsLabel: \"Lead volume\" },\n add_on: { primitive: \"bool\", smsLabel: \"\" },\n attribution: { primitive: \"enum\", smsLabel: \"\", uninteresting: true },\n} as const satisfies Record<string, LeadFormRoleSpec>;\n","// The typed lead-form submit (ADR-0024 §5–6): types from the site's generated contract, the\n// payload built from role specs, and a ladder that is loud in dev and never drops a prod lead.\n\nimport { safeParse } from \"zod/mini\";\n\nimport { registerCapability } from \"../capabilities\";\nimport type { FormDetailsMetadata } from \"../events/form-details\";\nimport {\n MAX_CONTRACT_VIOLATIONS,\n type FormContractViolation,\n type FormSubmitMetadata,\n type JsonValue,\n} from \"../events/form-submit\";\nimport { typedClientSettings } from \"../ingest-typed\";\nimport { toE164 } from \"../phone\";\nimport type { LeadFormContract } from \"../resources/conversion-config\";\nimport {\n getTrackingConfigRuntime,\n type TrackingConfigReference,\n} from \"../resources/tracking-config-runtime\";\nimport type { TrackingEnvironment } from \"../types\";\nimport { uuidV4 } from \"../uuid\";\nimport {\n LEAD_FORM_REGISTRY_VERSION,\n LEAD_FORM_ROLES,\n LEAD_FORM_ROLE_SPECS,\n leadFormPrimitiveSchemas,\n type LeadFormKind,\n type LeadFormPrimitive,\n type LeadFormRole,\n} from \"./lead-forms.gen\";\n\n// --- the generated contract (`aranova-tracking forms gen`) ---------------------------------\n\n/** One required role; `requiredWhen` is the one-level condition (ADR-0024 §3). */\nexport interface LeadFormRequirementDecl {\n readonly role: LeadFormRole;\n readonly requiredWhen: { readonly role: LeadFormRole; readonly anyOf: readonly string[] } | null;\n /** An operator recorded a waiver: optional in the type, still reported when missing. */\n readonly waived?: boolean;\n}\n\n/** What one kind of form must collect, as generated for the site. */\nexport interface LeadFormKindDecl {\n /** The published contract hash; null when nothing is bound for the kind. */\n readonly hash: string | null;\n readonly requires: readonly LeadFormRequirementDecl[];\n /** Each group is satisfied by any one of its roles. */\n readonly requiresAny: readonly (readonly LeadFormRole[])[];\n /** Live option keys per choice role; deprecated keys are left out. */\n readonly options: { readonly [role: string]: readonly string[] };\n}\n\n/** The module `aranova-tracking forms gen` writes: `ARANOVA_LEAD_FORMS`. */\nexport interface LeadFormContracts {\n readonly registryVersion: number;\n readonly siteId: string | null;\n readonly contracts: { readonly [K in LeadFormKind]?: LeadFormKindDecl };\n}\n\n// --- value and field types -----------------------------------------------------------------\n\n/** `{ start: \"HH:MM\", end: \"HH:MM\" | null, label? }`, business-local; pinned to the zod by test. */\nexport interface LeadFormTimeWindow {\n start: string;\n end: string | null;\n label?: string;\n}\n\nexport interface LeadFormAddress {\n line1: string;\n line2?: string;\n city?: string;\n region?: string;\n postal_code?: string;\n country?: string;\n}\n\ntype Specs = typeof LEAD_FORM_ROLE_SPECS;\ntype RegistryRole = (typeof LEAD_FORM_ROLES)[number];\ntype AddOnRole = `add_on:${string}`;\n\ntype SpecOf<R> = R extends AddOnRole ? Specs[\"add_on\"] : R extends keyof Specs ? Specs[R] : never;\ntype PrimitiveOf<R> = SpecOf<R>[\"primitive\"];\n\ntype EmptyKind = {\n hash: null;\n requires: readonly [];\n requiresAny: readonly [];\n options: Record<string, never>;\n};\ntype KindOf<\n C extends LeadFormContracts,\n K extends LeadFormKind,\n> = C[\"contracts\"][K] extends LeadFormKindDecl ? C[\"contracts\"][K] : EmptyKind;\n\n/** The site's live keys for a choice role, else the registry's fixed ones, else any key. */\ntype OptionKey<D extends LeadFormKindDecl, R> = R extends keyof D[\"options\"]\n ? D[\"options\"][R][number]\n : SpecOf<R> extends { options: readonly { key: infer Key }[] }\n ? Key\n : string;\n\ninterface PrimitiveValues {\n string: string;\n text: string;\n phone: string;\n email: string;\n date: string;\n bool: boolean;\n number: number;\n time_window: LeadFormTimeWindow;\n address: LeadFormAddress;\n}\n\n/** The value a role takes: option keys for a choice role, never labels. */\nexport type LeadFormValue<D extends LeadFormKindDecl, R> =\n PrimitiveOf<R> extends \"enum\"\n ? OptionKey<D, R>\n : PrimitiveOf<R> extends \"multi_enum\"\n ? readonly OptionKey<D, R>[]\n : PrimitiveValues[Extract<PrimitiveOf<R>, keyof PrimitiveValues>];\n\ntype Blank = null | undefined;\ntype RequiredRoles<D extends LeadFormKindDecl> = Exclude<\n Extract<D[\"requires\"][number], { readonly requiredWhen: null }>,\n { readonly waived: true }\n>[\"role\"];\n/** The roles one kind must always send (unconditional, not waived), per the generated contract. */\nexport type LeadFormRequiredRole<\n C extends LeadFormContracts,\n K extends LeadFormKind,\n> = RequiredRoles<KindOf<C, K>>;\ntype AnyOf<D extends LeadFormKindDecl, G extends readonly string[]> = {\n [R in G[number]]: { [P in R]: LeadFormValue<D, P> };\n}[G[number]];\ntype AllGroups<D extends LeadFormKindDecl, Gs> = Gs extends readonly [\n infer H extends readonly string[],\n ...infer T,\n]\n ? AnyOf<D, H> & AllGroups<D, T>\n : unknown;\n\n/**\n * The `fields` of one kind's submit: unconditionally required roles are required keys, each\n * any-of group needs one of its roles, everything else in the registry is optional (blank is\n * omitted); a `required_when` role is optional here and checked at runtime.\n */\nexport type LeadFormFields<C extends LeadFormContracts, K extends LeadFormKind> = {\n [R in RequiredRoles<KindOf<C, K>>]: LeadFormValue<KindOf<C, K>, R>;\n} & {\n [R in Exclude<RegistryRole, RequiredRoles<KindOf<C, K>>>]?:\n | LeadFormValue<KindOf<C, K>, R>\n | Blank;\n} & { [R in AddOnRole]?: boolean | Blank } & AllGroups<KindOf<C, K>, KindOf<C, K>[\"requiresAny\"]>;\n\n/** Who the lead is never changes after the submit; an email it lacked is the one exception. */\ntype DetailsRole = Exclude<\n RegistryRole,\n Exclude<\n { [R in RegistryRole]: Specs[R] extends { contactIdentity: true } ? R : never }[RegistryRole],\n \"email\"\n >\n>;\n\n/** Every non-identity role of one kind, all optional: details add to a sent lead, so none is owed. */\nexport type LeadFormDetailsFields<C extends LeadFormContracts, K extends LeadFormKind> = {\n [R in DetailsRole]?: LeadFormValue<KindOf<C, K>, R> | Blank;\n} & { [R in AddOnRole]?: boolean | Blank };\n\nexport interface LeadFormExtraField {\n key: string;\n label: string;\n value: JsonValue;\n}\n\nexport interface LeadFormSubmitInput<C extends LeadFormContracts, K extends LeadFormKind> {\n /** The `<form id>`, so `form_start` and `form_submit` line up. */\n formId: string;\n kind: K;\n fields: LeadFormFields<C, K>;\n /** Questions with no registry role yet: shown beside the contract, never texted. */\n extra?: LeadFormExtraField[];\n action?: string | null;\n}\n\n/** Names the lead one `submit()` sent, so `addDetails()` can add to it. */\nexport interface LeadFormSubmission<K extends LeadFormKind = LeadFormKind> {\n submissionId: string;\n formId: string;\n kind: K;\n}\n\nexport interface LeadFormDetailsInput<C extends LeadFormContracts, K extends LeadFormKind> {\n fields: LeadFormDetailsFields<C, K>;\n extra?: LeadFormExtraField[];\n}\n\n/** `no_fields`: every answer was blank, so nothing was sent. */\nexport type LeadFormDetailsResult = { sent: true } | { sent: false; reason: \"no_fields\" };\n\n/** Anything that can send a `form_submit`: `useTracking()` with `form_submit` registered. */\nexport interface LeadFormTracking {\n trackEvent(eventType: \"form_submit\", metadata: FormSubmitMetadata): void;\n}\n\nexport interface LeadFormSubmitOptions {\n /** Defaults to the tracking client's `createTracking({ debug })`. Throws on a violation. */\n debug?: boolean;\n /** Defaults to the tracking client's; `development` logs every violation. */\n environment?: TrackingEnvironment;\n /** Defaults to the tracking client's; validation and the hash use its live contracts. */\n trackingConfig?: TrackingConfigReference;\n}\n\nexport interface LeadFormSubmitter<C extends LeadFormContracts> {\n /** Send one typed `form_submit`; returns the metadata sent, violations included, and its handle. */\n submit<K extends LeadFormKind>(\n input: LeadFormSubmitInput<C, K>,\n ): FormSubmitMetadata & { submission: LeadFormSubmission<K> };\n /** Add optional answers to a sent lead as one `form_details`; never a second lead. */\n addDetails<K extends LeadFormKind>(\n submission: LeadFormSubmission<K>,\n input: LeadFormDetailsInput<C, K>,\n ): LeadFormDetailsResult;\n}\n\n// --- runtime -------------------------------------------------------------------------------\n\ninterface Checked {\n requires: readonly LeadFormRequirementDecl[];\n requiresAny: readonly (readonly string[])[];\n options: Readonly<Record<string, readonly string[]>>;\n}\n\nconst KNOWN_ROLES = new Set<string>(LEAD_FORM_ROLES);\nconst ROLE_ORDER = new Map<string, number>(LEAD_FORM_ROLES.map((role, index) => [role, index]));\nconst ADD_ON = /^add_on:[a-z0-9_-]{1,48}$/;\nconst CONTRACT_NAME = /^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?$/;\nconst INPUT_TYPES: Record<LeadFormPrimitive, string> = {\n string: \"text\",\n text: \"textarea\",\n enum: \"select\",\n multi_enum: \"select\",\n bool: \"checkbox\",\n date: \"date\",\n time_window: \"time\",\n phone: \"tel\",\n email: \"email\",\n address: \"address\",\n number: \"number\",\n};\n\n/**\n * Bind a tracking client to the site's generated contracts (`ARANOVA_LEAD_FORMS`).\n *\n * @example\n * ```ts\n * const leadForms = createLeadFormSubmit(useTracking(), ARANOVA_LEAD_FORMS);\n * const { submission } = leadForms.submit({ formId: \"booking-form\", kind: \"booking\", fields });\n * leadForms.addDetails(submission, { fields: { note } });\n * ```\n */\nexport function createLeadFormSubmit<C extends LeadFormContracts>(\n tracking: LeadFormTracking,\n contracts: C,\n options: LeadFormSubmitOptions = {},\n): LeadFormSubmitter<C> {\n registerCapability(\"typed_form_submit\");\n const settings = { ...typedClientSettings(tracking), ...options };\n return {\n submit(input) {\n const fields = presentFields(input.fields as Record<string, unknown>);\n const { violations, hash } = judgeLeadForm(\n input.kind,\n input.formId,\n fields,\n contracts,\n settings,\n );\n const submission = { submissionId: uuidV4(), formId: input.formId, kind: input.kind };\n const metadata: FormSubmitMetadata = {\n form: {\n id: input.formId,\n action: input.action ?? null,\n fields: fields.map(fieldEntry),\n kind: input.kind,\n contract_version: LEAD_FORM_REGISTRY_VERSION,\n ...(hash ? { contract_hash: hash } : {}),\n ...(input.extra?.length ? { extra: input.extra } : {}),\n ...(violations.length > 0\n ? { contract_violations: violations.slice(0, MAX_CONTRACT_VIOLATIONS) }\n : {}),\n submission_id: submission.submissionId,\n },\n page: { path: currentPath() },\n };\n tracking.trackEvent(\"form_submit\", metadata);\n return { ...metadata, submission };\n },\n addDetails(submission, input) {\n const fields = presentFields(input.fields as Record<string, unknown>);\n const extra = (input.extra ?? []).filter((item) => !isBlank(item.value));\n if (fields.length === 0 && extra.length === 0) return { sent: false, reason: \"no_fields\" };\n const { violations, hash } = judgeLeadForm(\n submission.kind,\n submission.formId,\n fields,\n contracts,\n settings,\n { partial: true },\n );\n const metadata: FormDetailsMetadata = {\n form: {\n id: submission.formId,\n submission_id: submission.submissionId,\n kind: submission.kind,\n fields: fields.map(fieldEntry),\n contract_version: LEAD_FORM_REGISTRY_VERSION,\n ...(hash ? { contract_hash: hash } : {}),\n ...(extra.length > 0 ? { extra } : {}),\n ...(violations.length > 0\n ? { contract_violations: violations.slice(0, MAX_CONTRACT_VIOLATIONS) }\n : {}),\n },\n page: { path: currentPath() },\n };\n // Typed for `form_submit` only so a client without `form_details` registered still binds;\n // the typed client validates by event name, not by registration.\n (tracking as unknown as DetailsTracking).trackEvent(\"form_details\", metadata);\n return { sent: true };\n },\n };\n}\n\ninterface DetailsTracking {\n trackEvent(eventType: \"form_details\", metadata: FormDetailsMetadata): void;\n}\n\nfunction fieldEntry([role, value]: [string, JsonValue]) {\n return {\n name: role,\n type: INPUT_TYPES[primitiveOf(role) ?? \"string\"],\n label: labelOf(role),\n value,\n role,\n };\n}\n\nfunction currentPath(): string {\n return typeof window === \"undefined\" ? \"/\" : window.location.pathname;\n}\n\n/**\n * The enforcement ladder for one submit: its violations against the live contract (the generated\n * one until the config loads) and the hash to send. Logs in development; throws under debug.\n * Shared with the SDK's booking submit; `partial` (a `form_details`) skips what is owed.\n */\nexport function judgeLeadForm(\n kind: LeadFormKind,\n formId: string,\n fields: Array<[string, JsonValue]>,\n contracts: LeadFormContracts | undefined,\n settings: LeadFormSubmitOptions,\n { partial = false }: { partial?: boolean } = {},\n): { violations: FormContractViolation[]; hash: string | null } {\n const generated: LeadFormKindDecl | undefined = contracts?.contracts[kind];\n const live = liveContract(settings.trackingConfig, kind);\n const violations = check(live === undefined ? generated : fromLive(live), fields, partial);\n const liveHash = live === undefined ? undefined : (live?.contract_hash ?? null);\n if (\n contracts &&\n ((liveHash !== undefined && (generated?.hash ?? null) !== liveHash) ||\n contracts.registryVersion !== LEAD_FORM_REGISTRY_VERSION)\n ) {\n violations.push({ role: null, rule: \"contract_behind\" });\n }\n if (violations.length > 0 && (settings.debug || settings.environment === \"development\")) {\n const message = `[AranovaTracking] lead form \"${formId}\" (${kind}) breaks its contract: ${violations\n .map((v) => `${v.rule}${v.role ? ` (${v.role})` : \"\"}`)\n .join(\", \")}`;\n // eslint-disable-next-line no-console\n console.error(message);\n if (settings.debug) throw new Error(message);\n }\n return {\n violations,\n hash: liveHash === undefined ? (generated?.hash ?? null) : liveHash,\n };\n}\n\n/** `undefined` until the config loads; `null` once it has and nothing is bound for `kind`. */\nfunction liveContract(\n ref: TrackingConfigReference | undefined,\n kind: LeadFormKind,\n): LeadFormContract | null | undefined {\n const leadForms = ref ? getTrackingConfigRuntime(ref).leadForms() : null;\n if (!leadForms) return undefined;\n return leadForms.contracts.find((c) => c.form_kind === kind) ?? null;\n}\n\nfunction fromLive(live: LeadFormContract | null): Checked | undefined {\n if (!live) return undefined;\n return {\n requires: live.requires.map((r) => ({\n role: r.role as LeadFormRole,\n requiredWhen: r.required_when\n ? { role: r.required_when.role as LeadFormRole, anyOf: r.required_when.any_of }\n : null,\n })),\n requiresAny: live.requires_any.map((g) => g.roles),\n // Deprecated keys still resolve: a cached form may send them (ADR-0024 §7).\n options: Object.fromEntries(live.options.map((o) => [o.role, o.options.map((x) => x.key)])),\n };\n}\n\n/** Answered fields in registry order, normalized; blanks are omitted. */\nfunction presentFields(fields: Record<string, unknown>): Array<[string, JsonValue]> {\n return Object.entries(fields)\n .map(([role, value]): [string, unknown] => [role, normalize(role, value)])\n .filter((entry): entry is [string, JsonValue] => !isBlank(entry[1]))\n .sort(([a], [b]) => (ROLE_ORDER.get(a) ?? 999) - (ROLE_ORDER.get(b) ?? 999));\n}\n\nfunction normalize(role: string, value: unknown): unknown {\n if (typeof value !== \"string\") return value;\n const trimmed = value.trim();\n const primitive = primitiveOf(role);\n if (primitive === \"phone\") return toE164(trimmed) ?? trimmed;\n if (primitive === \"email\") return trimmed.toLowerCase();\n return primitive === \"text\" ? value : trimmed;\n}\n\nfunction isBlank(value: unknown): boolean {\n return (\n value === undefined ||\n value === null ||\n (typeof value === \"string\" && value.trim() === \"\") ||\n (Array.isArray(value) && value.length === 0)\n );\n}\n\nfunction check(\n contract: Checked | undefined,\n fields: Array<[string, JsonValue]>,\n partial: boolean,\n) {\n const answered = new Map(fields);\n const violations: FormContractViolation[] = [];\n for (const [role, value] of fields) {\n const primitive = primitiveOf(role);\n if (!primitive) {\n violations.push({ role: CONTRACT_NAME.test(role) ? role : null, rule: \"unknown_role\" });\n continue;\n }\n if (!safeParse(leadFormPrimitiveSchemas[primitive], value).success) {\n violations.push({ role, rule: \"invalid_value\" });\n continue;\n }\n const allowed = contract?.options[role] ?? registryOptions(role);\n if (allowed?.length && optionKeys(value).some((key) => !allowed.includes(key))) {\n violations.push({ role, rule: \"unknown_option\" });\n }\n }\n if (partial) return violations;\n for (const { role, requiredWhen } of contract?.requires ?? []) {\n const owing =\n requiredWhen === null ||\n optionKeys(answered.get(requiredWhen.role)).some((key) => requiredWhen.anyOf.includes(key));\n if (owing && !answered.has(role)) violations.push({ role, rule: \"missing_required\" });\n }\n for (const group of contract?.requiresAny ?? []) {\n if (!group.some((role) => answered.has(role))) {\n violations.push({ role: group.join(\"|\"), rule: \"missing_any_of\" });\n }\n }\n return violations;\n}\n\nfunction primitiveOf(role: string): LeadFormPrimitive | null {\n if (ADD_ON.test(role)) return \"bool\";\n return KNOWN_ROLES.has(role) ? LEAD_FORM_ROLE_SPECS[role as RegistryRole].primitive : null;\n}\n\nfunction labelOf(role: string): string | null {\n if (ADD_ON.test(role)) return role.slice(\"add_on:\".length).replaceAll(/[_-]+/g, \" \");\n return KNOWN_ROLES.has(role) ? LEAD_FORM_ROLE_SPECS[role as RegistryRole].smsLabel || null : null;\n}\n\nfunction registryOptions(role: string): readonly string[] | undefined {\n const spec = KNOWN_ROLES.has(role) ? LEAD_FORM_ROLE_SPECS[role as RegistryRole] : undefined;\n return spec && \"options\" in spec ? spec.options.map((o) => o.key) : undefined;\n}\n\nfunction optionKeys(value: JsonValue | undefined): string[] {\n if (typeof value === \"string\") return [value];\n return Array.isArray(value) ? value.filter((v): v is string => typeof v === \"string\") : [];\n}\n\n// --- helpers -------------------------------------------------------------------------------\n\n/**\n * A picked slot as `preferred_date` + `preferred_time`, in the business's wall time.\n * Takes `YYYY-MM-DD[T ]HH:MM` with no offset; a `Z`/`±HH:MM` instant or a `Date` would be re-zoned.\n */\nexport function slotFields(dateTime: string): {\n preferred_date: string;\n preferred_time: LeadFormTimeWindow;\n} {\n const written = /^(\\d{4}-\\d{2}-\\d{2})[T ](\\d{2}:\\d{2})(?::\\d{2}(?:\\.\\d+)?)?$/.exec(dateTime);\n if (!written) {\n throw new TypeError(\n `slotFields needs the slot's wall time without an offset (\"2026-10-13T09:30\"), got \"${dateTime}\"`,\n );\n }\n return { preferred_date: written[1]!, preferred_time: { start: written[2]!, end: null } };\n}\n\n/** A `time_window` value: `timeWindow(\"09:00\", \"12:00\", \"Morning\")`; an exact time has no end. */\nexport function timeWindow(start: string, end?: string | null, label?: string): LeadFormTimeWindow {\n return { start, end: end ?? null, ...(label ? { label } : {}) };\n}\n","import { registerCapability } from \"../../capabilities\";\nimport {\n MAX_CONTRACT_VIOLATIONS,\n type FormSubmitMetadata,\n type JsonValue,\n} from \"../../events/form-submit\";\nimport { typedClientSettings } from \"../../ingest-typed\";\nimport {\n buildBookingHandoffUrl,\n getBookingIntegration,\n type BookingOptionKey,\n} from \"../../integrations-public\";\nimport {\n LEAD_FORM_REGISTRY_VERSION,\n type LEAD_FORM_ROLE_SPECS,\n type LEAD_FORM_ROLES,\n type LeadFormKind,\n} from \"../../lead-forms/lead-forms.gen\";\nimport {\n judgeLeadForm,\n type LeadFormContracts,\n type LeadFormFields,\n type LeadFormRequiredRole,\n} from \"../../lead-forms/submit\";\nimport { toE164 } from \"../../phone\";\nimport { peekSessionId } from \"../../session\";\nimport { uuidV4 } from \"../../uuid\";\nimport type { BookingMode, ConversionConfigStore } from \"../conversion-config\";\n\nexport { BOOKING_MODES, type BookingMode } from \"../conversion-config\";\nimport { AranovaApiError } from \"../http/errors\";\nimport { resolveTrackingConfigUrl, type TrackingConfigRuntime } from \"../tracking-config-runtime\";\nimport { salesRequest, type SalesTransportConfig } from \"./transport\";\n\n// The wire vocabulary; `bookings.test.tsx` pins each list to `contracts/tracking-bookings.json`.\nexport const BOOKING_RESPONSE_STATUSES = [\"created\", \"pending\", \"failed\"] as const;\n/** Developer-only outcomes for a simulated create; the production API ignores them. */\nexport const BOOKING_SIMULATIONS = [\"created\", \"rejected\", \"unknown\", \"failed\"] as const;\nexport const BOOKING_SIMULATE_HEADER = \"X-Aranova-Simulate\";\nexport const BOOKING_DROP_OFF_OPTIONS = [\"waitForIt\", \"dropOff\"] as const;\nexport const BOOKING_DETAILS_STATUSES = [\"updated\", \"queued\", \"notified_owner\"] as const;\nexport const BOOKING_ERROR_CODES = {\n unavailable: \"booking_unavailable\",\n stale: \"booking_config_stale\",\n rejected: \"booking_rejected\",\n attemptMismatch: \"booking_attempt_mismatch\",\n detailsConflict: \"idempotency_conflict\",\n} as const;\n\ntype BookingResponseStatus = (typeof BOOKING_RESPONSE_STATUSES)[number];\nexport type BookingDropOff = (typeof BOOKING_DROP_OFF_OPTIONS)[number];\nexport type BookingSimulation = (typeof BOOKING_SIMULATIONS)[number];\nexport type BookingDetailsStatus = (typeof BOOKING_DETAILS_STATUSES)[number];\n\n/** `POST /tracking/bookings` body; its keys are pinned against the contract by the test. */\nexport interface BookingRequestBody {\n idempotency_key: string;\n session_id: string;\n config_version: number;\n start_date: string;\n start_hour: number;\n first_name: string;\n last_name: string;\n phone: string;\n email: string | null;\n vehicle: { year: number; make: string; model: string } | null;\n drop_off: BookingDropOff | null;\n services: string[];\n notes: string | null;\n marketing_sms_consent: boolean;\n page_path: string | null;\n}\n\n/** `POST /tracking/bookings` and `GET /tracking/bookings/{id}` answer. */\nexport interface BookingResponseBody {\n id: string;\n status: BookingResponseStatus;\n request_code: string | null;\n fallback_url: string | null;\n /** Absent from an API older than this SDK: read as `live`. */\n mode?: BookingMode;\n}\n\n/** `POST /tracking/bookings/{id}/details` body; its keys are pinned against the contract. */\nexport interface BookingDetailsRequestBody {\n idempotency_key: string;\n vehicle: { year: number; make: string; model: string } | null;\n notes: string | null;\n heard_via: string | null;\n}\n\n/** `POST /tracking/bookings/{id}/details` answer. */\nexport interface BookingDetailsResponseBody {\n id: string;\n status: BookingDetailsStatus;\n}\n\n/** Added after the booking: the vehicle (when the rules defer it), notes, or both. */\nexport interface BookingDetailsInput {\n vehicle?: { year: number; make: string; model: string } | null;\n notes?: string | null;\n /**\n * The site's own \"How did you hear about us?\" answer, verbatim — the same values the form's\n * `form_submit` `heardVia` field carries, so one answer reports the same whether it is asked\n * on the form or on the success screen. Ours for reporting: never sent to the shop.\n */\n heardVia?: string | null;\n}\n\nexport interface BookingDetailsOptions {\n /** The `attemptKey` the booking was submitted with; only that submit may add to it. */\n attemptKey: string;\n}\n\nexport type BookingDetailsOutcome =\n /**\n * `updated`: on the shop's request. `queued`: still on its way. `notified_owner`: the shop's\n * request could no longer take them, so the owner was texted. All three mean \"saved\".\n */\n | { status: BookingDetailsStatus; id: string }\n /**\n * Not saved. `invalid`: a bad vehicle or note. `mismatch`: not this booking's attempt key.\n * `conflict`: other details were already saved. `not_found`: no such booking for this key.\n * `failed`: the network or the API failed; worth a retry.\n */\n | { status: BookingDetailsFailure; id: string; message: string };\n\nexport type BookingDetailsFailure = \"invalid\" | \"mismatch\" | \"conflict\" | \"not_found\" | \"failed\";\n\nfunction detailsFailure(error: unknown): BookingDetailsFailure {\n if (!(error instanceof AranovaApiError)) return \"failed\";\n if (error.code === BOOKING_ERROR_CODES.attemptMismatch) return \"mismatch\";\n if (error.code === BOOKING_ERROR_CODES.detailsConflict) return \"conflict\";\n if (error.status === 404) return \"not_found\";\n return error.status === 422 ? \"invalid\" : \"failed\";\n}\n\n/** What the booking form collected. Dates and hours are the shop's wall time. */\nexport interface BookingInput {\n /** `YYYY-MM-DD`, one of `bookableSlots(...)`'s dates. */\n startDate: string;\n /** A start hour offered on that date (8 = 08:00). */\n startHour: number;\n firstName: string;\n /** Required: the shop's system needs it, and a single-word name has none to split off. */\n lastName: string;\n /** Mobile number; the shop texts its confirmation to it. */\n phone: string;\n email?: string | null;\n vehicle?: BookingVehicle | null;\n /** Ask only when the rules list drop-off options. */\n dropOff?: BookingDropOff | null;\n /** Option keys from `useBookingIntegration(...).options`. */\n services: readonly string[];\n notes?: string | null;\n /**\n * The customer's own opt-in to marketing texts. Never default it on (CASL). Service texts\n * about this booking need no opt-in and are always on.\n */\n marketingSmsConsent?: boolean;\n}\n\nexport interface BookingVehicle {\n year: number;\n make: string;\n model: string;\n}\n\ntype BookingFields<C extends LeadFormContracts> = LeadFormFields<C, \"booking\">;\ntype Asked<C extends LeadFormContracts, R extends string, P extends string, V> =\n R extends LeadFormRequiredRole<C, \"booking\"> ? { [K in P]: V } : { [K in P]?: V | null };\n\n/** A service key the site's generated `booking` contract names, or one the live config offered. */\nexport type BookingServiceKey<C extends LeadFormContracts> =\n | NonNullable<BookingFields<C>[\"service\"]>[number]\n | BookingOptionKey;\n\n/**\n * `BookingInput` typed by the site's generated contract (`ARANOVA_LEAD_FORMS`): the drop-off,\n * vehicle and email the shop's rules require are required here, and services are its keys.\n */\nexport type BookingInputFor<C extends LeadFormContracts> = Omit<\n BookingInput,\n \"services\" | \"dropOff\" | \"vehicle\" | \"email\"\n> & { services: readonly BookingServiceKey<C>[] } & Asked<\n C,\n \"drop_off\",\n \"dropOff\",\n BookingDropOff\n > &\n Asked<C, \"vehicle\", \"vehicle\", BookingVehicle> &\n Asked<C, \"email\", \"email\", string>;\n\n/** The form's `form_submit`, sent once per booking attempt and tied to this visit. */\nexport interface BookingFormSubmit {\n /** The site's tracking client (`useTracking()`): the booking takes the visit it reports. */\n tracking: {\n trackEvent(eventType: \"form_submit\", metadata: FormSubmitMetadata): void;\n getSessionId(): string;\n };\n /** The `<form id>`, so `form_start` and `form_submit` line up. */\n formId: string;\n /**\n * The site's own questions (e.g. \"How did you hear about us?\"), appended to this\n * `form_submit` for our reporting; never sent to the shop. A name the booking already\n * reports (`phone`, `email`, ...) is dropped, so lead matching cannot be overridden.\n */\n extraFields?: FormSubmitField[];\n /** The site's generated `ARANOVA_LEAD_FORMS`: judged before the config loads, and drift flagged. */\n contracts?: LeadFormContracts;\n}\n\n/** One field of a `form_submit`. */\nexport type FormSubmitField = NonNullable<FormSubmitMetadata[\"form\"][\"fields\"]>[number];\n\nexport type BookingOutcome =\n /** The shop has the request; it texts and emails the customer itself. */\n | { status: \"created\"; id: string; requestCode: string | null; mode: BookingMode }\n /** Received and still being sent; the shop confirms. Show the same success. */\n | { status: \"pending\"; id: string; mode: BookingMode }\n /**\n * Not created. Still a success for the customer: show the prefilled shop booking link.\n * `id` is null when the booking never reached Aranova (offline, rate-limited, booking off).\n */\n | { status: \"failed\"; id: string | null; fallbackUrl: string | null; mode: BookingMode }\n /** The booking breaks the shop's rules; `message` is customer-safe. Nothing was sent. */\n | { status: \"invalid\"; message: string; reasons: string[] }\n /** The shop's times changed since the form loaded; the config is being refetched. */\n | { status: \"stale\"; message: string };\n\nexport interface BookingSubmitOptions {\n attemptKey: string;\n formSubmit?: BookingFormSubmit;\n /**\n * Development only: force a simulated create's outcome to exercise the success, pending and\n * fallback screens. The production API ignores it, so it can never change a real booking.\n */\n simulate?: BookingSimulation;\n /**\n * Called once when the server answers `pending`, before `submit` polls for the final answer\n * (up to ~30 s): show the pending success right away instead of a spinner.\n */\n onPending?: (outcome: Extract<BookingOutcome, { status: \"pending\" }>) => void;\n}\n\nexport interface SalesBookingsClient {\n /**\n * Send one booking. Never rejects for an API outcome: every answer is a {@link BookingOutcome}.\n * Pass the same `attemptKey` (a UUID, `newBookingAttemptKey()`) on a retry of the same submit,\n * so a double-click or a network retry can never book twice.\n */\n submit(input: BookingInput, options: BookingSubmitOptions): Promise<BookingOutcome>;\n /** A booking's current status by the id `submit` returned. */\n status(id: string): Promise<BookingOutcome>;\n /**\n * Add the vehicle, notes or \"How did you hear about us?\" after booking (e.g.\n * `rules.vehicleDeferred`, or an answer asked on the success screen). Pass the booking's own\n * `attemptKey`. Never rejects: they reach the shop's request, or the owner by text.\n */\n updateBooking(\n bookingId: string,\n details: BookingDetailsInput,\n options: BookingDetailsOptions,\n ): Promise<BookingDetailsOutcome>;\n}\n\nconst POLL_DELAYS_MS = [1000, 2000, 4000, 8000, 15000];\nconst INVALID_DETAILS = \"Please check your details and try again.\";\n\n/** A fresh per-submit key. Keep it for retries of the same submit; mint a new one after. */\nexport const newBookingAttemptKey = uuidV4;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nfunction outcomeOf(body: BookingResponseBody): BookingOutcome {\n const mode = body.mode ?? \"live\";\n if (body.status === \"created\")\n return { status: \"created\", id: body.id, requestCode: body.request_code, mode };\n if (body.status === \"pending\") return { status: \"pending\", id: body.id, mode };\n return { status: \"failed\", id: body.id, fallbackUrl: body.fallback_url, mode };\n}\n\ntype DropOffKey = (typeof LEAD_FORM_ROLE_SPECS)[\"drop_off\"][\"options\"][number][\"key\"];\n\nconst DROP_OFF_KEYS: Record<BookingDropOff, DropOffKey> = {\n waitForIt: \"wait-for-it\",\n dropOff: \"drop-off\",\n};\n\ninterface BookingField {\n name: string;\n type: string;\n label: string;\n value: JsonValue;\n role: (typeof LEAD_FORM_ROLES)[number];\n}\n\n/** A contract-typed `booking` submit (ADR-0024): option keys, never labels; blanks omitted. */\nfunction formSubmitPayload(\n input: BookingInput,\n formSubmit: BookingFormSubmit,\n runtime: TrackingConfigRuntime | null,\n): FormSubmitMetadata {\n const field = (\n role: BookingField[\"role\"],\n name: string,\n type: string,\n label: string,\n value: JsonValue | undefined,\n ) => ({ name, type, label, value, role });\n const text = (value: string | null | undefined) => value?.trim() || undefined;\n const { vehicle, dropOff } = input;\n const entries = [\n field(\"given_name\", \"first_name\", \"text\", \"First name\", text(input.firstName)),\n field(\"family_name\", \"last_name\", \"text\", \"Last name\", text(input.lastName)),\n // E.164 on the wire: it keys the customer and enhanced-conversion matching.\n field(\"phone\", \"phone\", \"tel\", \"Phone\", toE164(input.phone) ?? input.phone),\n field(\"email\", \"email\", \"email\", \"Email\", text(input.email)),\n field(\"service\", \"service\", \"select\", \"Service\", [...input.services]),\n field(\n \"vehicle\",\n \"vehicle\",\n \"text\",\n \"Vehicle\",\n vehicle ? `${vehicle.year} ${vehicle.make} ${vehicle.model}` : undefined,\n ),\n field(\n \"drop_off\",\n \"drop_off\",\n \"select\",\n \"Drop-off\",\n dropOff ? DROP_OFF_KEYS[dropOff] : undefined,\n ),\n field(\"preferred_date\", \"preferred_date\", \"date\", \"Date\", input.startDate),\n field(\"preferred_time\", \"preferred_time\", \"time\", \"Time\", {\n start: `${String(input.startHour).padStart(2, \"0\")}:00`,\n end: null,\n }),\n field(\"note\", \"note\", \"textarea\", \"Notes\", text(input.notes)),\n ];\n // Reserved even when blank, so an extra field can never stand in for one.\n const reported = new Set(entries.map((item) => item.name));\n const fields = entries.filter((item): item is BookingField => item.value !== undefined);\n const extra = (formSubmit.extraFields ?? []).filter((item) => !reported.has(item.name));\n const settings = typedClientSettings(formSubmit.tracking);\n const { violations, hash } = judgeLeadForm(\n \"booking\",\n formSubmit.formId,\n fields.map((item): [string, JsonValue] => [item.role, item.value]),\n formSubmit.contracts,\n { ...settings, trackingConfig: settings.trackingConfig ?? runtime?.ref },\n );\n return {\n form: {\n id: formSubmit.formId,\n action: \"/tracking/bookings\",\n fields: [...fields, ...extra],\n kind: \"booking\" satisfies LeadFormKind,\n contract_version: LEAD_FORM_REGISTRY_VERSION,\n ...(hash ? { contract_hash: hash } : {}),\n ...(violations.length > 0\n ? { contract_violations: violations.slice(0, MAX_CONTRACT_VIOLATIONS) }\n : {}),\n },\n page: { path: typeof window === \"undefined\" ? \"/\" : window.location.pathname },\n };\n}\n\ntype Firing = Pick<ConversionConfigStore, \"getFiring\"> | TrackingConfigRuntime | undefined;\n\n/** The runtime config the sales client was given, when it is the live runtime. */\nfunction runtimeOf(firing: Firing): TrackingConfigRuntime | null {\n return firing && \"integrations\" in firing ? firing : null;\n}\n\nfunction isLocalUrl(url: string): boolean {\n try {\n return [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(new URL(url).hostname);\n } catch {\n return false;\n }\n}\n\n/**\n * A local API with the production CDN books against prod's config_version and slots: the\n * local API refuses it as stale or unavailable. Config and API must come from one backend.\n */\nexport function bookingSetupMismatch(\n endpoint: string,\n runtime: Pick<TrackingConfigRuntime, \"ref\"> | null,\n): string | null {\n if (!runtime || !isLocalUrl(endpoint)) return null;\n const configUrl = resolveTrackingConfigUrl(runtime.ref);\n if (isLocalUrl(configUrl)) return null;\n return (\n `[aranova] Bookings go to the local API (${endpoint}) but the runtime config comes from ` +\n `${configUrl}. Set NEXT_PUBLIC_ARANOVA_CDN_BASE_URL to the local bucket ` +\n \"(http://localhost:9100/aranova-demos) so both come from one backend.\"\n );\n}\n\nexport function createSalesBookingsClient(\n config: SalesTransportConfig,\n firing: Firing,\n): SalesBookingsClient {\n const submittedForms = new Set<string>();\n const staleAttempts = new Set<string>();\n let isSetupChecked = false;\n\n function warnOnSetupMismatch(runtime: TrackingConfigRuntime | null): void {\n if (isSetupChecked) return;\n isSetupChecked = true;\n const warning = bookingSetupMismatch(config.endpoint, runtime);\n if (warning) console.warn(warning);\n }\n\n function handoff(input: BookingInput): string | null {\n const booking = getBookingIntegration(runtimeOf(firing)?.integrations());\n if (!booking) return null;\n return buildBookingHandoffUrl(booking.fallbackUrl, {\n firstName: input.firstName,\n lastName: input.lastName,\n phone: input.phone,\n email: input.email,\n });\n }\n\n /** Never reached Aranova: the mode is the one the runtime config announced. */\n function failedBeforeSend(input: BookingInput): BookingOutcome {\n const mode = getBookingIntegration(runtimeOf(firing)?.integrations())?.mode ?? \"live\";\n return { status: \"failed\", id: null, fallbackUrl: handoff(input), mode };\n }\n\n function withHandoff(outcome: BookingOutcome, input: BookingInput): BookingOutcome {\n if (outcome.status === \"failed\" && !outcome.fallbackUrl) {\n return { ...outcome, fallbackUrl: handoff(input) };\n }\n return outcome;\n }\n\n async function poll(id: string, mode: BookingMode): Promise<BookingOutcome> {\n let outcome: BookingOutcome = { status: \"pending\", id, mode };\n for (const delay of POLL_DELAYS_MS) {\n await sleep(delay);\n try {\n const path = `/bookings/${encodeURIComponent(id)}`;\n outcome = outcomeOf(await salesRequest<BookingResponseBody>(config, \"GET\", path));\n } catch {\n continue;\n }\n if (outcome.status !== \"pending\") return outcome;\n }\n // Still being sent after ~30 s: the workflow keeps going and alerts the owner if it fails.\n return outcome;\n }\n\n async function rejected(\n error: unknown,\n input: BookingInput,\n attemptKey: string,\n runtime: TrackingConfigRuntime | null,\n ): Promise<BookingOutcome> {\n if (!(error instanceof AranovaApiError)) {\n return failedBeforeSend(input);\n }\n if (error.code === BOOKING_ERROR_CODES.stale) {\n // A second stale answer means the published config has not caught up: hand off instead.\n if (staleAttempts.has(attemptKey)) return failedBeforeSend(input);\n staleAttempts.add(attemptKey);\n await runtime?.revalidate();\n return { status: \"stale\", message: error.message };\n }\n if (error.code === BOOKING_ERROR_CODES.rejected) {\n const reasons = error.context?.reasons;\n return {\n status: \"invalid\",\n message: error.message,\n reasons: Array.isArray(reasons) ? reasons.map(String) : [],\n };\n }\n if (error.code === \"validation_error\") {\n return { status: \"invalid\", message: INVALID_DETAILS, reasons: [] };\n }\n // Unavailable, rate-limited, offline or a server fault: the shop's own page still works.\n return failedBeforeSend(input);\n }\n\n return {\n async submit(input, { attemptKey, formSubmit, simulate, onPending }) {\n registerCapability(\"booking_integration\");\n const runtime = runtimeOf(firing);\n warnOnSetupMismatch(runtime);\n const booking = getBookingIntegration(runtime?.integrations());\n if (!booking?.acceptsBookings || booking.configVersion === null) {\n return failedBeforeSend(input);\n }\n if (formSubmit && !submittedForms.has(attemptKey)) {\n submittedForms.add(attemptKey);\n formSubmit.tracking.trackEvent(\n \"form_submit\",\n formSubmitPayload(input, formSubmit, runtime),\n );\n }\n const body: BookingRequestBody = {\n idempotency_key: attemptKey,\n // The tracking client's own visit, so the booking and its form_submit share one session.\n session_id:\n formSubmit?.tracking.getSessionId() ?? peekSessionId() ?? `no-session-${attemptKey}`,\n config_version: booking.configVersion,\n start_date: input.startDate,\n start_hour: input.startHour,\n first_name: input.firstName.trim(),\n last_name: input.lastName.trim(),\n phone: input.phone,\n email: input.email?.trim() || null,\n vehicle: input.vehicle ?? null,\n drop_off: input.dropOff ?? null,\n services: [...input.services],\n notes: input.notes?.trim() || null,\n marketing_sms_consent: input.marketingSmsConsent === true,\n page_path: typeof window === \"undefined\" ? null : window.location.pathname,\n };\n let response: BookingResponseBody;\n try {\n const headers = simulate ? { [BOOKING_SIMULATE_HEADER]: simulate } : undefined;\n response = await salesRequest<BookingResponseBody>(\n config,\n \"POST\",\n \"/bookings\",\n body,\n headers,\n );\n } catch (error) {\n return rejected(error, input, attemptKey, runtime);\n }\n const outcome = outcomeOf(response);\n if (outcome.status !== \"pending\") return withHandoff(outcome, input);\n onPending?.(outcome);\n return withHandoff(await poll(outcome.id, outcome.mode), input);\n },\n\n async updateBooking(bookingId, details, { attemptKey }) {\n const body: BookingDetailsRequestBody = {\n idempotency_key: attemptKey,\n vehicle: details.vehicle ?? null,\n notes: details.notes?.trim() || null,\n heard_via: details.heardVia?.trim() || null,\n };\n try {\n const response = await salesRequest<BookingDetailsResponseBody>(\n config,\n \"POST\",\n `/bookings/${encodeURIComponent(bookingId)}/details`,\n body,\n );\n return { status: response.status, id: response.id };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { status: detailsFailure(error), id: bookingId, message };\n }\n },\n\n async status(id) {\n return outcomeOf(\n await salesRequest<BookingResponseBody>(\n config,\n \"GET\",\n `/bookings/${encodeURIComponent(id)}`,\n ),\n );\n },\n };\n}\n","\"use client\";\n\nimport { useEffect, useMemo } from \"react\";\n\nimport {\n bootstrapGoogleAdsTracking,\n bootstrapMetaPixel,\n bootstrapMultipleGtags,\n bootstrapMultiplePixels,\n getTrackingConfigRuntime,\n registerCapability,\n resolveTrackingConfigUrl,\n type GtagEnvironmentMap,\n type MetaPixelEnvironmentMap,\n type TrackingConfigReference,\n} from \"../../tracking-core/src/index\";\n\n/**\n * Props for the combined ad-platform tag loader.\n *\n * Every field is optional and independent: pass the Google fields, the Meta\n * fields, or both. For each platform a labelled `*Ids` map (ALL loaded) takes\n * precedence over the single `*Id` shortcut.\n */\nexport interface AdPlatformTrackingProps {\n /** Single Google Ads tag id, e.g. `AW-123456789`. */\n gtagId?: string;\n /** Labelled Google Ads tag map — ALL loaded; wins over `gtagId`. */\n gtagIds?: GtagEnvironmentMap;\n /** R2-authoritative Google tracking config. When set, static gtagId(s) are ignored. */\n trackingConfig?: TrackingConfigReference;\n /** Fire a Google page view from this standalone loader. Leave false when using TrackingProvider. */\n standalonePageView?: boolean;\n /** Single Meta Pixel id, e.g. `123456789012345`. */\n metaPixelId?: string;\n /** Labelled Meta Pixel map — ALL loaded; wins over `metaPixelId`. */\n metaPixelIds?: MetaPixelEnvironmentMap;\n}\n\n/**\n * Client component that loads the configured ad-platform tags — the Google tag\n * (`gtag`) and/or the Meta Pixel (`fbq`) — each with the visitor's effective\n * consent applied as the default (opt-out model). One mount handles both\n * platforms; omit a platform's props to skip it. Renders nothing.\n *\n * On React you usually don't need this at all — `<TrackingProvider>` already\n * accepts the same `gtagId/gtagIds` + `metaPixelId/metaPixelIds` props. Use this\n * standalone component when you want the ad tags WITHOUT the event-ingest SDK.\n */\nexport function AdPlatformTracking({\n gtagId,\n gtagIds,\n trackingConfig,\n standalonePageView = false,\n metaPixelId,\n metaPixelIds,\n}: AdPlatformTrackingProps) {\n const trackingConfigKey = trackingConfig\n ? `${resolveTrackingConfigUrl(trackingConfig)}:${trackingConfig.businessId}:${trackingConfig.siteId ?? \"\"}:${trackingConfig.environment}`\n : \"\";\n // Stabilize object references so the effects don't re-fire when the parent\n // passes inline object literals on every render.\n const gtagIdsKey = useMemo(() => (gtagIds ? JSON.stringify(gtagIds) : \"\"), [gtagIds]);\n const metaPixelIdsKey = useMemo(\n () => (metaPixelIds ? JSON.stringify(metaPixelIds) : \"\"),\n [metaPixelIds],\n );\n\n if (trackingConfig || gtagId || (gtagIds && Object.keys(gtagIds).length > 0)) {\n registerCapability(\"ad_tags_google\");\n }\n if (metaPixelId || (metaPixelIds && Object.keys(metaPixelIds).length > 0)) {\n registerCapability(\"ad_tags_meta\");\n }\n\n useEffect(() => {\n if (trackingConfig) {\n const runtime = getTrackingConfigRuntime(trackingConfig);\n if (standalonePageView) runtime.queuePageView();\n else void runtime.revalidate();\n } else 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 // eslint-disable-next-line react-hooks/exhaustive-deps -- trackingConfigKey is the stable proxy for trackingConfig\n }, [gtagId, gtagIdsKey, trackingConfigKey, standalonePageView]);\n\n useEffect(() => {\n if (metaPixelIds && Object.keys(metaPixelIds).length > 0) {\n bootstrapMultiplePixels(metaPixelIds);\n } else if (metaPixelId) {\n bootstrapMetaPixel(metaPixelId);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- metaPixelIdsKey is the stable proxy for metaPixelIds\n }, [metaPixelId, metaPixelIdsKey]);\n\n return null;\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 with the visitor's effective\n * consent applied as the Consent Mode default (opt-out model — granted unless\n * an unexpired stored decline exists).\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(() => (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 return null;\n}\n","\"use client\";\n\nimport { createContext, useContext, useEffect, useMemo, type ReactNode } from \"react\";\n\nimport { version } from \"../package.json\";\nimport {\n attachAutoPageView,\n attachClientCapturesOnce,\n attachCtaClickCapture,\n attachFormStart,\n attachMultiPageSession,\n attachPageExit,\n attachPhoneClickCapture,\n attachScrollDepth,\n attachSiteLinkCapture,\n attachSpecificPageVisit,\n attachTimeOnSite,\n bootstrapGoogleAdsTracking,\n bootstrapMetaPixel,\n bootstrapMultipleGtags,\n bootstrapMultiplePixels,\n createConversionAutoFire,\n createTypedClient,\n decorateCrossSiteUrl,\n getOrCreateTrackingClient,\n getTrackingConfigRuntime,\n registerCapability,\n resolveConversionConfig,\n stripLinkParamFromLocation,\n withConversionAutoFire,\n type ConversionConfig,\n type GtagEnvironmentMap,\n type MetaPixelEnvironmentMap,\n type PhoneConfig,\n type TrackingConfigReference,\n type TrackingEnvironment,\n type TriggerRegistryConfig,\n type TypedTrackingClient,\n} from \"../../tracking-core/src/index\";\nimport { PhoneConfigProvider } from \"../../tracking-core/src/phone-react\";\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, `trackEvent()` metadata that fails its Zod schema throws. When\n * off, the invalid event is dropped with a console warning. Turn on in dev\n * to catch shape bugs early.\n */\n debug?: boolean;\n /**\n * Phone-field display + default-country config, read by `usePhoneField` /\n * `<PhoneField>`. Display is customizable; the transmitted value is always E.164.\n */\n phone?: PhoneConfig;\n /**\n * GAP28 / unified-goal: enable real-time on-site conversion firing. When set, the SDK\n * fetches the per-business config from `cdnUrl` (optional offline `baked` fallback) and\n * AUTOMATICALLY fires `gtag('event','conversion')` for any automatic event-goal\n * (scroll/time/page-view/…) whose trigger threshold a detector crosses. Omit to keep events\n * analytics-only. (Sale + manual-event firing lives in the server/browser sales client.)\n */\n conversionConfig?: { cdnUrl: string; baked?: ConversionConfig | null };\n /**\n * R2-authoritative config reference — `ARANOVA_TRACKING_CONFIG` from\n * `tracking-cli gen`. Wins over `conversionConfig`. The object URL is composed\n * from `businessId` + `environment` against the production CDN; spread in a\n * `cdnBaseUrl` to read it from somewhere else, e.g.\n * `{ ...ARANOVA_TRACKING_CONFIG, cdnBaseUrl: import.meta.env.VITE_ARANOVA_CDN_BASE_URL }`.\n */\n trackingConfig?: TrackingConfigReference;\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 /** Optional Meta Pixel id, for example `123456789012345`. */\n metaPixelId?: string;\n /**\n * Labelled map of Meta Pixel IDs. ALL are loaded simultaneously.\n * When provided, `metaPixelId` is ignored.\n */\n metaPixelIds?: MetaPixelEnvironmentMap;\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 * Append the cross-site identity token to a URL that points at a sibling\n * site of this business, for navigations the linker cannot see. Returns the\n * URL unchanged for any other target.\n */\n decorateUrl: (url: string) => string;\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 {\n apiKey,\n endpoint,\n triggers,\n environment,\n debug,\n phone,\n conversionConfig,\n trackingConfig,\n } = options;\n\n registerCapability(\"base_tracking\");\n // The R2 config reference is what wires the conversion auto-fire below, so\n // passing it IS the automatic-goal capability — no sales client involved.\n if (trackingConfig) registerCapability(\"conversion_goals_auto\");\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 // Still publish phone config so usePhoneField/<PhoneField> work even when\n // tracking is disabled (missing apiKey/endpoint).\n TrackingProvider: ({ children }: TrackingProviderProps) => (\n <PhoneConfigProvider value={phone ?? null}>{children}</PhoneConfigProvider>\n ),\n useTracking: () => noopTyped,\n decorateUrl: (url: string) => url,\n };\n }\n\n const TrackingContext = createContext<TypedTrackingClient<TRegistry> | null>(null);\n\n function TrackingProvider({\n gtagId,\n gtagIds,\n metaPixelId,\n metaPixelIds,\n children,\n }: TrackingProviderProps): ReactNode {\n // Stabilize object references when callers pass inline ID maps.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const gtagIdsKey = useMemo(() => (gtagIds ? JSON.stringify(gtagIds) : \"\"), [gtagIds]);\n\n // Resolve gtag IDs map for context and heartbeat.\n const resolvedGtagIds = useMemo(\n () =>\n gtagIds\n ? (Object.fromEntries(\n Object.entries(gtagIds).filter((e): e is [string, string] => e[1] != null),\n ) as Record<string, string>)\n : gtagId\n ? { default: gtagId }\n : undefined,\n // eslint-disable-next-line react-hooks/exhaustive-deps -- gtagIdsKey is stable proxy\n [gtagId, gtagIdsKey],\n );\n\n // Page-level singleton raw client. Surviving strict-mode unmount +\n // remount is handled by getOrCreateTrackingClient itself.\n const rawClient = useMemo(() => {\n const created = getOrCreateTrackingClient({\n apiKey,\n endpoint,\n surface: \"react\",\n packageName: \"@aranova/tracking-react\",\n sdkVersion: version,\n triggers,\n environment,\n activeGtagIds: resolvedGtagIds,\n configSiteId: trackingConfig?.siteId ?? null,\n debug,\n });\n // Before attachAutoPageView snapshots the URL (the client already consumed the token).\n stripLinkParamFromLocation();\n return created;\n }, [resolvedGtagIds]);\n const conversionStore = useMemo(\n () =>\n trackingConfig\n ? getTrackingConfigRuntime(trackingConfig)\n : conversionConfig\n ? resolveConversionConfig({\n cdnUrl: conversionConfig.cdnUrl,\n baked: conversionConfig.baked,\n })\n : null,\n [],\n );\n const conversionClient = useMemo(\n () =>\n conversionStore\n ? withConversionAutoFire(rawClient, createConversionAutoFire(conversionStore))\n : rawClient,\n [rawClient, conversionStore],\n );\n const client = useMemo(\n () =>\n createTypedClient<TRegistry>(conversionClient, triggers, {\n debug,\n environment,\n trackingConfig,\n }),\n [conversionClient],\n );\n\n useEffect(() => {\n if (trackingConfig) {\n getTrackingConfigRuntime(trackingConfig).start();\n } else 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 // eslint-disable-next-line react-hooks/exhaustive-deps\n const metaPixelIdsKey = useMemo(\n () => (metaPixelIds ? JSON.stringify(metaPixelIds) : \"\"),\n [metaPixelIds],\n );\n\n useEffect(() => {\n if (metaPixelIds && Object.keys(metaPixelIds).length > 0) {\n bootstrapMultiplePixels(metaPixelIds);\n } else if (metaPixelId) {\n bootstrapMetaPixel(metaPixelId);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- metaPixelIdsKey is the stable proxy for metaPixelIds\n }, [metaPixelId, metaPixelIdsKey]);\n\n // Attach the automatic triggers declared in the registry. These are all\n // document/window/pushState listeners, so they must attach ONCE per singleton\n // client, not once per provider mount — otherwise a page with multiple\n // <TrackingProvider>s stacks duplicate listeners and every interaction/navigation\n // emits its event N times. `attachClientCapturesOnce` ref-counts against the\n // singleton client: the first mount runs `build`, later mounts are no-ops, and the\n // last unmount runs the LIFO detach (so layered pushState patches restore cleanly).\n useEffect(() => {\n return attachClientCapturesOnce(rawClient, () => {\n const detachers: Array<() => void> = [];\n\n // GAP28 / unified-goal: when conversion firing is on, wrap the client so every\n // automatic detector event ALSO auto-fires a matching event-goal's conversion\n // (best-effort, never breaks ingest). Built inside `build` so it only resolves on\n // the mount that actually attaches (stale-while-revalidate against the CDN).\n const detectorClient = conversionStore ? conversionClient : rawClient;\n const pageClient =\n trackingConfig && conversionStore && \"queuePageView\" in conversionStore\n ? {\n ...detectorClient,\n trackEvent: (input: Parameters<typeof detectorClient.trackEvent>[0]) => {\n detectorClient.trackEvent(input);\n if (input.eventType === \"page_view\") conversionStore.queuePageView();\n },\n }\n : detectorClient;\n\n // page_view is always present (required in the type). page_exit is\n // SDK-internal: always attached, so dwell/exit analytics need no config.\n detachers.push(attachAutoPageView(pageClient));\n detachers.push(attachPageExit(detectorClient));\n\n const timeOnSite = triggers.automatic.time_on_site;\n if (timeOnSite) {\n detachers.push(attachTimeOnSite(detectorClient, timeOnSite));\n }\n\n detachers.push(\n attachSiteLinkCapture(detectorClient, {\n linkedHostnames: () => conversionStore?.linkedHostnames() ?? [],\n }),\n );\n\n const specificPageVisit = triggers.automatic.specific_page_visit;\n if (specificPageVisit) {\n detachers.push(attachSpecificPageVisit(detectorClient, specificPageVisit));\n }\n\n const scrollDepth = triggers.automatic.scroll_depth;\n if (scrollDepth) {\n detachers.push(attachScrollDepth(detectorClient, scrollDepth));\n }\n\n const multiPageSession = triggers.automatic.multi_page_session;\n if (multiPageSession) {\n detachers.push(attachMultiPageSession(detectorClient, multiPageSession));\n }\n\n const formStart = triggers.automatic.form_start;\n if (formStart) {\n detachers.push(attachFormStart(detectorClient, formStart));\n }\n\n const ctaClick = triggers.manual?.cta_click;\n if (ctaClick) {\n // No-ops unless the config opts into autoCapture.\n detachers.push(attachCtaClickCapture(detectorClient, ctaClick));\n }\n\n const phoneClick = triggers.manual?.phone_click;\n if (phoneClick) {\n // No-ops unless the config opts into autoCapture. A captured tel: tap also\n // drives conversion auto-fire for a linked phone_click goal.\n detachers.push(attachPhoneClickCapture(detectorClient, phoneClick));\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 }, [conversionClient, conversionStore, rawClient]);\n\n return (\n <TrackingContext.Provider value={client}>\n <PhoneConfigProvider value={phone ?? null}>{children}</PhoneConfigProvider>\n </TrackingContext.Provider>\n );\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 {\n TrackingProvider,\n useTracking,\n decorateUrl: (url: string) =>\n decorateCrossSiteUrl(\n url,\n trackingConfig ? getTrackingConfigRuntime(trackingConfig).linkedHostnames() : [],\n ),\n };\n}\n","{\n \"name\": \"@aranova/tracking-react\",\n \"version\": \"1.18.0\",\n \"private\": false,\n \"type\": \"commonjs\",\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 \"typesVersions\": {\n \"*\": {\n \"client\": [\n \"./dist/client.d.ts\"\n ]\n }\n },\n \"sideEffects\": false,\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 \"import\": {\n \"types\": \"./dist/index.d.mts\",\n \"default\": \"./dist/index.mjs\"\n },\n \"require\": {\n \"types\": \"./dist/index.d.ts\",\n \"default\": \"./dist/index.js\"\n }\n },\n \"./client\": {\n \"import\": {\n \"types\": \"./dist/client.d.mts\",\n \"default\": \"./dist/client.mjs\"\n },\n \"require\": {\n \"types\": \"./dist/client.d.ts\",\n \"default\": \"./dist/client.js\"\n }\n },\n \"./sales\": {\n \"import\": {\n \"types\": \"./dist/sales.d.mts\",\n \"default\": \"./dist/sales.mjs\"\n },\n \"require\": {\n \"types\": \"./dist/sales.d.ts\",\n \"default\": \"./dist/sales.js\"\n }\n },\n \"./phone\": {\n \"import\": {\n \"types\": \"./dist/phone.d.mts\",\n \"default\": \"./dist/phone.mjs\"\n },\n \"require\": {\n \"types\": \"./dist/phone.d.ts\",\n \"default\": \"./dist/phone.js\"\n }\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 \"libphonenumber-js\": \"^1.11.0\",\n \"zod\": \"^4.6.2\"\n },\n \"devDependencies\": {\n \"@types/react\": \"^19.2.2\",\n \"@types/react-dom\": \"^19.2.2\",\n \"esbuild\": \"^0.27.7\",\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","\"use client\";\n\n// Layer 2 — React phone field. Authored ONCE here in core and re-exported by\n// tracking-react / tracking-next (the existing core→framework edge). This module\n// is a SEPARATE `'use client'` subpath and is intentionally NOT exported from\n// core's main `index.ts`, so non-React consumers (browser IIFE, server) never\n// pull React. `react` is an OPTIONAL peer dependency.\n\nimport {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useMemo,\n useState,\n type ChangeEvent,\n type Context,\n type FocusEvent,\n type InputHTMLAttributes,\n type ReactNode,\n} from \"react\";\n\nimport { registerCapability } from \"./capabilities\";\nimport {\n DEFAULT_PHONE_COUNTRY,\n formatPhone,\n formatPhoneAsTyped,\n parsePhone,\n type CountryCode,\n type ParsedPhone,\n type PhoneDisplayFormat,\n} from \"./phone\";\nimport type { PhoneConfig } from \"./phone-field\";\n\n/** Carries `createTracking({ phone })` config to `usePhoneField`. Separate from the\n * tracking client context so `useTracking()`'s type is untouched.\n *\n * Created lazily on first access — NOT at module scope — so that importing a\n * barrel which re-exports this module from a React Server Component graph has no\n * eval-time side effect. A module-scope `createContext()` runs during server\n * module evaluation and throws \"createContext only works in Client Components\"\n * (Next 15/16), even when the importer only wanted a server-safe symbol bundled\n * alongside it. Behind a getter the module stays inert until a client component\n * actually renders the provider/hook.\n *\n * Caveat: this module is bundled into BOTH `@aranova/tracking-react` and\n * `@aranova/tracking-next`, so each package has its own context instance at\n * runtime. Within one installed package the provider + hook share it (correct);\n * mixing `createTracking` from one package with `usePhoneField` from the other\n * won't share config (the hook falls back to the `CA`/`national` defaults). Use a\n * single tracking package per app. */\nlet _phoneConfigContext: Context<PhoneConfig | null> | undefined;\nfunction phoneConfigContext(): Context<PhoneConfig | null> {\n return (_phoneConfigContext ??= createContext<PhoneConfig | null>(null));\n}\n\n/** Provides `createTracking({ phone })` config to `usePhoneField` / `<PhoneField>`.\n * Wired internally by each package's `TrackingProvider`; consumers configure phone\n * via `createTracking({ phone })` rather than mounting this directly. Replaces the\n * former raw `PhoneConfigContext` export so the context is never created at module\n * load (see the getter above). */\nexport function PhoneConfigProvider({\n value,\n children,\n}: {\n value: PhoneConfig | null;\n children: ReactNode;\n}): ReactNode {\n const Ctx = phoneConfigContext();\n return <Ctx.Provider value={value}>{children}</Ctx.Provider>;\n}\n\n/** Resolve the effective phone config (provider value or built-in defaults). */\nexport function usePhoneConfig(): { defaultCountry: CountryCode; display: PhoneDisplayFormat } {\n const ctx = useContext(phoneConfigContext());\n return {\n defaultCountry: ctx?.defaultCountry ?? DEFAULT_PHONE_COUNTRY,\n display: ctx?.display ?? \"national\",\n };\n}\n\nexport interface UsePhoneFieldOptions {\n defaultValue?: string;\n /** Overrides the provider's `defaultCountry`. */\n country?: CountryCode;\n /** Overrides the provider's `display` (applied to the settled value on blur). */\n display?: PhoneDisplayFormat;\n /** Notified with the canonical E.164 (or `null`) on every change. */\n onValueChange?: (e164: string | null) => void;\n}\n\nexport interface PhoneInputProps {\n value: string;\n onChange: (event: ChangeEvent<HTMLInputElement>) => void;\n onBlur: (event: FocusEvent<HTMLInputElement>) => void;\n type: \"tel\";\n inputMode: \"tel\";\n autoComplete: \"tel\";\n}\n\nexport interface PhoneFieldApi {\n /** Display value for the `<input>` (live `AsYouType` while typing). */\n value: string;\n /** Canonical E.164 — what gets transmitted. `null` while invalid/incomplete. */\n e164: string | null;\n isValid: boolean;\n /** Validation message, surfaced only after blur with non-empty invalid input. */\n error: string | null;\n parsed: ParsedPhone;\n /** Spread onto an `<input>`: pre-wires value/onChange/onBlur/type/inputMode/autoComplete. */\n inputProps: PhoneInputProps;\n}\n\n/** Headless phone field — the client owns the markup. */\nexport function usePhoneField(opts: UsePhoneFieldOptions = {}): PhoneFieldApi {\n // Reported from the hook rather than from `createTracking({ phone })`:\n // config presence only says the option was set, not that a field renders.\n // `<PhoneField>` is built on this hook, so both surfaces are covered here.\n registerCapability(\"phone_fields\");\n const cfg = usePhoneConfig();\n const country = opts.country ?? cfg.defaultCountry;\n const display = opts.display ?? cfg.display;\n const { onValueChange } = opts;\n\n const [value, setValue] = useState(() => formatPhoneAsTyped(opts.defaultValue ?? \"\", country));\n const [touched, setTouched] = useState(false);\n\n const parsed = useMemo(() => parsePhone(value, country), [value, country]);\n\n const onChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>) => {\n const next = formatPhoneAsTyped(event.target.value, country);\n setValue(next);\n onValueChange?.(parsePhone(next, country).e164);\n },\n [country, onValueChange],\n );\n\n const onBlur = useCallback(\n (_event: FocusEvent<HTMLInputElement>) => {\n setTouched(true);\n // Settle the field to the configured display format once it's valid.\n setValue((current) => {\n const p = parsePhone(current, country);\n return p.isValid ? formatPhone(current, display, country) : current;\n });\n },\n [country, display],\n );\n\n const error =\n touched && value.length > 0 && !parsed.isValid ? \"Enter a valid phone number\" : null;\n\n return {\n value,\n e164: parsed.e164,\n isValid: parsed.isValid,\n error,\n parsed,\n inputProps: { value, onChange, onBlur, type: \"tel\", inputMode: \"tel\", autoComplete: \"tel\" },\n };\n}\n\nexport interface PhoneFieldProps extends Omit<\n InputHTMLAttributes<HTMLInputElement>,\n \"type\" | \"value\" | \"onChange\"\n> {\n country?: CountryCode;\n /** Controlled display value. */\n value?: string;\n /** Uncontrolled initial value. */\n defaultValue?: string;\n /** Receives the native change event (RHF `register().onChange` or your own); the\n * event's `target.value` is already `AsYouType`-formatted. */\n onChange?: (event: ChangeEvent<HTMLInputElement>) => void;\n /** Receives the canonical E.164 (or `null`) on every change. */\n onE164Change?: (e164: string | null) => void;\n}\n\n/**\n * Batteries-included phone input. Composes identically with react-hook-form\n * `{...register('phone')}` and with controlled state — the `AsYouType` +\n * mutate-`e.target.value`-before-`onChange` technique lives inside, so RHF and\n * controlled parents both receive the formatted value, and the wire value stays\n * E.164.\n */\nexport const PhoneField = forwardRef<HTMLInputElement, PhoneFieldProps>(function PhoneField(\n { country, value, defaultValue, onChange, onE164Change, ...rest },\n ref,\n) {\n const cfg = usePhoneConfig();\n const resolvedCountry = country ?? cfg.defaultCountry;\n const isControlled = value !== undefined;\n const [internal, setInternal] = useState(() =>\n formatPhoneAsTyped(defaultValue ?? \"\", resolvedCountry),\n );\n\n const handleChange = (event: ChangeEvent<HTMLInputElement>): void => {\n const formatted = formatPhoneAsTyped(event.target.value, resolvedCountry);\n // Mutate the target BEFORE forwarding so RHF/controlled consumers see the\n // formatted value (and the input renders it).\n event.target.value = formatted;\n onE164Change?.(parsePhone(formatted, resolvedCountry).e164);\n if (!isControlled) setInternal(formatted);\n onChange?.(event);\n };\n\n const shown = isControlled ? formatPhoneAsTyped(value, resolvedCountry) : internal;\n\n return (\n <input\n {...rest}\n ref={ref}\n type=\"tel\"\n inputMode=\"tel\"\n autoComplete=\"tel\"\n value={shown}\n onChange={handleChange}\n />\n );\n});\n","\"use client\";\n\nimport type { AnchorHTMLAttributes, ReactNode } from \"react\";\nimport { useCallback, useMemo, useRef, useState, useSyncExternalStore } from \"react\";\n\nimport { registerCapability } from \"./capabilities\";\nimport {\n bookableSlots,\n buildBookingHandoffUrl,\n getBookingIntegration,\n type BookableDay,\n type BookingHandoffPrefill,\n type BookingIntegration,\n} from \"./integrations-public\";\nimport {\n newBookingAttemptKey,\n type BookingDetailsInput,\n type BookingDetailsOutcome,\n type BookingFormSubmit,\n type BookingInput,\n type BookingInputFor,\n type BookingOutcome,\n type BookingSimulation,\n type SalesBookingsClient,\n} from \"./resources/sales/bookings\";\nimport type { LeadFormContracts } from \"./lead-forms/submit\";\nimport type { ConversionIntegrations } from \"./resources/conversion-config\";\nimport {\n getTrackingConfigRuntime,\n type TrackingConfigReference,\n} from \"./resources/tracking-config-runtime\";\n\nfunction serverSnapshot(): ConversionIntegrations | null {\n return null;\n}\n\n/**\n * The site's booking provider from the live runtime config, or null before one loads.\n *\n * Pass the same `ARANOVA_TRACKING_CONFIG` reference given to `createTracking`; the config\n * runtime is shared, so this adds no fetch.\n */\nexport function useBookingIntegration(\n trackingConfig: TrackingConfigReference | undefined,\n): BookingIntegration | null {\n registerCapability(\"booking_handoff\");\n const subscribe = useCallback(\n (listener: () => void) =>\n trackingConfig ? getTrackingConfigRuntime(trackingConfig).subscribe(listener) : () => {},\n [trackingConfig],\n );\n const integrations = useSyncExternalStore(\n subscribe,\n () => (trackingConfig ? getTrackingConfigRuntime(trackingConfig).integrations() : null),\n serverSnapshot,\n );\n return useMemo(() => getBookingIntegration(integrations), [integrations]);\n}\n\nexport interface BookingHandoffOptions {\n /** Baked copy of the booking link, used when no runtime config has loaded. */\n fallbackUrl?: string;\n prefill?: BookingHandoffPrefill;\n}\n\n/**\n * The prefilled handoff URL, or null when neither the runtime config nor `fallbackUrl` names a\n * usable link. For sites that render the link with their own button component.\n */\nexport function useBookingHandoffUrl(\n trackingConfig: TrackingConfigReference | undefined,\n { fallbackUrl, prefill }: BookingHandoffOptions = {},\n): string | null {\n const booking = useBookingIntegration(trackingConfig);\n const base = booking?.fallbackUrl ?? fallbackUrl;\n return base ? buildBookingHandoffUrl(base, prefill) : null;\n}\n\nexport interface BookingHandoffLinkProps\n extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, \"href\">, BookingHandoffOptions {\n trackingConfig?: TrackingConfigReference;\n children: ReactNode;\n}\n\n/**\n * The optional \"pick your time\" step after a form submit: the shop's booking page, prefilled.\n * Renders nothing when neither the runtime config nor `fallbackUrl` names a usable link.\n */\nexport function BookingHandoffLink({\n trackingConfig,\n fallbackUrl,\n prefill,\n children,\n target = \"_blank\",\n rel = \"noopener noreferrer\",\n ...anchorProps\n}: BookingHandoffLinkProps): ReactNode {\n const href = useBookingHandoffUrl(trackingConfig, { fallbackUrl, prefill });\n if (!href) return null;\n return (\n <a {...anchorProps} href={href} target={target} rel={rel}>\n {children}\n </a>\n );\n}\n\n/**\n * Every bookable day and its start hours, split morning / afternoon, from the live rules.\n * Empty until the config loads or when the shop takes no online bookings. Recomputed each\n * render (cheap), so a tab left open past midnight never offers yesterday's notice window.\n */\nexport function useBookingSlots(\n trackingConfig: TrackingConfigReference | undefined,\n): BookableDay[] {\n const booking = useBookingIntegration(trackingConfig);\n if (!booking?.acceptsBookings || !booking.rules) return [];\n return bookableSlots(booking.rules, { timeZone: booking.timezone });\n}\n\nexport type BookingSubmitStatus = \"idle\" | \"submitting\" | BookingOutcome[\"status\"];\n\nexport interface BookedAttempt {\n bookingId: string;\n attemptKey: string;\n}\n\nexport interface BookingSubmitState<I = BookingInput> {\n status: BookingSubmitStatus;\n outcome: BookingOutcome | null;\n /** Send the booking. A retry of an unanswered or refused submit reuses its attempt key. */\n submit(input: I): Promise<BookingOutcome>;\n /**\n * The recorded booking and the key it was sent with; persist it (e.g. `sessionStorage`) to add\n * details after a reload. Null until the server has the booking.\n */\n booked: BookedAttempt | null;\n /**\n * Add the vehicle, notes or \"How did you hear about us?\" to `booked`, or to `target` (a\n * persisted one). Resolves `not_found` when there is neither.\n */\n updateBooking(\n details: BookingDetailsInput,\n target?: BookedAttempt,\n ): Promise<BookingDetailsOutcome>;\n /** Back to `idle` with a fresh attempt key, e.g. \"book another car\". */\n reset(): void;\n}\n\n/**\n * Submit state for a booking form over `salesClient.bookings`. Owns the parts that are easy to\n * get wrong: one attempt key per submit (a double-click never books twice), the visit's\n * `form_submit`, the stale-config refetch, and the fallback link on failure. A `pending` answer\n * shows at once (`status: \"pending\"`) while the final one is polled; the submit stays in flight.\n * Pass the site's `ARANOVA_LEAD_FORMS` as `contracts` to type the input by the shop's rules.\n */\nexport function useBookingSubmit<C extends LeadFormContracts | undefined = undefined>(\n bookings: SalesBookingsClient,\n options: {\n formSubmit?: BookingFormSubmit;\n /** Development only: a simulated create's outcome. The production API ignores it. */\n simulate?: BookingSimulation;\n contracts?: C;\n } = {},\n): BookingSubmitState<C extends LeadFormContracts ? BookingInputFor<C> : BookingInput> {\n registerCapability(\"booking_integration\");\n const [outcome, setOutcome] = useState<BookingOutcome | null>(null);\n const [submitting, setSubmitting] = useState(false);\n const attemptKey = useRef<string | null>(null);\n const [booked, setBooked] = useState<BookedAttempt | null>(null);\n const inFlight = useRef<Promise<BookingOutcome> | null>(null);\n const { simulate, contracts } = options;\n const formSubmit = useMemo(\n () =>\n options.formSubmit && contracts ? { ...options.formSubmit, contracts } : options.formSubmit,\n [options.formSubmit, contracts],\n );\n\n const submit = useCallback(\n (input: BookingInput) => {\n if (inFlight.current) return inFlight.current;\n attemptKey.current ??= newBookingAttemptKey();\n const key = attemptKey.current;\n setSubmitting(true);\n const run = bookings\n .submit(input, {\n attemptKey: key,\n formSubmit,\n simulate,\n // The server has it: show the pending success while the final answer is polled.\n onPending: (pending) => {\n setBooked({ bookingId: pending.id, attemptKey: key });\n setOutcome(pending);\n setSubmitting(false);\n },\n })\n .then((result) => {\n // Keep the key while nothing was recorded (invalid, stale, a POST that never answered),\n // so the retry replays instead of booking twice.\n const unrecorded =\n result.status === \"invalid\" ||\n result.status === \"stale\" ||\n (result.status === \"failed\" && result.id === null);\n if (!unrecorded) attemptKey.current = null;\n if (result.status !== \"invalid\" && result.status !== \"stale\" && result.id) {\n setBooked({ bookingId: result.id, attemptKey: key });\n }\n setOutcome(result);\n return result;\n })\n .finally(() => {\n inFlight.current = null;\n setSubmitting(false);\n });\n inFlight.current = run;\n return run;\n },\n [bookings, formSubmit, simulate],\n );\n\n const updateBooking = useCallback(\n async (\n details: BookingDetailsInput,\n target?: BookedAttempt,\n ): Promise<BookingDetailsOutcome> => {\n const attempt = target ?? booked;\n if (!attempt) return { status: \"not_found\", id: \"\", message: \"Nothing has been booked yet.\" };\n return bookings.updateBooking(attempt.bookingId, details, {\n attemptKey: attempt.attemptKey,\n });\n },\n [bookings, booked],\n );\n\n const reset = useCallback(() => {\n attemptKey.current = null;\n setBooked(null);\n setOutcome(null);\n }, []);\n\n const status: BookingSubmitStatus = submitting ? \"submitting\" : (outcome?.status ?? \"idle\");\n return { status, outcome, booked, submit, updateBooking, reset };\n}\n"],"mappings":"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","names":["import_react","getScriptMarker","record","listeners","CACHE_PREFIX","cacheKey","readCache","writeCache","z","MAX_BUFFERED_EVENTS","import_mini","z","z","z","z","z","z","z","z","z","z","z","z","z","import_mini","isRecord","currentPath","check","currentPath","STORAGE_KEY","check","currentPath","currentPath","currentPath","z","import_mini","currentPath","DEFAULT_CHOICE","import_react","trackingConfigKey","import_react","import_react","import_react","import_jsx_runtime","PhoneField","import_jsx_runtime","import_react","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/ConsentBanner.tsx","../src/hooks.ts","../../tracking-core/src/phone.ts","../../tracking-core/src/user-data.ts","../../tracking-core/src/consent.ts","../../tracking-core/src/capabilities.ts","../../tracking-core/src/cross-site-link.ts","../../tracking-core/src/tracking.ts","../../tracking-core/src/gtag.ts","../../tracking-core/src/fbq.ts","../../tracking-core/src/landing.ts","../../tracking-core/src/uuid.ts","../../tracking-core/src/session.ts","../../tracking-core/src/resources/conversion-firing.ts","../../tracking-core/src/resources/conversion-config.ts","../../tracking-core/src/resources/automatic-transaction.ts","../../tracking-core/src/resources/automatic-trigger.ts","../../tracking-core/src/resources/sales/money.ts","../../tracking-core/src/resources/tracking-config-runtime.ts","../../../contracts/autoleap-booking-links.json","../../tracking-core/src/integrations-public.ts","../../tracking-core/src/resources/conversion-autofire.ts","../../tracking-core/src/events/semantics.ts","../../tracking-core/src/page-view.ts","../../tracking-core/src/events/page-view.ts","../../tracking-core/src/heartbeat.ts","../../tracking-core/src/ingest.ts","../../tracking-core/src/ingest-typed.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-details.ts","../../tracking-core/src/events/form-submit.ts","../../tracking-core/src/events/form-step.ts","../../tracking-core/src/events/multi-page-session.ts","../../tracking-core/src/events/page-exit.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/site-link.ts","../../tracking-core/src/events/time-on-site.ts","../../tracking-core/src/events/video.ts","../../tracking-core/src/video-ranges.ts","../../tracking-core/src/events/registry.ts","../../tracking-core/src/form-submit-salvage.ts","../../tracking-core/src/triggers/time-on-site.ts","../../tracking-core/src/triggers/specific-page-visit.ts","../../tracking-core/src/triggers/navigation.ts","../../tracking-core/src/triggers/scroll-measurement.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/triggers/page-exit.ts","../../tracking-core/src/triggers/cta-click-capture.ts","../../tracking-core/src/triggers/site-link-capture.ts","../../tracking-core/src/triggers/phone-click-capture.ts","../../tracking-core/src/lead-forms/lead-forms.gen.ts","../../tracking-core/src/lead-forms/submit.ts","../../tracking-core/src/resources/sales/bookings.ts","../src/AdPlatformTracking.tsx","../src/GoogleAdsTracking.tsx","../src/factory.tsx","../package.json","../../tracking-core/src/phone-react.tsx","../../tracking-core/src/booking-react.tsx","../../tracking-core/src/video-react.tsx"],"sourcesContent":["\"use client\";\n// The React surface of @aranova/tracking-react, behind a real client boundary.\n//\n// Same build constraint as tracking-next: tsup runs with `splitting: false`,\n// so esbuild inlines non-entry modules into whatever entry imports them and\n// DROPS their `\"use client\"` directive. Only a directive leading an ENTRY file\n// survives. This file is that entry; it is marked external under its own\n// package specifier (see tsup.config.ts) so nothing inlines it back, and\n// index.ts re-exports from it.\n//\n// Consumers keep importing from the package root. The root entry itself stays\n// directive-free so its React-free helpers remain usable from server code.\n//\n// package.json needs a `typesVersions` entry for this subpath, not just an\n// `exports` one: node10 type resolution ignores `exports`, and the root\n// index.d.ts re-exports from this specifier — so without it, attw fails the\n// root entrypoint with an unresolvable internal import. (`blog-client` needs\n// no such entry: its specifier only appears in blog-server.d.ts, which node10\n// cannot reach at all, so it is never analyzed.)\n//\n// Keep the directive on line 1.\n\nexport { ConsentBanner, type ConsentBannerProps } from \"./ConsentBanner\";\nexport { AdPlatformTracking, type AdPlatformTrackingProps } from \"./AdPlatformTracking\";\nexport { GoogleAdsTracking, type GoogleAdsTrackingProps } from \"./GoogleAdsTracking\";\nexport {\n useConsent,\n useConsentState,\n useCookiePreferences,\n useGclid,\n useTrackingParams,\n type UseConsentResult,\n type UseCookiePreferencesOptions,\n type UseCookiePreferencesResult,\n} from \"./hooks\";\nexport {\n createTracking,\n type CreateTrackingOptions,\n type CreateTrackingResult,\n type TrackingProviderProps,\n} from \"./factory\";\nexport { PhoneField, usePhoneConfig, usePhoneField } from \"./phone\";\nexport type {\n PhoneFieldApi,\n PhoneFieldProps,\n PhoneInputProps,\n UsePhoneFieldOptions,\n} from \"./phone\";\nexport {\n BookingHandoffLink,\n useBookingHandoffUrl,\n useBookingIntegration,\n useBookingSlots,\n useBookingSubmit,\n} from \"./booking\";\nexport type {\n BookedAttempt,\n BookingHandoffLinkProps,\n BookingHandoffOptions,\n BookingSubmitState,\n BookingSubmitStatus,\n} from \"./booking\";\n// Typed lead-form submit (ADR-0024): here, not on the root, so it shares the tracking\n// client's module state — the factory's settings, the capability registry, the config runtime.\nexport { createLeadFormSubmit } from \"../../tracking-core/src/lead-forms/submit\";\nexport {\n DEFAULT_VIDEO_CHECKPOINTS,\n streamManifestUrl,\n streamPosterUrl,\n TrackedVideo,\n TrackedVideoStage,\n useTrackedVideoConfig,\n} from \"./video\";\nexport type {\n TrackedVideoCallbacks,\n TrackedVideoEvent,\n TrackedVideoProps,\n TrackedVideoSources,\n TrackedVideoStageProps,\n VideoQuality,\n} from \"./video\";\n","\"use client\";\n\nimport { useEffect, useState, type CSSProperties, type ReactNode } from \"react\";\n\nimport { useConsent } from \"./hooks\";\n\n/**\n * Legacy opt-in-era consent banner.\n *\n * @deprecated PERMANENTLY INERT since consent v2 (opt-out model): it renders\n * only while consent is `pending`, and the effective state is never `pending`\n * anymore, so this component always returns `null`. Tracking is on by default;\n * replace the banner with a footer \"cookie preferences\" control built on\n * {@link useCookiePreferences} (see the package README). Kept exported so\n * existing integrations keep compiling; scheduled for removal.\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\n/**\n * @deprecated Permanently inert since consent v2 — always renders `null`\n * because the effective consent state is never `pending`. Use a footer\n * control built on {@link useCookiePreferences} instead. See\n * {@link ConsentBannerProps} for details.\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_EXPIRES_AT_KEY,\n CONSENT_STATE_KEY,\n CONSENT_TIMESTAMP_KEY,\n createEmptyTrackingParams,\n getConsentChoice,\n getCookieValueFromDocument,\n getTrackingParamsFromCookieReader,\n onConsentChange,\n optIn,\n optOut,\n registerCapability,\n resetConsent,\n type ConsentChoice,\n type ConsentChoiceState,\n type ConsentSource,\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/** Options for {@link useCookiePreferences}. */\nexport interface UseCookiePreferencesOptions {\n /** Days an explicit decline is honored. Defaults to 90. */\n declineTtlDays?: number;\n}\n\n/**\n * The headless cookie-preferences surface returned by\n * {@link useCookiePreferences}.\n */\nexport interface UseCookiePreferencesResult {\n /** Effective consent — `granted` unless an unexpired explicit decline exists. */\n state: ConsentChoiceState;\n /** `default` = no valid explicit choice stored; `explicit` = visitor chose. */\n source: ConsentSource;\n /** True when the visitor has made no (valid, unexpired) explicit choice. */\n isDefault: boolean;\n isGranted: boolean;\n isDenied: boolean;\n /** ISO timestamp of the explicit choice; null for the default state. */\n updatedAt: string | null;\n /** ISO expiry of an unexpired decline; null otherwise. */\n expiresAt: string | null;\n /** Explicitly opt out of ad tracking (honored for 90 days by default). */\n optOut: () => void;\n /** Explicitly opt in (never expires). */\n optIn: () => void;\n /** Clear the explicit choice — back to default-granted. */\n reset: () => void;\n}\n\nconst DEFAULT_CHOICE: ConsentChoice = {\n state: \"granted\",\n source: \"default\",\n updatedAt: null,\n expiresAt: null,\n};\n\n/**\n * Headless cookie-preferences hook for the opt-out consent model (consent v2).\n *\n * Tracking is ON by default; this hook is how each client site wires its own\n * footer \"Cookie preferences\" control (button, dialog, toggle — the packages\n * ship no consent UI). State stays in sync with actions from other components\n * in the same tab (via `onConsentChange`) and other tabs (via `storage`\n * events).\n *\n * ```tsx\n * function CookiePreferences() {\n * const { isDenied, optOut, optIn } = useCookiePreferences();\n * return isDenied ? (\n * <button onClick={optIn}>Enable ad measurement</button>\n * ) : (\n * <button onClick={optOut}>Opt out of ad measurement</button>\n * );\n * }\n * ```\n */\nexport function useCookiePreferences(\n options?: UseCookiePreferencesOptions,\n): UseCookiePreferencesResult {\n // This hook is the only way a site can offer the mandatory footer opt-out\n // control, so its use is the signal that the control actually shipped.\n registerCapability(\"consent_controls\");\n // SSR/pre-mount value = the effective default; synced from storage on mount.\n const [choice, setChoice] = useState<ConsentChoice>(DEFAULT_CHOICE);\n const ttlDays = options?.declineTtlDays;\n\n useEffect(() => {\n const sync = (): void => setChoice(getConsentChoice());\n sync();\n\n // Cross-tab: storage events (key === null means storage.clear()). The\n // timestamp key matters too — a re-affirmed explicit choice in another tab\n // writes the same state value (no event for it), only a new timestamp.\n const handleStorage = (event: StorageEvent): void => {\n if (\n event.key === null ||\n event.key === CONSENT_STATE_KEY ||\n event.key === CONSENT_EXPIRES_AT_KEY ||\n event.key === CONSENT_TIMESTAMP_KEY\n )\n sync();\n };\n window.addEventListener(\"storage\", handleStorage);\n\n // Same-tab: another component's optIn/optOut/reset.\n const unsubscribe = onConsentChange(sync);\n\n return () => {\n window.removeEventListener(\"storage\", handleStorage);\n unsubscribe();\n };\n }, []);\n\n // State updates flow back through onConsentChange — actions never setState.\n const optOutAction = useCallback((): void => {\n optOut(ttlDays != null ? { declineTtlDays: ttlDays } : undefined);\n }, [ttlDays]);\n\n const optInAction = useCallback((): void => {\n optIn();\n }, []);\n\n const reset = useCallback((): void => {\n resetConsent();\n }, []);\n\n return {\n state: choice.state,\n source: choice.source,\n isDefault: choice.source === \"default\",\n isGranted: choice.state === \"granted\",\n isDenied: choice.state === \"denied\",\n updatedAt: choice.updatedAt,\n expiresAt: choice.expiresAt,\n optOut: optOutAction,\n optIn: optInAction,\n reset,\n };\n}\n\n/**\n * Read the current visitor consent state.\n *\n * @deprecated Since consent v2 (opt-out model) the state is never `pending`.\n * Use {@link useCookiePreferences} — it exposes the effective state plus\n * `source` so you can tell a default grant from an explicit one.\n */\nexport function useConsentState(): ConsentState {\n return useConsent().state;\n}\n\n/**\n * Result shape of the deprecated {@link useConsent} hook.\n *\n * @deprecated Use {@link UseCookiePreferencesResult} via\n * {@link useCookiePreferences}. `isPending` is always `false` since consent v2.\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\n/**\n * Legacy opt-in-era consent hook.\n *\n * @deprecated Since consent v2 tracking defaults ON (opt-out model): the state\n * is never `pending`, so banner UIs gated on `isPending` never render. Use\n * {@link useCookiePreferences} for footer \"cookie preferences\" controls.\n * `accept` / `decline` still work and map to `optIn` / `optOut`.\n */\nexport function useConsent(): UseConsentResult {\n const {\n state,\n isGranted,\n isDenied,\n optIn: accept,\n optOut: decline,\n reset,\n } = useCookiePreferences();\n\n return {\n state,\n isPending: false,\n isGranted,\n isDenied,\n accept,\n decline,\n reset,\n };\n}\n","// Framework-agnostic phone-number utilities — isomorphic (browser + Node/RSC),\n// zero React. Bundled `libphonenumber-js` (standard metadata) so clients add no\n// dependency. The transmitted value is ALWAYS E.164; display is the only knob.\n\nimport { AsYouType, parsePhoneNumberFromString, type CountryCode } from \"libphonenumber-js\";\n\nexport type { CountryCode };\n\n/**\n * How a phone number is shown in the UI. The transmitted value is always E.164\n * and is deliberately NOT part of this — only the display format is configurable.\n * A function form covers the long tail (`(parsed) => string`).\n */\nexport type PhoneDisplayFormat =\n | \"national\"\n | \"international\"\n | \"e164\"\n | ((parsed: ParsedPhone) => string);\n\nexport interface ParsedPhone {\n /** E.164 (`\"+14165550199\"`) or `null` when the input isn't a valid number. This is what gets transmitted. */\n e164: string | null;\n /** National display form (`\"(416) 555-0199\"`); empty string when unparseable. */\n national: string;\n /** International display form (`\"+1 416 555 0199\"`); empty string when unparseable. */\n international: string;\n /** ISO-3166 country resolved by libphonenumber, or `null`. */\n country: CountryCode | null;\n isValid: boolean;\n}\n\n/** Region assumed for numbers typed without a country code. */\nexport const DEFAULT_PHONE_COUNTRY: CountryCode = \"CA\";\n\n/** Parse a raw/display string into every representation at once (one parse → display + wire never drift). */\nexport function parsePhone(raw: string, country?: CountryCode): ParsedPhone {\n const region = country ?? DEFAULT_PHONE_COUNTRY;\n const parsed = parsePhoneNumberFromString(raw ?? \"\", region);\n if (!parsed) {\n return { e164: null, national: \"\", international: \"\", country: region, isValid: false };\n }\n const isValid = parsed.isValid();\n return {\n // E.164 is only surfaced for a *valid* number — a possible-but-invalid input\n // (e.g. too few digits) still parses but must not be transmitted.\n e164: isValid ? parsed.number : null,\n national: parsed.formatNational(),\n international: parsed.formatInternational(),\n country: parsed.country ?? region,\n isValid,\n };\n}\n\n/** Normalize any raw/display value to E.164, or `null` if it isn't a valid number. */\nexport function toE164(raw: string, country?: CountryCode): string | null {\n return parsePhone(raw, country).e164;\n}\n\n/** Format a value for display. Defaults to `'national'`. Never affects the wire value. */\nexport function formatPhone(\n value: string,\n format: PhoneDisplayFormat = \"national\",\n country?: CountryCode,\n): string {\n const parsed = parsePhone(value, country);\n if (typeof format === \"function\") return format(parsed);\n switch (format) {\n case \"international\":\n return parsed.international || value;\n case \"e164\":\n return parsed.e164 ?? value;\n case \"national\":\n default:\n return parsed.national || value;\n }\n}\n\n/** Live, incremental formatting for an `<input>` as the user types (`AsYouType`). */\nexport function formatPhoneAsTyped(raw: string, country?: CountryCode): string {\n return new AsYouType(country ?? DEFAULT_PHONE_COUNTRY).input(raw ?? \"\");\n}\n","// Enhanced-conversions user_data bridge (Google Ads \"user-provided data\").\n//\n// Why this exists: Google's automatic user-provided-data capture demonstrably\n// misses phone fields on client sites (verified live: a filled tel input never\n// produced a `pn` hit parameter), and our offline sale uploads are phone-hash\n// keyed — without a phone↔click pairing built at conversion time, none of them\n// can match. This module makes the SDK the deterministic capture path: it\n// extracts the visitor's own email/phone from `form_submit` fields (or an\n// explicit override / a recorded sale's customer fields), normalizes them\n// (E.164 phone, lowercased email), and sets them as PLAINTEXT\n// `gtag('set','user_data', …)` immediately before a conversion fires. gtag.js\n// normalizes further and SHA-256 hashes IN THE BROWSER — plaintext never\n// leaves the page, and we never own Google's hashing contract.\n//\n// Privacy posture: the stash is module-level memory only — never persisted to\n// any storage, never added to our own ingest payloads (the form fields were\n// already part of the site's `form_submit` metadata; we only read them).\n// Consent gating happens at the egress in `fireConversionWithConsent` (the\n// same gate every conversion already passes through); an explicit consent\n// denial additionally clears the stash and nulls gtag's page state (see\n// consent.ts). This module must NOT import consent.ts — consent.ts imports us\n// for that clearing hook, and the reverse edge would be a cycle.\n\nimport { toE164, type CountryCode } from \"./phone\";\n\n/** Raw identifiers a caller may hand us (normalized before use). */\nexport interface ConversionUserData {\n email?: string | null;\n phone?: string | null;\n}\n\n/** Normalized identifiers as gtag's `user_data` expects them. */\nexport interface NormalizedUserData {\n email: string | null;\n /** E.164 (`\"+16477836797\"`) — the format Google's parser reliably accepts. */\n phoneNumber: string | null;\n}\n\nconst EMAIL_SHAPE = /^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/;\nconst EMAIL_NAME_HINT = /e[-_]?mail/i;\n// `\\b` won't do: underscores are word chars, so `customer_phone` would miss.\n// The leading guard keeps bare \"tel\" from matching e.g. \"hotel\".\nconst PHONE_NAME_HINT = /(^|[^a-z])(phone|tel|mobile|cell)/i;\n\n// Page-lifetime, memory-only. Freshest non-null value wins per identifier.\nlet stash: NormalizedUserData = { email: null, phoneNumber: null };\n\n/**\n * Trim + lowercase + shape-check. Deliberately NO gmail dot/plus\n * canonicalization — gtag.js applies Google's own normalization before\n * hashing, and owning that contract here would be drift waiting to happen.\n */\nexport function normalizeEmail(raw: unknown): string | null {\n if (typeof raw !== \"string\") return null;\n const cleaned = raw.trim().toLowerCase();\n return EMAIL_SHAPE.test(cleaned) ? cleaned : null;\n}\n\ninterface FieldLike {\n name?: unknown;\n type?: unknown;\n label?: unknown;\n value?: unknown;\n}\n\nfunction fieldText(field: FieldLike, key: \"name\" | \"type\" | \"label\"): string {\n const value = field[key];\n return typeof value === \"string\" ? value : \"\";\n}\n\n/**\n * Deterministic extraction from `form_submit` `fields[]`: for each identifier,\n * a typed field (`type=\"email\"` / `type=\"tel\"`) wins over a name/label match\n * (`user_email`, `customer_phone`, …), in document order. A field whose value\n * fails normalization does NOT consume the slot — scanning continues, so one\n * malformed phone field can't mask a later valid one. Never throws.\n */\nexport function extractUserDataFromFormFields(\n fields: unknown,\n country?: CountryCode,\n): NormalizedUserData {\n const result: NormalizedUserData = { email: null, phoneNumber: null };\n if (!Array.isArray(fields)) return result;\n const passes: Array<(field: FieldLike, hint: RegExp, type: string) => boolean> = [\n (field, _hint, type) => fieldText(field, \"type\").toLowerCase() === type,\n (field, hint) => hint.test(fieldText(field, \"name\")) || hint.test(fieldText(field, \"label\")),\n ];\n for (const matches of passes) {\n for (const raw of fields) {\n if (raw === null || typeof raw !== \"object\") continue;\n const field = raw as FieldLike;\n if (typeof field.value !== \"string\" || field.value.length === 0) continue;\n if (result.email === null && matches(field, EMAIL_NAME_HINT, \"email\")) {\n result.email = normalizeEmail(field.value);\n }\n if (result.phoneNumber === null && matches(field, PHONE_NAME_HINT, \"tel\")) {\n try {\n result.phoneNumber = toE164(field.value, country);\n } catch {\n // libphonenumber choked on garbage — leave the slot open\n }\n }\n }\n if (result.email !== null && result.phoneNumber !== null) break;\n }\n return result;\n}\n\n/** Normalize + merge into the stash (non-null values win over existing). */\nexport function stashUserData(data: ConversionUserData, country?: CountryCode): void {\n const email = normalizeEmail(data.email);\n let phoneNumber: string | null = null;\n if (typeof data.phone === \"string\" && data.phone.length > 0) {\n try {\n phoneNumber = toE164(data.phone, country);\n } catch {\n phoneNumber = null;\n }\n }\n stash = {\n email: email ?? stash.email,\n phoneNumber: phoneNumber ?? stash.phoneNumber,\n };\n}\n\n/** Extract from `form_submit` fields and merge into the stash. Never throws. */\nexport function stashUserDataFromFormFields(fields: unknown, country?: CountryCode): void {\n try {\n const extracted = extractUserDataFromFormFields(fields, country);\n stash = {\n email: extracted.email ?? stash.email,\n phoneNumber: extracted.phoneNumber ?? stash.phoneNumber,\n };\n } catch {\n // extraction must never break event tracking\n }\n}\n\n/** Snapshot of the current stash (copy — mutations don't leak back). */\nexport function getStashedUserData(): NormalizedUserData {\n return { ...stash };\n}\n\n/** Drop everything (consent denial, tests). */\nexport function clearStashedUserData(): void {\n stash = { email: null, phoneNumber: null };\n}\n\n/**\n * Set gtag's per-page `user_data` right before a conversion fires: explicit\n * values win per-key over the stash; an empty merge sets nothing at all.\n * Browser-gated and throw-proof — the conversion must fire regardless.\n * NOT consent-checked here: the single caller (`fireConversionWithConsent`)\n * has already returned on denial before reaching this.\n *\n * Returns whether a `gtag('set','user_data', …)` call was made.\n */\nexport function applyUserDataForConversion(\n explicit?: ConversionUserData | null,\n country?: CountryCode,\n): boolean {\n if (typeof window === \"undefined\" || typeof window.gtag !== \"function\") return false;\n let email = stash.email;\n let phoneNumber = stash.phoneNumber;\n if (explicit) {\n const normalizedEmail = normalizeEmail(explicit.email);\n if (normalizedEmail) email = normalizedEmail;\n if (typeof explicit.phone === \"string\" && explicit.phone.length > 0) {\n try {\n phoneNumber = toE164(explicit.phone, country) ?? phoneNumber;\n } catch {\n // keep the stashed phone\n }\n }\n }\n if (email === null && phoneNumber === null) return false;\n try {\n window.gtag(\"set\", \"user_data\", {\n ...(email !== null ? { email } : {}),\n ...(phoneNumber !== null ? { phone_number: phoneNumber } : {}),\n });\n return true;\n } catch {\n return false;\n }\n}\n","import type { ConsentState } from \"./types\";\nimport { clearStashedUserData } from \"./user-data\";\n\n/**\n * localStorage key for the visitor's explicit 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 * localStorage key for the ISO timestamp when an explicit decline expires.\n *\n * Written alongside a `denied` choice; absent for grants (they never expire).\n * Computed at WRITE time so every reader — including the inline\n * `beforeInteractive` scripts that can't import this module — only has to\n * compare a stored ISO string against the clock.\n */\nexport const CONSENT_EXPIRES_AT_KEY = \"consent_expires_at\";\n\n/**\n * How long an explicit decline is honored before the visitor reverts to the\n * default-granted state. Explicit grants never expire.\n */\nexport const DEFAULT_DECLINE_TTL_DAYS = 90;\n\nconst DAY_MS = 86_400_000;\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/** Effective consent state — the opt-out model has no \"pending\". */\nexport type ConsentChoiceState = \"granted\" | \"denied\";\n\n/**\n * Where the effective state came from: `explicit` when the visitor made a\n * stored, still-valid choice; `default` otherwise (no choice, expired decline,\n * storage blocked, SSR).\n */\nexport type ConsentSource = \"default\" | \"explicit\";\n\n/**\n * The effective consent decision plus its provenance.\n */\nexport interface ConsentChoice {\n state: ConsentChoiceState;\n source: ConsentSource;\n /** ISO timestamp of the explicit choice; null for the default state. */\n updatedAt: string | null;\n /** ISO expiry of an unexpired decline; null for grants and the default state. */\n expiresAt: string | null;\n}\n\n/** Options for explicit consent writes. */\nexport interface SetConsentOptions {\n /** Days an explicit decline is honored. Defaults to {@link DEFAULT_DECLINE_TTL_DAYS}. */\n declineTtlDays?: number;\n}\n\nconst DEFAULT_CHOICE: ConsentChoice = {\n state: \"granted\",\n source: \"default\",\n updatedAt: null,\n expiresAt: null,\n};\n\ntype ConsentChangeListener = (choice: ConsentChoice) => void;\nconst changeListeners = new Set<ConsentChangeListener>();\n\n/**\n * Subscribe to consent changes (opt-in, opt-out, reset) made in THIS tab.\n * Cross-tab changes surface via the browser's `storage` event instead.\n * Returns an unsubscribe function.\n */\nexport function onConsentChange(listener: ConsentChangeListener): () => void {\n changeListeners.add(listener);\n return () => {\n changeListeners.delete(listener);\n };\n}\n\n// Takes the intended choice rather than re-reading storage: when localStorage\n// is blocked, a re-read would report default-granted right after an opt-out\n// click even though the live gtag/fbq revoke DID apply for this page load.\nfunction notifyConsentChanged(choice: ConsentChoice): void {\n for (const listener of changeListeners) {\n try {\n listener(choice);\n } catch {\n // a listener must never break the consent flow\n }\n }\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 * Resolve the visitor's effective consent (opt-out model).\n *\n * Default is GRANTED. Only a stored, unexpired explicit decline yields\n * `denied`. SSR, blocked storage, and expired declines all resolve to the\n * default. A legacy decline stored by the opt-in-era SDK (no expiry key) stays\n * denied and gets a fresh 90-day expiry backfilled, keeping this reader\n * consistent with the inline scripts (which treat a missing expiry as denied).\n */\nexport function getConsentChoice(): ConsentChoice {\n if (typeof window === \"undefined\") return DEFAULT_CHOICE;\n\n try {\n const stored = window.localStorage.getItem(CONSENT_STATE_KEY);\n const updatedAt = window.localStorage.getItem(CONSENT_TIMESTAMP_KEY);\n\n if (stored === \"granted\")\n return { state: \"granted\", source: \"explicit\", updatedAt, expiresAt: null };\n\n if (stored === \"denied\") {\n let expiresAt = window.localStorage.getItem(CONSENT_EXPIRES_AT_KEY);\n if (!expiresAt) {\n expiresAt = new Date(Date.now() + DEFAULT_DECLINE_TTL_DAYS * DAY_MS).toISOString();\n try {\n window.localStorage.setItem(CONSENT_EXPIRES_AT_KEY, expiresAt);\n } catch {\n // backfill is best-effort; the decline is still honored this read\n }\n }\n // A garbled expiry parses to NaN, the comparison is false, and the decline\n // stays honored (fail closed on the decliner's side). Only a parseable\n // expiry in the past reverts to the default.\n if (!(Date.parse(expiresAt) <= Date.now()))\n return { state: \"denied\", source: \"explicit\", updatedAt, expiresAt };\n }\n } catch {\n // localStorage blocked (sandboxed iframe / storage disabled) — default\n }\n\n return DEFAULT_CHOICE;\n}\n\n/**\n * Read the visitor's effective consent state.\n *\n * Opt-out model: returns `granted` unless a stored, unexpired explicit decline\n * exists. Never returns `pending` — that value survives in {@link ConsentState}\n * only so pre-v2 call sites keep compiling.\n */\nexport function getConsentState(): ConsentState {\n return getConsentChoice().state;\n}\n\nfunction pushConsentToPlatforms(state: GtagConsentValue): void {\n if (typeof window === \"undefined\") return;\n\n if (typeof window.gtag === \"function\")\n window.gtag(\"consent\", \"update\", buildConsentPayload(state));\n\n // Mirror to the Meta Pixel so one control gates both ad platforms.\n if (typeof window.fbq === \"function\")\n window.fbq(\"consent\", state === \"granted\" ? \"grant\" : \"revoke\");\n}\n\n/**\n * Persist an explicit visitor consent choice and push it live to Google\n * Consent Mode and the Meta Pixel.\n *\n * Declines expire after {@link DEFAULT_DECLINE_TTL_DAYS} days (override via\n * `options.declineTtlDays`); grants never expire.\n */\nexport function setConsentState(state: GtagConsentValue, options?: SetConsentOptions): void {\n if (typeof window === \"undefined\") return;\n\n // Compute the full intended choice BEFORE touching storage so (a) a garbage\n // TTL can't throw mid-write (state written, expiry not), and (b) listeners\n // get the visitor's actual choice even when storage is blocked.\n const requestedTtl = options?.declineTtlDays;\n const ttlDays =\n typeof requestedTtl === \"number\" && Number.isFinite(requestedTtl) && requestedTtl > 0\n ? requestedTtl\n : DEFAULT_DECLINE_TTL_DAYS;\n const updatedAt = new Date().toISOString();\n const expiresAt =\n state === \"denied\" ? new Date(Date.now() + ttlDays * DAY_MS).toISOString() : null;\n\n try {\n window.localStorage.setItem(CONSENT_STATE_KEY, state);\n window.localStorage.setItem(CONSENT_TIMESTAMP_KEY, updatedAt);\n if (expiresAt !== null) {\n window.localStorage.setItem(CONSENT_EXPIRES_AT_KEY, expiresAt);\n } else {\n window.localStorage.removeItem(CONSENT_EXPIRES_AT_KEY);\n }\n } catch {\n // localStorage blocked — still push the live consent update below so the\n // visitor's click takes effect for this page load even if it can't persist\n }\n\n pushConsentToPlatforms(state);\n if (state === \"denied\") {\n // A mid-session revoke also scrubs enhanced-conversions page state: drop\n // the stashed identifiers and best-effort null gtag's user_data.\n clearStashedUserData();\n try {\n if (typeof window.gtag === \"function\") window.gtag(\"set\", \"user_data\", null);\n } catch {\n // scrubbing is best-effort; the consent update above already gates fires\n }\n }\n notifyConsentChanged({ state, source: \"explicit\", updatedAt, expiresAt });\n}\n\n/**\n * Explicitly opt the visitor in to ad tracking (never expires).\n */\nexport function optIn(): void {\n setConsentState(\"granted\");\n}\n\n/**\n * Explicitly opt the visitor out of ad tracking for\n * {@link DEFAULT_DECLINE_TTL_DAYS} days (override via `options.declineTtlDays`).\n *\n * This is the primitive a client site's footer \"cookie preferences\" control\n * should call — the packages ship no consent UI of their own.\n */\nexport function optOut(options?: SetConsentOptions): void {\n setConsentState(\"denied\", options);\n}\n\n/**\n * Clear the stored explicit choice, returning the visitor to the\n * default-granted state, and push that state live to gtag + Meta.\n *\n * Power a \"Cookie preferences\" reset in a footer:\n *\n * ```tsx\n * const { reset } = useCookiePreferences();\n * <button onClick={reset}>Reset cookie preferences</button>\n * ```\n */\nexport function resetConsent(): void {\n if (typeof window === \"undefined\") return;\n\n try {\n window.localStorage.removeItem(CONSENT_STATE_KEY);\n window.localStorage.removeItem(CONSENT_TIMESTAMP_KEY);\n window.localStorage.removeItem(CONSENT_EXPIRES_AT_KEY);\n } catch {\n // localStorage blocked — nothing to clear\n }\n\n pushConsentToPlatforms(\"granted\");\n notifyConsentChanged(DEFAULT_CHOICE);\n}\n\n/**\n * Re-apply a stored explicit consent choice to Google Consent Mode + Meta.\n *\n * @deprecated The v2 consent-default path ({@link applyDefaultConsentState} /\n * `createConsentDefaultScript`) already applies the effective state at\n * bootstrap, making this redundant. Pushes platform updates directly — it must\n * NOT route through {@link setConsentState}, which would renew the decline's\n * 90-day expiry on every page load.\n */\nexport function restoreStoredConsent(): ConsentState {\n const choice = getConsentChoice();\n\n if (choice.source === \"explicit\") pushConsentToPlatforms(choice.state);\n\n return choice.state;\n}\n\n/**\n * Inline-JS fragment that resolves the effective consent value into\n * `var aranovaConsent = 'granted' | 'denied'`.\n *\n * Single source of the expiry logic for every inline `<script>` builder (gtag\n * and Meta), so the runtime reader and the inline readers can't drift. Must\n * stay dependency-free ES5 and never throw (localStorage access throws in\n * sandboxed iframes).\n */\nexport function createEffectiveConsentSnippet(): string {\n // IIFE keeps the temporaries off the page's global scope — only\n // `aranovaConsent` (the value consumed by the rest of the script) leaks.\n return `\nvar aranovaConsent = (function () {\n try {\n if (window.localStorage.getItem('${CONSENT_STATE_KEY}') === 'denied') {\n var exp = window.localStorage.getItem('${CONSENT_EXPIRES_AT_KEY}');\n var expMs = exp ? Date.parse(exp) : NaN;\n if (!(expMs <= Date.now())) return 'denied';\n }\n } catch (e) {}\n return 'granted';\n})();\n`.trim();\n}\n","// Capability self-report registry.\n//\n// Every SDK construct registers itself here at construction time, and the\n// registered set rides out on `TrackingClientContext.capabilities`, which the\n// backend persists on the session row. The dashboard derives what a client site\n// ACTUALLY has installed from those sessions, rather than trusting an operator\n// checkbox — see contracts/tracking-capabilities.json, which pins this id list\n// against the backend enum and the dashboard type union.\n//\n// Browser-only by design: a server-side client (calendar bookings) emits no\n// session, and its artifact is better evidence than a self-report anyway.\n//\n// This module deliberately imports nothing: it is the one thing every other\n// resource can import without adding an edge that could close a cycle.\n\n/**\n * Every capability an install can report. Pinned by\n * `contracts/tracking-capabilities.json` — adding one here without a catalog\n * entry fails the drift test.\n */\nexport const TRACKING_CAPABILITIES = [\n \"ad_tags_google\",\n \"ad_tags_meta\",\n \"base_tracking\",\n \"blog_rendering\",\n \"booking_handoff\",\n \"booking_integration\",\n \"calendar_read\",\n \"calendar_write\",\n \"consent_controls\",\n \"conversion_goals_auto\",\n \"conversion_goals_manual\",\n \"cross_site_linker\",\n \"cta_click_capture\",\n \"form_capture\",\n \"phone_click_capture\",\n \"phone_fields\",\n \"typed_form_submit\",\n \"video_tracking\",\n] as const;\n\nexport type TrackingCapability = (typeof TRACKING_CAPABILITIES)[number];\n\n/**\n * DOM attribute a server-rendered surface stamps to report itself.\n *\n * Blog rendering never calls the Aranova API — it reads the CDN directly — so\n * neither the ingest payload nor the request-header path can see it. The\n * renderer stamps this attribute instead and the browser context collector\n * picks it up on the next batch.\n */\nexport const CAPABILITY_DOM_ATTRIBUTE = \"data-aranova-capability\";\n\nconst registered = new Set<TrackingCapability>();\n\n/**\n * Record that a capability is present in this runtime.\n *\n * Idempotent and side-effect free — safe to call on every construction,\n * including React strict-mode double mounts.\n */\nexport function registerCapability(capability: TrackingCapability): void {\n registered.add(capability);\n}\n\n/**\n * Capabilities registered so far in this runtime, sorted for a stable wire\n * value (so the backend can hash the array to skip redundant writes).\n *\n * Also collects `data-aranova-capability` markers from the DOM, which is how\n * server-rendered surfaces that never call our API report themselves.\n */\nexport function getRegisteredCapabilities(): TrackingCapability[] {\n const all = new Set<TrackingCapability>(registered);\n for (const marker of readDomMarkers()) all.add(marker);\n return Array.from(all).sort();\n}\n\nfunction readDomMarkers(): TrackingCapability[] {\n if (typeof document === \"undefined\") return [];\n const known = new Set<string>(TRACKING_CAPABILITIES);\n const found: TrackingCapability[] = [];\n // Unknown values are dropped rather than forwarded: the backend rejects ids\n // outside its enum, and a stray attribute on a client's page must never be\n // able to fail an otherwise-valid ingest batch.\n for (const node of document.querySelectorAll(`[${CAPABILITY_DOM_ATTRIBUTE}]`)) {\n const value = node.getAttribute(CAPABILITY_DOM_ATTRIBUTE);\n if (value && known.has(value)) found.push(value as TrackingCapability);\n }\n return found;\n}\n\n/** Test-only: drop everything registered so far. */\nexport function resetRegisteredCapabilitiesForTests(): void {\n registered.clear();\n}\n","// Cross-site session linkage: the origin site's SDK appends an `_arv` identity\n// token to links pointing at a sibling hostname; the destination consumes it\n// before minting ids. Sessions are LINKED (`origin_*`), never shared. The token\n// never reaches the backend — only its decoded fields do.\n\nimport * as z from \"zod/mini\";\n\nimport { getConsentChoice } from \"./consent\";\nimport { FBC_COOKIE, getFbcCookie } from \"./fbq\";\nimport { adoptVisitorId, getOrRotateSessionId, getVisitorId } from \"./session\";\nimport { persistCookieValue, readCookieValue, setTrackingCookie } from \"./tracking\";\n\n/** Query parameter the token rides in. */\nexport const LINK_PARAM = \"_arv\";\n\n/** Token version byte; bump only when a key's meaning changes. */\nexport const LINK_TOKEN_VERSION = \"1\";\n\n/** A token older than this is treated as a shared/bookmarked URL, not a hop. */\nexport const LINK_TOKEN_TTL_MS = 5 * 60 * 1000;\n\n// Clock skew tolerance: a token stamped slightly in the future is still fresh.\nconst FUTURE_SKEW_MS = 60 * 1000;\n\n/** Carried click ids the token transports: short wire key → attribution param. */\nconst CARRIED_KEYS = {\n g: \"gclid\",\n w: \"wbraid\",\n gb: \"gbraid\",\n f: \"fbclid\",\n y: \"ylpcid\",\n} as const;\n\ntype CarriedWireKey = keyof typeof CARRIED_KEYS;\ntype CarriedParamKey = (typeof CARRIED_KEYS)[CarriedWireKey];\nconst CARRIED_ENTRIES = Object.entries(CARRIED_KEYS) as Array<[CarriedWireKey, CarriedParamKey]>;\n\nconst identifier = z.string().check(z.minLength(1), z.maxLength(128));\nconst optionalIdentifier = z.optional(identifier);\n\n/** Decoded token payload — the single schema every reader and writer shares.\n * `z.object`, not `strictObject`: an unknown key from a newer writer is dropped\n * rather than failing the whole token, which is what makes a mixed fleet safe. */\nexport const linkTokenSchema = z.object({\n v: identifier,\n s: identifier,\n // Epoch ms in base36.\n t: z.string().check(z.regex(/^[0-9a-z]{1,12}$/)),\n g: optionalIdentifier,\n w: optionalIdentifier,\n gb: optionalIdentifier,\n f: optionalIdentifier,\n fc: optionalIdentifier,\n y: optionalIdentifier,\n});\n\nexport type LinkTokenPayload = z.infer<typeof linkTokenSchema>;\n\n/** Typed result of consuming a valid, fresh token. */\nexport interface OriginLink {\n visitorId: string;\n sessionId: string;\n /** Epoch ms the origin site stamped the link. */\n linkedAt: number;\n carried: Partial<Record<CarriedParamKey, string>>;\n fbc: string | null;\n}\n\n/** `stale` = older than the TTL (a shared or bookmarked URL); `invalid` = unreadable,\n * unknown version, or stamped beyond the clock-skew tolerance. */\nexport type InboundLinkOutcome = \"linked\" | \"stale\" | \"invalid\";\n\nexport interface InboundLinkResult {\n outcome: InboundLinkOutcome;\n link: OriginLink | null;\n /** True when this browser had no visitor id and took the origin's. */\n adoptedVisitor: boolean;\n}\n\nfunction toBase64Url(input: string): string {\n const bytes = new TextEncoder().encode(input);\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nfunction fromBase64Url(input: string): string | null {\n try {\n const padded = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const binary = atob(padded + \"=\".repeat((4 - (padded.length % 4)) % 4));\n const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));\n return new TextDecoder().decode(bytes);\n } catch {\n return null;\n }\n}\n\n/** Encode a payload into the `_arv` value. */\nexport function encodeLinkToken(payload: LinkTokenPayload): string {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(payload)) {\n if (typeof value === \"string\" && value.length > 0) params.set(key, value);\n }\n return `${LINK_TOKEN_VERSION}.${toBase64Url(params.toString())}`;\n}\n\n/** Decode an `_arv` value; an unknown version or a bad payload is an undecorated URL. */\nexport function decodeLinkToken(raw: string): LinkTokenPayload | null {\n const dot = raw.indexOf(\".\");\n if (dot <= 0 || raw.slice(0, dot) !== LINK_TOKEN_VERSION) return null;\n const decoded = fromBase64Url(raw.slice(dot + 1));\n if (decoded === null) return null;\n const params = new URLSearchParams(decoded);\n const candidate: Record<string, string> = {};\n params.forEach((value, key) => {\n candidate[key] = value;\n });\n const result = linkTokenSchema.safeParse(candidate);\n return result.success ? result.data : null;\n}\n\nfunction payloadToLink(payload: LinkTokenPayload): OriginLink {\n const carried: Partial<Record<CarriedParamKey, string>> = {};\n for (const [wire, param] of CARRIED_ENTRIES) {\n const value = payload[wire];\n if (value) carried[param] = value;\n }\n return {\n visitorId: payload.v,\n sessionId: payload.s,\n linkedAt: Number.parseInt(payload.t, 36),\n carried,\n fbc: payload.fc ?? null,\n };\n}\n\nfunction stripWww(hostname: string): string {\n return hostname.toLowerCase().replace(/^www\\./, \"\");\n}\n\n/** True when `hostname` is `sibling` or a subdomain of it (www-insensitive). */\nexport function isSiblingHost(hostname: string, sibling: string): boolean {\n const host = stripWww(hostname);\n const target = stripWww(sibling);\n return host === target || host.endsWith(`.${target}`);\n}\n\n/** One outbound hop: the sibling this link reaches, and the URL to navigate to. */\nexport interface CrossSiteLink {\n /** The roster hostname matched. */\n hostname: string;\n /** `url` with a fresh token, or `url` unchanged under an explicit consent decline. */\n decorated: string;\n}\n\n/**\n * Resolve a link to a sibling site and decorate it in the same pass — the\n * caller learns \"this is a hop\" independently of whether the URL changed.\n * An explicit decline still resolves (the hop is ordinary analytics) but\n * carries no token, so no identity crosses. Own host and non-http(s) never qualify.\n */\nexport function resolveCrossSiteLink(\n url: string,\n linkedHostnames: readonly string[],\n now: number = Date.now(),\n): CrossSiteLink | null {\n if (typeof window === \"undefined\" || linkedHostnames.length === 0) return null;\n let target: URL;\n try {\n target = new URL(url, window.location.href);\n } catch {\n return null;\n }\n if (target.protocol !== \"http:\" && target.protocol !== \"https:\") return null;\n const here = window.location.hostname;\n if (here && isSiblingHost(target.hostname, here)) return null;\n const hostname = linkedHostnames.find((sibling) => isSiblingHost(target.hostname, sibling));\n if (hostname === undefined) return null;\n const consent = getConsentChoice();\n if (consent.state === \"denied\" && consent.source === \"explicit\") {\n return { hostname, decorated: url };\n }\n target.searchParams.set(LINK_PARAM, buildOutboundLinkToken(now));\n return { hostname, decorated: target.href };\n}\n\n/** Remove the token from a URL string. Returns the input untouched when unparseable. */\nexport function stripLinkParam(url: string): string {\n try {\n const parsed = new URL(url, typeof window === \"undefined\" ? undefined : window.location.href);\n if (!parsed.searchParams.has(LINK_PARAM)) return url;\n parsed.searchParams.delete(LINK_PARAM);\n return parsed.href;\n } catch {\n return url;\n }\n}\n\n/** Remove the token from a raw `?a=b` search string. */\nexport function stripLinkParamFromSearch(search: string): string {\n if (!search.includes(LINK_PARAM)) return search;\n const params = new URLSearchParams(search);\n if (!params.has(LINK_PARAM)) return search;\n params.delete(LINK_PARAM);\n const rest = params.toString();\n return rest.length > 0 ? `?${rest}` : \"\";\n}\n\nfunction readCarried(): Partial<Record<CarriedWireKey, string>> {\n const out: Partial<Record<CarriedWireKey, string>> = {};\n for (const [wire, param] of CARRIED_ENTRIES) {\n const value = readCookieValue(param);\n if (value) out[wire] = value;\n }\n return out;\n}\n\n/** A fresh token from this browser's identity; the session rotates if idle (a click is activity). */\nexport function buildOutboundLinkToken(now: number = Date.now()): string {\n const fbc = getFbcCookie();\n return encodeLinkToken({\n v: getVisitorId(),\n s: getOrRotateSessionId(now).id,\n t: now.toString(36),\n ...readCarried(),\n ...(fbc ? { fc: fbc } : {}),\n });\n}\n\n/** Append (or replace) the token on a link to a sibling site; every other URL passes through. */\nexport function decorateCrossSiteUrl(\n url: string,\n linkedHostnames: readonly string[],\n now: number = Date.now(),\n): string {\n return resolveCrossSiteLink(url, linkedHostnames, now)?.decorated ?? url;\n}\n\n/**\n * Consume an inbound token; must run before this origin mints identity. The\n * visitor id and carried click ids fill in only where this origin has none.\n */\nexport function consumeInboundLink(\n href: string = typeof window === \"undefined\" ? \"\" : window.location.href,\n now: number = Date.now(),\n): InboundLinkResult | null {\n if (typeof window === \"undefined\" || !href) return null;\n let raw: string | null;\n try {\n raw = new URL(href, window.location.href).searchParams.get(LINK_PARAM);\n } catch {\n return null;\n }\n if (!raw) return null;\n\n const payload = decodeLinkToken(raw);\n if (payload === null) return { outcome: \"invalid\", link: null, adoptedVisitor: false };\n const link = payloadToLink(payload);\n const age = now - link.linkedAt;\n if (!Number.isFinite(age) || age < -FUTURE_SKEW_MS) {\n return { outcome: \"invalid\", link: null, adoptedVisitor: false };\n }\n if (age > LINK_TOKEN_TTL_MS) {\n return { outcome: \"stale\", link: null, adoptedVisitor: false };\n }\n\n const adoptedVisitor = adoptVisitorId(link.visitorId);\n for (const [param, value] of Object.entries(link.carried) as Array<[CarriedParamKey, string]>) {\n if (!readCookieValue(param)) setTrackingCookie(param, value);\n }\n // A Pixel-set `_fbc` on this origin is canonical; the origin's only fills a gap.\n if (link.fbc && !getFbcCookie()) persistCookieValue(FBC_COOKIE, link.fbc);\n\n return { outcome: \"linked\", link, adoptedVisitor };\n}\n\n/** Strip the token from the address bar. `null` state, or Next's patched `replaceState` short-circuits. */\nexport function stripLinkParamFromLocation(): void {\n if (typeof window === \"undefined\" || typeof history === \"undefined\") return;\n const clean = stripLinkParam(window.location.href);\n if (clean === window.location.href) return;\n try {\n history.replaceState(null, \"\", clean);\n } catch {\n // never break the host site\n }\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 // Google's iOS/Safari replacement click IDs — issued when privacy features\n // withhold gclid (wbraid: web-to-web, gbraid: app-to-web). First-class\n // citizens: captured, persisted, and attributed exactly like gclid.\n \"wbraid\",\n \"gbraid\",\n // Yelp Ads click id (40 hex chars), appended to ad-click URLs.\n \"ylpcid\",\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 wbraid: null,\n gbraid: null,\n ylpcid: 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 * localStorage fallback prefix. Meta/Facebook in-app browsers (Instagram / FB\n * webviews) frequently block `document.cookie`; when a cookie write can't be\n * confirmed we mirror the value here so attribution (and `_fbc`) survives the\n * session and subsequent in-webview reloads instead of being silently lost.\n */\nconst FALLBACK_STORAGE_PREFIX = \"_aranova_track_\";\n\nfunction fallbackKey(name: string): string {\n return `${FALLBACK_STORAGE_PREFIX}${name}`;\n}\n\n/**\n * Persist a cookie value AND mirror it to localStorage. Generic over the cookie\n * name so it serves the attribution params AND the Meta `_fbc` cookie. Never\n * throws.\n *\n * The localStorage mirror is written UNCONDITIONALLY — it is NOT gated on a\n * cookie read-back. Meta/Instagram in-app webviews partition cookies: the write\n * is visible to a same-page read (so a verify-after-write would falsely pass)\n * yet is silently dropped on the next navigation/reload. Gating the fallback on\n * that read-back would leave the value unrecoverable in exactly the case it\n * exists for; a stale same-name cookie from a prior visit would also mask a\n * blocked write. So we always write both. {@link readCookieValue} prefers the\n * cookie when present, which keeps the mirror harmless when cookies work and\n * load-bearing when they don't survive navigation.\n */\nexport function persistCookieValue(\n name: string,\n value: string,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): void {\n const encoded = encodeURIComponent(value);\n if (typeof document !== \"undefined\") {\n try {\n document.cookie = `${name}=${encoded}; Max-Age=${maxAgeSeconds}; Path=/; SameSite=Lax`;\n } catch {\n // cookie blocked — the localStorage mirror below is the fallback\n }\n }\n if (typeof window !== \"undefined\") {\n try {\n window.localStorage.setItem(fallbackKey(name), encoded);\n } catch {\n // both blocked — give up silently, never break the host site\n }\n }\n}\n\n/**\n * Read a cookie value, falling back to the localStorage mirror written by\n * {@link persistCookieValue} when the cookie is absent (blocked webview).\n */\nexport function readCookieValue(name: string): string | null {\n if (typeof document !== \"undefined\") {\n const cookies = document.cookie ? document.cookie.split(\"; \") : [];\n const match = cookies.find((cookie) => cookie.startsWith(`${name}=`));\n if (match) {\n const [, rawValue = \"\"] = match.split(\"=\");\n return normalizeTrackingCookieValue(decodeURIComponent(rawValue));\n }\n }\n if (typeof window !== \"undefined\") {\n try {\n const stored = window.localStorage.getItem(fallbackKey(name));\n if (stored) return normalizeTrackingCookieValue(decodeURIComponent(stored));\n } catch {\n // ignore\n }\n }\n return null;\n}\n\n/**\n * Read a tracking cookie from `document.cookie` (or the localStorage fallback).\n *\n * Returns `null` during SSR or when neither source has the value.\n */\nexport function getCookieValueFromDocument(key: TrackingParamKey): string | null {\n return readCookieValue(key);\n}\n\n/**\n * Persist one attribution value as a first-party cookie (localStorage fallback\n * when cookies are blocked).\n */\nexport function setTrackingCookie(\n key: TrackingParamKey,\n value: string,\n maxAgeSeconds = TRACKING_COOKIE_MAX_AGE_SECONDS,\n): void {\n persistCookieValue(key, value, maxAgeSeconds);\n}\n\n/**\n * Merge two attribution param sets, preferring `primary`'s non-null values and\n * filling the gaps from `fallback`. Used to combine the cookie-backed read with\n * the synchronous init-time snapshot so a value captured before an SPA router\n * stripped the URL (or before a webview blocked the cookie) still wins.\n */\nexport function mergeTrackingParams(\n primary: TrackingParams,\n fallback: TrackingParams,\n): TrackingParams {\n return TRACKING_PARAM_KEYS.reduce<TrackingParams>((merged, key) => {\n merged[key] = primary[key] ?? fallback[key];\n return merged;\n }, createEmptyTrackingParams());\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 =\n 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","// This module loads the Google tag (gtag.js), applies Consent Mode, and fires\n// `gtag('config', …)` (remarketing) plus real-time `gtag('event','conversion')`\n// via `fireGtagConversion` (GAP28). Consent v2 is OPT-OUT: the effective state\n// is granted unless the visitor stored an explicit, unexpired decline, so tags\n// run from first paint for the default visitor.\n// See docs/google-ads-deployment/gtags-and-conversion-tracking.md.\n\nimport { buildConsentPayload, createEffectiveConsentSnippet, getConsentState } 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 the visitor's EFFECTIVE\n * consent as the Consent Mode default (opt-out model).\n *\n * Resolves granted/denied synchronously from localStorage in one shot — a\n * decliner never gets a granted window and the default visitor never gets a\n * denied one, so no `wait_for_update` and no follow-up restore script is\n * needed. Run it `beforeInteractive` so the default lands before gtag.js.\n */\nexport function createConsentDefaultScript(): string {\n return `\nwindow.dataLayer = window.dataLayer || [];\nfunction gtag(){dataLayer.push(arguments);}\nwindow.gtag = gtag;\n${createEffectiveConsentSnippet()}\ngtag('consent', 'default', {\n ad_storage: aranovaConsent,\n ad_user_data: aranovaConsent,\n ad_personalization: aranovaConsent,\n analytics_storage: aranovaConsent\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 pushes the effective consent as an update.\n *\n * @deprecated Redundant since consent v2 — `createConsentDefaultScript()`\n * already resolves the effective state (including decline expiry) into the\n * Consent Mode default, so no restore pass is needed. Kept expiry-aware for\n * integrations that still render it.\n */\nexport function createConsentRestoreScript(): string {\n return `\n${createEffectiveConsentSnippet()}\nif (window.gtag) {\n window.gtag('consent', 'update', {\n ad_storage: aranovaConsent,\n ad_user_data: aranovaConsent,\n ad_personalization: aranovaConsent,\n analytics_storage: aranovaConsent\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\") return window.gtag;\n\n // CRITICAL: push the `arguments` object, NOT a rest-param array. gtag.js only\n // treats the canonical `dataLayer.push(arguments)` form as a command — a plain\n // array pushed to the dataLayer is ignored. With the array form, every\n // gtag('config' | 'consent' | 'event') call silently no-ops: the account never\n // registers, consent mode never applies, and conversions never fire (verified\n // live — the array form sends zero network hits; the arguments form sends the\n // pagead/conversion ping). Mirror Google's canonical inline snippet exactly.\n function gtag(): void {\n // eslint-disable-next-line prefer-rest-params\n window.dataLayer?.push(arguments);\n }\n window.gtag = gtag;\n\n return window.gtag;\n}\n\nconst SEND_TO_RE = /^AW-[A-Za-z0-9]+\\/[A-Za-z0-9_-]+$/;\n\n/** A gtag `send_to` target, e.g. `AW-123456789/AbC-D_efg`. */\nexport function isValidSendTo(sendTo: string): boolean {\n return SEND_TO_RE.test(sendTo);\n}\n\nexport interface GtagConversionInput {\n /** `AW-<id>/<label>` — the conversion action's firing target. */\n sendTo: string;\n /** Conversion value in MAJOR units; omit to let Google apply the action's default. */\n value?: number | null;\n currency?: string | null;\n transactionId?: string | null;\n}\n\n/**\n * Fire a real-time on-site conversion — `gtag('event','conversion',{ send_to, … })`\n * (GAP28). Browser-gated and never throws: `record()` is isomorphic and runs server-side\n * under a secret key where `window`/`gtag` are absent, so this no-ops there. Consent\n * gating + de-dup are the caller's responsibility (see `fireConversionWithConsent`).\n * Returns whether the event was actually pushed.\n */\nexport function fireGtagConversion(input: GtagConversionInput): boolean {\n if (typeof window === \"undefined\" || typeof window.gtag !== \"function\") return false;\n if (!isValidSendTo(input.sendTo)) return false;\n const params: Record<string, unknown> = { send_to: input.sendTo };\n if (input.value != null) params.value = input.value;\n if (input.currency) params.currency = input.currency;\n if (input.transactionId) params.transaction_id = input.transactionId;\n try {\n window.gtag(\"event\", \"conversion\", params);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Apply the visitor's EFFECTIVE consent as the Consent Mode default\n * (opt-out model): granted unless a stored, unexpired decline exists.\n *\n * Reading via `getConsentState()` also backfills the expiry key for\n * legacy opt-in-era declines, keeping later inline reads consistent.\n */\nexport function applyDefaultConsentState(): void {\n const gtag = ensureGtagFunction();\n gtag(\n \"consent\",\n \"default\",\n buildConsentPayload(getConsentState() === \"denied\" ? \"denied\" : \"granted\"),\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\") return;\n\n const marker = getScriptMarker(\"gtag-loader\");\n const existingScript = document.querySelector<HTMLScriptElement>(\n `script[${TRACKING_SCRIPT_ATTRIBUTE}=\"${marker}\"]`,\n );\n if (existingScript) 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.append(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 current effective consent to gtag as an update.\n *\n * @deprecated Redundant since consent v2 — `applyDefaultConsentState()`\n * already applies the effective state at bootstrap. Pushes directly (no\n * storage writes), so it never renews a decline's expiry.\n */\nexport function restoreConsentState(): void {\n if (typeof window === \"undefined\") 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\") return;\n if (!isValidGtagId(gtagId)) return;\n\n applyDefaultConsentState();\n loadGtagScript(gtagId);\n initializeGtag(gtagId);\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\") return;\n\n const ids = Object.values(gtagIds).filter(\n (id): id is string => typeof id === \"string\" && isValidGtagId(id),\n );\n if (ids.length === 0) 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\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","// The Meta Pixel (`fbq`) loader — the Facebook analogue of `gtag.ts`. It loads\n// fbevents.js and fires `fbq('init', …)` + `fbq('track','PageView')` to build Meta\n// remarketing / custom audiences (and let Meta optimize delivery + attribute).\n// Consent v2 is OPT-OUT: the pixel runs from first paint unless the visitor\n// stored an explicit, unexpired decline. This is NOT our in-house analytics\n// (that's the event ingest pipeline); it's the ad-platform tag, exactly like the\n// Google tag is for Google Ads.\n//\n// It also forms the Conversions-API-canonical `_fbc` cookie from the landing\n// `fbclid` (and reads the Pixel-set `_fbp`) so a future server-side Meta\n// Conversions API writeback has everything it needs.\n\nimport { createEffectiveConsentSnippet, getConsentState } from \"./consent\";\nimport { persistCookieValue, readCookieValue, TRACKING_COOKIE_MAX_AGE_SECONDS } from \"./tracking\";\nimport { TRACKING_SCRIPT_ATTRIBUTE } from \"./gtag\";\nimport type { MetaPixelEnvironmentMap } from \"./types\";\n\n/** Meta Pixel script host. */\nexport const FB_EVENTS_SCRIPT_HOST = \"https://connect.facebook.net/en_US/fbevents.js\";\n\n/** Cookie names Meta uses for the Conversions API. */\nexport const FBC_COOKIE = \"_fbc\";\nexport const FBP_COOKIE = \"_fbp\";\n\n/**\n * Valid Meta Pixel ID: a 15-16 digit number. IDs that don't match are dropped to\n * prevent script injection when interpolated into inline `<script>` content\n * (mirrors GTAG_ID_PATTERN).\n */\nconst META_PIXEL_ID_PATTERN = /^\\d{15,16}$/;\n\nexport function isValidMetaPixelId(id: string): boolean {\n return META_PIXEL_ID_PATTERN.test(id);\n}\n\n// ---------------------------------------------------------------------------\n// _fbc / _fbp cookie formation\n// ---------------------------------------------------------------------------\n\n/**\n * subdomainIndex for the `_fbc` value: `com` → 0, `example.com` → 1,\n * `www.example.com` → 2 (dot-label count minus one).\n */\nexport function computeFbSubdomainIndex(hostname: string): number {\n const labels = hostname.split(\".\").filter(Boolean);\n return Math.max(0, labels.length - 1);\n}\n\n/**\n * Build the CAPI-canonical `_fbc` value: `fb.<subdomainIndex>.<creationMs>.<fbclid>`.\n * Never hashed.\n */\nexport function buildFbc(fbclid: string, now: number, hostname?: string): string {\n const host = hostname ?? (typeof window === \"undefined\" ? \"\" : window.location.hostname);\n return `fb.${computeFbSubdomainIndex(host)}.${now}.${fbclid}`;\n}\n\n/** Current `_fbc` cookie (or localStorage fallback), if any. */\nexport function getFbcCookie(): string | null {\n return readCookieValue(FBC_COOKIE);\n}\n\n/** Current `_fbp` cookie — set by the Meta Pixel; we only ever read it. */\nexport function getFbpCookie(): string | null {\n return readCookieValue(FBP_COOKIE);\n}\n\nfunction readFbclidFromUrl(): string | null {\n if (typeof window === \"undefined\") return null;\n try {\n const value = new URL(window.location.href).searchParams.get(\"fbclid\");\n return value && value.trim().length > 0 ? value : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Form and persist `_fbc` from the landing `fbclid` when the Pixel hasn't already\n * set it. Reads the fbclid from the URL first, then the captured attribution\n * cookie. No-ops when there's no fbclid or `_fbc` already exists. `_fbp` is\n * Pixel-only — never written here.\n */\nexport function captureFbc(now = typeof Date === \"undefined\" ? 0 : Date.now()): void {\n if (typeof window === \"undefined\") return;\n // A Pixel-set `_fbc` is canonical — don't overwrite it.\n if (getFbcCookie()) return;\n const fbclid = readFbclidFromUrl() ?? readCookieValue(\"fbclid\");\n if (!fbclid) return;\n persistCookieValue(FBC_COOKIE, buildFbc(fbclid, now), TRACKING_COOKIE_MAX_AGE_SECONDS);\n}\n\n// ---------------------------------------------------------------------------\n// fbq loader (mirrors gtag.ts)\n// ---------------------------------------------------------------------------\n\ntype FbqStub = ((...args: unknown[]) => void) & {\n callMethod?: (...args: unknown[]) => void;\n queue: unknown[][];\n push: unknown;\n loaded: boolean;\n version: string;\n};\n\nfunction getScriptMarker(id: string): string {\n return `aranova-${id}`;\n}\n\n/**\n * Ensure `window.fbq` exists (the standard fbevents bootstrap stub) and return\n * it. Safe to call repeatedly — it only initializes once.\n */\nexport function ensureFbqFunction(): NonNullable<Window[\"fbq\"]> {\n const w = window as Window & { fbq?: FbqStub; _fbq?: FbqStub };\n if (typeof w.fbq === \"function\") return w.fbq;\n\n const fbq = function (this: unknown, ...args: unknown[]) {\n if (fbq.callMethod) fbq.callMethod.apply(fbq, args);\n else fbq.queue.push(args);\n } as FbqStub;\n\n fbq.push = fbq;\n fbq.loaded = true;\n fbq.version = \"2.0\";\n fbq.queue = [];\n\n w.fbq = fbq;\n if (!w._fbq) w._fbq = fbq;\n return fbq;\n}\n\n/**\n * Apply the visitor's EFFECTIVE consent to the Meta Pixel (opt-out model):\n * grant unless a stored, unexpired decline exists.\n */\nexport function applyDefaultMetaConsentState(): void {\n ensureFbqFunction()(\"consent\", getConsentState() === \"denied\" ? \"revoke\" : \"grant\");\n}\n\n/** Load fbevents.js once. */\nexport function loadFbeventsScript(): void {\n if (typeof document === \"undefined\") return;\n const marker = getScriptMarker(\"fbq-loader\");\n const existing = document.querySelector<HTMLScriptElement>(\n `script[${TRACKING_SCRIPT_ATTRIBUTE}=\"${marker}\"]`,\n );\n if (existing) return;\n\n const script = document.createElement(\"script\");\n script.async = true;\n script.src = FB_EVENTS_SCRIPT_HOST;\n script.setAttribute(TRACKING_SCRIPT_ATTRIBUTE, marker);\n document.head.append(script);\n}\n\n/** `fbq('init', id)` + the initial PageView. */\nexport function initializeMetaPixel(pixelId: string): void {\n const fbq = ensureFbqFunction();\n fbq(\"init\", pixelId);\n fbq(\"track\", \"PageView\");\n}\n\n/**\n * Push the current effective consent to fbq.\n *\n * @deprecated Redundant since consent v2 — `applyDefaultMetaConsentState()`\n * already applies the effective state at bootstrap. Pushes directly (no\n * storage writes), so it never renews a decline's expiry.\n */\nexport function restoreMetaConsentState(): void {\n if (typeof window === \"undefined\") return;\n const state = getConsentState();\n if (state === \"granted\") window.fbq?.(\"consent\", \"grant\");\n else if (state === \"denied\") window.fbq?.(\"consent\", \"revoke\");\n}\n\n/**\n * Load + initialize one Meta Pixel with Consent Mode support, and form `_fbc`.\n */\nexport function bootstrapMetaPixel(pixelId: string): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return;\n if (!isValidMetaPixelId(pixelId)) return;\n\n applyDefaultMetaConsentState();\n loadFbeventsScript();\n initializeMetaPixel(pixelId);\n captureFbc();\n}\n\n/**\n * Load + initialize ALL labelled Meta Pixel IDs (script loads once; `fbq('init')`\n * fires per id — the Pixel supports multiple pixels on one page).\n */\nexport function bootstrapMultiplePixels(pixelIds: MetaPixelEnvironmentMap): void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return;\n const ids = Object.values(pixelIds).filter(\n (id): id is string => typeof id === \"string\" && isValidMetaPixelId(id),\n );\n if (ids.length === 0) return;\n\n applyDefaultMetaConsentState();\n loadFbeventsScript();\n const fbq = ensureFbqFunction();\n for (const id of ids) {\n fbq(\"init\", id);\n }\n fbq(\"track\", \"PageView\");\n captureFbc();\n}\n\n// ---------------------------------------------------------------------------\n// Inline <script> builders for the Next.js <Script> path (mirror gtag.ts)\n// ---------------------------------------------------------------------------\n\n/** The standard fbevents stub as an inline string. */\nfunction fbqStubScript(): string {\n return `\n!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?\nn.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;\nn.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;\nt.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,\ndocument,'script','${FB_EVENTS_SCRIPT_HOST}');`.trim();\n}\n\n/**\n * Inline script that loads the fbevents stub and applies the visitor's\n * EFFECTIVE consent (opt-out model) before any `init`/`PageView` fires.\n */\nexport function createMetaConsentDefaultScript(): string {\n return `${fbqStubScript()}\n${createEffectiveConsentSnippet()}\nfbq('consent', aranovaConsent === 'denied' ? 'revoke' : 'grant');`;\n}\n\n/** Inline script that inits one pixel + PageView. */\nexport function createMetaPixelInitScript(pixelId: string): string {\n if (!isValidMetaPixelId(pixelId)) return \"\";\n return `fbq('init', '${pixelId}');\\nfbq('track', 'PageView');`;\n}\n\n/** Inline script that inits multiple pixels + a single PageView. */\nexport function createMetaPixelInitScriptMulti(pixelIds: string[]): string {\n const inits = pixelIds.filter(isValidMetaPixelId).map((id) => `fbq('init', '${id}');`);\n if (inits.length === 0) return \"\";\n return `${inits.join(\"\\n\")}\\nfbq('track', 'PageView');`;\n}\n\n/**\n * Inline script that pushes the effective consent into fbq.\n *\n * @deprecated Redundant since consent v2 — `createMetaConsentDefaultScript()`\n * already applies the effective state (including decline expiry) before the\n * pixel initializes. Kept expiry-aware for integrations that still render it.\n */\nexport function createMetaConsentRestoreScript(): string {\n return `\n${createEffectiveConsentSnippet()}\nif (window.fbq) {\n window.fbq('consent', aranovaConsent === 'denied' ? 'revoke' : 'grant');\n}`.trim();\n}\n","// Session-scoped LANDING attribution (ADR-016).\n//\n// The carried attribution channel (tracking.ts) persists gclid/fbclid/utm_*\n// for 90 days so journey stitching and conversion writeback keep working\n// across sessions. This module answers a different question — \"what did THIS\n// session's landing URL actually carry?\" — so the dashboard's source label is\n// never poisoned by a click from an earlier session.\n//\n// One localStorage record, keyed to the current session id and overwritten\n// whenever a new session is minted: self-cleaning, O(1) storage, survives\n// in-webview reloads (localStorage, not cookies — Meta/IG webviews silently\n// drop cookie writes, the exact failure mode the carried channel already\n// engineers around). An in-memory mirror covers storage-blocked environments.\n\nimport { getTrackingQueryValues, TRACKING_PARAM_KEYS, type TrackingParamKey } from \"./tracking\";\n\n/**\n * localStorage key for the landing-params record (same `_aranova_track_`\n * family as the carried-channel cookie mirrors).\n */\nexport const LANDING_STORAGE_KEY = \"_aranova_track_landing\";\n\n/**\n * Serialized landing record: the params present in the landing URL of the\n * session identified by `session_id`.\n */\nexport interface StoredLandingRecord {\n session_id: string;\n params: Partial<Record<TrackingParamKey, string>>;\n}\n\nlet memoryRecord: StoredLandingRecord | null = null;\n\nfunction sanitizeParams(value: unknown): Partial<Record<TrackingParamKey, string>> {\n if (typeof value !== \"object\" || value === null) return {};\n const source = value as Record<string, unknown>;\n return TRACKING_PARAM_KEYS.reduce<Partial<Record<TrackingParamKey, string>>>((params, key) => {\n const entry = source[key];\n if (typeof entry === \"string\" && entry.length > 0) params[key] = entry;\n return params;\n }, {});\n}\n\nfunction readStoredRecord(): StoredLandingRecord | null {\n try {\n const raw = window.localStorage.getItem(LANDING_STORAGE_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw) as Partial<StoredLandingRecord>;\n if (typeof parsed.session_id !== \"string\" || parsed.session_id.length === 0) return null;\n return { session_id: parsed.session_id, params: sanitizeParams(parsed.params) };\n } catch {\n return null;\n }\n}\n\nfunction writeRecord(record: StoredLandingRecord): void {\n memoryRecord = record;\n try {\n window.localStorage.setItem(LANDING_STORAGE_KEY, JSON.stringify(record));\n } catch {\n // Storage blocked — the in-memory mirror above still serves this page.\n }\n}\n\nfunction captureFromUrl(url?: string): Partial<Record<TrackingParamKey, string>> {\n try {\n const resolved = new URL(url ?? window.location.href, window.location.origin);\n return getTrackingQueryValues(resolved.searchParams);\n } catch {\n return {};\n }\n}\n\n/**\n * Return the landing params for `sessionId`, capturing them from the current\n * URL when no record for that session exists yet.\n *\n * First-wins: a record whose `session_id` matches is returned as-is (a second\n * tab's URL never clobbers the session's true landing). A missing or\n * mismatched record means a new session just started — the current URL IS that\n * session's landing, so it is captured and persisted. Read-only with respect\n * to the carried channel: never touches cookies. Never throws; returns `{}`\n * during SSR.\n */\nexport function getOrCaptureLandingParams(\n sessionId: string,\n url?: string,\n): Partial<Record<TrackingParamKey, string>> {\n if (typeof window === \"undefined\") return {};\n\n const stored = readStoredRecord();\n if (stored && stored.session_id === sessionId) {\n memoryRecord = stored;\n return stored.params;\n }\n if (memoryRecord && memoryRecord.session_id === sessionId) {\n return memoryRecord.params;\n }\n\n const record: StoredLandingRecord = { session_id: sessionId, params: captureFromUrl(url) };\n writeRecord(record);\n return record.params;\n}\n\n/**\n * The 10 landing wire fields, null-filled. Key PRESENCE tells the backend the\n * landing is explicitly known; when it is NOT known the keys must be omitted\n * entirely so the backend falls back to parsing first_page.\n */\nexport interface LandingPayloadFields {\n landing_gclid: string | null;\n landing_wbraid: string | null;\n landing_gbraid: string | null;\n landing_ylpcid: string | null;\n landing_fbclid: string | null;\n landing_utm_source: string | null;\n landing_utm_medium: string | null;\n landing_utm_campaign: string | null;\n landing_utm_term: string | null;\n landing_utm_content: string | null;\n}\n\n/**\n * Build the landing portion of a session payload — the single place the\n * \"present iff known\" wire contract is enforced. Returns `{}` (omit the keys)\n * when there is no landing knowledge: during SSR the session's landing URL is\n * simply not observable, and sending explicit nulls would wrongly tell the\n * backend \"this session landed with no params\", suppressing its fallback.\n */\nexport function buildLandingPayloadFields(\n sessionId: string,\n override?: Partial<Record<TrackingParamKey, string>>,\n): Partial<LandingPayloadFields> {\n const params =\n override ?? (typeof window === \"undefined\" ? null : getOrCaptureLandingParams(sessionId));\n if (params === null) return {};\n return {\n landing_gclid: params.gclid ?? null,\n landing_wbraid: params.wbraid ?? null,\n landing_gbraid: params.gbraid ?? null,\n landing_ylpcid: params.ylpcid ?? null,\n landing_fbclid: params.fbclid ?? null,\n landing_utm_source: params.utm_source ?? null,\n landing_utm_medium: params.utm_medium ?? null,\n landing_utm_campaign: params.utm_campaign ?? null,\n landing_utm_term: params.utm_term ?? null,\n landing_utm_content: params.utm_content ?? null,\n };\n}\n\n/**\n * Drop the landing record (localStorage + memory). Paired with\n * `resetTrackingIdentity()`; also used by tests.\n */\nexport function clearLandingRecord(): void {\n memoryRecord = null;\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.removeItem(LANDING_STORAGE_KEY);\n } catch {\n // ignored\n }\n}\n","/** A UUID v4 from Web Crypto, with a `getRandomValues` (then `Math.random`) fallback. */\nexport function uuidV4(): string {\n const cryptoApi = globalThis.crypto;\n if (typeof cryptoApi?.randomUUID === \"function\") return cryptoApi.randomUUID();\n const bytes = new Uint8Array(16);\n if (typeof cryptoApi?.getRandomValues === \"function\") cryptoApi.getRandomValues(bytes);\n else for (let i = 0; i < 16; i += 1) bytes[i] = Math.floor(Math.random() * 256);\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\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\nimport { clearLandingRecord } from \"./landing\";\nimport { uuidV4 } from \"./uuid\";\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 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\") return uuidV4();\n\n const existing = readLocalStorage(VISITOR_STORAGE_KEY);\n if (existing && existing.length > 0) return existing;\n\n const fresh = uuidV4();\n writeLocalStorage(VISITOR_STORAGE_KEY, fresh);\n return fresh;\n}\n\n/** Adopt `visitorId` only when this browser has none — an existing id is never orphaned. */\nexport function adoptVisitorId(visitorId: string): boolean {\n if (typeof window === \"undefined\" || visitorId.length === 0) return false;\n const existing = readLocalStorage(VISITOR_STORAGE_KEY);\n if (existing && existing.length > 0) return false;\n writeLocalStorage(VISITOR_STORAGE_KEY, visitorId);\n return readLocalStorage(VISITOR_STORAGE_KEY) === visitorId;\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\") return { id: uuidV4(), 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: uuidV4(), last_event_at: now };\n writeLocalStorage(SESSION_STORAGE_KEY, JSON.stringify(fresh));\n return { id: fresh.id, isNew: true };\n}\n\n/** This browser's visitor id, read without creating one; `null` before tracking has run. */\nexport function peekVisitorId(): string | null {\n if (typeof window === \"undefined\") return null;\n const existing = readLocalStorage(VISITOR_STORAGE_KEY);\n return existing && existing.length > 0 ? existing : null;\n}\n\n/**\n * The live session id, read without refreshing or rotating it; `null` once the idle window\n * has lapsed, since the next event would start a new session anyway.\n */\nexport function peekSessionId(now: number = Date.now()): string | null {\n if (typeof window === \"undefined\") return null;\n const raw = readLocalStorage(SESSION_STORAGE_KEY);\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as Partial<StoredSession>;\n if (typeof parsed.id !== \"string\" || typeof parsed.last_event_at !== \"number\") return null;\n return now - parsed.last_event_at <= SESSION_IDLE_MS ? parsed.id : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Clear visitor and session identity from localStorage, including the\n * session-scoped landing record (a landing must never outlive its session).\n *\n * Intended for tests, debugging, and explicit user reset flows.\n */\nexport function resetTrackingIdentity(): void {\n clearLandingRecord();\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.removeItem(VISITOR_STORAGE_KEY);\n window.localStorage.removeItem(SESSION_STORAGE_KEY);\n } catch {\n // ignored\n }\n}\n","// Consent-gated, de-duped orchestration around the raw `fireGtagConversion` (GAP28).\n//\n// Consent v2 (opt-out model): fire unless the visitor's effective consent is an\n// explicit, unexpired decline. There is no \"pending\" state and therefore no\n// queue — the default visitor converts in real time.\n//\n// `fireGtagConversion` is itself browser-gated and never throws, so this is safe to call\n// from the isomorphic sales `record()` — server-side it simply no-ops.\n\nimport { getConsentState } from \"../consent\";\nimport { fireGtagConversion, isValidSendTo, type GtagConversionInput } from \"../gtag\";\nimport { applyUserDataForConversion, type ConversionUserData } from \"../user-data\";\n\nconst DEDUP_PREFIX = \"_aranova_conv_\";\n\nexport type ConversionFireOutcome = \"fired\" | \"denied\" | \"duplicate\" | \"invalid\" | \"retryable\";\n\nfunction dedupKey(input: GtagConversionInput): string {\n return `${DEDUP_PREFIX}${input.transactionId ?? \"\"}:${input.sendTo}`;\n}\n\n// Has this (transaction_id, send_to) already fired? Read-only — the key is written ONLY\n// after a successful fire (see `fireOnce`), so a fire that never happened (e.g. gtag not\n// ready yet) is never suppressed on retry. Best-effort: a blocked sessionStorage reads as\n// \"not fired\" so the conversion still gets a chance.\nfunction alreadyFired(input: GtagConversionInput): boolean {\n if (!input.transactionId || typeof window === \"undefined\") return false;\n try {\n return window.sessionStorage.getItem(dedupKey(input)) !== null;\n } catch {\n return false;\n }\n}\n\nfunction markFired(input: GtagConversionInput): void {\n if (!input.transactionId || typeof window === \"undefined\") return;\n try {\n window.sessionStorage.setItem(dedupKey(input), \"1\");\n } catch {\n // sessionStorage unavailable — Google's counting_type is the authoritative backstop\n }\n}\n\n/**\n * Fire a conversion through the consent gate + de-dup guard.\n *\n * Enhanced conversions: right before the fire (past the consent gate AND the\n * de-dup guard — a duplicate that won't emit an event must not mutate gtag's\n * page-level user_data either), gtag's `user_data` is set from the explicit\n * `options.userData` merged over the form-submit stash — this is the single\n * choke point all firing paths (recordSale, trackConversion, autofire) flow\n * through, so every conversion hit carries the visitor's hashed identifiers\n * when any are known. Re-setting before each fire is idempotent (gtag scopes\n * user_data to the page), so a later autofire on the same page matches too.\n * The de-dup key is written only AFTER a successful fire, so a fire that\n * never happened (gtag not ready) is never suppressed on retry.\n */\nexport function fireConversionWithConsent(\n input: GtagConversionInput,\n options?: { userData?: ConversionUserData | null },\n): ConversionFireOutcome {\n if (getConsentState() === \"denied\") return \"denied\";\n if (alreadyFired(input)) return \"duplicate\";\n if (!isValidSendTo(input.sendTo)) return \"invalid\";\n try {\n applyUserDataForConversion(options?.userData);\n if (!fireGtagConversion(input)) return \"retryable\";\n markFired(input);\n return \"fired\";\n } catch {\n return \"retryable\";\n }\n}\n\n/**\n * Flush conversions queued while consent was pending.\n *\n * @deprecated No-op since consent v2 — there is no \"pending\" state, so nothing\n * is ever queued. Kept exported so pre-v2 integrations keep compiling.\n */\nexport function flushPendingConversions(): void {\n // intentionally empty\n}\n","// The client tracking config the SDK reads at runtime (GAP28 / ENG-46).\n//\n// The browser fetches the per-business object published to R2/CDN. This module is a\n// TOLERANT reader — it ignores unknown fields, defaults every field, and never throws — so\n// the backend can add fields freely (additive-only within the `v1` schema major) without\n// breaking an older SDK, and a newer object never breaks an older reader. See\n// docs/tracking-package/conversion-config-schema.md for the contract.\n\nexport interface ServiceFiring {\n send_to: string;\n /** Action's configured default value (cents); the SDK fires it only when a sale has no amount. */\n value_cents?: number | null;\n currency?: string | null;\n}\n\n/** Serializable on-page trigger (owned by tracking-core/src/events/trigger-spec.ts). The shape\n * varies by `event_type`; only the parameter for that type is set. */\nexport interface ConfigTriggerSpec {\n event_type: string;\n threshold_percent?: number;\n threshold_seconds?: number;\n page_threshold?: number;\n page_name?: string;\n metric?: \"percent\" | \"seconds\";\n threshold?: number;\n video_id?: string | null;\n}\n\nexport interface TrackedVideoSourcesConfig {\n desktop: string;\n mobile?: string | null;\n}\n\nexport interface TrackedVideoConfig {\n key: string;\n title: string;\n host: string;\n sources: TrackedVideoSourcesConfig;\n poster_time?: number;\n poster_time_s?: number;\n duration_s?: number | null;\n}\n\n/** One unified conversion goal: a revenue `sale` or an on-page `event`. */\nexport interface ConversionGoal {\n key: string;\n label?: string;\n kind: \"sale\" | \"event\";\n /** Structured fire-when for event-goals; null for sales. */\n trigger: ConfigTriggerSpec | null;\n firing: ServiceFiring | null;\n}\n\nexport interface ConversionConfig {\n schema_version: number;\n config_version: number;\n business_id?: string;\n customer_id?: string | null;\n environment?: string;\n google_tracking_state: \"active\" | \"disabled\";\n gtag_ids: Record<string, string>;\n meta_pixel_ids: Record<string, string>;\n /** LEGACY: sale-goals only (for pre-unified-goal readers). */\n services: Array<{\n key: string;\n label?: string;\n firing: ServiceFiring | null;\n }>;\n /** Unified goal list (sales + on-page events). Superset of `services`. */\n goals: ConversionGoal[];\n /** Sibling hostnames of this site (roster minus self); the cross-site linker's targets. */\n linked_hostnames?: string[];\n /** Third-party integrations the site renders UI for; a missing provider means none. */\n integrations?: ConversionIntegrations;\n /** The lead-form contract (ADR-0024); null from an API that predates it. */\n lead_forms?: LeadFormsConfig | null;\n /** Cloudflare Stream tracked videos configured for this site (ADR-0042). */\n videos?: Record<string, TrackedVideoConfig>;\n}\n\nexport interface LeadFormsConfig {\n /** The registry version the object was published under. */\n registry_version: number;\n /** One per form kind a bound consumer needs something from; an absent kind needs nothing. */\n contracts: LeadFormContract[];\n}\n\n/** What one kind of form must collect for this business, derived server-side. */\nexport interface LeadFormContract {\n form_kind: string;\n requires: LeadFormRequirement[];\n /** Each group is satisfied by any one of its roles. */\n requires_any: Array<{ roles: string[]; waived: boolean }>;\n options: Array<{ role: string; options: LeadFormContractOption[] }>;\n /** Echo on `form_submit` as `form.contract_hash`. */\n contract_hash: string;\n}\n\nexport interface LeadFormRequirement {\n role: string;\n /** Required only when `role` holds one of `any_of`; never nested. */\n required_when: { role: string; any_of: string[] } | null;\n waived: boolean;\n}\n\nexport interface LeadFormContractOption {\n key: string;\n label: string;\n is_non_answer: boolean;\n /** Dropped from the form; still accepted from a cached config. */\n deprecated: boolean;\n}\n\n/** Bookable start hours on one weekday, shop wall time (8 = 08:00); both empty = closed. */\nexport interface BookingDayWindows {\n morning: number[];\n afternoon: number[];\n}\n\nexport interface BookingRulesConfig {\n min_days_ahead: number;\n max_days_ahead: number;\n /** Keyed `monday` … `sunday`. */\n hours: Record<string, BookingDayWindows>;\n /** `YYYY-MM-DD` shop dates the portal refuses although the weekday is open. */\n closed_dates: string[];\n /** `YYYY-MM-DD` shop dates whose start hours replace their weekday's; never also closed. */\n special_hours: Record<string, BookingDayWindows>;\n vehicle_required: boolean;\n /** False from an API older than the deferred vehicle. */\n vehicle_deferred: boolean;\n /** Empty when the shop hides the drop-off question. */\n drop_off_options: string[];\n /**\n * `YYYY-MM-DD` shop dates → live bookings that day (synced appointments, pending requests,\n * website bookings not yet synced), today to the end of the window; days with none are left\n * out. Empty from an API older than the counts. The site decides when a day is full.\n */\n booked_counts: Record<string, number>;\n}\n\n/** One service choice on the site's booking form; the shop system's ids stay server-side. */\nexport interface BookingOptionConfig {\n /** What the site posts in `services`. */\n key: string;\n label: string;\n /** The shop's service category; options arrive grouped, each category in one run. */\n group: string | null;\n /** Optional helper text under the label. */\n description: string | null;\n}\n\n/** Where a booking went; decided by the API. A real customer always gets `live`. */\nexport const BOOKING_MODES = [\"live\", \"test\", \"simulated\"] as const;\n/**\n * `live`: the shop's real queue. `test`: a real request on the shop's test lead source, never\n * counted. `simulated`: never sent; nothing reached the shop or the customer.\n */\nexport type BookingMode = (typeof BOOKING_MODES)[number];\n\nfunction isBookingMode(value: unknown): value is BookingMode {\n return (BOOKING_MODES as readonly unknown[]).includes(value);\n}\n\n/** `integrations.autoleap.booking` — never carries a secret. */\nexport interface AutoLeapBookingConfig {\n /** False: show only `fallback_url`. The site never blocks the customer on it. */\n enabled: boolean;\n /** The shop's own booking page; always present. */\n fallback_url: string;\n timezone: string;\n /** Snapshot the rules came from; null before the first one. */\n config_version: number | null;\n /** `enabled` and at least one option mapped: the site may post a booking. */\n accepts_bookings: boolean;\n rules: BookingRulesConfig | null;\n options: BookingOptionConfig[];\n /** Development objects only: `test` or `simulated`, so a dev page can say so up front. */\n mode?: BookingMode;\n}\n\nexport interface ConversionIntegrations {\n autoleap?: { booking: AutoLeapBookingConfig };\n}\n\nexport interface ConversionConfigStore {\n /** Firing config for a goal/service key, or null when it doesn't fire on-site. */\n getFiring(key: string): ServiceFiring | null;\n /** The full goal for a key, or null when unknown. */\n getGoal(key: string): ConversionGoal | null;\n /** Every adopted goal (sales + events). */\n listGoals(): ConversionGoal[];\n /** Sibling hostnames for the cross-site linker; empty until a config resolves. */\n linkedHostnames(): readonly string[];\n /** The currently adopted config (baked / cached fallback until the fetch lands). */\n current(): ConversionConfig | null;\n /** True once a config is adopted (seeded synchronously from cache/baked, or fetched). */\n isReady(): boolean;\n /**\n * Run `listener` when a config first becomes available — immediately if already\n * ready, otherwise on the first adopt. Lets auto-fire replay automatic events that\n * occurred before the async CDN fetch resolved. Returns an unsubscribe fn.\n */\n onResolve(listener: () => void): () => void;\n /** Force a background revalidate against the CDN object. */\n revalidate(): Promise<void>;\n}\n\nfunction isStringMap(value: unknown): value is Record<string, string> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n Object.values(value).every((v) => typeof v === \"string\")\n );\n}\n\nfunction parseFiring(value: unknown): ServiceFiring | null {\n if (!value || typeof value !== \"object\") return null;\n const f = value as Record<string, unknown>;\n if (typeof f.send_to !== \"string\") return null;\n return {\n send_to: f.send_to,\n value_cents: typeof f.value_cents === \"number\" ? f.value_cents : null,\n currency: typeof f.currency === \"string\" ? f.currency : null,\n };\n}\n\nfunction isNumberList(value: unknown): value is number[] {\n return Array.isArray(value) && value.every((v) => typeof v === \"number\");\n}\n\nfunction isStringList(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((v) => typeof v === \"string\");\n}\n\nfunction parseWindowsMap(value: unknown): Record<string, BookingDayWindows> {\n const windowsByKey: Record<string, BookingDayWindows> = {};\n if (!value || typeof value !== \"object\") return windowsByKey;\n for (const [key, windows] of Object.entries(value as Record<string, unknown>)) {\n if (!windows || typeof windows !== \"object\") continue;\n const w = windows as Record<string, unknown>;\n windowsByKey[key] = {\n morning: isNumberList(w.morning) ? w.morning : [],\n afternoon: isNumberList(w.afternoon) ? w.afternoon : [],\n };\n }\n return windowsByKey;\n}\n\nfunction parseBookingRules(value: unknown): BookingRulesConfig | null {\n if (!value || typeof value !== \"object\") return null;\n const r = value as Record<string, unknown>;\n if (typeof r.min_days_ahead !== \"number\" || typeof r.max_days_ahead !== \"number\") return null;\n if (!r.hours || typeof r.hours !== \"object\") return null;\n const hours = parseWindowsMap(r.hours);\n return {\n min_days_ahead: r.min_days_ahead,\n max_days_ahead: r.max_days_ahead,\n hours,\n closed_dates: isStringList(r.closed_dates) ? r.closed_dates : [],\n special_hours: parseWindowsMap(r.special_hours),\n vehicle_required: r.vehicle_required === true,\n vehicle_deferred: r.vehicle_deferred === true,\n drop_off_options: isStringList(r.drop_off_options) ? r.drop_off_options : [],\n booked_counts: parseCounts(r.booked_counts),\n };\n}\n\n/** `{date: n}` with only non-negative integer counts kept; anything else is `{}`. */\nfunction parseCounts(value: unknown): Record<string, number> {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return {};\n const counts: Record<string, number> = {};\n for (const [date, count] of Object.entries(value as Record<string, unknown>)) {\n if (typeof count === \"number\" && Number.isInteger(count) && count >= 0) counts[date] = count;\n }\n return counts;\n}\n\nfunction parseBookingOption(value: unknown): BookingOptionConfig[] {\n if (!value || typeof value !== \"object\") return [];\n const o = value as Record<string, unknown>;\n if (typeof o.key !== \"string\" || !o.key || typeof o.label !== \"string\") return [];\n return [\n {\n key: o.key,\n label: o.label,\n group: typeof o.group === \"string\" && o.group ? o.group : null,\n description: typeof o.description === \"string\" && o.description ? o.description : null,\n },\n ];\n}\n\nfunction parseIntegrations(value: unknown): ConversionIntegrations {\n if (!value || typeof value !== \"object\") return {};\n const autoleap = (value as Record<string, unknown>).autoleap;\n if (!autoleap || typeof autoleap !== \"object\") return {};\n const booking = (autoleap as Record<string, unknown>).booking;\n if (!booking || typeof booking !== \"object\") return {};\n const b = booking as Record<string, unknown>;\n // Without a fallback there is nothing safe to render, so the block is unusable.\n if (typeof b.fallback_url !== \"string\" || !b.fallback_url) return {};\n return {\n autoleap: {\n booking: {\n enabled: b.enabled === true,\n fallback_url: b.fallback_url,\n timezone: typeof b.timezone === \"string\" ? b.timezone : \"UTC\",\n config_version: typeof b.config_version === \"number\" ? b.config_version : null,\n accepts_bookings: b.accepts_bookings === true,\n rules: parseBookingRules(b.rules),\n options: Array.isArray(b.options) ? b.options.flatMap(parseBookingOption) : [],\n ...(isBookingMode(b.mode) ? { mode: b.mode } : {}),\n },\n },\n };\n}\n\nfunction parseTrigger(value: unknown): ConfigTriggerSpec | null {\n if (!value || typeof value !== \"object\") return null;\n const t = value as Record<string, unknown>;\n if (typeof t.event_type !== \"string\") return null;\n const spec: ConfigTriggerSpec = { event_type: t.event_type };\n if (typeof t.threshold_percent === \"number\") spec.threshold_percent = t.threshold_percent;\n if (typeof t.threshold_seconds === \"number\") spec.threshold_seconds = t.threshold_seconds;\n if (typeof t.page_threshold === \"number\") spec.page_threshold = t.page_threshold;\n if (typeof t.page_name === \"string\") spec.page_name = t.page_name;\n if (t.metric === \"percent\" || t.metric === \"seconds\") spec.metric = t.metric;\n if (typeof t.threshold === \"number\") spec.threshold = t.threshold;\n if (typeof t.video_id === \"string\" || t.video_id === null) spec.video_id = t.video_id;\n return spec;\n}\n\nfunction parseVideoEntry(\n fallbackKey: string,\n entry: Record<string, unknown>,\n): TrackedVideoConfig | null {\n const key = typeof entry.key === \"string\" && entry.key ? entry.key : fallbackKey;\n if (!key) return null;\n const host = typeof entry.host === \"string\" ? entry.host : \"\";\n const title = typeof entry.title === \"string\" && entry.title ? entry.title : key;\n const sourcesRaw = isRecord(entry.sources) ? entry.sources : null;\n const desktop =\n sourcesRaw && typeof sourcesRaw.desktop === \"string\"\n ? sourcesRaw.desktop\n : typeof entry.desktop_uid === \"string\"\n ? entry.desktop_uid\n : \"\";\n if (!host || !desktop) return null;\n const mobile =\n sourcesRaw && typeof sourcesRaw.mobile === \"string\"\n ? sourcesRaw.mobile\n : typeof entry.mobile_uid === \"string\"\n ? entry.mobile_uid\n : null;\n const posterTime =\n typeof entry.poster_time === \"number\"\n ? entry.poster_time\n : typeof entry.poster_time_s === \"number\"\n ? entry.poster_time_s\n : 4;\n return {\n key,\n title,\n host,\n sources: { desktop, mobile },\n poster_time: posterTime,\n duration_s: typeof entry.duration_s === \"number\" ? entry.duration_s : null,\n };\n}\n\nfunction parseVideos(value: unknown): Record<string, TrackedVideoConfig> {\n const out: Record<string, TrackedVideoConfig> = {};\n if (Array.isArray(value)) {\n for (const item of value) {\n if (!isRecord(item)) continue;\n const parsed = parseVideoEntry(\"\", item);\n if (parsed) out[parsed.key] = parsed;\n }\n return out;\n }\n if (!isRecord(value)) return out;\n for (const [key, entry] of Object.entries(value)) {\n if (!isRecord(entry)) continue;\n const parsed = parseVideoEntry(key, entry);\n if (parsed) out[parsed.key] = parsed;\n }\n return out;\n}\n\n/** Tolerant parse of a raw config object — never throws; returns null only when unusable. */\nexport function parseConversionConfig(raw: unknown): ConversionConfig | null {\n if (!raw || typeof raw !== \"object\") return null;\n const obj = raw as Record<string, unknown>;\n const servicesRaw = Array.isArray(obj.services) ? obj.services : [];\n const services = servicesRaw.flatMap((entry) => {\n if (!entry || typeof entry !== \"object\") return [];\n const s = entry as Record<string, unknown>;\n if (typeof s.key !== \"string\") return [];\n return [\n {\n key: s.key,\n label: typeof s.label === \"string\" ? s.label : undefined,\n firing: parseFiring(s.firing),\n },\n ];\n });\n const goalsRaw = Array.isArray(obj.goals) ? obj.goals : null;\n // Back-compat: an object from a pre-unified-goal backend has no `goals` — derive them from\n // the legacy `services` (all sale-goals) so the store always has a unified list.\n const goals: ConversionGoal[] = goalsRaw\n ? goalsRaw.flatMap((entry) => {\n if (!entry || typeof entry !== \"object\") return [];\n const g = entry as Record<string, unknown>;\n if (typeof g.key !== \"string\") return [];\n return [\n {\n key: g.key,\n label: typeof g.label === \"string\" ? g.label : undefined,\n kind: g.kind === \"event\" ? \"event\" : \"sale\",\n trigger: parseTrigger(g.trigger),\n firing: parseFiring(g.firing),\n },\n ];\n })\n : services.map((s) => ({\n key: s.key,\n label: s.label,\n kind: \"sale\" as const,\n trigger: null,\n firing: s.firing,\n }));\n return {\n schema_version: typeof obj.schema_version === \"number\" ? obj.schema_version : 1,\n config_version: typeof obj.config_version === \"number\" ? obj.config_version : 0,\n business_id: typeof obj.business_id === \"string\" ? obj.business_id : undefined,\n customer_id: typeof obj.customer_id === \"string\" ? obj.customer_id : null,\n environment: typeof obj.environment === \"string\" ? obj.environment : undefined,\n google_tracking_state: obj.google_tracking_state === \"active\" ? \"active\" : \"disabled\",\n gtag_ids: isStringMap(obj.gtag_ids) ? obj.gtag_ids : {},\n meta_pixel_ids: isStringMap(obj.meta_pixel_ids) ? obj.meta_pixel_ids : {},\n services,\n goals,\n linked_hostnames: Array.isArray(obj.linked_hostnames)\n ? obj.linked_hostnames.filter((h): h is string => typeof h === \"string\" && h.length > 0)\n : [],\n integrations: parseIntegrations(obj.integrations),\n lead_forms: parseLeadForms(obj.lead_forms),\n videos: parseVideos(obj.videos),\n };\n}\n\nfunction parseLeadForms(value: unknown): LeadFormsConfig | null {\n if (!isRecord(value) || typeof value.registry_version !== \"number\") return null;\n return {\n registry_version: value.registry_version,\n contracts: records(value.contracts).flatMap((c) =>\n typeof c.form_kind === \"string\" && typeof c.contract_hash === \"string\"\n ? [\n {\n form_kind: c.form_kind,\n requires: records(c.requires).flatMap((r) =>\n typeof r.role === \"string\"\n ? [\n {\n role: r.role,\n required_when: parseCondition(r.required_when),\n waived: r.waived === true,\n },\n ]\n : [],\n ),\n requires_any: records(c.requires_any).map((g) => ({\n roles: strings(g.roles),\n waived: g.waived === true,\n })),\n options: records(c.options).flatMap((o) =>\n typeof o.role === \"string\"\n ? [{ role: o.role, options: records(o.options).flatMap(parseContractOption) }]\n : [],\n ),\n contract_hash: c.contract_hash,\n },\n ]\n : [],\n ),\n };\n}\n\nfunction parseCondition(value: unknown): LeadFormRequirement[\"required_when\"] {\n if (!isRecord(value) || typeof value.role !== \"string\") return null;\n return { role: value.role, any_of: strings(value.any_of) };\n}\n\nfunction parseContractOption(value: Record<string, unknown>): LeadFormContractOption[] {\n if (typeof value.key !== \"string\" || typeof value.label !== \"string\") return [];\n return [\n {\n key: value.key,\n label: value.label,\n is_non_answer: value.is_non_answer === true,\n deprecated: value.deprecated === true,\n },\n ];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction records(value: unknown): Array<Record<string, unknown>> {\n return Array.isArray(value) ? value.filter(isRecord) : [];\n}\n\nfunction strings(value: unknown): string[] {\n return Array.isArray(value) ? value.filter((v): v is string => typeof v === \"string\") : [];\n}\n\n// --- stale-while-revalidate cache (sessionStorage) -----------------------------------------\n\nconst CACHE_PREFIX = \"_aranova_cfg_\";\n\ninterface CachedEntry {\n etag: string | null;\n config: ConversionConfig;\n}\n\nfunction cacheKey(url: string): string {\n return `${CACHE_PREFIX}${url}`;\n}\n\nfunction readCache(url: string): CachedEntry | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = window.sessionStorage.getItem(cacheKey(url));\n if (!raw) return null;\n const parsed = JSON.parse(raw) as { etag?: unknown; config?: unknown };\n const config = parseConversionConfig(parsed.config);\n if (!config) return null;\n return {\n etag: typeof parsed.etag === \"string\" ? parsed.etag : null,\n config,\n };\n } catch {\n return null;\n }\n}\n\nfunction writeCache(url: string, entry: CachedEntry): void {\n if (typeof window === \"undefined\") return;\n try {\n window.sessionStorage.setItem(cacheKey(url), JSON.stringify(entry));\n } catch {\n // sessionStorage unavailable — runtime adoption still works, just not across inits\n }\n}\n\nexport interface ResolveConversionConfigOptions {\n /** Full URL of the per-business CDN object. */\n cdnUrl: string;\n /** Offline-correct fallback (e.g. the CLI-baked snapshot). */\n baked?: ConversionConfig | null;\n /** Injectable for tests / non-global-fetch runtimes. */\n fetchImpl?: typeof fetch;\n}\n\n/**\n * Resolve the config with stale-while-revalidate: seed synchronously from the\n * sessionStorage cache (or the baked fallback), then conditionally re-fetch the CDN object\n * with `If-None-Match`. A fetched object is adopted only if its `config_version` is strictly\n * greater than what's cached, so a reordered edge copy can't downgrade fresher state.\n * Non-blocking and browser-only; a failed fetch leaves the seed in place.\n */\nexport function resolveConversionConfig(\n options: ResolveConversionConfigOptions,\n): ConversionConfigStore {\n const cached = readCache(options.cdnUrl);\n let current: ConversionConfig | null = cached?.config ?? options.baked ?? null;\n let etag: string | null = cached?.etag ?? null;\n const goalsByKey = new Map<string, ConversionGoal>();\n const resolveListeners = new Set<() => void>();\n\n function rebuildIndex(): void {\n goalsByKey.clear();\n for (const goal of current?.goals ?? []) {\n goalsByKey.set(goal.key, goal);\n }\n }\n\n function notifyResolved(): void {\n // One-shot: only the FIRST adoption matters for replay. Snapshot then clear so a\n // listener that re-subscribes inside its callback isn't double-run.\n const listeners = [...resolveListeners];\n resolveListeners.clear();\n // Isolate each listener: the set is already cleared, so one throwing subscriber\n // must not drop the others' buffered replays. Swallow (best-effort) to match the\n // SDK's policy of never surfacing tracking errors to the host page.\n for (const listener of listeners) {\n try {\n listener();\n } catch {\n // never let one subscriber break conversion replay\n }\n }\n }\n\n function adopt(next: ConversionConfig | null, nextEtag: string | null): void {\n if (!next) return;\n if (current && next.config_version <= current.config_version) return;\n const wasEmpty = current === null;\n current = next;\n etag = nextEtag;\n rebuildIndex();\n writeCache(options.cdnUrl, { etag, config: next });\n if (wasEmpty) notifyResolved();\n }\n\n async function revalidate(): Promise<void> {\n if (typeof window === \"undefined\") return;\n try {\n const doFetch = options.fetchImpl ?? globalThis.fetch;\n if (!doFetch) return;\n const headers: Record<string, string> = {};\n if (etag) headers[\"If-None-Match\"] = etag;\n const response = await doFetch(options.cdnUrl, {\n method: \"GET\",\n headers,\n });\n if (response.status === 304 || !response.ok) return;\n adopt(parseConversionConfig(await response.json()), response.headers.get(\"ETag\"));\n } catch {\n // best-effort; the cached / baked fallback stays in place\n }\n }\n\n rebuildIndex();\n void revalidate();\n\n return {\n getFiring: (key) => goalsByKey.get(key)?.firing ?? null,\n getGoal: (key) => goalsByKey.get(key) ?? null,\n listGoals: () => [...goalsByKey.values()],\n linkedHostnames: () => current?.linked_hostnames ?? [],\n current: () => current,\n isReady: () => current !== null,\n onResolve: (listener) => {\n if (current !== null) {\n listener();\n return () => {};\n }\n resolveListeners.add(listener);\n return () => resolveListeners.delete(listener);\n },\n revalidate,\n };\n}\n","const STORAGE_KEY = \"_aranova_auto_txn_map\";\nconst transactionIds = new Map<string, string>();\nlet legacyCounter = 0;\n\ninterface StoredTransactionMap {\n sessionId: string;\n entries: Record<string, string>;\n}\n\nfunction scopeKey(sessionId: string, goalKey: string, path: string): string {\n return JSON.stringify([sessionId, goalKey, path]);\n}\n\nfunction randomId(): string {\n const cryptoApi = globalThis.crypto;\n if (typeof cryptoApi?.randomUUID === \"function\") return cryptoApi.randomUUID();\n if (typeof cryptoApi?.getRandomValues === \"function\") {\n const bytes = cryptoApi.getRandomValues(new Uint8Array(16));\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n }\n // Ancient-browser fallback. Keep fixed-length opaque shape; modern browsers\n // always use Web Crypto above.\n legacyCounter = (legacyCounter + 1) % 0x1_0000_0000;\n const timestamp = Date.now().toString(16).padStart(12, \"0\");\n const counter = legacyCounter.toString(16).padStart(8, \"0\");\n const random = Math.floor(Math.random() * 0xffffffffffff)\n .toString(16)\n .padStart(12, \"0\");\n return `${timestamp}${counter}${random}`.slice(0, 32);\n}\n\nfunction isValidTransactionId(value: unknown): value is string {\n return (\n typeof value === \"string\" &&\n /^auto:(?:[0-9a-f]{32}|[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$/i.test(\n value,\n ) &&\n value.length <= 64\n );\n}\n\nfunction readStoredMap(sessionId: string): StoredTransactionMap {\n if (typeof window === \"undefined\") return { sessionId, entries: {} };\n try {\n const raw = window.localStorage.getItem(STORAGE_KEY);\n if (!raw) return { sessionId, entries: {} };\n const parsed = JSON.parse(raw) as Partial<StoredTransactionMap>;\n if (\n parsed.sessionId !== sessionId ||\n !parsed.entries ||\n typeof parsed.entries !== \"object\" ||\n Array.isArray(parsed.entries)\n ) {\n return { sessionId, entries: {} };\n }\n return { sessionId, entries: parsed.entries };\n } catch {\n return { sessionId, entries: {} };\n }\n}\n\nfunction writeStoredMap(stored: StoredTransactionMap): void {\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.setItem(STORAGE_KEY, JSON.stringify(stored));\n } catch {\n // Storage unavailable; page-lifetime stability is still preserved.\n }\n}\n\n/**\n * Return one opaque, bounded Google transaction ID per session/goal/path.\n *\n * The raw session id and URL path stay in first-party local storage and are\n * never sent to Google. The one-current-session map preserves deduplication\n * across tabs while discarding prior-session mappings.\n */\nexport function getAutomaticTransactionId(\n sessionId: string,\n goalKey: string,\n path: string,\n): string {\n const key = scopeKey(sessionId, goalKey, path);\n const existing = transactionIds.get(key);\n if (existing) return existing;\n\n const stored = readStoredMap(sessionId);\n const storedId = stored.entries[key];\n if (isValidTransactionId(storedId)) {\n transactionIds.set(key, storedId);\n return storedId;\n }\n\n const transactionId = `auto:${randomId()}`;\n transactionIds.set(key, transactionId);\n // Re-read before write so another tab that won the race remains authoritative.\n const latest = readStoredMap(sessionId);\n const concurrentId = latest.entries[key];\n if (isValidTransactionId(concurrentId)) {\n transactionIds.set(key, concurrentId);\n return concurrentId;\n }\n latest.entries[key] = transactionId;\n writeStoredMap(latest);\n return transactionId;\n}\n\nexport function resetAutomaticTransactionIdsForTests(): void {\n transactionIds.clear();\n legacyCounter = 0;\n}\n","import type { ConversionGoal } from \"./conversion-config\";\n\nfunction hasNumberField<K extends string>(\n metadata: Readonly<Record<string, unknown>>,\n key: K,\n): metadata is Readonly<Record<string, unknown> & Record<K, number>> {\n return typeof metadata[key] === \"number\";\n}\n\nfunction hasStringField<K extends string>(\n metadata: Readonly<Record<string, unknown>>,\n key: K,\n): metadata is Readonly<Record<string, unknown> & Record<K, string>> {\n return typeof metadata[key] === \"string\";\n}\n\n/** Shared automatic-goal matcher used by every SDK runtime. */\nexport function automaticThresholdMet(\n goal: ConversionGoal,\n eventType: string,\n metadata: Readonly<Record<string, unknown>>,\n): boolean {\n const trigger = goal.trigger;\n if (!trigger || trigger.event_type !== eventType) return false;\n switch (eventType) {\n case \"scroll_depth\":\n return (\n hasNumberField(metadata, \"depth_percent\") &&\n trigger.threshold_percent != null &&\n metadata.depth_percent >= trigger.threshold_percent\n );\n case \"time_on_site\":\n return (\n hasNumberField(metadata, \"duration_ms\") &&\n trigger.threshold_seconds != null &&\n metadata.duration_ms >= trigger.threshold_seconds * 1000\n );\n case \"multi_page_session\":\n return (\n hasNumberField(metadata, \"page_count\") &&\n trigger.page_threshold != null &&\n metadata.page_count >= trigger.page_threshold\n );\n case \"specific_page_visit\":\n return hasStringField(metadata, \"page_name\") && metadata.page_name === trigger.page_name;\n case \"page_view\":\n case \"form_start\":\n case \"phone_click\":\n return true;\n case \"video_play\":\n return (\n trigger.video_id == null ||\n (hasStringField(metadata, \"video_id\") && metadata.video_id === trigger.video_id)\n );\n case \"video_progress\": {\n if (\n trigger.video_id != null &&\n (!hasStringField(metadata, \"video_id\") || metadata.video_id !== trigger.video_id)\n ) {\n return false;\n }\n if (typeof trigger.threshold !== \"number\") return false;\n if (trigger.metric === \"seconds\") {\n return hasNumberField(metadata, \"watched_s\") && metadata.watched_s >= trigger.threshold;\n }\n if (trigger.metric === \"percent\") {\n return (\n (hasNumberField(metadata, \"max_percent\") && metadata.max_percent >= trigger.threshold) ||\n (hasNumberField(metadata, \"watched_percent\") &&\n metadata.watched_percent >= trigger.threshold)\n );\n }\n return false;\n }\n default:\n return false;\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(cents: number, currency: SupportedCurrency, locale?: string): string {\n return new Intl.NumberFormat(locale, { style: \"currency\", currency }).format(\n fromMinor(cents, currency),\n );\n}\n\n// Hoisted to `resources/intl/date` so the calendar resource shares the same\n// binding; still public API via `sales-public.ts`.\nexport { formatDateInTz } from \"../intl/date\";\n","import { stripLinkParam } from \"../cross-site-link\";\nimport { applyDefaultConsentState, isValidGtagId, loadGtagScript } from \"../gtag\";\nimport {\n parseConversionConfig,\n type ConversionConfig,\n type ConversionGoal,\n type ConversionIntegrations,\n type LeadFormsConfig,\n type TrackedVideoConfig,\n} from \"./conversion-config\";\nimport { fireConversionWithConsent } from \"./conversion-firing\";\nimport { getAutomaticTransactionId } from \"./automatic-transaction\";\nimport { automaticThresholdMet } from \"./automatic-trigger\";\nimport { fromMinor } from \"./sales/money\";\nimport type { SupportedCurrency } from \"./sales/schema\";\n\n/**\n * Where a business's published tracking config lives.\n *\n * `businessId` + `environment` are the stable identity; the ORIGIN it is fetched\n * from is deployment-specific (production CDN vs. a local object store), which is\n * why `cdnBaseUrl` exists. Codegen bakes only the identity, so one committed\n * generated file works in every environment and only an env var changes.\n *\n * `cdnUrl` remains supported and WINS when present — every client repo generated\n * before `cdnBaseUrl` passes one, and those must keep working untouched.\n */\nexport interface TrackingConfigReference {\n /**\n * Fully-resolved object URL. Optional: omit it and pass `cdnBaseUrl` (or rely\n * on the production default) to have it composed from the identity below.\n */\n cdnUrl?: string;\n /**\n * Origin (optionally with a path prefix) the config object is served from, e.g.\n * `https://demos.aranova.io` in production or\n * `http://localhost:9100/aranova-demos` against a local MinIO. Ignored when\n * `cdnUrl` is set; blank or omitted falls back to {@link DEFAULT_CDN_BASE_URL}.\n * `tracking-cli gen` wires this to an env var so retargeting is config, not code.\n */\n cdnBaseUrl?: string;\n businessId: string;\n /**\n * The site whose config object to load (ENG-284). Selects the per-site\n * `tracking-config/v1/sites/` key; omitted (older generated files) resolves\n * the legacy per-business key, which is the primary site's config.\n */\n siteId?: string;\n environment: \"production\" | \"test\";\n}\n\n/** Production object-storage origin — the default when nothing overrides it. */\nexport const DEFAULT_CDN_BASE_URL = \"https://demos.aranova.io\";\n\n/**\n * Object key for a config, mirroring the backend's\n * `core/storage.py::tracking_config_key` (per-site) and its legacy per-business\n * key. The backend stays the authority: it\n * publishes at this key and returns the absolute URL, which is what the CLI bakes\n * as the legacy `cdnUrl`. This is the composition path for references that carry\n * identity instead of a URL — the `v1` prefix is the schema MAJOR and must move in\n * lockstep with the backend if it is ever cut to `v2`.\n */\nfunction trackingConfigKey(ref: TrackingConfigReference): string {\n if (ref.siteId) return `tracking-config/v1/sites/${ref.siteId}-${ref.environment}.json`;\n return `tracking-config/v1/${ref.businessId}-${ref.environment}.json`;\n}\n\n/**\n * The URL a reference resolves to. An explicit `cdnUrl` wins (back-compat);\n * otherwise compose from `cdnBaseUrl` (or the production default).\n *\n * A blank/whitespace `cdnBaseUrl` is treated as UNSET rather than composed: the\n * generated module reads it from an env var, and a var that is present-but-empty\n * (a stray `NEXT_PUBLIC_ARANOVA_CDN_BASE_URL=` line) would otherwise produce\n * `/tracking-config/...` — a same-origin request to the client's own site.\n */\nexport function resolveTrackingConfigUrl(ref: TrackingConfigReference): string {\n if (ref.cdnUrl) return ref.cdnUrl;\n const configured = ref.cdnBaseUrl?.trim();\n const base = (configured || DEFAULT_CDN_BASE_URL).replace(/\\/+$/, \"\");\n return `${base}/${trackingConfigKey(ref)}`;\n}\n\ntype RuntimeState = \"unconfirmed\" | \"active\" | \"tombstone\";\n\ninterface CachedEntry {\n etag: string | null;\n config: ConversionConfig;\n}\n\ninterface QueuedConversion {\n key: string;\n value?: number | null;\n currency?: string | null;\n transactionId?: string | null;\n}\n\nexport type TrackingConfigConversionOptions = Omit<QueuedConversion, \"key\">;\n\ninterface QueuedPageView {\n href: string;\n title: string | null;\n referrer: string | null;\n}\n\ninterface QueuedAutomaticEvent {\n eventType: string;\n metadata: Record<string, unknown>;\n transactionPath: string;\n transactionScope: string;\n}\n\nconst CACHE_PREFIX = \"_aranova_cfg_runtime_\";\nconst AUTHORITY_TTL_MS = 60_000;\n// Queues are bounded rather than cleared while authority is unconfirmed: the\n// config can appear at any time (a first publish, or R2 recovering), and the\n// most recent items are still worth firing when it does. Unbounded, a site whose\n// config 404s accumulates an entry per page view for the life of the tab.\nconst MAX_QUEUE_LENGTH = 50;\n// While unconfirmed, revalidation is spaced by a growing delay instead of riding\n// every queued item. Without it a missing config produces one no-cache request\n// per page view, forever. `focus` / `visibilitychange` still revalidate, so\n// recovery stays prompt once the object exists.\nconst RETRY_BASE_MS = 5_000;\nconst RETRY_MAX_MS = 5 * 60_000;\n\nconst runtimes = new Map<string, TrackingConfigRuntime>();\nconst configuredIds = new Set<string>();\nconst EMPTY_GOALS: ConversionGoal[] = [];\nconst EMPTY_STRINGS: readonly string[] = [];\nconst EMPTY_VIDEOS: Record<string, TrackedVideoConfig> = {};\nlet scriptLoad: Promise<void> | null = null;\nlet jsInitialized = false;\n\nfunction cacheKey(url: string): string {\n return `${CACHE_PREFIX}${url}`;\n}\n\n/** Append, dropping the oldest entry once the cap is reached. */\nfunction pushBounded<T>(queue: T[], item: T): void {\n queue.push(item);\n if (queue.length > MAX_QUEUE_LENGTH) queue.splice(0, queue.length - MAX_QUEUE_LENGTH);\n}\n\nfunction readCache(url: string): CachedEntry | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = window.sessionStorage.getItem(cacheKey(url));\n if (!raw) return null;\n const parsed = JSON.parse(raw) as { etag?: unknown; config?: unknown };\n const config = parseConversionConfig(parsed.config);\n if (!config) return null;\n return {\n etag: typeof parsed.etag === \"string\" ? parsed.etag : null,\n config,\n };\n } catch {\n return null;\n }\n}\n\nfunction writeCache(url: string, entry: CachedEntry): void {\n if (typeof window === \"undefined\") return;\n try {\n window.sessionStorage.setItem(cacheKey(url), JSON.stringify(entry));\n } catch {\n // sessionStorage unavailable — runtime still works, just without a conditional candidate\n }\n}\n\nfunction isTombstone(config: ConversionConfig): boolean {\n return config.google_tracking_state === \"disabled\" || Object.keys(config.gtag_ids).length === 0;\n}\n\nfunction validateConfig(config: ConversionConfig, ref: TrackingConfigReference): boolean {\n return (\n config.business_id === ref.businessId &&\n config.environment === ref.environment &&\n (config.google_tracking_state === \"active\" || config.google_tracking_state === \"disabled\")\n );\n}\n\nfunction pageViewSnapshot(): QueuedPageView | null {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return null;\n return {\n href: stripLinkParam(window.location.href),\n title: document.title || null,\n referrer: document.referrer || null,\n };\n}\n\nfunction ensureScript(gtagId: string): Promise<void> {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return Promise.resolve();\n applyDefaultConsentState();\n loadGtagScript(gtagId);\n if (scriptLoad) return scriptLoad;\n scriptLoad = new Promise((resolve) => {\n const script = document.querySelector<HTMLScriptElement>(\n 'script[data-aranova-tracking=\"aranova-gtag-loader\"]',\n );\n if (!script) {\n resolve();\n return;\n }\n if ((script as HTMLScriptElement & { dataset: DOMStringMap }).dataset.loaded === \"true\") {\n resolve();\n return;\n }\n window.setTimeout(resolve, 0);\n script.addEventListener(\n \"load\",\n () => {\n script.dataset.loaded = \"true\";\n resolve();\n },\n { once: true },\n );\n script.addEventListener(\"error\", () => resolve(), { once: true });\n });\n return scriptLoad;\n}\n\nexport class TrackingConfigRuntime {\n private readonly fetchImpl: typeof fetch;\n /** Resolved config URL — see the constructor for why it is computed once. */\n private readonly url: string;\n private current: ConversionConfig | null = null;\n private etag: string | null = null;\n private stateValue: RuntimeState = \"unconfirmed\";\n private confirmedAt = 0;\n private authorityGeneration = 0;\n private inFlight: Promise<void> | null = null;\n private flushInFlight: Promise<void> | null = null;\n private flushRequested = false;\n private retryTimer: number | null = null;\n private started = false;\n /** Consecutive failed authority attempts — drives the revalidate backoff. */\n private authorityFailures = 0;\n /** Epoch ms before which `ensureAuthority` must not issue another request. */\n private nextAuthorityAttemptAt = 0;\n private readonly conversionQueue: QueuedConversion[] = [];\n private readonly automaticQueue: QueuedAutomaticEvent[] = [];\n private readonly pageQueue: QueuedPageView[] = [];\n private readonly listeners = new Set<() => void>();\n\n constructor(\n readonly ref: TrackingConfigReference,\n fetchImpl: typeof fetch = globalThis.fetch,\n ) {\n // Native browser fetch requires Window/WorkerGlobalScope as its receiver.\n // Calling an unbound native fetch stored on `this` throws \"Illegal invocation\".\n this.fetchImpl = fetchImpl.bind(globalThis);\n // Resolved once: the cache key, the fetch target, and the write-back key must\n // all be the same string, or a reference that composes its URL would read a\n // cache entry it never writes.\n this.url = resolveTrackingConfigUrl(ref);\n const cached = readCache(this.url);\n this.current = cached?.config ?? null;\n this.etag = cached?.etag ?? null;\n this.start();\n }\n\n /** The URL this runtime actually fetches (composed or explicit). */\n configUrl(): string {\n return this.url;\n }\n\n /**\n * Explicitly start authority resolution and Google-tag bootstrap.\n *\n * Idempotent so framework effects can call it after hydration without\n * depending on constructor timing.\n */\n start(): void {\n if (this.started) {\n void this.flush();\n return;\n }\n this.started = true;\n void this.revalidate();\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"visible\") void this.revalidate();\n });\n window.addEventListener(\"focus\", () => void this.revalidate());\n }\n }\n\n state(): RuntimeState {\n return this.stateValue;\n }\n\n config(): ConversionConfig | null {\n return this.stateValue === \"active\" || this.stateValue === \"tombstone\" ? this.current : null;\n }\n\n __unsafeExpireAuthorityForTests(): void {\n this.confirmedAt = Number.NEGATIVE_INFINITY;\n this.nextAuthorityAttemptAt = 0;\n }\n\n /** Queue depths — asserted by tests to pin the bound. */\n __queueDepthsForTests(): { conversions: number; automatic: number; pages: number } {\n return {\n conversions: this.conversionQueue.length,\n automatic: this.automaticQueue.length,\n pages: this.pageQueue.length,\n };\n }\n\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n async ensureAuthority(): Promise<boolean> {\n if (this.stateValue !== \"unconfirmed\" && Date.now() - this.confirmedAt < AUTHORITY_TTL_MS) {\n return true;\n }\n // Backoff applies only to this implicit path (driven by queued work). An\n // explicit `revalidate()` — the visibility/focus listeners, or a caller —\n // always goes to the network so recovery is never delayed by the timer.\n if (Date.now() < this.nextAuthorityAttemptAt) return false;\n await this.revalidateAuthority();\n return this.stateValue !== \"unconfirmed\" && Date.now() - this.confirmedAt < AUTHORITY_TTL_MS;\n }\n\n async revalidate(): Promise<void> {\n this.nextAuthorityAttemptAt = 0;\n await this.revalidateAuthority();\n await this.flush();\n }\n\n private async revalidateAuthority(): Promise<void> {\n if (typeof window === \"undefined\" || !this.fetchImpl) return;\n if (this.inFlight) return this.inFlight;\n this.inFlight = this.revalidateNow().finally(() => {\n this.inFlight = null;\n });\n return this.inFlight;\n }\n\n queuePageView(snapshot: QueuedPageView | null = pageViewSnapshot()): void {\n if (!snapshot) return;\n pushBounded(this.pageQueue, snapshot);\n void this.flush();\n }\n\n fireConversion(key: string, options?: TrackingConfigConversionOptions): void {\n pushBounded(this.conversionQueue, { key, ...options });\n void this.flush();\n }\n\n queueAutomaticEvent(\n eventType: string,\n metadata: Record<string, unknown>,\n transactionPath: string,\n transactionScope: string,\n ): void {\n pushBounded(this.automaticQueue, {\n eventType,\n metadata,\n transactionPath,\n transactionScope,\n });\n void this.flush();\n }\n\n listGoals(): ConversionGoal[] {\n return this.current?.goals ?? EMPTY_GOALS;\n }\n\n /** Sibling hostnames for the linker — read from the cached object even while unconfirmed. */\n linkedHostnames(): readonly string[] {\n return this.current?.linked_hostnames ?? EMPTY_STRINGS;\n }\n\n /**\n * Integration blocks, from the cached object even while unconfirmed: the booking fallback\n * link is safe to show stale. Same reference until the next adopt, for `useSyncExternalStore`.\n */\n integrations(): ConversionIntegrations | null {\n return this.current?.integrations ?? null;\n }\n\n /** The lead-form contracts, from the cached object like `integrations()`. */\n leadForms(): LeadFormsConfig | null {\n return this.current?.lead_forms ?? null;\n }\n\n /** Configured Cloudflare Stream tracked videos for this site. */\n videos(): Record<string, TrackedVideoConfig> {\n return this.current?.videos ?? EMPTY_VIDEOS;\n }\n\n /** Look up one configured Cloudflare Stream tracked video by key. */\n getVideo(key: string): TrackedVideoConfig | null {\n return this.current?.videos?.[key] ?? null;\n }\n\n private async revalidateNow(): Promise<void> {\n try {\n const headers: Record<string, string> = {};\n if (this.etag) headers[\"If-None-Match\"] = this.etag;\n const response = await this.fetchImpl(this.url, {\n method: \"GET\",\n headers,\n cache: \"no-cache\",\n });\n if (response.status === 304 && this.current && validateConfig(this.current, this.ref)) {\n this.confirm(this.current, this.etag);\n return;\n }\n if (!response.ok) {\n this.expireAuthority();\n return;\n }\n const next = parseConversionConfig(await response.json());\n if (!next || !validateConfig(next, this.ref)) {\n this.expireAuthority();\n return;\n }\n if (this.current && next.config_version < this.current.config_version) {\n this.expireAuthority();\n return;\n }\n this.confirm(next, response.headers.get(\"ETag\"));\n } catch {\n this.expireAuthority();\n }\n }\n\n private expireAuthority(): void {\n this.authorityGeneration += 1;\n if (this.stateValue !== \"unconfirmed\") this.stateValue = \"unconfirmed\";\n // Exponential, capped. Queued work keeps accumulating (bounded), so the\n // config appearing later still fires the most recent items.\n this.authorityFailures += 1;\n const delay = Math.min(RETRY_MAX_MS, RETRY_BASE_MS * 2 ** (this.authorityFailures - 1));\n this.nextAuthorityAttemptAt = Date.now() + delay;\n }\n\n private confirm(config: ConversionConfig, etag: string | null): void {\n this.authorityGeneration += 1;\n this.current = config;\n this.etag = etag;\n this.confirmedAt = Date.now();\n this.authorityFailures = 0;\n this.nextAuthorityAttemptAt = 0;\n this.stateValue = isTombstone(config) ? \"tombstone\" : \"active\";\n writeCache(this.url, { etag, config });\n if (this.stateValue === \"tombstone\") {\n if (this.retryTimer !== null) {\n window.clearTimeout(this.retryTimer);\n this.retryTimer = null;\n }\n this.conversionQueue.length = 0;\n this.automaticQueue.length = 0;\n this.pageQueue.length = 0;\n }\n for (const listener of this.listeners) listener();\n }\n\n private async flush(): Promise<void> {\n if (this.flushInFlight) {\n this.flushRequested = true;\n return this.flushInFlight;\n }\n this.flushRequested = false;\n this.flushInFlight = this.flushNow().finally(() => {\n this.flushInFlight = null;\n if (this.flushRequested && this.stateValue === \"active\") void this.flush();\n });\n return this.flushInFlight;\n }\n\n private scheduleRetry(): void {\n if (this.retryTimer !== null || typeof window === \"undefined\") return;\n this.retryTimer = window.setTimeout(() => {\n this.retryTimer = null;\n void this.flush();\n }, 1000);\n }\n\n private async flushNow(): Promise<void> {\n if (!(await this.ensureAuthority())) return;\n if (this.stateValue !== \"active\" || !this.current) return;\n const config = this.current;\n const generation = this.authorityGeneration;\n const ids = Object.values(config.gtag_ids).filter(\n (id): id is string => typeof id === \"string\" && isValidGtagId(id),\n );\n if (ids.length === 0) return;\n await ensureScript(ids[0]!);\n if (\n this.authorityGeneration !== generation ||\n this.stateValue !== \"active\" ||\n this.current !== config\n ) {\n return;\n }\n const gtag = window.gtag;\n if (typeof gtag !== \"function\") return;\n try {\n if (!jsInitialized) {\n gtag(\"js\", new Date());\n jsInitialized = true;\n }\n for (const id of ids) {\n if (configuredIds.has(id)) continue;\n gtag(\"config\", id, { send_page_view: false });\n configuredIds.add(id);\n }\n } catch {\n this.scheduleRetry();\n return;\n }\n while (this.pageQueue.length > 0) {\n const page = this.pageQueue[0]!;\n try {\n gtag(\"event\", \"page_view\", {\n page_location: page.href,\n page_title: page.title ?? undefined,\n page_referrer: page.referrer ?? undefined,\n });\n this.pageQueue.shift();\n } catch {\n this.scheduleRetry();\n return;\n }\n }\n while (this.automaticQueue.length > 0) {\n const event = this.automaticQueue[0]!;\n for (const goal of config.goals) {\n if (goal.kind !== \"event\" || !goal.firing) continue;\n if (!automaticThresholdMet(goal, event.eventType, event.metadata)) continue;\n pushBounded(this.conversionQueue, {\n key: goal.key,\n transactionId: getAutomaticTransactionId(\n event.transactionScope,\n goal.key,\n event.transactionPath,\n ),\n });\n }\n this.automaticQueue.shift();\n }\n while (this.conversionQueue.length > 0) {\n const item = this.conversionQueue[0]!;\n const goal = config.goals.find((g) => g.key === item.key);\n const firing = goal?.firing;\n if (!firing) {\n this.conversionQueue.shift();\n continue;\n }\n const currency = item.currency ?? firing.currency ?? null;\n const value =\n item.value ??\n (firing.value_cents != null && currency\n ? fromMinor(firing.value_cents, currency as SupportedCurrency)\n : null);\n const outcome = fireConversionWithConsent({\n sendTo: firing.send_to,\n value,\n currency,\n transactionId: item.transactionId ?? null,\n });\n if (outcome === \"retryable\") {\n this.scheduleRetry();\n return;\n }\n if (\n outcome === \"fired\" &&\n typeof window !== \"undefined\" &&\n typeof window.fbq === \"function\"\n ) {\n try {\n window.fbq(\"trackCustom\", item.key, {\n ...(value != null ? { value } : {}),\n ...(currency ? { currency } : {}),\n ...(item.transactionId ? { transaction_id: item.transactionId } : {}),\n });\n } catch {\n // never let Meta Pixel custom event break conversion firing\n }\n }\n this.conversionQueue.shift();\n }\n }\n}\n\nexport function getTrackingConfigRuntime(\n ref: TrackingConfigReference,\n fetchImpl?: typeof fetch,\n): TrackingConfigRuntime {\n // Keyed on the RESOLVED url: `{cdnUrl}` and an equivalent\n // `{cdnBaseUrl,businessId,environment}` name the same object and must share one\n // runtime, or each would keep its own authority state and gtag bootstrap.\n const key = `${resolveTrackingConfigUrl(ref)}|${ref.businessId}|${ref.environment}`;\n const existing = runtimes.get(key);\n if (existing) return existing;\n const runtime = new TrackingConfigRuntime(ref, fetchImpl ?? globalThis.fetch);\n runtimes.set(key, runtime);\n return runtime;\n}\n\nexport function resetTrackingConfigRuntimesForTests(): void {\n runtimes.clear();\n configuredIds.clear();\n scriptLoad = null;\n jsInitialized = false;\n}\n","{\n \"hosts\": [\"myalp.io\"]\n}\n","// Provider-agnostic booking read over the published config (`integrations.*.booking`).\n// React-free: the hooks and the link live in `booking-react.tsx`.\n\nimport bookingLinks from \"../../../contracts/autoleap-booking-links.json\";\nimport { parsePhone } from \"./phone\";\nimport type {\n BookingDayWindows,\n BookingMode,\n BookingRulesConfig,\n ConversionIntegrations,\n} from \"./resources/conversion-config\";\n\n/** Hosts a handoff link may point at: the backend's short-link allowlist, from one contract. */\nconst HANDOFF_HOSTS = new Set<string>(bookingLinks.hosts);\n\nexport type BookingProvider = \"autoleap\";\n\n/**\n * An option key the live config offered. Branded so a contract-typed booking accepts it beside the\n * generated keys: a key newer than the site's generated file still books (reported as behind).\n */\nexport type BookingOptionKey = string & { readonly __bookingOptionKey: true };\n\n/** One service choice on the booking form, by the site's own key. */\nexport interface BookingOption {\n key: BookingOptionKey;\n label: string;\n /** The shop's service category, for a heading over its choices; null when it has none. */\n group: string | null;\n /** Optional helper text to show under the label. */\n description: string | null;\n}\n\n/** One heading on the form and the choices under it; `group` null holds the ungrouped ones. */\nexport interface BookingOptionGroup {\n group: string | null;\n options: BookingOption[];\n}\n\n/** The booking rules in camelCase, all in the shop's wall time. */\nexport interface BookingRules {\n minDaysAhead: number;\n maxDaysAhead: number;\n /** Start hours per weekday, `monday` … `sunday`; both lists empty = closed. */\n hours: Record<string, BookingDayWindows>;\n /** `YYYY-MM-DD` shop dates with no bookings although the weekday is open. */\n closedDates: string[];\n /** `YYYY-MM-DD` shop dates whose start hours replace their weekday's. */\n specialHours: Record<string, BookingDayWindows>;\n vehicleRequired: boolean;\n /** The vehicle may come after booking: send it with `bookings.updateBooking`. */\n vehicleDeferred: boolean;\n /** `waitForIt` / `dropOff`; empty when the shop hides the question. */\n dropOffOptions: string[];\n /** `YYYY-MM-DD` shop dates → live bookings already held that day; days with none absent. */\n bookedCounts: Record<string, number>;\n}\n\nexport interface BookingIntegration {\n provider: BookingProvider;\n /** False: render only the handoff link. Never block the customer on it. */\n enabled: boolean;\n /** The shop's own booking page. */\n fallbackUrl: string;\n /** True when the site may post a booking: `enabled`, rules captured and options mapped. */\n acceptsBookings: boolean;\n /** The shop's IANA timezone; every date and hour here is its wall time. */\n timezone: string;\n /** Sent back with each booking; a stale one is refused and the config refetched. */\n configVersion: number | null;\n rules: BookingRules | null;\n options: BookingOption[];\n /**\n * Where a booking from this page goes: `live` in production; on a development config `test`\n * (the shop's test lead source) or `simulated` (never sent). Show a banner when not `live`.\n */\n mode: BookingMode;\n}\n\n/** What the visitor already typed, carried onto the shop's booking page. */\nexport interface BookingHandoffPrefill {\n /** A single full-name field; split on the first space when `firstName` is not given. */\n name?: string | null;\n firstName?: string | null;\n lastName?: string | null;\n /** Any format; sent as national digits (no country code), as the shop's portal stores it. */\n phone?: string | null;\n email?: string | null;\n}\n\n/** The enabled-or-fallback booking provider in a config's integrations, if any. */\nexport function getBookingIntegration(\n integrations: ConversionIntegrations | null | undefined,\n): BookingIntegration | null {\n const booking = integrations?.autoleap?.booking;\n if (!booking) return null;\n const rules = booking.rules ? camelRules(booking.rules) : null;\n return {\n provider: \"autoleap\",\n enabled: booking.enabled,\n fallbackUrl: booking.fallback_url,\n acceptsBookings: booking.accepts_bookings && rules !== null && booking.config_version !== null,\n timezone: booking.timezone,\n configVersion: booking.config_version,\n rules,\n options: booking.options.map((option) => ({\n key: option.key as BookingOptionKey,\n label: option.label,\n group: option.group,\n description: option.description,\n })),\n mode: booking.mode ?? \"live\",\n };\n}\n\nfunction camelRules(rules: BookingRulesConfig): BookingRules {\n return {\n minDaysAhead: rules.min_days_ahead,\n maxDaysAhead: rules.max_days_ahead,\n hours: rules.hours,\n closedDates: rules.closed_dates,\n specialHours: rules.special_hours,\n vehicleRequired: rules.vehicle_required,\n vehicleDeferred: rules.vehicle_deferred,\n dropOffOptions: rules.drop_off_options,\n bookedCounts: rules.booked_counts ?? {},\n };\n}\n\nconst WEEKDAYS = [\"sunday\", \"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\", \"saturday\"];\n\n/** One bookable day: its start hours split as the shop's portal shows them. */\nexport interface BookableDay {\n /** `YYYY-MM-DD`, shop-local. */\n date: string;\n /** `monday` … `sunday`. */\n weekday: string;\n /** Start hours before noon (8 = 08:00). */\n morning: number[];\n /** Start hours from noon on. */\n afternoon: number[];\n /**\n * Live bookings the shop already holds that day (0 when none). Never filters a day out:\n * a site with a daily cap marks the day full itself.\n */\n booked: number;\n}\n\n/** Today's date in `timeZone` as `YYYY-MM-DD`; the server judges notice by the same date. */\nexport function shopToday(timeZone: string, now: Date = new Date()): string {\n const parts = new Intl.DateTimeFormat(\"en-CA\", {\n timeZone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n }).formatToParts(now);\n const part = (type: string) => parts.find((p) => p.type === type)?.value ?? \"\";\n return `${part(\"year\")}-${part(\"month\")}-${part(\"day\")}`;\n}\n\nfunction addDays(isoDate: string, days: number): string {\n const [year, month, day] = isoDate.split(\"-\").map(Number);\n const date = new Date(Date.UTC(year, month - 1, day + days));\n return date.toISOString().slice(0, 10);\n}\n\n/**\n * Every day a customer may book, from the minimum notice to the end of the window, with its\n * start hours. Exactly what the server accepts: closed dates removed, special hours applied.\n */\nexport function bookableSlots(\n rules: BookingRules,\n { timeZone, now = new Date() }: { timeZone: string; now?: Date },\n): BookableDay[] {\n const today = shopToday(timeZone, now);\n const days: BookableDay[] = [];\n const closed = new Set(rules.closedDates);\n for (let offset = rules.minDaysAhead; offset <= rules.maxDaysAhead; offset += 1) {\n const date = addDays(today, offset);\n if (closed.has(date)) continue;\n const weekday = WEEKDAYS[new Date(`${date}T00:00:00Z`).getUTCDay()];\n const windows = rules.specialHours[date] ?? rules.hours[weekday];\n if (!windows || (windows.morning.length === 0 && windows.afternoon.length === 0)) continue;\n days.push({\n date,\n weekday,\n morning: [...windows.morning],\n afternoon: [...windows.afternoon],\n booked: rules.bookedCounts?.[date] ?? 0,\n });\n }\n return days;\n}\n\n/**\n * The handoff URL with the visitor's details prefilled, or null when `fallbackUrl` is not an\n * https link on the shop's booking host (a config value never becomes an arbitrary redirect).\n */\nexport function buildBookingHandoffUrl(\n fallbackUrl: string,\n prefill: BookingHandoffPrefill = {},\n): string | null {\n let url: URL;\n try {\n url = new URL(fallbackUrl);\n } catch {\n return null;\n }\n if (\n url.protocol !== \"https:\" ||\n !HANDOFF_HOSTS.has(url.hostname) ||\n url.username ||\n url.password ||\n url.port\n ) {\n return null;\n }\n const [firstName, lastName] = prefill.firstName?.trim()\n ? [prefill.firstName, prefill.lastName]\n : splitFullName(prefill.name);\n const values: Array<[string, string | null | undefined]> = [\n [\"firstName\", firstName?.trim()],\n [\"lastName\", lastName?.trim()],\n [\"phone\", prefill.phone ? nationalDigits(prefill.phone) : \"\"],\n [\"email\", prefill.email?.trim()],\n ];\n for (const [key, value] of values) {\n if (value) url.searchParams.set(key, value);\n }\n return url.toString();\n}\n\nfunction splitFullName(name: string | null | undefined): [string, string] {\n const [first = \"\", ...rest] = (name ?? \"\").trim().split(/\\s+/);\n return [first, rest.join(\" \")];\n}\n\n/** \"+1 (416) 555-0100\" → \"4165550100\"; an unparseable value keeps its raw digits. */\nfunction nationalDigits(phone: string): string {\n const parsed = parsePhone(phone);\n return (parsed.isValid ? parsed.national : phone).replace(/\\D/g, \"\");\n}\n\n/**\n * The options as headed sections, in published order: one per category, choices in order\n * under it. The API saves them grouped, so a category is never split; this keeps the first run.\n */\nexport function groupBookingOptions(options: readonly BookingOption[]): BookingOptionGroup[] {\n const groups = new Map<string | null, BookingOption[]>();\n for (const option of options) {\n const members = groups.get(option.group);\n if (members) members.push(option);\n else groups.set(option.group, [option]);\n }\n return [...groups].map(([group, members]) => ({ group, options: members }));\n}\n","// Auto-fire on-page conversions for AUTOMATIC event-goals (GAP28 / unified-goal ADR).\n//\n// The SDK's automatic detectors (scroll_depth, time_on_site, multi_page_session,\n// specific_page_visit, page_view, form_start) already emit analytics events via\n// `client.trackEvent`. This layer observes those events and ALSO fires a\n// `gtag('event','conversion')` when a published EVENT-goal's trigger threshold is met — so the\n// consumer writes ZERO conversion code for automatic goals (the config drives everything).\n//\n// `phone_click` is the one manual event we ALSO auto-fire: a `tel:` tap (captured by\n// `attachPhoneClickCapture`) is an unambiguous on-site conversion, so a linked phone_click\n// event-goal fires with zero consumer code (see thresholdMet). The other manual event-goals\n// (form_submit/cta_click) are NOT auto-fired — the consumer fires them via `trackConversion`.\n// Sales fire via `recordSale`.\n\nimport type { TrackingClient, TrackEventInput } from \"../ingest\";\nimport { getAutomaticTransactionId } from \"./automatic-transaction\";\nimport { automaticThresholdMet } from \"./automatic-trigger\";\nimport type { ConversionConfigStore } from \"./conversion-config\";\nimport { fireConversionWithConsent } from \"./conversion-firing\";\nimport { fromMinor } from \"./sales/money\";\nimport type { SupportedCurrency } from \"./sales/schema\";\nimport type { TrackingConfigRuntime } from \"./tracking-config-runtime\";\n\nfunction currentPath(): string {\n return typeof window === \"undefined\" ? \"\" : window.location.pathname;\n}\n\nexport interface ConversionAutoFire {\n /** Fire any matching automatic event-goal's conversion for a just-emitted detector event. */\n onAutomaticEvent(\n eventType: string,\n metadata: Record<string, unknown>,\n transactionScope: string,\n ): void;\n}\n\n// Bound the pre-config buffer so a page that never resolves its config (offline, bad\n// URL) can't grow it without limit.\nconst MAX_BUFFERED_EVENTS = 50;\n\n/** Build the auto-fire matcher bound to a config store. */\nexport function createConversionAutoFire(\n store:\n | Pick<ConversionConfigStore, \"listGoals\" | \"isReady\" | \"onResolve\">\n | Pick<TrackingConfigRuntime, \"listGoals\" | \"fireConversion\" | \"queueAutomaticEvent\">,\n): ConversionAutoFire {\n // Detectors fire synchronously, but the CDN config resolves async. A detector can emit\n // before a cold-cache fetch lands (an early real scroll, a fast time_on_site threshold,\n // the initial page_view) and won't re-emit on the same page. Buffer events that arrive\n // before the config is ready and replay them on the first resolve, so an early trigger\n // still converts (a beat late) instead of being silently dropped.\n const pending: Array<{\n eventType: string;\n metadata: Record<string, unknown>;\n transactionScope: string;\n }> = [];\n let subscribed = false;\n\n function fireMatching(\n eventType: string,\n metadata: Record<string, unknown>,\n transactionScope: string,\n ): void {\n for (const goal of store.listGoals()) {\n if (goal.kind !== \"event\" || !goal.firing) continue;\n if (!automaticThresholdMet(goal, eventType, metadata)) continue;\n const transactionId = getAutomaticTransactionId(transactionScope, goal.key, currentPath());\n if (\"queueAutomaticEvent\" in store) {\n store.fireConversion(goal.key, {\n transactionId,\n });\n continue;\n }\n const firing = goal.firing;\n const cents = firing.value_cents ?? null;\n const currency = firing.currency ?? null;\n const value =\n cents != null && currency ? fromMinor(cents, currency as SupportedCurrency) : null;\n const outcome = fireConversionWithConsent({\n sendTo: firing.send_to,\n value,\n currency,\n transactionId,\n });\n if (\n outcome === \"fired\" &&\n typeof window !== \"undefined\" &&\n typeof window.fbq === \"function\"\n ) {\n try {\n window.fbq(\"trackCustom\", goal.key, {\n event_type: eventType,\n ...(value != null ? { value } : {}),\n ...(currency ? { currency } : {}),\n transaction_id: transactionId,\n });\n } catch {\n // never let Meta Pixel custom event break conversion firing\n }\n }\n }\n }\n\n return {\n onAutomaticEvent(eventType, metadata, transactionScope) {\n if (\"queueAutomaticEvent\" in store) {\n store.queueAutomaticEvent(eventType, metadata, currentPath(), transactionScope);\n return;\n }\n if (store.isReady()) {\n fireMatching(eventType, metadata, transactionScope);\n return;\n }\n // Config still loading — buffer (bounded) and replay on the first resolve.\n if (pending.length < MAX_BUFFERED_EVENTS) {\n pending.push({ eventType, metadata, transactionScope });\n }\n if (!subscribed) {\n subscribed = true;\n store.onResolve(() => {\n const buffered = pending.splice(0);\n for (const event of buffered) {\n fireMatching(event.eventType, event.metadata, event.transactionScope);\n }\n });\n }\n },\n };\n}\n\n/**\n * Wrap a {@link TrackingClient} so every automatic event it tracks ALSO drives conversion\n * auto-fire. Pass the wrapped client to the `attach*` detectors. The auto-fire is best-effort\n * and never breaks ingest.\n */\nexport function withConversionAutoFire(\n client: TrackingClient,\n autoFire: ConversionAutoFire,\n): TrackingClient {\n return {\n ...client,\n trackEvent: (input: TrackEventInput) => {\n client.trackEvent(input);\n try {\n autoFire.onAutomaticEvent(input.eventType, input.metadata ?? {}, client.getSessionId());\n } catch {\n // never let conversion firing break analytics ingest\n }\n },\n };\n}\n","export type EventOutcomeRole = \"lead\" | \"engagement\" | \"navigation\" | \"diagnostic\";\nexport type EventCategory = \"page\" | \"engagement\" | \"lead\" | \"commerce\" | \"system\";\nexport type EventClientVisibility = \"simple\" | \"detailed\" | \"hidden\";\n\nexport interface EventSemantics {\n label: string;\n category: EventCategory;\n outcomeRole: EventOutcomeRole;\n clientVisibility: EventClientVisibility;\n}\n\n/**\n * Lightweight runtime event metadata. This module intentionally imports no\n * Zod schemas so dashboards and framework adapters can consume it without\n * pulling the validation registry into their bundles.\n */\nexport const EVENT_SEMANTICS = {\n page_view: {\n label: \"Page view\",\n category: \"page\",\n outcomeRole: \"navigation\",\n clientVisibility: \"simple\",\n },\n time_on_site: {\n label: \"Time on site\",\n category: \"engagement\",\n outcomeRole: \"engagement\",\n clientVisibility: \"detailed\",\n },\n specific_page_visit: {\n label: \"Key page visit\",\n category: \"engagement\",\n outcomeRole: \"engagement\",\n clientVisibility: \"detailed\",\n },\n scroll_depth: {\n label: \"Scroll depth\",\n category: \"engagement\",\n outcomeRole: \"engagement\",\n clientVisibility: \"detailed\",\n },\n multi_page_session: {\n label: \"Multi-page session\",\n category: \"engagement\",\n outcomeRole: \"engagement\",\n clientVisibility: \"detailed\",\n },\n form_start: {\n label: \"Form started\",\n category: \"engagement\",\n outcomeRole: \"engagement\",\n clientVisibility: \"simple\",\n },\n sdk_heartbeat: {\n label: \"SDK heartbeat\",\n category: \"system\",\n outcomeRole: \"diagnostic\",\n clientVisibility: \"hidden\",\n },\n page_exit: {\n label: \"Page exit\",\n category: \"engagement\",\n outcomeRole: \"diagnostic\",\n clientVisibility: \"detailed\",\n },\n form_step: {\n label: \"Form step\",\n category: \"engagement\",\n outcomeRole: \"engagement\",\n clientVisibility: \"detailed\",\n },\n // Adds to the submit it names; counting it as a lead would double every lead.\n form_details: {\n label: \"Form details added\",\n category: \"lead\",\n outcomeRole: \"engagement\",\n clientVisibility: \"detailed\",\n },\n form_submit: {\n label: \"Form submitted\",\n category: \"lead\",\n outcomeRole: \"lead\",\n clientVisibility: \"simple\",\n },\n phone_click: {\n label: \"Phone click\",\n category: \"lead\",\n outcomeRole: \"lead\",\n clientVisibility: \"simple\",\n },\n cta_click: {\n label: \"CTA click\",\n category: \"engagement\",\n outcomeRole: \"engagement\",\n clientVisibility: \"simple\",\n },\n site_link_click: {\n label: \"Left for sibling site\",\n category: \"page\",\n outcomeRole: \"navigation\",\n clientVisibility: \"detailed\",\n },\n site_link_arrival: {\n label: \"Arrived from sibling site\",\n category: \"page\",\n outcomeRole: \"navigation\",\n clientVisibility: \"simple\",\n },\n video_play: {\n label: \"Video played\",\n category: \"engagement\",\n outcomeRole: \"engagement\",\n clientVisibility: \"simple\",\n },\n video_progress: {\n label: \"Video watched\",\n category: \"engagement\",\n outcomeRole: \"engagement\",\n clientVisibility: \"detailed\",\n },\n} as const satisfies Record<string, EventSemantics>;\n\nexport type EventName = keyof typeof EVENT_SEMANTICS;\n\nexport function getEventSemantics(eventName: string): EventSemantics | null {\n return eventName in EVENT_SEMANTICS ? EVENT_SEMANTICS[eventName as EventName] : null;\n}\n","// Auto page_view emission: on attach, on pushState/replaceState/popstate, and on\n// bfcache restore, all through one per-route gate. The Next provider drives the\n// gate from `usePathname` instead of patching history.\n\nimport { parse } from \"zod/mini\";\n\nimport { stripLinkParamFromSearch } from \"./cross-site-link\";\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 autoPageViewSlot().key = null;\n}\n\n// Last auto-emitted route, per document. On `globalThis`, not module state, so a\n// second copy of the bundle (script tag loaded twice) shares it.\nconst AUTO_PAGE_VIEW_SLOT = Symbol.for(\"aranova.tracking.autoPageView\");\n\ninterface AutoPageViewSlot {\n key: string | null;\n}\n\nfunction autoPageViewSlot(): AutoPageViewSlot {\n const scope = globalThis as { [AUTO_PAGE_VIEW_SLOT]?: AutoPageViewSlot };\n scope[AUTO_PAGE_VIEW_SLOT] ??= { key: null };\n return scope[AUTO_PAGE_VIEW_SLOT];\n}\n\n/**\n * Canonical route identity for page_view dedupe: path plus query, without the\n * cross-site token and with an empty query spelled one way (`\"\"`, never `\"?\"`).\n */\nexport function pageViewRouteKey(pathname: string, search: string): string {\n const query = stripLinkParamFromSearch(search);\n return query.length > 1 ? `${pathname}${query}` : pathname;\n}\n\nexport interface FireAutoPageViewOptions {\n /** Emit even for the route already emitted (a bfcache restore is a real re-entry). */\n force?: boolean;\n}\n\n/**\n * Emit the automatic `page_view` unless this route was the last one emitted; returns whether it did.\n *\n * The single gate for every automatic emitter, so extra providers, remounts, re-inits or a\n * second script copy cannot double-count one navigation (ENG-358).\n */\nexport function fireAutoPageView(\n client: TrackingClient,\n options: FireAutoPageViewOptions = {},\n): boolean {\n if (typeof window === \"undefined\") return false;\n const key = pageViewRouteKey(window.location.pathname, window.location.search);\n const slot = autoPageViewSlot();\n if (!options.force && slot.key === key) return false;\n slot.key = key;\n fireManualPageView(client);\n return true;\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\") 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 parse(pageViewMetadataSchema, {\n page: {\n title: document.title || null,\n path: window.location.pathname,\n search: stripLinkParamFromSearch(window.location.search),\n hash: window.location.hash,\n },\n referrer: 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, unconditionally.\n *\n * Automatic emitters go through `fireAutoPageView`, which dedupes per route.\n */\nexport function fireManualPageView(client: TrackingClient): void {\n if (typeof window === \"undefined\") 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 ? previousFiredUrl : 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 = 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 fireAutoPageView(client, { force: true });\n }\n\n window.addEventListener(\"pageshow\", handlePageShow);\n return () => {\n window.removeEventListener(\"pageshow\", handlePageShow);\n };\n}\n\n/**\n * Attach automatic page view tracking for a browser SPA.\n *\n * Fires for the current route, then on every history navigation and bfcache\n * restore; `fireAutoPageView` drops same-route repeats. Returns a detacher.\n */\nexport function attachAutoPageView(client: TrackingClient): () => void {\n if (typeof window === \"undefined\" || typeof history === \"undefined\") {\n return () => {};\n }\n\n function fireForCurrentRoute(): void {\n fireAutoPageView(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(fireForCurrentRoute, 0);\n }\n function patchedReplaceState(this: History, ...args: Parameters<History[\"replaceState\"]>): void {\n originalReplaceState(...args);\n setTimeout(fireForCurrentRoute, 0);\n }\n\n history.pushState = patchedPushState as History[\"pushState\"];\n history.replaceState = patchedReplaceState as History[\"replaceState\"];\n window.addEventListener(\"popstate\", fireForCurrentRoute);\n // popstate does not fire on a bfcache restore; pageshow(persisted) does.\n const detachBfcacheRestore = attachBfcacheRestore(client);\n\n fireForCurrentRoute();\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener(\"popstate\", fireForCurrentRoute);\n detachBfcacheRestore();\n };\n}\n","import * as z from \"zod/mini\";\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.strictObject({\n page: z.strictObject({\n title: z.nullable(z.string()),\n path: z.string(),\n search: z.string(),\n hash: z.string(),\n }),\n referrer: z.nullable(z.string()),\n // Optional + nullable — 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.optional(\n z.nullable(\n z.strictObject({\n w: z.number(),\n h: z.number(),\n }),\n ),\n ),\n});\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.strictObject({});\nexport type PageViewConfig = z.infer<typeof pageViewConfigSchema>;\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, non-empty trigger config, and which site's config object the SDK\n * loads (`config_site_id`; null on a legacy per-business reference).\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 configSiteId: string | null = null,\n): SdkHeartbeatMetadata {\n const automaticNames = triggers ? Object.keys(triggers.automatic) : [];\n const manualNames = triggers?.manual ? Object.keys(triggers.manual) : [];\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 config_site_id: configSiteId,\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 { getRegisteredCapabilities } from \"./capabilities\";\nimport { getConsentChoice, getConsentState } from \"./consent\";\nimport { consumeInboundLink, stripLinkParam, type OriginLink } from \"./cross-site-link\";\nimport { getEventSemantics } from \"./events/semantics\";\nimport { resetPageViewState } from \"./page-view\";\nimport type { PhoneConfig } from \"./phone-field\";\nimport { captureFbc, getFbcCookie, getFbpCookie } from \"./fbq\";\nimport {\n captureTrackingParamsFromLocation,\n createEmptyTrackingParams,\n getCookieValueFromDocument,\n getTrackingParamsFromCookieReader,\n mergeTrackingParams,\n} from \"./tracking\";\nimport type { TriggerRegistryConfig } from \"./events/registry\";\nimport { buildHeartbeatMetadata } from \"./heartbeat\";\nimport { buildLandingPayloadFields, getOrCaptureLandingParams } from \"./landing\";\nimport { getOrRotateSessionId, getVisitorId } from \"./session\";\nimport { stashUserDataFromFormFields } from \"./user-data\";\nimport type {\n TrackingClientContext,\n TrackingEnvironment,\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 * Ceiling on the exponential backoff between failed delivery attempts.\n */\nexport const MAX_RETRY_BACKOFF_MS = 60_000;\n\n/**\n * Cap on events held in the durable retry buffer. Oldest batches are dropped\n * first once this is exceeded — an unbounded buffer would eventually blow the\n * localStorage quota and take the whole SDK down with it.\n */\nexport const MAX_BUFFERED_EVENTS = 200;\n\n/**\n * localStorage key prefix for the durable retry buffer. Versioned so a future\n * shape change can't be misread as the current one.\n */\nexport const RETRY_BUFFER_KEY_PREFIX = \"aranova_tracking_pending_v1\";\n\n/**\n * What to do with a batch after an attempted delivery.\n *\n * `retry` covers the transport failing and the server saying \"later\" (408, 429,\n * 5xx). `drop` covers a permanent rejection — a 422 from a malformed payload\n * will never succeed, and retrying it forever would wedge every batch behind it.\n */\ntype DeliveryOutcome = \"ok\" | \"retry\" | \"drop\";\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 /** `siteId` of the tracking-config reference in use; reported on the heartbeat. */\n configSiteId?: string | null;\n /** When true, swallow nothing — useful for tests. */\n debug?: boolean;\n /** Phone-field config, carried for parity; the React hook reads it via context. */\n phone?: PhoneConfig;\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 (fetch, non-keepalive). */\n flush: () => Promise<void>;\n /**\n * Flush queued events through the keepalive transport so the request\n * survives document unload. Use from `pagehide`/`visibilitychange:hidden`\n * handlers — a plain `flush()` there is aborted by the browser on unload.\n */\n flushBeacon: () => void;\n /**\n * Register a synchronous pre-unload hook invoked right before `flushBeacon`\n * snapshots the queue into a keepalive batch. Used by `page_exit` and\n * `<TrackedVideo>` so their final segment/progress events ride in the same\n * keepalive request as any queued events.\n */\n onBeforeUnload?: (listener: () => void) => () => 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 // Rebuilt per flush (this runs inside the payload builder), so a client\n // constructed later on a deeper route still gets reported.\n capabilities: getRegisteredCapabilities(),\n };\n}\n\nfunction readTrackingParams(): TrackingParams {\n if (typeof window === \"undefined\") 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 // Effective consent + provenance so the dashboard can tell default-granted\n // (opt-out model, no interaction) apart from an explicit choice.\n const choice = getConsentChoice();\n return {\n state: choice.state,\n source: choice.source,\n updated_at: choice.updatedAt,\n expires_at: choice.expiresAt,\n };\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<DeliveryOutcome> {\n // No fetch at all (a non-browser runtime): there is nothing to retry against,\n // and scheduling one would spin a timer forever. Give up on the batch.\n if (typeof fetch !== \"function\") return \"drop\";\n try {\n const response = 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 if (response.ok) return \"ok\";\n if (response.status === 408 || response.status === 429 || response.status >= 500) {\n return \"retry\";\n }\n // 401/403/422 and friends: the payload or the key is wrong, and will still\n // be wrong in 30 seconds.\n return \"drop\";\n } catch {\n // Network-level failure (offline, DNS, CORS preflight, aborted). Never\n // throws to the host site; the batch stays buffered for the next attempt.\n return \"retry\";\n }\n}\n\n/**\n * A batch awaiting delivery. Each one carries its OWN session snapshot: a batch\n * can outlive the session that produced it (buffered across a reload), and\n * re-sending it under whatever session is current would silently misattribute\n * those events.\n */\ninterface PendingBatch {\n session: TrackingSessionUpsertPayload;\n events: QueuedEvent[];\n}\n\nfunction retryBufferKey(apiKey: string, endpoint: string): string {\n return `${RETRY_BUFFER_KEY_PREFIX}:${apiKey}:${endpoint}`;\n}\n\nfunction readPendingBatches(key: string): PendingBatch[] {\n if (typeof window === \"undefined\") return [];\n try {\n const raw = window.localStorage.getItem(key);\n if (!raw) return [];\n const parsed: unknown = JSON.parse(raw);\n if (!Array.isArray(parsed)) return [];\n return parsed.filter(\n (entry): entry is PendingBatch =>\n typeof entry === \"object\" &&\n entry !== null &&\n \"session\" in entry &&\n Array.isArray((entry as PendingBatch).events),\n );\n } catch {\n // Storage blocked, or a corrupt/foreign value — start clean rather than\n // letting a bad read break the host site.\n return [];\n }\n}\n\n/**\n * Mirror the buffer to storage unconditionally.\n *\n * Deliberately no read-back verification: under partitioned storage a write can\n * succeed for this document and still not be visible to a verifying read, so a\n * verify-gate false-negatives exactly where durability matters most.\n */\nfunction writePendingBatches(key: string, batches: PendingBatch[]): void {\n if (typeof window === \"undefined\") return;\n try {\n if (batches.length === 0) window.localStorage.removeItem(key);\n else window.localStorage.setItem(key, JSON.stringify(batches));\n } catch {\n // Quota exceeded or storage blocked — in-memory retry still works.\n }\n}\n\n/** Drop the oldest batches until the buffer is back under the event cap. */\nfunction trimToBufferCap(batches: PendingBatch[]): PendingBatch[] {\n let total = batches.reduce((sum, batch) => sum + batch.events.length, 0);\n const trimmed = batches.slice();\n while (total > MAX_BUFFERED_EVENTS && trimmed.length > 1) {\n const dropped = trimmed.shift();\n total -= dropped ? dropped.events.length : 0;\n }\n return trimmed;\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 // A config swap is a genuinely new client, not a remount of this one — the old client's\n // route already latched the page_view gate, which would otherwise suppress the new client's.\n resetPageViewState();\n }\n globalClient = createTrackingClient(config);\n globalClientKey = key;\n return globalClient;\n}\n\n// Global/delegated captures (document/window listeners: page_exit, scroll_depth,\n// time_on_site, form_start, cta_click, phone_click, bfcache restore) must attach ONCE\n// per page-singleton client — NOT once per React provider mount. A page can mount several\n// <TrackingProvider>s (a supported island pattern: a global provider for page-level\n// triggers plus per-component providers so `useSearchParams()` doesn't opt the whole tree\n// out of static rendering). Since the client is a singleton but each provider runs its own\n// attach effect, N mounts would stack N document listeners and every interaction would emit\n// its event N times. We ref-count attaches against the singleton client's identity: the\n// first mount attaches, later mounts are no-ops, and the last unmount detaches.\ninterface ClientCaptureEntry {\n detach: () => void;\n refCount: number;\n}\nconst clientCaptureRegistry = new WeakMap<TrackingClient, ClientCaptureEntry>();\n\n/**\n * Attach a set of global/delegated captures against a singleton `client` exactly once,\n * ref-counted across provider mounts. `build` performs the actual `document`/`window`\n * listener attachment and returns a single detacher for all of them; it runs only on the\n * first mount for a given client. The returned release decrements the ref-count and runs\n * `build`'s detacher when the last holder releases. Release is idempotent — React\n * StrictMode invokes an effect's cleanup twice in dev, and a double release must not\n * double-decrement.\n */\nexport function attachClientCapturesOnce(\n client: TrackingClient,\n build: () => () => void,\n): () => void {\n let entry = clientCaptureRegistry.get(client);\n if (entry === undefined) {\n entry = { detach: build(), refCount: 0 };\n clientCaptureRegistry.set(client, entry);\n }\n entry.refCount += 1;\n\n let released = false;\n return () => {\n if (released) return;\n released = true;\n const current = clientCaptureRegistry.get(client);\n if (current === undefined) return;\n current.refCount -= 1;\n if (current.refCount <= 0) {\n current.detach();\n clientCaptureRegistry.delete(client);\n }\n };\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 // Force-detach any captures still attached to this client so their document/window\n // listeners can't leak into the next test even if a provider didn't unmount.\n const entry = clientCaptureRegistry.get(globalClient);\n if (entry !== undefined) {\n entry.detach();\n clientCaptureRegistry.delete(globalClient);\n }\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 // Batches that have been handed to the network at least once and not yet\n // acknowledged. Mirrored to localStorage so a reload — or a deploy window that\n // 5xxs every request — costs latency instead of data.\n const bufferKey = retryBufferKey(config.apiKey, endpointBase);\n let pending: PendingBatch[] = readPendingBatches(bufferKey);\n // Flushes are serialized through one chain. Batches are no longer removed from\n // the buffer before the response arrives, so two overlapping flushes would\n // send the same batch twice. Chaining (rather than a \"busy\" flag that returns\n // early) keeps `await flush()` meaning \"flushed\" for the caller.\n let flushChain: Promise<void> = Promise.resolve();\n let retryAttempt = 0;\n let firstPage: string | null = null;\n // Attribution captured synchronously at client creation (before any SPA router\n // can strip the query string). Merged into every session payload as a fallback\n // so a blocked-cookie webview or a stripped URL still attributes.\n let initialParams = createEmptyTrackingParams();\n let destroyed = false;\n\n // Consumed BEFORE identity is minted: it may supply the visitor id itself.\n let inboundLink: ReturnType<typeof consumeInboundLink> = null;\n if (typeof window !== \"undefined\") {\n try {\n inboundLink = consumeInboundLink();\n } catch {\n // never break the host site\n }\n }\n // The sibling-site session this page load arrived from; cleared on rotation.\n let originLink: OriginLink | null = inboundLink?.link ?? null;\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 = stripLinkParam(window.location.href);\n try {\n initialParams = captureTrackingParamsFromLocation();\n } catch {\n // ignore — never break the host site\n }\n // Pin this session's LANDING params now, before any SPA router strips the\n // query string (reuses the stored record when the session already has one).\n getOrCaptureLandingParams(sessionId);\n try {\n captureFbc();\n } catch {\n // ignore\n }\n }\n\n /** The live page URL as stored — never carrying the transport token. */\n function currentPageUrl(): string | null {\n return typeof window === \"undefined\" ? null : stripLinkParam(window.location.href);\n }\n\n // SDK-internal events are queued at construction, before `trackEvent` exists.\n function enqueueInternal(\n eventType: string,\n metadata: Record<string, unknown>,\n pageUrl: string | null,\n ): void {\n queue.push({\n event_type: eventType,\n page_url: pageUrl,\n metadata,\n occurred_at: new Date().toISOString(),\n });\n }\n\n // Re-read per call: a session can rotate mid-page, and the heartbeat reports\n // the page the visitor is on then, not the one the client was built on.\n function enqueueHeartbeat(): void {\n enqueueInternal(\n \"sdk_heartbeat\",\n buildHeartbeatMetadata(\n config.surface,\n sdkVersion,\n packageName,\n config.triggers ?? null,\n activeGtagIds,\n config.configSiteId ?? null,\n ),\n currentPageUrl(),\n );\n }\n\n if (initialSession.isNew) {\n enqueueHeartbeat();\n }\n\n // A rejected token is reported too: the backend never sees one, only this verdict.\n if (inboundLink !== null && typeof window !== \"undefined\") {\n const link = inboundLink.link;\n enqueueInternal(\n \"site_link_arrival\",\n {\n outcome: inboundLink.outcome,\n origin_session_id: link?.sessionId ?? null,\n origin_visitor_id: link?.visitorId ?? null,\n transit_ms: link ? Math.max(0, Date.now() - link.linkedAt) : null,\n adopted_visitor: inboundLink.adoptedVisitor,\n page: { path: window.location.pathname },\n },\n firstPage,\n );\n scheduleFlush();\n }\n\n // Anything left buffered by a previous page load goes out on the normal\n // debounce, ahead of whatever this page produces.\n if (pending.length > 0) {\n scheduleFlush();\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 originLink = null;\n }\n sessionId = rotated.id;\n // Prefer the live cookie/localStorage read; fall back to the init-time\n // snapshot (covers a webview that blocked the cookie AND a router that\n // already stripped the landing URL by flush time).\n const params = mergeTrackingParams(readTrackingParams(), initialParams);\n const context = buildContext(\n config.surface,\n sdkVersion,\n packageName,\n environment,\n activeGtagIds,\n );\n return {\n session_id: sessionId,\n visitor_id: visitorId,\n gclid: params.gclid,\n wbraid: params.wbraid,\n gbraid: params.gbraid,\n ylpcid: params.ylpcid,\n fbclid: params.fbclid,\n fbc: getFbcCookie(),\n fbp: getFbpCookie(),\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 // Landing params for the CURRENT session id — captured on the spot when\n // the session just rotated (the current URL is the rotated session's\n // landing), reused from the stored record otherwise. Keys are omitted\n // entirely when the landing isn't observable (SSR).\n ...buildLandingPayloadFields(sessionId),\n ...(originLink\n ? {\n origin_session_id: originLink.sessionId,\n origin_visitor_id: originLink.visitorId,\n origin_linked_at: new Date(originLink.linkedAt).toISOString(),\n }\n : {}),\n first_page: firstPage,\n consent_state: consentSnapshot(),\n context,\n };\n }\n\n function scheduleFlush(delayMs: number = flushIntervalMs): void {\n if (flushTimer !== null || destroyed) return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n void flush();\n }, delayMs);\n }\n\n function clearScheduledFlush(): void {\n if (flushTimer !== null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n }\n\n /** Move everything currently queued into the durable buffer. */\n function bufferQueued(): void {\n if (queue.length === 0) return;\n const events = queue.slice(0, HARD_MAX_BATCH);\n queue = queue.slice(events.length);\n pending = trimToBufferCap([...pending, { session: buildSessionPayload(), events }]);\n writePendingBatches(bufferKey, pending);\n }\n\n /**\n * Drain the durable buffer, oldest batch first.\n *\n * A batch leaves the buffer only on `ok` (acknowledged) or `drop` (permanently\n * rejected). Anything else keeps it, so a 5xx window costs latency, not data.\n * At-least-once: the backend drops a re-sent event by `tracking.events.dedupe_key`.\n */\n function flush(): Promise<void> {\n // `.catch` keeps one unexpected throw from poisoning every later flush.\n flushChain = flushChain.then(runFlush).catch(() => {});\n return flushChain;\n }\n\n async function runFlush(): Promise<void> {\n if (destroyed) return;\n clearScheduledFlush();\n bufferQueued();\n if (pending.length === 0) return;\n\n try {\n while (pending.length > 0) {\n const batch = pending[0]!;\n const body: IngestRequestBody = { session: batch.session, events: batch.events };\n // Plain fetch on a live page so the outcome is observable; only the unload\n // path needs keepalive, which outlives the document but can't be observed.\n const outcome = await postWithFetch(\n eventsUrl,\n JSON.stringify(body),\n config.apiKey,\n identityHeaders,\n false,\n );\n // Destroyed mid-send: the successor client owns the shared buffer now.\n if (destroyed) return;\n if (outcome === \"retry\") {\n retryAttempt += 1;\n return;\n }\n // By identity, not position: an unload during the await may already have\n // taken this batch, and `pending[0]` is then one that was never sent.\n pending = pending.filter((entry) => entry !== batch);\n writePendingBatches(bufferKey, pending);\n retryAttempt = 0;\n }\n } finally {\n if (pending.length > 0) {\n const backoff = Math.min(flushIntervalMs * 2 ** retryAttempt, MAX_RETRY_BACKOFF_MS);\n scheduleFlush(backoff);\n } else if (queue.length > 0) {\n // `bufferQueued` takes at most HARD_MAX_BATCH per pass, so a burst\n // larger than one batch still has events waiting.\n scheduleFlush();\n }\n }\n }\n\n function trackEvent(input: TrackEventInput): void {\n if (destroyed) return;\n if (!input || typeof input.eventType !== \"string\" || input.eventType.length === 0) return;\n\n // Enhanced conversions: a form submit is the one moment the visitor's own\n // email/phone pass through the SDK — stash them (memory only) so the\n // conversion that fires next carries user_data. NOT done for phone_click:\n // its metadata holds the BUSINESS's number, not the visitor's. An explicit\n // consent decline also skips the stash entirely — egress is already gated,\n // but a decliner's identifiers shouldn't sit in page memory either.\n if (input.eventType === \"form_submit\" && getConsentState() !== \"denied\") {\n try {\n const fields = (input.metadata as { form?: { fields?: unknown } } | null)?.form?.fields;\n if (fields) stashUserDataFromFormFields(fields, config.phone?.defaultCountry);\n } catch {\n // user-data capture must never break ingest\n }\n }\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 // A caller's href still carries `_arv` until the surface strips it after hydration.\n page_url: input.pageUrl != null ? stripLinkParam(input.pageUrl) : currentPageUrl(),\n metadata: input.metadata ?? null,\n occurred_at: occurredAt,\n });\n\n const triggerReason = (input.metadata as { trigger?: unknown } | null)?.trigger;\n const isUrgentVideoEvent =\n input.eventType === \"video_play\" ||\n (input.eventType === \"video_progress\" &&\n (triggerReason === \"ended\" || triggerReason === \"goal\" || triggerReason === \"checkpoint\"));\n\n // A lead (or `form_details`, tapped just before the tab closes, or high-value video\n // start/checkpoint/goal/ended) is sent while the page is alive: a hide or leave\n // usually follows, and the unload path can't retry.\n if (\n queue.length >= maxQueueSize ||\n input.eventType === \"form_details\" ||\n getEventSemantics(input.eventType)?.outcomeRole === \"lead\" ||\n isUrgentVideoEvent\n ) {\n void flush();\n } else {\n scheduleFlush();\n }\n }\n\n const beforeUnloadListeners = new Set<() => void>();\n let runningBeforeUnload = false;\n\n /**\n * Last-gasp keepalive send on pagehide/visibilitychange.\n *\n * Its outcome is unobservable, so the batch leaves the buffer optimistically;\n * re-sending one `runFlush` has in flight is intended (unload may abort that).\n */\n function flushOnUnload(): void {\n if (destroyed) return;\n if (!runningBeforeUnload && beforeUnloadListeners.size > 0) {\n runningBeforeUnload = true;\n try {\n for (const listener of beforeUnloadListeners) {\n try {\n listener();\n } catch {\n // never let a listener abort unload flush\n }\n }\n } finally {\n runningBeforeUnload = false;\n }\n }\n bufferQueued();\n clearScheduledFlush();\n if (pending.length === 0) return;\n\n const batch = pending[0]!;\n pending = pending.slice(1);\n writePendingBatches(bufferKey, pending);\n\n const body: IngestRequestBody = { session: batch.session, events: batch.events };\n void postWithFetch(eventsUrl, JSON.stringify(body), config.apiKey, identityHeaders, true);\n }\n\n function onVisibilityChange(): void {\n if (document.visibilityState === \"hidden\") flushOnUnload();\n }\n\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"pagehide\", flushOnUnload);\n window.addEventListener(\"visibilitychange\", onVisibilityChange);\n }\n\n return {\n trackEvent,\n flush,\n flushBeacon: flushOnUnload,\n onBeforeUnload: (listener: () => void) => {\n beforeUnloadListeners.add(listener);\n return () => {\n beforeUnloadListeners.delete(listener);\n };\n },\n getSessionId: () => sessionId,\n getVisitorId: () => visitorId,\n destroy: () => {\n if (destroyed) return;\n // Keepalive-send what's queued, or StrictMode's dev unmount drops the first page_view.\n if (queue.length > 0) {\n flushOnUnload();\n }\n // Only after that final send: from here the successor owns the shared buffer.\n destroyed = true;\n clearScheduledFlush();\n queue = [];\n beforeUnloadListeners.clear();\n if (typeof window !== \"undefined\") {\n window.removeEventListener(\"pagehide\", flushOnUnload);\n window.removeEventListener(\"visibilitychange\", onVisibilityChange);\n }\n },\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. Runtime validation of the metadata against the event's Zod schema.\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 { parse, safeParse } from \"zod/mini\";\n\nimport { registerCapability } from \"./capabilities\";\nimport { getEventDefinition } from \"./events/registry\";\nimport type {\n EventMetadata,\n RegisteredManualEvents,\n TriggerRegistryConfig,\n} from \"./events/registry\";\nimport type { TrackingClient } from \"./ingest\";\nimport { salvageFormSubmit } from \"./form-submit-salvage\";\nimport type { TrackingConfigReference } from \"./resources/tracking-config-runtime\";\nimport type { TrackingEnvironment } from \"./types\";\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, metadata that fails its Zod schema throws, which is what you\n * want in dev. Otherwise the event is dropped with a console warning, so an\n * analytics bug never takes down the host site — except `form_submit`, which\n * is salvaged and sent tagged `invalid_shape`, because it is a lead.\n */\n debug?: boolean;\n /** The factory's environment; the lead-form ladder is loud outside production. */\n environment?: TrackingEnvironment;\n /** The factory's config reference; the lead-form ladder reads live contracts from it. */\n trackingConfig?: TrackingConfigReference;\n /** Underlying low-level client (with conversion auto-fire attached when configured). */\n rawClient?: TrackingClient;\n}\n\n// Keyed by client so `createLeadFormSubmit(useTracking(), …)` inherits the factory's settings.\nconst clientSettings = new WeakMap<object, CreateTypedClientOptions>();\n\nlet latestTrackingConfig: TrackingConfigReference | undefined;\nlet latestRawClient: TrackingClient | null = null;\n\n/** Record the factory's `trackingConfig` so SDK components (`<TrackedVideo>`) can resolve it automatically. */\nexport function setDefaultTrackingConfig(ref: TrackingConfigReference | undefined): void {\n if (ref) latestTrackingConfig = ref;\n}\n\n/** Return the active low-level `TrackingClient` and `TrackingConfigReference` from the most recent provider/factory. */\nexport function getActiveTrackingEnv(): {\n rawClient: TrackingClient | null;\n trackingConfig: TrackingConfigReference | undefined;\n} {\n return {\n rawClient: latestRawClient,\n trackingConfig: latestTrackingConfig,\n };\n}\n\n/** The options a typed client was created with; empty for any other object. */\nexport function typedClientSettings(client: object): CreateTypedClientOptions {\n return clientSettings.get(client) ?? {};\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 // Reported from the registry, not from the first firing: a site whose form\n // gets a handful of submissions a month is still fully wired, and keying the\n // capability on a real submit would let the presence window mark it removed\n // between them. One registration site covers next/react/browser.\n if (registry.manual?.form_submit) registerCapability(\"form_capture\");\n\n const client: TypedTrackingClient<TRegistry> = {\n trackEvent<K extends RegisteredManualEvents<TRegistry>>(\n eventType: K,\n metadata: EventMetadata<K>,\n opts?: TypedTrackEventOptions,\n ): void {\n // The API 422s the whole batch on one invalid event, so drop just this one.\n const def = getEventDefinition(eventType);\n let sent: unknown = metadata;\n if (debug) {\n parse(def.metadataSchema, metadata);\n } else if (!safeParse(def.metadataSchema, metadata).success) {\n sent = eventType === \"form_submit\" ? salvageFormSubmit(metadata) : null;\n // eslint-disable-next-line no-console\n console.warn(\n sent\n ? `[AranovaTracking] trackEvent: metadata for \"form_submit\" failed schema validation; sent salvaged`\n : `[AranovaTracking] trackEvent: metadata for \"${eventType}\" failed schema validation; event dropped`,\n );\n if (!sent) return;\n }\n\n raw.trackEvent({\n eventType: eventType as string,\n metadata: sent 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 latestRawClient = raw;\n if (options.trackingConfig) latestTrackingConfig = options.trackingConfig;\n clientSettings.set(client, { ...options, rawClient: raw });\n return client;\n}\n","import * as z from \"zod/mini\";\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.strictObject({\n cta_name: z.string(),\n page: z.strictObject({\n path: z.string(),\n }),\n section: z.optional(z.nullable(z.string())),\n destination_url: z.optional(z.nullable(z.string())),\n // Set by auto-capture (and available to manual callers): the link target\n // and a short element descriptor (tag#id) for tying clicks to specific UI.\n href: z.optional(z.nullable(z.string())),\n element: z.optional(z.nullable(z.string())),\n});\n\nexport type CtaClickMetadata = z.infer<typeof ctaClickMetadataSchema>;\n\n/**\n * Registration config for `cta_click`.\n *\n * The event stays manually fireable; `autoCapture` additionally attaches a\n * delegated click listener that fires it for any element matching `selector`\n * (default `[data-aranova-cta]`) — tag your CTAs, get analytics for free.\n */\nexport const ctaClickConfigSchema = z.strictObject({\n autoCapture: z.optional(\n z.strictObject({\n selector: z.optional(z.string()),\n }),\n ),\n});\nexport type CtaClickConfig = z.infer<typeof ctaClickConfigSchema>;\n","import * as z from \"zod/mini\";\n\n/**\n * Registered trigger names reported by the SDK heartbeat.\n */\nexport const sdkHeartbeatTriggersSchema = z.strictObject({\n automatic: z.array(z.string()),\n manual: z.array(z.string()),\n});\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.strictObject({\n sdk_version: z.string(),\n package_name: z.nullable(z.string()),\n surface: z.enum([\"next\", \"react\", \"script\"]),\n triggers: sdkHeartbeatTriggersSchema,\n trigger_config: z.optional(z.nullable(z.record(z.string(), z.record(z.string(), z.unknown())))),\n configured_gtag_ids: z.optional(z.nullable(z.record(z.string(), z.string()))),\n // Site whose config object the SDK resolves; null on a legacy business ref.\n config_site_id: z.optional(z.nullable(z.uuid())),\n});\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.strictObject({});\nexport type SdkHeartbeatConfig = z.infer<typeof sdkHeartbeatConfigSchema>;\n","import * as z from \"zod/mini\";\n\n/**\n * Metadata for the automatic `form_start` event.\n *\n * The SDK emits this once per form when the visitor first focuses or taps\n * anything inside it.\n */\nexport const formStartMetadataSchema = z.strictObject({\n form: z.strictObject({\n id: z.string(),\n action: z.nullable(z.string()),\n }),\n page: z.strictObject({\n path: z.string(),\n }),\n});\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.strictObject({\n selector: z.optional(z.string()),\n});\n\nexport type FormStartConfig = z.infer<typeof formStartConfigSchema>;\n","import * as z from \"zod/mini\";\n\nimport {\n contractHashSchema,\n contractNameSchema,\n contractVersionSchema,\n formContractViolationsSchema,\n formExtraSchema,\n formFieldSchema,\n type JsonValue,\n} from \"./form-submit\";\n\n// In scope so the bundled .d.ts names JsonValue locally, not from a bare `src` module.\nexport type { JsonValue };\n\n/**\n * A manual `form_details`: optional answers the API folds into the `form_submit` whose\n * `submission_id` it names (add-only, never a lead). Built by `leadForms.addDetails()`.\n */\nexport const formDetailsMetadataSchema = z.strictObject({\n form: z.strictObject({\n id: z.string(),\n submission_id: z.uuidv4(),\n kind: z.optional(z.nullable(contractNameSchema)),\n fields: z.array(formFieldSchema),\n contract_version: contractVersionSchema,\n contract_hash: contractHashSchema,\n extra: formExtraSchema,\n contract_violations: formContractViolationsSchema,\n }),\n page: z.strictObject({\n path: z.string(),\n }),\n});\n\nexport type FormDetailsMetadata = z.infer<typeof formDetailsMetadataSchema>;\n\n/**\n * Registration config for `form_details`.\n *\n * Manual-only with no options; the empty object enables typed\n * `trackEvent('form_details', ...)` calls.\n */\nexport const formDetailsConfigSchema = z.strictObject({});\nexport type FormDetailsConfig = z.infer<typeof formDetailsConfigSchema>;\n","import * as z from \"zod/mini\";\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\n// `.meta({ id })` names the emitted definition: recursion forces Zod to emit a\n// `$ref`, and without an id it would be positional (`__schema0`) and churn the\n// committed snapshot whenever emission order changed.\nexport const jsonValueSchema: z.ZodMiniType<JsonValue> = z\n .lazy(() =>\n z.union([\n z.string(),\n z.number(),\n z.boolean(),\n z.null(),\n z.array(jsonValueSchema),\n z.record(z.string(), jsonValueSchema),\n ]),\n )\n .register(z.globalRegistry, { id: \"JsonSerializableValue\" });\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 value must be JSON-serializable. Client\n * integrations may intentionally capture raw submitted lead data (including name,\n * email, phone, address, selections, and free text) for first-party analytics and\n * lead operations. Submitted file data is supported after conversion to a\n * JSON-serializable representation; upload larger files separately and send a storage\n * reference. Never include credentials, authentication tokens, payment-card or bank\n * secrets, or private keys.\n *\n * No byte limit is enforced on this metadata, here or on the server — `fields` and its\n * values are unbounded. Keep inlined payloads small anyway: a submit fired just before\n * navigation flushes over `keepalive`, which browsers cap near 64 KB and drop silently.\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 */\n// ADR-0024 lead-form contract keys, all optional; a field's `role` picks how the API renders its\n// value. Kind and role are pattern-checked, not enum-checked, on the wire; the registry's types\n// in `lead-forms.gen.ts` are where a site gets the strict ones.\nexport const contractNameSchema = z\n .string()\n .check(z.regex(/^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?$/));\n// A role, or an any-of group's roles joined by `|` (the API's `form_contract_violations.role`).\nconst violationRole = z\n .string()\n .check(\n z.maxLength(200),\n z.regex(\n /^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?(\\|[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?)*$/,\n ),\n );\n\n/** Most violations one submit reports; the SDK truncates past it. */\nexport const MAX_CONTRACT_VIOLATIONS = 50;\n\n/** What the SDK's ladder found wrong with a submit it sent anyway (ADR-0024 §6). */\nexport const contractViolationSchema = z.strictObject({\n role: z.nullable(violationRole),\n rule: z.string().check(z.regex(/^[a-z][a-z_]{0,47}$/)),\n});\nexport type FormContractViolation = z.infer<typeof contractViolationSchema>;\n\n/** One answered field, shared by `form_submit` and `form_details`. */\nexport const formFieldSchema = z.strictObject({\n name: z.string(),\n type: z.string(),\n label: z.nullable(z.string()),\n value: jsonValueSchema,\n role: z.optional(z.nullable(contractNameSchema)),\n});\n\n/** Questions with no registry role, shared by `form_submit` and `form_details`. */\nexport const formExtraSchema = z.optional(\n z.nullable(\n z\n .array(\n z.strictObject({\n key: z.string().check(z.maxLength(64)),\n label: z.string().check(z.maxLength(200)),\n value: jsonValueSchema,\n }),\n )\n .check(z.maxLength(20)),\n ),\n);\n\nexport const contractVersionSchema = z.optional(z.nullable(z.int().check(z.gte(1))));\nexport const contractHashSchema = z.optional(z.nullable(z.string().check(z.maxLength(64))));\n\nexport const formContractViolationsSchema = z.optional(\n z.nullable(z.array(contractViolationSchema).check(z.maxLength(MAX_CONTRACT_VIOLATIONS))),\n);\n\nexport const formSubmitMetadataSchema = z.strictObject({\n form: z.strictObject({\n id: z.string(),\n action: z.nullable(z.string()),\n fields: z.optional(z.array(formFieldSchema)),\n kind: z.optional(z.nullable(contractNameSchema)),\n contract_version: contractVersionSchema,\n contract_hash: contractHashSchema,\n extra: formExtraSchema,\n contract_violations: formContractViolationsSchema,\n // Minted per submit; a later `form_details` names it to add answers to this lead.\n submission_id: z.optional(z.nullable(z.uuidv4())),\n }),\n page: z.strictObject({\n path: z.string(),\n }),\n});\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.strictObject({});\nexport type FormSubmitConfig = z.infer<typeof formSubmitConfigSchema>;\n","import * as z from \"zod/mini\";\n\n/**\n * Metadata for a manually fired `form_step` event.\n *\n * `form.id` must equal `form_start`/`form_submit`'s id. See\n * docs/tracking-package/installing.md#multi-step-forms-form_step for the emit rule and an example.\n */\nexport const formStepMetadataSchema = z.strictObject({\n form: z.strictObject({\n id: z.string(),\n action: z.nullable(z.string()),\n }),\n step: z.strictObject({\n id: z.string().check(z.minLength(1), z.maxLength(64), z.regex(/^[A-Za-z0-9_-]+$/)),\n // 1-based position in the flow the visitor is on right now.\n index: z.int().check(z.gte(1), z.lte(50)),\n // Null when the number of steps depends on earlier answers.\n total: z.nullable(z.int().check(z.gte(1), z.lte(50))),\n }),\n direction: z.enum([\"forward\", \"back\"]),\n page: z.strictObject({\n path: z.string(),\n }),\n});\n\nexport type FormStepMetadata = z.infer<typeof formStepMetadataSchema>;\n\n/**\n * Registration config for `form_step`.\n *\n * Manual-only with no options; the empty object enables typed\n * `trackEvent('form_step', ...)` calls.\n */\nexport const formStepConfigSchema = z.strictObject({});\nexport type FormStepConfig = z.infer<typeof formStepConfigSchema>;\n","import * as z from \"zod/mini\";\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.strictObject({\n page_count: z.int().check(z.gte(2)),\n page: z.strictObject({\n path: z.string(),\n }),\n});\n\nexport type MultiPageSessionMetadata = z.infer<typeof multiPageSessionMetadataSchema>;\n\n/**\n * Registration config for automatic `multi_page_session`.\n */\nexport const multiPageSessionConfigSchema = z.strictObject({\n pageThreshold: z.int().check(z.gte(2)),\n});\n\nexport type MultiPageSessionConfig = z.infer<typeof multiPageSessionConfigSchema>;\n","import * as z from \"zod/mini\";\n\n/**\n * Metadata for the automatic `page_exit` event.\n *\n * Fired when the user leaves a page (SPA navigation away, tab hidden, or\n * pagehide). `dwell_ms` is the ACTIVE (visible) time spent on the page segment\n * being closed — hidden time never counts, matching `time_on_site` semantics.\n * A page revisited after being hidden emits another `page_exit` for the next\n * visible segment, so summing `dwell_ms` per page/session yields total active\n * dwell without double counting.\n */\nexport const pageExitMetadataSchema = z.strictObject({\n dwell_ms: z.int().check(z.gte(0)),\n // null = left without any scroll signal; floor is 0 so a valid 0% is never\n // rejected (a single bad field 422s the whole keepalive beacon batch).\n max_scroll_percent: z.nullable(z.int().check(z.gte(0), z.lte(100))),\n // The gating baseline: the fraction of the page visible at load with\n // zero scrolling — or, for pages that grew after the post-paint snapshot\n // (skeleton/streaming renders), the first-scroll position that\n // established it. null = page had no scrollable range, or the segment\n // ended before the snapshot. Optionality is load-bearing: SDK builds\n // predating this field keep POSTing page_exit without the key —\n // requiring it would 422 whole keepalive beacon batches.\n scroll_baseline_percent: z.optional(z.nullable(z.int().check(z.gte(0), z.lte(100)))),\n page: z.strictObject({\n path: z.string(),\n }),\n});\n\nexport type PageExitMetadata = z.infer<typeof pageExitMetadataSchema>;\n\n/**\n * Registration config for automatic `page_exit`.\n *\n * SDK-internal: attached unconditionally (like `sdk_heartbeat`), so there are\n * no registration options.\n */\nexport const pageExitConfigSchema = z.strictObject({});\nexport type PageExitConfig = z.infer<typeof pageExitConfigSchema>;\n","import * as z from \"zod/mini\";\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.strictObject({\n phone_number: z.string(),\n page: z.strictObject({\n path: z.string(),\n }),\n section: z.optional(z.nullable(z.string())),\n});\n\nexport type PhoneClickMetadata = z.infer<typeof phoneClickMetadataSchema>;\n\n/**\n * Registration config for `phone_click`.\n *\n * The event stays manually fireable; `autoCapture` additionally attaches a\n * delegated click listener that fires it for any `tel:` link matching\n * `selector` (default `a[href^=\"tel:\"]`) — link your phone number, get the\n * analytics event (and, for a linked phone-click goal, the conversion) for free.\n */\nexport const phoneClickConfigSchema = z.strictObject({\n autoCapture: z.optional(\n z.strictObject({\n selector: z.optional(z.string()),\n }),\n ),\n});\nexport type PhoneClickConfig = z.infer<typeof phoneClickConfigSchema>;\n","import * as z from \"zod/mini\";\n\n/**\n * Metadata for the automatic `scroll_depth` event.\n *\n * Fired once per configured threshold per page.\n */\nexport const scrollDepthMetadataSchema = z.strictObject({\n depth_percent: z.int().check(z.gte(1), z.lte(100)),\n page: z.strictObject({\n path: z.string(),\n }),\n});\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.strictObject({\n thresholds: z.array(z.int().check(z.gte(1), z.lte(100))).check(z.minLength(1)),\n});\n\nexport type ScrollDepthConfig = z.infer<typeof scrollDepthConfigSchema>;\n","import * as z from \"zod/mini\";\n\nexport const PAGE_IDENTITY_MAX_LENGTH = 64;\nexport const PAGE_IDENTITY_PATTERN = /^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/;\n\n/** Common page identities offered as presets by operator tooling. */\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 const pageIdentitySchema = z.string().check(\n z.maxLength(PAGE_IDENTITY_MAX_LENGTH),\n z.regex(PAGE_IDENTITY_PATTERN, {\n message:\n \"page identity must be lowercase snake_case, start with a letter, and contain at most 64 characters\",\n }),\n);\n\nexport type PageIdentity = z.infer<typeof pageIdentitySchema>;\n\n/** @deprecated Use PageIdentity. Kept as a compatibility alias for existing consumers. */\nexport type SpecificPageName = PageIdentity;\n\n/** @deprecated Use pageIdentitySchema. Kept for source compatibility. */\nexport const specificPageNameSchema = pageIdentitySchema;\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.strictObject({\n page_name: specificPageNameSchema,\n page: z.strictObject({\n path: z.string(),\n }),\n});\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 stable, validated identity with a `RegExp` that\n * matches the pathname. The identity can use a preset above or a client-specific\n * lowercase snake-case slug.\n */\nexport const specificPageVisitConfigSchema = z.strictObject({\n pages: z\n .array(\n z.strictObject({\n name: specificPageNameSchema,\n pathPattern: z.custom<RegExp>((value) => value instanceof RegExp, {\n message: \"pathPattern must be a RegExp\",\n }),\n }),\n )\n .check(z.minLength(1)),\n});\n\nexport type SpecificPageVisitConfig = z.infer<typeof specificPageVisitConfigSchema>;\n","import * as z from \"zod/mini\";\n\n/** Metadata for `site_link_click`: a decorated hop to a sibling site (`destination_url` sans token). */\nexport const siteLinkClickMetadataSchema = z.strictObject({\n destination_host: z.string(),\n destination_url: z.string(),\n page: z.strictObject({\n path: z.string(),\n }),\n});\n\nexport type SiteLinkClickMetadata = z.infer<typeof siteLinkClickMetadataSchema>;\n\n/** `site_link_click` is SDK-internal; no consumer options. */\nexport const siteLinkClickConfigSchema = z.strictObject({});\nexport type SiteLinkClickConfig = z.infer<typeof siteLinkClickConfigSchema>;\n\n/**\n * Metadata for `site_link_arrival`: this page load carried a linker token.\n * `stale` / `invalid` are reported but never adopted; origin ids only for `linked`.\n */\nexport const siteLinkArrivalMetadataSchema = z.strictObject({\n outcome: z.enum([\"linked\", \"stale\", \"invalid\"]),\n origin_session_id: z.optional(z.nullable(z.string())),\n origin_visitor_id: z.optional(z.nullable(z.string())),\n // Milliseconds between the origin click and this page load.\n transit_ms: z.optional(z.nullable(z.number())),\n // True when this browser had no visitor id and took the origin's.\n adopted_visitor: z.boolean(),\n page: z.strictObject({\n path: z.string(),\n }),\n});\n\nexport type SiteLinkArrivalMetadata = z.infer<typeof siteLinkArrivalMetadataSchema>;\n\n/** `site_link_arrival` is SDK-internal; no consumer options. */\nexport const siteLinkArrivalConfigSchema = z.strictObject({});\nexport type SiteLinkArrivalConfig = z.infer<typeof siteLinkArrivalConfigSchema>;\n","import * as z from \"zod/mini\";\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.strictObject({\n duration_ms: z.int().check(z.nonnegative()),\n page: z.strictObject({\n path: z.string(),\n }),\n});\n\nexport type TimeOnSiteMetadata = z.infer<typeof timeOnSiteMetadataSchema>;\n\n/**\n * Registration config for automatic `time_on_site`.\n */\nexport const timeOnSiteConfigSchema = z.strictObject({\n thresholdSeconds: z.int().check(z.positive()),\n});\nexport type TimeOnSiteConfig = z.infer<typeof timeOnSiteConfigSchema>;\n","import * as z from \"zod/mini\";\n\nimport {\n mergeVideoRanges,\n roundVideoSeconds,\n sumVideoRangesSeconds,\n VIDEO_ID_MAX_LENGTH,\n VIDEO_ID_PATTERN,\n} from \"../video-ranges\";\n\nexport {\n mergeVideoRanges,\n roundVideoSeconds,\n sumVideoRangesSeconds,\n VIDEO_ID_MAX_LENGTH,\n VIDEO_ID_PATTERN,\n};\n\nexport const videoIdSchema = z\n .string()\n .check(z.minLength(1), z.maxLength(VIDEO_ID_MAX_LENGTH), z.regex(VIDEO_ID_PATTERN));\n\nexport const videoVariantSchema = z.enum([\"16x9\", \"9x16\"]);\nexport type VideoVariant = z.infer<typeof videoVariantSchema>;\n\nexport const videoProgressTriggerReasonSchema = z.enum([\n \"checkpoint\",\n \"goal\",\n \"pause\",\n \"seek\",\n \"ended\",\n \"page_exit\",\n]);\nexport type VideoProgressTriggerReason = z.infer<typeof videoProgressTriggerReasonSchema>;\n\nexport const videoRangeSchema = z.strictObject({\n start_s: z.number().check(z.gte(0)),\n end_s: z.number().check(z.gte(0)),\n});\nexport type VideoWatchRange = z.infer<typeof videoRangeSchema>;\n\n/** Metadata for `video_play` emitted when playback starts or resumes on `<TrackedVideo>`. */\nexport const videoPlayMetadataSchema = z.strictObject({\n video_id: videoIdSchema,\n variant: videoVariantSchema,\n current_s: z.number().check(z.gte(0)),\n duration_s: z.number().check(z.gte(0)),\n play_sequence: z.int().check(z.gte(1)),\n page: z.strictObject({\n path: z.string(),\n }),\n});\n\nexport type VideoPlayMetadata = z.infer<typeof videoPlayMetadataSchema>;\n\nexport const videoPlayConfigSchema = z.strictObject({});\nexport type VideoPlayConfig = z.infer<typeof videoPlayConfigSchema>;\n\n/**\n * Metadata for `video_progress` emitted at checkpoints, goal thresholds, pause, seek, ended, and page_exit.\n * `ranges` carries cumulative merged `[start_s, end_s]` intervals watched in the session so far.\n */\nexport const videoProgressMetadataSchema = z.strictObject({\n video_id: videoIdSchema,\n variant: videoVariantSchema,\n trigger: videoProgressTriggerReasonSchema,\n current_s: z.number().check(z.gte(0)),\n furthest_s: z.number().check(z.gte(0)),\n watched_s: z.number().check(z.gte(0)),\n duration_s: z.number().check(z.gte(0)),\n max_percent: z.int().check(z.gte(0), z.lte(100)),\n watched_percent: z.int().check(z.gte(0), z.lte(100)),\n play_count: z.int().check(z.gte(1)),\n seek_count: z.int().check(z.gte(0)),\n ranges: z.array(videoRangeSchema),\n page: z.strictObject({\n path: z.string(),\n }),\n});\n\nexport type VideoProgressMetadata = z.infer<typeof videoProgressMetadataSchema>;\n\nexport const videoProgressConfigSchema = z.strictObject({});\nexport type VideoProgressConfig = z.infer<typeof videoProgressConfigSchema>;\n","export const VIDEO_ID_MAX_LENGTH = 64;\nexport const VIDEO_ID_PATTERN = /^[A-Za-z0-9_-]+$/;\n\nexport interface VideoWatchRangeItem {\n start_s: number;\n end_s: number;\n}\n\n/** Round to 2 decimal places so wire payloads stay compact and deterministic. */\nexport function roundVideoSeconds(value: number): number {\n if (!Number.isFinite(value) || value < 0) return 0;\n return Math.round(value * 100) / 100;\n}\n\n/**\n * Merge overlapping or adjacent `[start_s, end_s]` playback intervals.\n * Intervals shorter than 0.05s or inverted are discarded.\n */\nexport function mergeVideoRanges(\n ranges: ReadonlyArray<VideoWatchRangeItem>,\n epsilonSeconds = 0.25,\n): VideoWatchRangeItem[] {\n const valid = ranges\n .filter((r) => Number.isFinite(r.start_s) && Number.isFinite(r.end_s) && r.end_s > r.start_s)\n .map((r) => ({\n start_s: roundVideoSeconds(r.start_s),\n end_s: roundVideoSeconds(r.end_s),\n }))\n .filter((r) => r.end_s - r.start_s >= 0.05)\n .sort((a, b) => a.start_s - b.start_s || a.end_s - b.end_s);\n\n if (valid.length === 0) return [];\n\n const merged: VideoWatchRangeItem[] = [{ ...valid[0]! }];\n for (let i = 1; i < valid.length; i += 1) {\n const cur = valid[i]!;\n const last = merged[merged.length - 1]!;\n if (cur.start_s <= last.end_s + epsilonSeconds) {\n last.end_s = roundVideoSeconds(Math.max(last.end_s, cur.end_s));\n } else {\n merged.push({ ...cur });\n }\n }\n return merged;\n}\n\n/** Total unique watched seconds across merged ranges, clamped to `durationSeconds` when > 0. */\nexport function sumVideoRangesSeconds(\n ranges: ReadonlyArray<VideoWatchRangeItem>,\n durationSeconds = 0,\n): number {\n const merged = mergeVideoRanges(ranges);\n const raw = merged.reduce((acc, r) => acc + Math.max(0, r.end_s - r.start_s), 0);\n const clamped = durationSeconds > 0 ? Math.min(raw, durationSeconds) : raw;\n return roundVideoSeconds(clamped);\n}\n","// `$ZodType` is the shared base both the classic and the Mini builders\n// implement, so the registry accepts a schema from either.\nimport type { $ZodType } from \"zod/v4/core\";\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 formDetailsConfigSchema,\n formDetailsMetadataSchema,\n type FormDetailsConfig,\n type FormDetailsMetadata,\n} from \"./form-details\";\nimport {\n formStepConfigSchema,\n formStepMetadataSchema,\n type FormStepConfig,\n type FormStepMetadata,\n} from \"./form-step\";\nimport {\n formSubmitConfigSchema,\n formSubmitMetadataSchema,\n type FormSubmitConfig,\n type FormSubmitMetadata,\n type JsonValue,\n} from \"./form-submit\";\nimport {\n multiPageSessionConfigSchema,\n multiPageSessionMetadataSchema,\n type MultiPageSessionConfig,\n type MultiPageSessionMetadata,\n} from \"./multi-page-session\";\nimport {\n pageExitConfigSchema,\n pageExitMetadataSchema,\n type PageExitConfig,\n type PageExitMetadata,\n} from \"./page-exit\";\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 siteLinkArrivalConfigSchema,\n siteLinkArrivalMetadataSchema,\n siteLinkClickConfigSchema,\n siteLinkClickMetadataSchema,\n type SiteLinkArrivalConfig,\n type SiteLinkArrivalMetadata,\n type SiteLinkClickConfig,\n type SiteLinkClickMetadata,\n} from \"./site-link\";\nimport {\n timeOnSiteConfigSchema,\n timeOnSiteMetadataSchema,\n type TimeOnSiteConfig,\n type TimeOnSiteMetadata,\n} from \"./time-on-site\";\nimport {\n videoPlayConfigSchema,\n videoPlayMetadataSchema,\n videoProgressConfigSchema,\n videoProgressMetadataSchema,\n type VideoPlayConfig,\n type VideoPlayMetadata,\n type VideoProgressConfig,\n type VideoProgressMetadata,\n} from \"./video\";\nimport { EVENT_SEMANTICS, type EventName, type EventSemantics } from \"./semantics\";\n\nexport { EVENT_SEMANTICS, getEventSemantics } from \"./semantics\";\nexport type {\n EventCategory,\n EventClientVisibility,\n EventName,\n EventOutcomeRole,\n EventSemantics,\n} from \"./semantics\";\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// In scope so the emitted registry type names JsonValue locally; without it the bundled\n// .d.ts imports it from a bare `src` module. `check-dts-runtime.cjs` fails on that.\nexport type { JsonValue };\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 semantics: EVENT_SEMANTICS.page_view,\n metadataSchema: pageViewMetadataSchema,\n configSchema: pageViewConfigSchema,\n },\n time_on_site: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.time_on_site,\n metadataSchema: timeOnSiteMetadataSchema,\n configSchema: timeOnSiteConfigSchema,\n },\n specific_page_visit: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.specific_page_visit,\n metadataSchema: specificPageVisitMetadataSchema,\n configSchema: specificPageVisitConfigSchema,\n },\n scroll_depth: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.scroll_depth,\n metadataSchema: scrollDepthMetadataSchema,\n configSchema: scrollDepthConfigSchema,\n },\n multi_page_session: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.multi_page_session,\n metadataSchema: multiPageSessionMetadataSchema,\n configSchema: multiPageSessionConfigSchema,\n },\n form_start: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.form_start,\n metadataSchema: formStartMetadataSchema,\n configSchema: formStartConfigSchema,\n },\n // --- SDK-internal automatic (not consumer-configurable) ---\n sdk_heartbeat: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.sdk_heartbeat,\n metadataSchema: sdkHeartbeatMetadataSchema,\n configSchema: sdkHeartbeatConfigSchema,\n },\n page_exit: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.page_exit,\n metadataSchema: pageExitMetadataSchema,\n configSchema: pageExitConfigSchema,\n },\n // Cross-site linkage: emitted by the linker (roster comes from the site\n // config, not the trigger registry), so neither is consumer-configurable.\n site_link_click: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.site_link_click,\n metadataSchema: siteLinkClickMetadataSchema,\n configSchema: siteLinkClickConfigSchema,\n },\n site_link_arrival: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.site_link_arrival,\n metadataSchema: siteLinkArrivalMetadataSchema,\n configSchema: siteLinkArrivalConfigSchema,\n },\n video_play: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.video_play,\n metadataSchema: videoPlayMetadataSchema,\n configSchema: videoPlayConfigSchema,\n },\n video_progress: {\n kind: \"automatic\",\n semantics: EVENT_SEMANTICS.video_progress,\n metadataSchema: videoProgressMetadataSchema,\n configSchema: videoProgressConfigSchema,\n },\n // --- manual triggers ---\n form_submit: {\n kind: \"manual\",\n semantics: EVENT_SEMANTICS.form_submit,\n metadataSchema: formSubmitMetadataSchema,\n configSchema: formSubmitConfigSchema,\n },\n form_step: {\n kind: \"manual\",\n semantics: EVENT_SEMANTICS.form_step,\n metadataSchema: formStepMetadataSchema,\n configSchema: formStepConfigSchema,\n },\n form_details: {\n kind: \"manual\",\n semantics: EVENT_SEMANTICS.form_details,\n metadataSchema: formDetailsMetadataSchema,\n configSchema: formDetailsConfigSchema,\n },\n phone_click: {\n kind: \"manual\",\n semantics: EVENT_SEMANTICS.phone_click,\n metadataSchema: phoneClickMetadataSchema,\n configSchema: phoneClickConfigSchema,\n },\n cta_click: {\n kind: \"manual\",\n semantics: EVENT_SEMANTICS.cta_click,\n metadataSchema: ctaClickMetadataSchema,\n configSchema: ctaClickConfigSchema,\n },\n} as const satisfies Record<\n EventName,\n {\n kind: EventKind;\n semantics: EventSemantics;\n metadataSchema: $ZodType;\n configSchema: $ZodType;\n }\n>;\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 page_exit: PageExitMetadata;\n site_link_click: SiteLinkClickMetadata;\n site_link_arrival: SiteLinkArrivalMetadata;\n video_play: VideoPlayMetadata;\n video_progress: VideoProgressMetadata;\n form_submit: FormSubmitMetadata;\n form_step: FormStepMetadata;\n form_details: FormDetailsMetadata;\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 page_exit: PageExitConfig;\n site_link_click: SiteLinkClickConfig;\n site_link_arrival: SiteLinkArrivalConfig;\n video_play: VideoPlayConfig;\n video_progress: VideoProgressConfig;\n form_submit: FormSubmitConfig;\n form_step: FormStepConfig;\n form_details: FormDetailsConfig;\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 * Events that represent a lead/outcome in first-party tracking projections.\n */\nexport const ALL_LEAD_EVENT_NAMES: readonly EventName[] = (\n Object.entries(EVENT_REGISTRY) as Array<[EventName, (typeof EVENT_REGISTRY)[EventName]]>\n)\n .filter(([, def]) => def.semantics.outcomeRole === \"lead\")\n .map(([name]) => name);\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 form_step: EventConfig<\"form_step\">;\n form_details: EventConfig<\"form_details\">;\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> = Extract<\n keyof NonNullable<TRegistry[\"manual\"]>,\n ManualEventName\n>;\n\n/**\n * Automatic event names registered in a concrete trigger registry.\n */\nexport type RegisteredAutomaticEvents<TRegistry extends TriggerRegistryConfig> = Extract<\n keyof TRegistry[\"automatic\"],\n AutomaticEventName\n>;\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 semantics: EventSemantics;\n metadataSchema: $ZodType;\n configSchema: $ZodType;\n} {\n return EVENT_REGISTRY[name] as unknown as {\n kind: EventKind;\n semantics: EventSemantics;\n metadataSchema: $ZodType;\n configSchema: $ZodType;\n };\n}\n","// A production form_submit that fails its schema is a lead, so it is coerced, not dropped (ADR-0024 §6).\n\nimport { safeParse } from \"zod/mini\";\n\nimport {\n contractNameSchema,\n contractViolationSchema,\n formSubmitMetadataSchema,\n MAX_CONTRACT_VIOLATIONS,\n type FormContractViolation,\n type FormSubmitMetadata,\n type JsonValue,\n} from \"./events/form-submit\";\n\nconst formShape = formSubmitMetadataSchema.shape.form.shape;\nconst INVALID_SHAPE: FormContractViolation = { role: null, rule: \"invalid_shape\" };\n\n/**\n * The closest schema-valid `form_submit` to `metadata`, tagged `invalid_shape`; null when there is\n * no `form` object to salvage, or the coerced event still fails.\n */\nexport function salvageFormSubmit(metadata: unknown): FormSubmitMetadata | null {\n if (!isRecord(metadata) || !isRecord(metadata.form)) return null;\n const form = metadata.form;\n const page = isRecord(metadata.page) ? metadata.page : {};\n const reported = Array.isArray(form.contract_violations)\n ? form.contract_violations.filter((v) => safeParse(contractViolationSchema, v).success)\n : [];\n const salvaged = {\n form: {\n id: typeof form.id === \"string\" ? form.id : String(form.id ?? \"\"),\n action: typeof form.action === \"string\" ? form.action : null,\n fields: Array.isArray(form.fields) ? form.fields.filter(isRecord).map(salvageField) : [],\n ...valid(\"kind\", form.kind),\n ...valid(\"contract_version\", form.contract_version),\n ...valid(\"contract_hash\", form.contract_hash),\n ...valid(\"extra\", form.extra),\n ...valid(\"submission_id\", form.submission_id),\n contract_violations: [...reported, INVALID_SHAPE].slice(-MAX_CONTRACT_VIOLATIONS),\n },\n page: { path: typeof page.path === \"string\" ? page.path : currentPath() },\n };\n const parsed = safeParse(formSubmitMetadataSchema, salvaged);\n return parsed.success ? parsed.data : null;\n}\n\nfunction salvageField(field: Record<string, unknown>) {\n return {\n name: String(field.name ?? \"\"),\n type: String(field.type ?? \"\"),\n label: typeof field.label === \"string\" ? field.label : null,\n value: toJson(field.value),\n ...(typeof field.role === \"string\" && safeParse(contractNameSchema, field.role).success\n ? { role: field.role }\n : {}),\n };\n}\n\nfunction valid<K extends \"kind\" | \"contract_version\" | \"contract_hash\" | \"extra\" | \"submission_id\">(\n key: K,\n value: unknown,\n): Partial<Record<K, unknown>> {\n if (value == null || !safeParse(formShape[key], value).success) return {};\n return { [key]: value } as Partial<Record<K, unknown>>;\n}\n\n/** JSONB-safe: dates as ISO strings, non-finite numbers and unrepresentable values as null. */\nfunction toJson(value: unknown): JsonValue {\n if (value === null || typeof value === \"string\" || typeof value === \"boolean\") return value;\n if (typeof value === \"number\") return Number.isFinite(value) ? value : null;\n if (typeof value === \"bigint\") return value.toString();\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value.toISOString();\n if (Array.isArray(value)) return value.map(toJson);\n if (isRecord(value)) {\n return Object.fromEntries(\n Object.entries(value)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => [k, toJson(v)]),\n );\n }\n return null;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction currentPath(): string {\n return typeof window === \"undefined\" ? \"/\" : window.location.pathname;\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(client: TrackingClient, config: TimeOnSiteConfig): () => 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 validated lowercase snake-case page identity.\n// 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","// Shared SPA-navigation signal for triggers.\n//\n// history.pushState/replaceState can only be observed by monkeypatching, and\n// per-trigger patches don't compose: each trigger captured the originals at\n// attach and blindly restored them at detach, so a non-LIFO detach order\n// stomped the other trigger's patch (it silently stopped seeing SPA\n// navigations). This module installs ONE ref-counted patch: it goes in with\n// the first subscriber and is restored with the last unsubscribe, making\n// detach order irrelevant.\n//\n// Notification timing preserves the previous per-trigger behavior:\n// pushState/replaceState notify via setTimeout(0) (the URL updates\n// synchronously but frameworks render after), popstate notifies\n// synchronously. Listeners are expected to read window.location themselves\n// and pathname-guard (query-only changes are their no-op, not ours).\n\ntype HistoryChangeListener = () => void;\n\nconst listeners = new Set<HistoryChangeListener>();\nlet restorePatch: (() => void) | null = null;\n\nfunction notify(): void {\n // Set iteration tolerates concurrent delete: a listener that unsubscribes\n // itself (or another) mid-notification is simply skipped if not yet visited.\n for (const listener of listeners) listener();\n}\n\nfunction notifyDeferred(): void {\n setTimeout(notify, 0);\n}\n\nfunction installPatch(): void {\n // Keep the exact original references (no .bind) so restore reinstates the\n // identical functions — a bound copy would change identity and stack a new\n // bind wrapper on every install/restore cycle.\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n\n function patchedPushState(this: History, ...args: Parameters<History[\"pushState\"]>): void {\n originalPushState.apply(this, args);\n notifyDeferred();\n }\n function patchedReplaceState(this: History, ...args: Parameters<History[\"replaceState\"]>): void {\n originalReplaceState.apply(this, args);\n notifyDeferred();\n }\n\n history.pushState = patchedPushState as History[\"pushState\"];\n history.replaceState = patchedReplaceState as History[\"replaceState\"];\n window.addEventListener(\"popstate\", notify);\n\n restorePatch = () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener(\"popstate\", notify);\n restorePatch = null;\n };\n}\n\n/**\n * Subscribe to SPA navigations (pushState / replaceState / popstate).\n * Returns an unsubscribe function. Callers must be browser-guarded\n * (`typeof window !== \"undefined\"`) before subscribing.\n */\nexport function onHistoryChange(listener: HistoryChangeListener): () => void {\n if (listeners.size === 0) installPatch();\n listeners.add(listener);\n return () => {\n if (!listeners.delete(listener)) return;\n if (listeners.size === 0) restorePatch?.();\n };\n}\n","// Shared scroll-depth measurement for the scroll_depth and page_exit triggers.\n//\n// The metric is \"percent of page content the bottom of the viewport has\n// passed\": (scrollTop + viewportHeight) / pageHeight. Crucially, a page with\n// no scrollable range (it fits the viewport) measures as `null`, NOT 100 —\n// scroll depth is undefined there, and treating it as 100 is what used to\n// fire every threshold on short pages without any user scrolling.\n\n// Pixels of slack at the true bottom: fractional scroll positions under\n// browser zoom / DPR scaling can leave scrollTop + clientHeight 1-2px short\n// of scrollHeight, which would make 100% unreachable.\nexport const BOTTOM_EPSILON_PX = 2;\n\n/**\n * Current scroll percent of the page, or `null` when the page has no\n * scrollable range (`scrollHeight <= clientHeight`) or the metrics are\n * degenerate. scrollTop is clamped to the scrollable range so iOS\n * rubber-band overscroll can't produce out-of-range values.\n */\nexport function measureScrollPercent(): number | null {\n const root = document.scrollingElement ?? document.documentElement;\n const scrollHeight = root.scrollHeight;\n const clientHeight = root.clientHeight;\n if (scrollHeight <= 0 || clientHeight <= 0) return null;\n // Effectively unscrollable includes pages within the bottom epsilon of the\n // viewport: at scrollTop 0 the epsilon check below would already report 100,\n // turning a 1-2px-taller page into a phantom full read on any stray scroll.\n if (scrollHeight <= clientHeight + BOTTOM_EPSILON_PX) return null;\n const maxTop = scrollHeight - clientHeight;\n const scrollTop = Math.min(Math.max(root.scrollTop, 0), maxTop);\n if (scrollTop + clientHeight >= scrollHeight - BOTTOM_EPSILON_PX) return 100;\n return Math.max(1, Math.min(100, Math.round(((scrollTop + clientHeight) / scrollHeight) * 100)));\n}\n\n/**\n * Snapshot the baseline scroll percent — the fraction of the page already\n * visible with zero scrolling — once the page has actually painted.\n *\n * Double requestAnimationFrame: on attach and especially on SPA navigation\n * the new page's content and the framework's scroll-to-top haven't settled\n * yet when our code runs; measuring after two frames reads the settled\n * layout. Returns a cancel function — call it on detach or when scheduling\n * a replacement snapshot (re-navigation).\n */\nexport function scheduleBaselineSnapshot(\n onSnapshot: (baselinePercent: number | null) => void,\n): () => void {\n let rafId = requestAnimationFrame(() => {\n rafId = requestAnimationFrame(() => {\n onSnapshot(measureScrollPercent());\n });\n });\n return () => cancelAnimationFrame(rafId);\n}\n\nexport interface BaselineGate {\n /** Discard state and schedule a fresh post-paint snapshot (attach / SPA nav). */\n rebaseline(): void;\n /** Cancel any pending snapshot without touching state (call on detach). */\n cancel(): void;\n /**\n * The gating baseline, or null while the snapshot is pending or when the\n * page had no scrollable range. For pages that grow after the snapshot\n * (skeleton/streaming renders) this is the position of the first scroll\n * that found a scrollable page, not the at-load fraction.\n */\n baseline(): number | null;\n /**\n * Sample the current scroll percent through the gate. Returns null when the\n * scroll event must be ignored: the snapshot hasn't landed (SPA\n * scroll-to-top / restoration noise), the page is unscrollable, or this\n * very sample established the lazy baseline (depth counts from the NEXT\n * scroll). A non-null return guarantees baseline() is non-null.\n */\n sample(): number | null;\n}\n\n/**\n * The baseline machinery shared by the scroll_depth and page_exit triggers —\n * one implementation so the two can't drift.\n */\nexport function createBaselineGate(): BaselineGate {\n let baselinePercent: number | null = null;\n let ready = false;\n let cancelSnapshot: (() => void) | null = null;\n\n return {\n rebaseline() {\n cancelSnapshot?.();\n ready = false;\n baselinePercent = null;\n cancelSnapshot = scheduleBaselineSnapshot((b) => {\n baselinePercent = b;\n ready = true;\n });\n },\n cancel() {\n cancelSnapshot?.();\n },\n baseline() {\n return ready ? baselinePercent : null;\n },\n sample() {\n if (!ready) return null;\n if (baselinePercent === null) {\n // Lazy re-baseline: the page wasn't scrollable at snapshot time.\n baselinePercent = measureScrollPercent();\n return null;\n }\n return measureScrollPercent();\n },\n };\n}\n","// scroll_depth automatic trigger.\n//\n// Fires once per configured threshold per page, but only for depth the user\n// actually earned by scrolling:\n//\n// - A baseline percent (the fraction of the page visible with zero\n// scrolling) is snapshotted after the page paints (double-rAF, via the\n// shared BaselineGate). Thresholds at or below the baseline were \"free\"\n// at load and are suppressed for the whole pageview — a page that fits\n// the viewport (baseline null) never fires at all. This scales across\n// viewports automatically: the same page has a lower baseline on a\n// phone, so more thresholds become earnable there.\n// - Thresholds are only evaluated from scroll events, and scroll events\n// are ignored until the baseline snapshot lands — so SPA scroll-to-top /\n// popstate scroll restoration during a navigation can't fire anything.\n// Restoration that lands AFTER the snapshot (browsers defer it on\n// incrementally-rendered pages) is indistinguishable from a user scroll\n// and re-emits threshold events for depth reached on a prior visit to\n// the path; the linked gtag conversions stay deduped (session-scoped\n// per goal+path) and backend evaluation takes the session max, so the\n// cost is a duplicate analytics event, not a duplicate conversion.\n// - Lazy re-baseline: if the page wasn't scrollable at snapshot time\n// (skeleton/streaming render), the first scroll event that finds a\n// scrollable page establishes the baseline instead of firing.\n//\n// Suppression is deliberately permanent per pageview: content that grows\n// after the snapshot can't un-suppress a threshold (conservative — every\n// timing race under-counts rather than false-fires).\n//\n// The fired set resets on SPA navigation when the pathname actually changes\n// (pushState / replaceState / popstate); same-path replaceState (query param\n// updates) is a no-op via the pathname guard.\n\nimport type { ScrollDepthConfig } from \"../events/scroll-depth\";\nimport type { TrackingClient } from \"../ingest\";\nimport { onHistoryChange } from \"./navigation\";\nimport { createBaselineGate } from \"./scroll-measurement\";\n\n/**\n * Attach the automatic `scroll_depth` trigger.\n *\n * Installs a throttled scroll listener, fires each configured threshold once\n * per page (baseline-gated, real scrolls only), and returns a detach function.\n */\nexport function attachScrollDepth(client: TrackingClient, config: ScrollDepthConfig): () => 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 const gate = createBaselineGate();\n\n function checkThresholds(): void {\n const percent = gate.sample();\n if (percent === null) return;\n const baseline = gate.baseline();\n if (baseline === null) return; // unreachable when percent !== null; keeps the types honest\n for (const threshold of thresholds) {\n if (threshold <= baseline) continue; // visible at load — never earnable this pageview\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 gate.rebaseline();\n }\n\n const unsubscribeNav = onHistoryChange(resetIfPathChanged);\n window.addEventListener(\"scroll\", onScroll, { passive: true });\n\n gate.rebaseline();\n\n return () => {\n if (rafId !== null) cancelAnimationFrame(rafId);\n gate.cancel();\n unsubscribeNav();\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// Fires on the first focus, click or tap inside a <form>. A tap is a pointerdown released without\n// a pointercancel or travel past TAP_SLOP_PX: every mobile touch-scroll starts with pointerdown.\n// Once per form per page, keyed on form id/action, reset on SPA navigation.\n\nimport type { FormStartConfig } from \"../events/form-start\";\nimport type { TrackingClient } from \"../ingest\";\n\nconst TAP_SLOP_PX = 10;\n\ninterface PendingTap {\n pointerId: number;\n x: number;\n y: number;\n target: EventTarget | null;\n}\n\n/** Attach the automatic `form_start` trigger; returns a detach function. */\nexport function attachFormStart(client: TrackingClient, config: FormStartConfig): () => 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 let pendingTap: PendingTap | null = null;\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 startFrom(event.target);\n }\n\n function onClick(event: MouseEvent): void {\n if (event.button !== 0) return;\n startFrom(event.target);\n }\n\n function onPointerDown(event: PointerEvent): void {\n // A right-click opens a context menu; it is not the visitor starting the form.\n pendingTap =\n event.button === 0\n ? { pointerId: event.pointerId, x: event.clientX, y: event.clientY, target: event.target }\n : null;\n }\n\n function onPointerCancel(): void {\n pendingTap = null;\n }\n\n function onPointerUp(event: PointerEvent): void {\n const tap = pendingTap;\n pendingTap = null;\n if (!tap || tap.pointerId !== event.pointerId) return;\n if (Math.hypot(event.clientX - tap.x, event.clientY - tap.y) > TAP_SLOP_PX) return;\n // iOS Safari neither focuses a tapped <button> nor clicks a plain element, so the tap counts.\n startFrom(tap.target);\n }\n\n function startFrom(target: EventTarget | null): void {\n // Element, not HTMLElement: a tap can land on an <svg> icon in a button.\n if (!(target instanceof Element)) 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 const passiveCapture = { capture: true, passive: true };\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 document.addEventListener(\"click\", onClick, passiveCapture);\n document.addEventListener(\"pointerdown\", onPointerDown, passiveCapture);\n document.addEventListener(\"pointerup\", onPointerUp, passiveCapture);\n document.addEventListener(\"pointercancel\", onPointerCancel, passiveCapture);\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener(\"popstate\", resetIfPathChanged);\n document.removeEventListener(\"focusin\", onFocusIn);\n document.removeEventListener(\"click\", onClick, { capture: true });\n document.removeEventListener(\"pointerdown\", onPointerDown, { capture: true });\n document.removeEventListener(\"pointerup\", onPointerUp, { capture: true });\n document.removeEventListener(\"pointercancel\", onPointerCancel, { capture: true });\n };\n}\n","// page_exit automatic trigger (SDK-internal — attached unconditionally).\n//\n// Emits one `page_exit` per visible page segment with the ACTIVE dwell time\n// and the max scroll depth reached, on three leave signals:\n// - SPA navigation away (pushState / replaceState / popstate, pathname change)\n// - the tab going hidden (visibilitychange — the reliable mobile signal)\n// - pagehide (tab close / hard navigation)\n//\n// Dwell is visibility-aware like time_on_site: hidden time never counts. When\n// a hidden tab becomes visible again, the accumulator resets and the next\n// leave emits another segment — the backend sums segments per session.\n//\n// CRITICAL: on pagehide/hidden this trigger flushes the client itself via\n// `flushBeacon()` (keepalive transport). The ingest client's own\n// flush-on-pagehide listener is registered at client creation — BEFORE this\n// trigger attaches — so it fires first, sending the queue WITHOUT our\n// just-enqueued page_exit. We must therefore push it ourselves, and a plain\n// `flush()` is aborted by the browser on unload — only the keepalive beacon\n// survives, so `flushBeacon` (not `flush`) is load-bearing here.\n\nimport type { TrackingClient } from \"../ingest\";\nimport { onHistoryChange } from \"./navigation\";\nimport { createBaselineGate } from \"./scroll-measurement\";\n\n// Visible segments shorter than this are noise (no human reads a page for tens\n// of ms) and, load-bearingly, this drops the phantom emit from the\n// pagehide/visibilitychange double-fire. See emitSegment.\nconst MIN_SEGMENT_MS = 50;\n\nexport function attachPageExit(client: TrackingClient): () => void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return () => {};\n }\n\n let currentPath = window.location.pathname;\n let activeSince: number | null = document.visibilityState === \"visible\" ? Date.now() : null;\n let accumulatedMs = 0;\n let maxScrollPercent: number | null = null;\n let rafId: number | null = null;\n // Shared baseline machinery with the scroll_depth trigger: scroll events\n // are gated until the post-paint snapshot lands (SPA scroll-to-top noise\n // can't register as depth), and a page with no scrollable range keeps\n // max_scroll_percent null instead of the old phantom 100.\n const gate = createBaselineGate();\n\n function onScroll(): void {\n if (rafId !== null) return;\n rafId = requestAnimationFrame(() => {\n rafId = null;\n const percent = gate.sample();\n if (percent !== null && (maxScrollPercent === null || percent > maxScrollPercent)) {\n maxScrollPercent = percent;\n }\n });\n }\n\n function settledDwellMs(): number {\n let total = accumulatedMs;\n if (activeSince !== null) {\n total += Date.now() - activeSince;\n }\n return Math.max(0, Math.round(total));\n }\n\n function emitSegment(path: string, flush: boolean): void {\n const dwell = settledDwellMs();\n // A segment shorter than this carries no signal — skip it. This also\n // absorbs the phantom emit when pagehide and visibilitychange:hidden both\n // fire on the same unload: the first emits the real segment and closes it\n // (activeSince = null below), so the second computes ~0ms and is dropped.\n if (dwell < MIN_SEGMENT_MS) return;\n client.trackEvent({\n eventType: \"page_exit\",\n metadata: {\n dwell_ms: dwell,\n max_scroll_percent: maxScrollPercent,\n // Lets the backend tell \"scrolled to the bottom\" apart from \"the page\n // was barely scrollable\". null = unscrollable page or the segment\n // ended before the post-paint snapshot landed. For pages that grew\n // after the snapshot this is the first-scroll position, not the\n // at-load fraction (see BaselineGate.baseline).\n scroll_baseline_percent: gate.baseline(),\n page: { path },\n } as unknown as Record<string, unknown>,\n pageUrl: window.location.href,\n occurredAt: null,\n });\n if (flush) {\n // The client's own pagehide flush already ran without this event — push\n // it through the keepalive beacon so it survives unload.\n client.flushBeacon();\n }\n // Close the segment. A new one only begins on an explicit visible/navigate\n // (both set activeSince themselves) — NOT here: at pagehide the document is\n // often still \"visible\", so restarting a segment would make the following\n // visibilitychange:hidden emit a phantom ~0ms duplicate.\n accumulatedMs = 0;\n activeSince = null;\n }\n\n function onNavigate(): void {\n const newPath = window.location.pathname;\n if (newPath === currentPath) return;\n emitSegment(currentPath, false);\n currentPath = newPath;\n maxScrollPercent = null;\n gate.rebaseline();\n accumulatedMs = 0;\n activeSince = document.visibilityState === \"visible\" ? Date.now() : null;\n }\n\n function onVisibilityChange(): void {\n if (document.visibilityState === \"hidden\") {\n emitSegment(currentPath, true);\n } else {\n // Back from hidden: a fresh segment starts accruing.\n activeSince = Date.now();\n }\n }\n\n function onPageHide(): void {\n emitSegment(currentPath, true);\n }\n\n const unsubscribeNav = onHistoryChange(onNavigate);\n const unsubscribeBeforeUnload = client.onBeforeUnload?.(() => {\n emitSegment(currentPath, false);\n });\n window.addEventListener(\"scroll\", onScroll, { passive: true });\n document.addEventListener(\"visibilitychange\", onVisibilityChange);\n window.addEventListener(\"pagehide\", onPageHide);\n\n gate.rebaseline();\n\n return () => {\n if (rafId !== null) cancelAnimationFrame(rafId);\n gate.cancel();\n unsubscribeNav();\n unsubscribeBeforeUnload?.();\n window.removeEventListener(\"scroll\", onScroll);\n document.removeEventListener(\"visibilitychange\", onVisibilityChange);\n window.removeEventListener(\"pagehide\", onPageHide);\n };\n}\n","// cta_click auto-capture (opt-in via the cta_click registration config).\n//\n// A single delegated click listener fires `cta_click` for any element matching\n// the configured selector (default `[data-aranova-cta]`). The event itself\n// stays `manual` in the registry — consumers can still fire it from code; this\n// trigger just removes the need to hand-instrument every button.\n//\n// CTA identity: the `data-aranova-cta` attribute value when present, else the\n// element's trimmed text (capped). `href` and a short `element` descriptor\n// (tag#id) ride along so the dashboard can tie clicks to concrete UI.\n\nimport { registerCapability } from \"../capabilities\";\nimport type { CtaClickConfig } from \"../events/cta-click\";\nimport type { TrackingClient } from \"../ingest\";\n\nexport const DEFAULT_CTA_SELECTOR = \"[data-aranova-cta]\";\n\nconst CTA_NAME_MAX_LENGTH = 120;\n\nfunction describeElement(el: Element): string {\n const tag = el.tagName.toLowerCase();\n return el.id ? `${tag}#${el.id}` : tag;\n}\n\nfunction resolveCtaName(el: Element): string {\n const explicit = el.getAttribute(\"data-aranova-cta\");\n if (explicit && explicit.trim().length > 0) return explicit.trim();\n const text = (el.textContent ?? \"\").trim().replaceAll(/\\s+/g, \" \");\n if (text.length > 0) return text.slice(0, CTA_NAME_MAX_LENGTH);\n return describeElement(el);\n}\n\n/**\n * Attach the delegated cta_click auto-capture listener. Returns a detach\n * function. No-ops (returns a noop detacher) when `autoCapture` is absent.\n */\nexport function attachCtaClickCapture(client: TrackingClient, config: CtaClickConfig): () => void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return () => {};\n }\n const autoCapture = config.autoCapture;\n if (!autoCapture) {\n return () => {};\n }\n // Registered past the guard, so the capability means \"the listener is\n // attached\", not \"the option was passed\".\n registerCapability(\"cta_click_capture\");\n const selector = autoCapture.selector ?? DEFAULT_CTA_SELECTOR;\n\n function onClick(event: MouseEvent): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n let matched: Element | null = null;\n try {\n matched = target.closest(selector);\n } catch {\n return; // invalid selector — never break the host site\n }\n if (matched === null) return;\n const href =\n matched instanceof HTMLAnchorElement ? matched.href || null : matched.getAttribute(\"href\");\n client.trackEvent({\n eventType: \"cta_click\",\n metadata: {\n cta_name: resolveCtaName(matched),\n page: { path: window.location.pathname },\n section: matched.getAttribute(\"data-aranova-section\"),\n destination_url: href,\n href,\n element: describeElement(matched),\n } as unknown as Record<string, unknown>,\n pageUrl: window.location.href,\n occurredAt: null,\n });\n }\n\n // Capture phase so navigations that stop propagation still get counted.\n document.addEventListener(\"click\", onClick, true);\n return () => {\n document.removeEventListener(\"click\", onClick, true);\n };\n}\n","// Cross-site linker: decorates links to sibling sites (roster read at click time)\n// and emits `site_link_click`. `pointerdown` covers middle-click and drag-to-tab,\n// which never reach `click`; `click` covers keyboard activation. The secondary\n// button is excluded: decorating before a context menu opens would put the token\n// behind \"Copy link address\".\n\nimport { registerCapability } from \"../capabilities\";\nimport { resolveCrossSiteLink, stripLinkParam, type CrossSiteLink } from \"../cross-site-link\";\nimport type { TrackingClient } from \"../ingest\";\n\nexport interface SiteLinkCaptureOptions {\n /** Sibling hostnames of this site; empty until the config resolves. */\n linkedHostnames: () => readonly string[];\n}\n\ninterface Hop {\n anchor: HTMLAnchorElement;\n link: CrossSiteLink;\n}\n\nfunction anchorFrom(target: EventTarget | null): HTMLAnchorElement | null {\n if (!(target instanceof Element)) return null;\n const anchor = target.closest(\"a[href]\");\n // SVG <a> exposes `href` as an SVGAnimatedString; only HTML anchors navigate.\n return anchor instanceof HTMLAnchorElement ? anchor : null;\n}\n\n/** Primary (0) and middle (1) open the link; 2 is the context menu. */\nfunction opensTheLink(event: MouseEvent): boolean {\n return event.button === 0 || event.button === 1;\n}\n\n/** Attach the linker. Returns a detach function; a noop outside the browser. */\nexport function attachSiteLinkCapture(\n client: TrackingClient,\n options: SiteLinkCaptureOptions,\n): () => void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return () => {};\n }\n registerCapability(\"cross_site_linker\");\n\n function hopFrom(event: Event): Hop | null {\n const anchor = anchorFrom(event.target);\n if (anchor === null) return null;\n const link = resolveCrossSiteLink(anchor.href, options.linkedHostnames());\n return link === null ? null : { anchor, link };\n }\n\n function onPointerDown(event: PointerEvent): void {\n if (!opensTheLink(event)) return;\n const hop = hopFrom(event);\n if (hop !== null) hop.anchor.href = hop.link.decorated;\n }\n\n function onClick(event: MouseEvent): void {\n if (!opensTheLink(event)) return;\n const hop = hopFrom(event);\n if (hop === null) return;\n hop.anchor.href = hop.link.decorated;\n client.trackEvent({\n eventType: \"site_link_click\",\n metadata: {\n destination_host: hop.anchor.hostname,\n destination_url: stripLinkParam(hop.anchor.href),\n page: { path: window.location.pathname },\n },\n pageUrl: window.location.href,\n occurredAt: null,\n });\n // The visitor is leaving; the debounce would drop this on the floor.\n client.flushBeacon();\n }\n\n document.addEventListener(\"pointerdown\", onPointerDown, true);\n document.addEventListener(\"click\", onClick, true);\n return () => {\n document.removeEventListener(\"pointerdown\", onPointerDown, true);\n document.removeEventListener(\"click\", onClick, true);\n };\n}\n","// phone_click auto-capture (opt-in via the phone_click registration config).\n//\n// A single delegated click listener fires `phone_click` for any `tel:` link\n// matching the configured selector (default `a[href^=\"tel:\"]`). The event\n// itself stays `manual` in the registry — consumers can still fire it from\n// code; this trigger just removes the need to hand-instrument every call link.\n//\n// Unlike cta_click, a captured phone_click ALSO drives conversion auto-fire: a\n// `tel:` tap is an unambiguous on-site conversion, so `conversion-autofire`\n// fires a linked `phone_click` event-goal (see thresholdMet). The number rides\n// along as E.164 (`toE164`, falling back to the raw href value) so the\n// dashboard can tie clicks to a concrete number.\n//\n// Overlap caveat: the default selectors don't collide (`a[href^=\"tel:\"]` vs\n// cta_click's `[data-aranova-cta]`). If an operator tags a `tel:` link with\n// `data-aranova-cta` AND enables both auto-captures, one tap emits two analytics\n// events — but only phone_click auto-fires a conversion (cta_click is fire-on-\n// demand via trackConversion), so there's no double-count of the conversion.\n\nimport { registerCapability } from \"../capabilities\";\nimport type { PhoneClickConfig, PhoneClickMetadata } from \"../events/phone-click\";\nimport type { TrackingClient } from \"../ingest\";\nimport { toE164 } from \"../phone\";\n\nexport const DEFAULT_TEL_SELECTOR = 'a[href^=\"tel:\"]';\n\n/** decodeURIComponent throws `URIError` on a malformed `%` sequence (e.g. `tel:+1%`),\n * which a `tel:` href can legitimately carry — return the raw value instead of letting\n * it escape the click listener (the module's no-throw contract). */\nfunction safeDecodeURIComponent(value: string): string {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n}\n\n/** The dialable number from a `tel:` href, normalized to E.164 when possible. */\nfunction resolvePhoneNumber(el: Element): string {\n const href = el instanceof HTMLAnchorElement ? el.href : (el.getAttribute(\"href\") ?? \"\");\n // Strip the scheme (and any `;`-suffixed params like `tel:+123;ext=9`) before normalizing.\n const raw = safeDecodeURIComponent(href.replace(/^tel:/i, \"\").split(\";\")[0]).trim();\n return toE164(raw) ?? raw;\n}\n\n/**\n * Attach the delegated phone_click auto-capture listener. Returns a detach\n * function. No-ops (returns a noop detacher) when `autoCapture` is absent.\n */\nexport function attachPhoneClickCapture(\n client: TrackingClient,\n config: PhoneClickConfig,\n): () => void {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return () => {};\n }\n const autoCapture = config.autoCapture;\n if (!autoCapture) {\n return () => {};\n }\n // Registered past the guard, so the capability means \"the listener is\n // attached\", not \"the option was passed\".\n registerCapability(\"phone_click_capture\");\n const selector = autoCapture.selector ?? DEFAULT_TEL_SELECTOR;\n\n function onClick(event: MouseEvent): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n let matched: Element | null = null;\n try {\n matched = target.closest(selector);\n } catch {\n return; // invalid selector — never break the host site\n }\n if (matched === null) return;\n // Typed so the emitted shape is checked against the schema (not an opaque cast).\n const metadata: PhoneClickMetadata = {\n phone_number: resolvePhoneNumber(matched),\n page: { path: window.location.pathname },\n section: matched.getAttribute(\"data-aranova-section\"),\n };\n client.trackEvent({\n eventType: \"phone_click\",\n metadata,\n pageUrl: window.location.href,\n occurredAt: null,\n });\n }\n\n // Capture phase so navigations that stop propagation still get counted.\n document.addEventListener(\"click\", onClick, true);\n return () => {\n document.removeEventListener(\"click\", onClick, true);\n };\n}\n","// GENERATED FILE — DO NOT EDIT BY HAND.\n// Regenerate from apps/api with:\n// poetry run python scripts/generate_lead_form_contract.py\n// Source of truth: apps/api/src/services/lead_forms/ (ADR-0024). Pinned by\n// lead-forms.contract.test.ts and test_lead_form_contract.py.\n\nimport * as z from \"zod/mini\";\n\n/** Bump-on-change registry version; travels on every contract-typed form_submit. */\nexport const LEAD_FORM_REGISTRY_VERSION = 2;\n\nexport const LEAD_FORM_KINDS = [\"booking\", \"quote\", \"contact\", \"consult\", \"callback\"] as const;\n/** What a form is for; `form_id` stays the instance handle. */\nexport type LeadFormKind = (typeof LEAD_FORM_KINDS)[number];\n\nexport const LEAD_FORM_ROLES = [\n \"given_name\",\n \"family_name\",\n \"phone\",\n \"email\",\n \"service\",\n \"preferred_date\",\n \"preferred_time\",\n \"note\",\n \"visit\",\n \"intent\",\n \"vehicle\",\n \"drop_off\",\n \"address\",\n \"patient_type\",\n \"matter\",\n \"language\",\n \"products\",\n \"goal\",\n \"business_type\",\n \"lead_volume\",\n \"attribution\",\n] as const;\n/** A role from the registry, or `add_on:<key>` for a yes/no extra with its own label. */\nexport type LeadFormRole = (typeof LEAD_FORM_ROLES)[number] | `add_on:${string}`;\n\nexport const LEAD_FORM_PRIMITIVES = [\n \"string\",\n \"text\",\n \"enum\",\n \"multi_enum\",\n \"bool\",\n \"date\",\n \"time_window\",\n \"phone\",\n \"email\",\n \"address\",\n \"number\",\n] as const;\nexport type LeadFormPrimitive = (typeof LEAD_FORM_PRIMITIVES)[number];\n\n/** The wire schema of each primitive's value. */\n// Pure, so a bundle that only reads the version or the types drops the schemas.\nexport const leadFormPrimitiveSchemas = /* @__PURE__ */ (() =>\n ({\n string: z.string().check(z.maxLength(120)),\n text: z.string().check(z.maxLength(2000)),\n enum: z.string().check(z.regex(/^[a-z0-9-]{1,48}$/)),\n multi_enum: z.array(z.string().check(z.regex(/^[a-z0-9-]{1,48}$/))).check(z.maxLength(10)),\n bool: z.boolean(),\n date: z.string().check(z.regex(/^\\d{4}-\\d{2}-\\d{2}$/)),\n time_window: z.strictObject({\n start: z.string().check(z.regex(/^([01]\\d|2[0-3]):[0-5]\\d$/)),\n end: z.nullable(z.string().check(z.regex(/^([01]\\d|2[0-3]):[0-5]\\d$/))),\n label: z.optional(z.string().check(z.maxLength(60))),\n }),\n phone: z.string().check(z.regex(/^\\+[1-9]\\d{6,14}$/)),\n email: z.string().check(z.maxLength(320), z.regex(/^[^\\s@]+@[^\\s@]+$/)),\n address: z.strictObject({\n line1: z.string().check(z.maxLength(120)),\n line2: z.optional(z.string().check(z.maxLength(120))),\n city: z.optional(z.string().check(z.maxLength(120))),\n region: z.optional(z.string().check(z.maxLength(120))),\n postal_code: z.optional(z.string().check(z.maxLength(120))),\n country: z.optional(z.string().check(z.maxLength(120))),\n }),\n number: z.number(),\n }) as const)();\n\nexport interface LeadFormRoleOption {\n key: string;\n label: string;\n isNonAnswer?: true;\n}\n\nexport interface LeadFormRoleSpec {\n primitive: LeadFormPrimitive;\n smsLabel: string;\n contactIdentity?: true;\n uninteresting?: true;\n options?: readonly LeadFormRoleOption[];\n}\n\n/** Every role's primitive and text label; `add_on` is the template for `add_on:<key>`. */\nexport const LEAD_FORM_ROLE_SPECS = {\n given_name: { primitive: \"string\", smsLabel: \"\", contactIdentity: true },\n family_name: { primitive: \"string\", smsLabel: \"\", contactIdentity: true },\n phone: { primitive: \"phone\", smsLabel: \"\", contactIdentity: true },\n email: { primitive: \"email\", smsLabel: \"\", contactIdentity: true },\n service: { primitive: \"multi_enum\", smsLabel: \"Service\" },\n preferred_date: { primitive: \"date\", smsLabel: \"Date\" },\n preferred_time: { primitive: \"time_window\", smsLabel: \"Time\" },\n note: { primitive: \"text\", smsLabel: \"Note\" },\n visit: {\n primitive: \"enum\",\n smsLabel: \"Visit\",\n options: [\n { key: \"in-shop\", label: \"In shop\" },\n { key: \"in-home\", label: \"In home\" },\n { key: \"remote\", label: \"Remote\" },\n { key: \"on-site\", label: \"On site\" },\n ],\n },\n intent: {\n primitive: \"enum\",\n smsLabel: \"Wants\",\n options: [\n { key: \"book\", label: \"Book\" },\n { key: \"quote\", label: \"Quote\" },\n ],\n },\n vehicle: { primitive: \"string\", smsLabel: \"Vehicle\" },\n drop_off: {\n primitive: \"enum\",\n smsLabel: \"Drop-off\",\n options: [\n { key: \"wait-for-it\", label: \"Waiting\" },\n { key: \"drop-off\", label: \"Dropping off\" },\n ],\n },\n address: { primitive: \"address\", smsLabel: \"Address\" },\n patient_type: { primitive: \"enum\", smsLabel: \"Patient\" },\n matter: { primitive: \"enum\", smsLabel: \"Matter\" },\n language: { primitive: \"enum\", smsLabel: \"Language\" },\n products: { primitive: \"multi_enum\", smsLabel: \"Products\" },\n goal: { primitive: \"enum\", smsLabel: \"Goal\" },\n business_type: { primitive: \"enum\", smsLabel: \"Business\" },\n lead_volume: { primitive: \"enum\", smsLabel: \"Lead volume\" },\n add_on: { primitive: \"bool\", smsLabel: \"\" },\n attribution: { primitive: \"enum\", smsLabel: \"\", uninteresting: true },\n} as const satisfies Record<string, LeadFormRoleSpec>;\n","// The typed lead-form submit (ADR-0024 §5–6): types from the site's generated contract, the\n// payload built from role specs, and a ladder that is loud in dev and never drops a prod lead.\n\nimport { safeParse } from \"zod/mini\";\n\nimport { registerCapability } from \"../capabilities\";\nimport type { FormDetailsMetadata } from \"../events/form-details\";\nimport {\n MAX_CONTRACT_VIOLATIONS,\n type FormContractViolation,\n type FormSubmitMetadata,\n type JsonValue,\n} from \"../events/form-submit\";\nimport { typedClientSettings } from \"../ingest-typed\";\nimport { toE164 } from \"../phone\";\nimport type { LeadFormContract } from \"../resources/conversion-config\";\nimport {\n getTrackingConfigRuntime,\n type TrackingConfigReference,\n} from \"../resources/tracking-config-runtime\";\nimport type { TrackingEnvironment } from \"../types\";\nimport { uuidV4 } from \"../uuid\";\nimport {\n LEAD_FORM_REGISTRY_VERSION,\n LEAD_FORM_ROLES,\n LEAD_FORM_ROLE_SPECS,\n leadFormPrimitiveSchemas,\n type LeadFormKind,\n type LeadFormPrimitive,\n type LeadFormRole,\n} from \"./lead-forms.gen\";\n\n// --- the generated contract (`aranova-tracking forms gen`) ---------------------------------\n\n/** One required role; `requiredWhen` is the one-level condition (ADR-0024 §3). */\nexport interface LeadFormRequirementDecl {\n readonly role: LeadFormRole;\n readonly requiredWhen: { readonly role: LeadFormRole; readonly anyOf: readonly string[] } | null;\n /** An operator recorded a waiver: optional in the type, still reported when missing. */\n readonly waived?: boolean;\n}\n\n/** What one kind of form must collect, as generated for the site. */\nexport interface LeadFormKindDecl {\n /** The published contract hash; null when nothing is bound for the kind. */\n readonly hash: string | null;\n readonly requires: readonly LeadFormRequirementDecl[];\n /** Each group is satisfied by any one of its roles. */\n readonly requiresAny: readonly (readonly LeadFormRole[])[];\n /** Live option keys per choice role; deprecated keys are left out. */\n readonly options: { readonly [role: string]: readonly string[] };\n}\n\n/** The module `aranova-tracking forms gen` writes: `ARANOVA_LEAD_FORMS`. */\nexport interface LeadFormContracts {\n readonly registryVersion: number;\n readonly siteId: string | null;\n readonly contracts: { readonly [K in LeadFormKind]?: LeadFormKindDecl };\n}\n\n// --- value and field types -----------------------------------------------------------------\n\n/** `{ start: \"HH:MM\", end: \"HH:MM\" | null, label? }`, business-local; pinned to the zod by test. */\nexport interface LeadFormTimeWindow {\n start: string;\n end: string | null;\n label?: string;\n}\n\nexport interface LeadFormAddress {\n line1: string;\n line2?: string;\n city?: string;\n region?: string;\n postal_code?: string;\n country?: string;\n}\n\ntype Specs = typeof LEAD_FORM_ROLE_SPECS;\ntype RegistryRole = (typeof LEAD_FORM_ROLES)[number];\ntype AddOnRole = `add_on:${string}`;\n\ntype SpecOf<R> = R extends AddOnRole ? Specs[\"add_on\"] : R extends keyof Specs ? Specs[R] : never;\ntype PrimitiveOf<R> = SpecOf<R>[\"primitive\"];\n\ntype EmptyKind = {\n hash: null;\n requires: readonly [];\n requiresAny: readonly [];\n options: Record<string, never>;\n};\ntype KindOf<\n C extends LeadFormContracts,\n K extends LeadFormKind,\n> = C[\"contracts\"][K] extends LeadFormKindDecl ? C[\"contracts\"][K] : EmptyKind;\n\n/** The site's live keys for a choice role, else the registry's fixed ones, else any key. */\ntype OptionKey<D extends LeadFormKindDecl, R> = R extends keyof D[\"options\"]\n ? D[\"options\"][R][number]\n : SpecOf<R> extends { options: readonly { key: infer Key }[] }\n ? Key\n : string;\n\ninterface PrimitiveValues {\n string: string;\n text: string;\n phone: string;\n email: string;\n date: string;\n bool: boolean;\n number: number;\n time_window: LeadFormTimeWindow;\n address: LeadFormAddress;\n}\n\n/** The value a role takes: option keys for a choice role, never labels. */\nexport type LeadFormValue<D extends LeadFormKindDecl, R> =\n PrimitiveOf<R> extends \"enum\"\n ? OptionKey<D, R>\n : PrimitiveOf<R> extends \"multi_enum\"\n ? readonly OptionKey<D, R>[]\n : PrimitiveValues[Extract<PrimitiveOf<R>, keyof PrimitiveValues>];\n\ntype Blank = null | undefined;\ntype RequiredRoles<D extends LeadFormKindDecl> = Exclude<\n Extract<D[\"requires\"][number], { readonly requiredWhen: null }>,\n { readonly waived: true }\n>[\"role\"];\n/** The roles one kind must always send (unconditional, not waived), per the generated contract. */\nexport type LeadFormRequiredRole<\n C extends LeadFormContracts,\n K extends LeadFormKind,\n> = RequiredRoles<KindOf<C, K>>;\ntype AnyOf<D extends LeadFormKindDecl, G extends readonly string[]> = {\n [R in G[number]]: { [P in R]: LeadFormValue<D, P> };\n}[G[number]];\ntype AllGroups<D extends LeadFormKindDecl, Gs> = Gs extends readonly [\n infer H extends readonly string[],\n ...infer T,\n]\n ? AnyOf<D, H> & AllGroups<D, T>\n : unknown;\n\n/**\n * The `fields` of one kind's submit: unconditionally required roles are required keys, each\n * any-of group needs one of its roles, everything else in the registry is optional (blank is\n * omitted); a `required_when` role is optional here and checked at runtime.\n */\nexport type LeadFormFields<C extends LeadFormContracts, K extends LeadFormKind> = {\n [R in RequiredRoles<KindOf<C, K>>]: LeadFormValue<KindOf<C, K>, R>;\n} & {\n [R in Exclude<RegistryRole, RequiredRoles<KindOf<C, K>>>]?:\n | LeadFormValue<KindOf<C, K>, R>\n | Blank;\n} & { [R in AddOnRole]?: boolean | Blank } & AllGroups<KindOf<C, K>, KindOf<C, K>[\"requiresAny\"]>;\n\n/** Who the lead is never changes after the submit; an email it lacked is the one exception. */\ntype DetailsRole = Exclude<\n RegistryRole,\n Exclude<\n { [R in RegistryRole]: Specs[R] extends { contactIdentity: true } ? R : never }[RegistryRole],\n \"email\"\n >\n>;\n\n/** Every non-identity role of one kind, all optional: details add to a sent lead, so none is owed. */\nexport type LeadFormDetailsFields<C extends LeadFormContracts, K extends LeadFormKind> = {\n [R in DetailsRole]?: LeadFormValue<KindOf<C, K>, R> | Blank;\n} & { [R in AddOnRole]?: boolean | Blank };\n\nexport interface LeadFormExtraField {\n key: string;\n label: string;\n value: JsonValue;\n}\n\nexport interface LeadFormSubmitInput<C extends LeadFormContracts, K extends LeadFormKind> {\n /** The `<form id>`, so `form_start` and `form_submit` line up. */\n formId: string;\n kind: K;\n fields: LeadFormFields<C, K>;\n /** Questions with no registry role yet: shown beside the contract, never texted. */\n extra?: LeadFormExtraField[];\n action?: string | null;\n /** Optional caller-supplied UUID so a server booking route and browser `form_submit` share one id. */\n submissionId?: string;\n}\n\n/** Names the lead one `submit()` sent, so `addDetails()` can add to it. */\nexport interface LeadFormSubmission<K extends LeadFormKind = LeadFormKind> {\n submissionId: string;\n formId: string;\n kind: K;\n}\n\nexport interface LeadFormDetailsInput<C extends LeadFormContracts, K extends LeadFormKind> {\n fields: LeadFormDetailsFields<C, K>;\n extra?: LeadFormExtraField[];\n}\n\n/** `no_fields`: every answer was blank, so nothing was sent. */\nexport type LeadFormDetailsResult = { sent: true } | { sent: false; reason: \"no_fields\" };\n\n/** Anything that can send a `form_submit`: `useTracking()` with `form_submit` registered. */\nexport interface LeadFormTracking {\n trackEvent(eventType: \"form_submit\", metadata: FormSubmitMetadata): void;\n}\n\nexport interface LeadFormSubmitOptions {\n /** Defaults to the tracking client's `createTracking({ debug })`. Throws on a violation. */\n debug?: boolean;\n /** Defaults to the tracking client's; `development` logs every violation. */\n environment?: TrackingEnvironment;\n /** Defaults to the tracking client's; validation and the hash use its live contracts. */\n trackingConfig?: TrackingConfigReference;\n}\n\nexport interface LeadFormSubmitter<C extends LeadFormContracts> {\n /** Send one typed `form_submit`; returns the metadata sent, violations included, and its handle. */\n submit<K extends LeadFormKind>(\n input: LeadFormSubmitInput<C, K>,\n ): FormSubmitMetadata & { submission: LeadFormSubmission<K> };\n /** Add optional answers to a sent lead as one `form_details`; never a second lead. */\n addDetails<K extends LeadFormKind>(\n submission: LeadFormSubmission<K>,\n input: LeadFormDetailsInput<C, K>,\n ): LeadFormDetailsResult;\n}\n\n// --- runtime -------------------------------------------------------------------------------\n\ninterface Checked {\n requires: readonly LeadFormRequirementDecl[];\n requiresAny: readonly (readonly string[])[];\n options: Readonly<Record<string, readonly string[]>>;\n}\n\nconst KNOWN_ROLES = new Set<string>(LEAD_FORM_ROLES);\nconst ROLE_ORDER = new Map<string, number>(LEAD_FORM_ROLES.map((role, index) => [role, index]));\nconst ADD_ON = /^add_on:[a-z0-9_-]{1,48}$/;\nconst CONTRACT_NAME = /^[a-z][a-z0-9_]{0,47}(:[a-z0-9_-]{1,48})?$/;\nconst INPUT_TYPES: Record<LeadFormPrimitive, string> = {\n string: \"text\",\n text: \"textarea\",\n enum: \"select\",\n multi_enum: \"select\",\n bool: \"checkbox\",\n date: \"date\",\n time_window: \"time\",\n phone: \"tel\",\n email: \"email\",\n address: \"address\",\n number: \"number\",\n};\n\n/**\n * Bind a tracking client to the site's generated contracts (`ARANOVA_LEAD_FORMS`).\n *\n * @example\n * ```ts\n * const leadForms = createLeadFormSubmit(useTracking(), ARANOVA_LEAD_FORMS);\n * const { submission } = leadForms.submit({ formId: \"booking-form\", kind: \"booking\", fields });\n * leadForms.addDetails(submission, { fields: { note } });\n * ```\n */\nexport function createLeadFormSubmit<C extends LeadFormContracts>(\n tracking: LeadFormTracking,\n contracts: C,\n options: LeadFormSubmitOptions = {},\n): LeadFormSubmitter<C> {\n registerCapability(\"typed_form_submit\");\n const settings = { ...typedClientSettings(tracking), ...options };\n return {\n submit(input) {\n const fields = presentFields(input.fields as Record<string, unknown>);\n const { violations, hash } = judgeLeadForm(\n input.kind,\n input.formId,\n fields,\n contracts,\n settings,\n );\n const submission = {\n submissionId: input.submissionId ?? uuidV4(),\n formId: input.formId,\n kind: input.kind,\n };\n const metadata: FormSubmitMetadata = {\n form: {\n id: input.formId,\n action: input.action ?? null,\n fields: fields.map(fieldEntry),\n kind: input.kind,\n contract_version: LEAD_FORM_REGISTRY_VERSION,\n ...(hash ? { contract_hash: hash } : {}),\n ...(input.extra?.length ? { extra: input.extra } : {}),\n ...(violations.length > 0\n ? { contract_violations: violations.slice(0, MAX_CONTRACT_VIOLATIONS) }\n : {}),\n submission_id: submission.submissionId,\n },\n page: { path: currentPath() },\n };\n tracking.trackEvent(\"form_submit\", metadata);\n return { ...metadata, submission };\n },\n addDetails(submission, input) {\n const fields = presentFields(input.fields as Record<string, unknown>);\n const extra = (input.extra ?? []).filter((item) => !isBlank(item.value));\n if (fields.length === 0 && extra.length === 0) return { sent: false, reason: \"no_fields\" };\n const { violations, hash } = judgeLeadForm(\n submission.kind,\n submission.formId,\n fields,\n contracts,\n settings,\n { partial: true },\n );\n const metadata: FormDetailsMetadata = {\n form: {\n id: submission.formId,\n submission_id: submission.submissionId,\n kind: submission.kind,\n fields: fields.map(fieldEntry),\n contract_version: LEAD_FORM_REGISTRY_VERSION,\n ...(hash ? { contract_hash: hash } : {}),\n ...(extra.length > 0 ? { extra } : {}),\n ...(violations.length > 0\n ? { contract_violations: violations.slice(0, MAX_CONTRACT_VIOLATIONS) }\n : {}),\n },\n page: { path: currentPath() },\n };\n // Typed for `form_submit` only so a client without `form_details` registered still binds;\n // the typed client validates by event name, not by registration.\n (tracking as unknown as DetailsTracking).trackEvent(\"form_details\", metadata);\n return { sent: true };\n },\n };\n}\n\ninterface DetailsTracking {\n trackEvent(eventType: \"form_details\", metadata: FormDetailsMetadata): void;\n}\n\nfunction fieldEntry([role, value]: [string, JsonValue]) {\n return {\n name: role,\n type: INPUT_TYPES[primitiveOf(role) ?? \"string\"],\n label: labelOf(role),\n value,\n role,\n };\n}\n\nfunction currentPath(): string {\n return typeof window === \"undefined\" ? \"/\" : window.location.pathname;\n}\n\n/**\n * The enforcement ladder for one submit: its violations against the live contract (the generated\n * one until the config loads) and the hash to send. Logs in development; throws under debug.\n * Shared with the SDK's booking submit; `partial` (a `form_details`) skips what is owed.\n */\nexport function judgeLeadForm(\n kind: LeadFormKind,\n formId: string,\n fields: Array<[string, JsonValue]>,\n contracts: LeadFormContracts | undefined,\n settings: LeadFormSubmitOptions,\n { partial = false }: { partial?: boolean } = {},\n): { violations: FormContractViolation[]; hash: string | null } {\n const generated: LeadFormKindDecl | undefined = contracts?.contracts[kind];\n const live = liveContract(settings.trackingConfig, kind);\n const violations = check(live === undefined ? generated : fromLive(live), fields, partial);\n const liveHash = live === undefined ? undefined : (live?.contract_hash ?? null);\n if (\n contracts &&\n ((liveHash !== undefined && (generated?.hash ?? null) !== liveHash) ||\n contracts.registryVersion !== LEAD_FORM_REGISTRY_VERSION)\n ) {\n violations.push({ role: null, rule: \"contract_behind\" });\n }\n if (violations.length > 0 && (settings.debug || settings.environment === \"development\")) {\n const message = `[AranovaTracking] lead form \"${formId}\" (${kind}) breaks its contract: ${violations\n .map((v) => `${v.rule}${v.role ? ` (${v.role})` : \"\"}`)\n .join(\", \")}`;\n // eslint-disable-next-line no-console\n console.error(message);\n if (settings.debug) throw new Error(message);\n }\n return {\n violations,\n hash: liveHash === undefined ? (generated?.hash ?? null) : liveHash,\n };\n}\n\n/** `undefined` until the config loads; `null` once it has and nothing is bound for `kind`. */\nfunction liveContract(\n ref: TrackingConfigReference | undefined,\n kind: LeadFormKind,\n): LeadFormContract | null | undefined {\n const leadForms = ref ? getTrackingConfigRuntime(ref).leadForms() : null;\n if (!leadForms) return undefined;\n return leadForms.contracts.find((c) => c.form_kind === kind) ?? null;\n}\n\nfunction fromLive(live: LeadFormContract | null): Checked | undefined {\n if (!live) return undefined;\n return {\n requires: live.requires.map((r) => ({\n role: r.role as LeadFormRole,\n requiredWhen: r.required_when\n ? { role: r.required_when.role as LeadFormRole, anyOf: r.required_when.any_of }\n : null,\n })),\n requiresAny: live.requires_any.map((g) => g.roles),\n // Deprecated keys still resolve: a cached form may send them (ADR-0024 §7).\n options: Object.fromEntries(live.options.map((o) => [o.role, o.options.map((x) => x.key)])),\n };\n}\n\n/** Answered fields in registry order, normalized; blanks are omitted. */\nfunction presentFields(fields: Record<string, unknown>): Array<[string, JsonValue]> {\n return Object.entries(fields)\n .map(([role, value]): [string, unknown] => [role, normalize(role, value)])\n .filter((entry): entry is [string, JsonValue] => !isBlank(entry[1]))\n .sort(([a], [b]) => (ROLE_ORDER.get(a) ?? 999) - (ROLE_ORDER.get(b) ?? 999));\n}\n\nfunction normalize(role: string, value: unknown): unknown {\n if (typeof value !== \"string\") return value;\n const trimmed = value.trim();\n const primitive = primitiveOf(role);\n if (primitive === \"phone\") return toE164(trimmed) ?? trimmed;\n if (primitive === \"email\") return trimmed.toLowerCase();\n return primitive === \"text\" ? value : trimmed;\n}\n\nfunction isBlank(value: unknown): boolean {\n return (\n value === undefined ||\n value === null ||\n (typeof value === \"string\" && value.trim() === \"\") ||\n (Array.isArray(value) && value.length === 0)\n );\n}\n\nfunction check(\n contract: Checked | undefined,\n fields: Array<[string, JsonValue]>,\n partial: boolean,\n) {\n const answered = new Map(fields);\n const violations: FormContractViolation[] = [];\n for (const [role, value] of fields) {\n const primitive = primitiveOf(role);\n if (!primitive) {\n violations.push({ role: CONTRACT_NAME.test(role) ? role : null, rule: \"unknown_role\" });\n continue;\n }\n if (!safeParse(leadFormPrimitiveSchemas[primitive], value).success) {\n violations.push({ role, rule: \"invalid_value\" });\n continue;\n }\n const allowed = contract?.options[role] ?? registryOptions(role);\n if (allowed?.length && optionKeys(value).some((key) => !allowed.includes(key))) {\n violations.push({ role, rule: \"unknown_option\" });\n }\n }\n if (partial) return violations;\n for (const { role, requiredWhen } of contract?.requires ?? []) {\n const owing =\n requiredWhen === null ||\n optionKeys(answered.get(requiredWhen.role)).some((key) => requiredWhen.anyOf.includes(key));\n if (owing && !answered.has(role)) violations.push({ role, rule: \"missing_required\" });\n }\n for (const group of contract?.requiresAny ?? []) {\n if (!group.some((role) => answered.has(role))) {\n violations.push({ role: group.join(\"|\"), rule: \"missing_any_of\" });\n }\n }\n return violations;\n}\n\nfunction primitiveOf(role: string): LeadFormPrimitive | null {\n if (ADD_ON.test(role)) return \"bool\";\n return KNOWN_ROLES.has(role) ? LEAD_FORM_ROLE_SPECS[role as RegistryRole].primitive : null;\n}\n\nfunction labelOf(role: string): string | null {\n if (ADD_ON.test(role)) return role.slice(\"add_on:\".length).replaceAll(/[_-]+/g, \" \");\n return KNOWN_ROLES.has(role) ? LEAD_FORM_ROLE_SPECS[role as RegistryRole].smsLabel || null : null;\n}\n\nfunction registryOptions(role: string): readonly string[] | undefined {\n const spec = KNOWN_ROLES.has(role) ? LEAD_FORM_ROLE_SPECS[role as RegistryRole] : undefined;\n return spec && \"options\" in spec ? spec.options.map((o) => o.key) : undefined;\n}\n\nfunction optionKeys(value: JsonValue | undefined): string[] {\n if (typeof value === \"string\") return [value];\n return Array.isArray(value) ? value.filter((v): v is string => typeof v === \"string\") : [];\n}\n\n// --- helpers -------------------------------------------------------------------------------\n\n/**\n * A picked slot as `preferred_date` + `preferred_time`, in the business's wall time.\n * Takes `YYYY-MM-DD[T ]HH:MM` with no offset; a `Z`/`±HH:MM` instant or a `Date` would be re-zoned.\n */\nexport function slotFields(dateTime: string): {\n preferred_date: string;\n preferred_time: LeadFormTimeWindow;\n} {\n const written = /^(\\d{4}-\\d{2}-\\d{2})[T ](\\d{2}:\\d{2})(?::\\d{2}(?:\\.\\d+)?)?$/.exec(dateTime);\n if (!written) {\n throw new TypeError(\n `slotFields needs the slot's wall time without an offset (\"2026-10-13T09:30\"), got \"${dateTime}\"`,\n );\n }\n return { preferred_date: written[1]!, preferred_time: { start: written[2]!, end: null } };\n}\n\n/** A `time_window` value: `timeWindow(\"09:00\", \"12:00\", \"Morning\")`; an exact time has no end. */\nexport function timeWindow(start: string, end?: string | null, label?: string): LeadFormTimeWindow {\n return { start, end: end ?? null, ...(label ? { label } : {}) };\n}\n","import { registerCapability } from \"../../capabilities\";\nimport {\n MAX_CONTRACT_VIOLATIONS,\n type FormSubmitMetadata,\n type JsonValue,\n} from \"../../events/form-submit\";\nimport { typedClientSettings } from \"../../ingest-typed\";\nimport {\n buildBookingHandoffUrl,\n getBookingIntegration,\n type BookingOptionKey,\n} from \"../../integrations-public\";\nimport {\n LEAD_FORM_REGISTRY_VERSION,\n type LEAD_FORM_ROLE_SPECS,\n type LEAD_FORM_ROLES,\n type LeadFormKind,\n} from \"../../lead-forms/lead-forms.gen\";\nimport {\n judgeLeadForm,\n type LeadFormContracts,\n type LeadFormFields,\n type LeadFormRequiredRole,\n} from \"../../lead-forms/submit\";\nimport { toE164 } from \"../../phone\";\nimport { peekSessionId } from \"../../session\";\nimport { uuidV4 } from \"../../uuid\";\nimport type { BookingMode, ConversionConfigStore } from \"../conversion-config\";\n\nexport { BOOKING_MODES, type BookingMode } from \"../conversion-config\";\nimport { AranovaApiError } from \"../http/errors\";\nimport { resolveTrackingConfigUrl, type TrackingConfigRuntime } from \"../tracking-config-runtime\";\nimport { salesRequest, type SalesTransportConfig } from \"./transport\";\n\n// The wire vocabulary; `bookings.test.tsx` pins each list to `contracts/tracking-bookings.json`.\nexport const BOOKING_RESPONSE_STATUSES = [\"created\", \"pending\", \"failed\"] as const;\n/** Developer-only outcomes for a simulated create; the production API ignores them. */\nexport const BOOKING_SIMULATIONS = [\"created\", \"rejected\", \"unknown\", \"failed\"] as const;\nexport const BOOKING_SIMULATE_HEADER = \"X-Aranova-Simulate\";\nexport const BOOKING_DROP_OFF_OPTIONS = [\"waitForIt\", \"dropOff\"] as const;\nexport const BOOKING_DETAILS_STATUSES = [\"updated\", \"queued\", \"notified_owner\"] as const;\nexport const BOOKING_ERROR_CODES = {\n unavailable: \"booking_unavailable\",\n stale: \"booking_config_stale\",\n rejected: \"booking_rejected\",\n attemptMismatch: \"booking_attempt_mismatch\",\n detailsConflict: \"idempotency_conflict\",\n} as const;\n\ntype BookingResponseStatus = (typeof BOOKING_RESPONSE_STATUSES)[number];\nexport type BookingDropOff = (typeof BOOKING_DROP_OFF_OPTIONS)[number];\nexport type BookingSimulation = (typeof BOOKING_SIMULATIONS)[number];\nexport type BookingDetailsStatus = (typeof BOOKING_DETAILS_STATUSES)[number];\n\n/** `POST /tracking/bookings` body; its keys are pinned against the contract by the test. */\nexport interface BookingRequestBody {\n idempotency_key: string;\n session_id: string;\n config_version: number;\n start_date: string;\n start_hour: number;\n first_name: string;\n last_name: string;\n phone: string;\n email: string | null;\n vehicle: { year: number; make: string; model: string } | null;\n drop_off: BookingDropOff | null;\n services: string[];\n notes: string | null;\n marketing_sms_consent: boolean;\n page_path: string | null;\n}\n\n/** `POST /tracking/bookings` and `GET /tracking/bookings/{id}` answer. */\nexport interface BookingResponseBody {\n id: string;\n status: BookingResponseStatus;\n request_code: string | null;\n fallback_url: string | null;\n /** Absent from an API older than this SDK: read as `live`. */\n mode?: BookingMode;\n}\n\n/** `POST /tracking/bookings/{id}/details` body; its keys are pinned against the contract. */\nexport interface BookingDetailsRequestBody {\n idempotency_key: string;\n vehicle: { year: number; make: string; model: string } | null;\n notes: string | null;\n heard_via: string | null;\n}\n\n/** `POST /tracking/bookings/{id}/details` answer. */\nexport interface BookingDetailsResponseBody {\n id: string;\n status: BookingDetailsStatus;\n}\n\n/** Added after the booking: the vehicle (when the rules defer it), notes, or both. */\nexport interface BookingDetailsInput {\n vehicle?: { year: number; make: string; model: string } | null;\n notes?: string | null;\n /**\n * The site's own \"How did you hear about us?\" answer, verbatim — the same values the form's\n * `form_submit` `heardVia` field carries, so one answer reports the same whether it is asked\n * on the form or on the success screen. Ours for reporting: never sent to the shop.\n */\n heardVia?: string | null;\n}\n\nexport interface BookingDetailsOptions {\n /** The `attemptKey` the booking was submitted with; only that submit may add to it. */\n attemptKey: string;\n}\n\nexport type BookingDetailsOutcome =\n /**\n * `updated`: on the shop's request. `queued`: still on its way. `notified_owner`: the shop's\n * request could no longer take them, so the owner was texted. All three mean \"saved\".\n */\n | { status: BookingDetailsStatus; id: string }\n /**\n * Not saved. `invalid`: a bad vehicle or note. `mismatch`: not this booking's attempt key.\n * `conflict`: other details were already saved. `not_found`: no such booking for this key.\n * `failed`: the network or the API failed; worth a retry.\n */\n | { status: BookingDetailsFailure; id: string; message: string };\n\nexport type BookingDetailsFailure = \"invalid\" | \"mismatch\" | \"conflict\" | \"not_found\" | \"failed\";\n\nfunction detailsFailure(error: unknown): BookingDetailsFailure {\n if (!(error instanceof AranovaApiError)) return \"failed\";\n if (error.code === BOOKING_ERROR_CODES.attemptMismatch) return \"mismatch\";\n if (error.code === BOOKING_ERROR_CODES.detailsConflict) return \"conflict\";\n if (error.status === 404) return \"not_found\";\n return error.status === 422 ? \"invalid\" : \"failed\";\n}\n\n/** What the booking form collected. Dates and hours are the shop's wall time. */\nexport interface BookingInput {\n /** `YYYY-MM-DD`, one of `bookableSlots(...)`'s dates. */\n startDate: string;\n /** A start hour offered on that date (8 = 08:00). */\n startHour: number;\n firstName: string;\n /** Required: the shop's system needs it, and a single-word name has none to split off. */\n lastName: string;\n /** Mobile number; the shop texts its confirmation to it. */\n phone: string;\n email?: string | null;\n vehicle?: BookingVehicle | null;\n /** Ask only when the rules list drop-off options. */\n dropOff?: BookingDropOff | null;\n /** Option keys from `useBookingIntegration(...).options`. */\n services: readonly string[];\n notes?: string | null;\n /**\n * The customer's own opt-in to marketing texts. Never default it on (CASL). Service texts\n * about this booking need no opt-in and are always on.\n */\n marketingSmsConsent?: boolean;\n}\n\nexport interface BookingVehicle {\n year: number;\n make: string;\n model: string;\n}\n\ntype BookingFields<C extends LeadFormContracts> = LeadFormFields<C, \"booking\">;\ntype Asked<C extends LeadFormContracts, R extends string, P extends string, V> =\n R extends LeadFormRequiredRole<C, \"booking\"> ? { [K in P]: V } : { [K in P]?: V | null };\n\n/** A service key the site's generated `booking` contract names, or one the live config offered. */\nexport type BookingServiceKey<C extends LeadFormContracts> =\n | NonNullable<BookingFields<C>[\"service\"]>[number]\n | BookingOptionKey;\n\n/**\n * `BookingInput` typed by the site's generated contract (`ARANOVA_LEAD_FORMS`): the drop-off,\n * vehicle and email the shop's rules require are required here, and services are its keys.\n */\nexport type BookingInputFor<C extends LeadFormContracts> = Omit<\n BookingInput,\n \"services\" | \"dropOff\" | \"vehicle\" | \"email\"\n> & { services: readonly BookingServiceKey<C>[] } & Asked<\n C,\n \"drop_off\",\n \"dropOff\",\n BookingDropOff\n > &\n Asked<C, \"vehicle\", \"vehicle\", BookingVehicle> &\n Asked<C, \"email\", \"email\", string>;\n\n/** The form's `form_submit`, sent once per booking attempt and tied to this visit. */\nexport interface BookingFormSubmit {\n /** The site's tracking client (`useTracking()`): the booking takes the visit it reports. */\n tracking: {\n trackEvent(eventType: \"form_submit\", metadata: FormSubmitMetadata): void;\n getSessionId(): string;\n };\n /** The `<form id>`, so `form_start` and `form_submit` line up. */\n formId: string;\n /**\n * The site's own questions (e.g. \"How did you hear about us?\"), appended to this\n * `form_submit` for our reporting; never sent to the shop. A name the booking already\n * reports (`phone`, `email`, ...) is dropped, so lead matching cannot be overridden.\n */\n extraFields?: FormSubmitField[];\n /** The site's generated `ARANOVA_LEAD_FORMS`: judged before the config loads, and drift flagged. */\n contracts?: LeadFormContracts;\n}\n\n/** One field of a `form_submit`. */\nexport type FormSubmitField = NonNullable<FormSubmitMetadata[\"form\"][\"fields\"]>[number];\n\nexport type BookingOutcome =\n /** The shop has the request; it texts and emails the customer itself. */\n | { status: \"created\"; id: string; requestCode: string | null; mode: BookingMode }\n /** Received and still being sent; the shop confirms. Show the same success. */\n | { status: \"pending\"; id: string; mode: BookingMode }\n /**\n * Not created. Still a success for the customer: show the prefilled shop booking link.\n * `id` is null when the booking never reached Aranova (offline, rate-limited, booking off).\n */\n | { status: \"failed\"; id: string | null; fallbackUrl: string | null; mode: BookingMode }\n /** The booking breaks the shop's rules; `message` is customer-safe. Nothing was sent. */\n | { status: \"invalid\"; message: string; reasons: string[] }\n /** The shop's times changed since the form loaded; the config is being refetched. */\n | { status: \"stale\"; message: string };\n\nexport interface BookingSubmitOptions {\n attemptKey: string;\n formSubmit?: BookingFormSubmit;\n /**\n * Development only: force a simulated create's outcome to exercise the success, pending and\n * fallback screens. The production API ignores it, so it can never change a real booking.\n */\n simulate?: BookingSimulation;\n /**\n * Called once when the server answers `pending`, before `submit` polls for the final answer\n * (up to ~30 s): show the pending success right away instead of a spinner.\n */\n onPending?: (outcome: Extract<BookingOutcome, { status: \"pending\" }>) => void;\n}\n\nexport interface SalesBookingsClient {\n /**\n * Send one booking. Never rejects for an API outcome: every answer is a {@link BookingOutcome}.\n * Pass the same `attemptKey` (a UUID, `newBookingAttemptKey()`) on a retry of the same submit,\n * so a double-click or a network retry can never book twice.\n */\n submit(input: BookingInput, options: BookingSubmitOptions): Promise<BookingOutcome>;\n /** A booking's current status by the id `submit` returned. */\n status(id: string): Promise<BookingOutcome>;\n /**\n * Add the vehicle, notes or \"How did you hear about us?\" after booking (e.g.\n * `rules.vehicleDeferred`, or an answer asked on the success screen). Pass the booking's own\n * `attemptKey`. Never rejects: they reach the shop's request, or the owner by text.\n */\n updateBooking(\n bookingId: string,\n details: BookingDetailsInput,\n options: BookingDetailsOptions,\n ): Promise<BookingDetailsOutcome>;\n}\n\nconst POLL_DELAYS_MS = [1000, 2000, 4000, 8000, 15000];\nconst INVALID_DETAILS = \"Please check your details and try again.\";\n\n/** A fresh per-submit key. Keep it for retries of the same submit; mint a new one after. */\nexport const newBookingAttemptKey = uuidV4;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nfunction outcomeOf(body: BookingResponseBody): BookingOutcome {\n const mode = body.mode ?? \"live\";\n if (body.status === \"created\")\n return { status: \"created\", id: body.id, requestCode: body.request_code, mode };\n if (body.status === \"pending\") return { status: \"pending\", id: body.id, mode };\n return { status: \"failed\", id: body.id, fallbackUrl: body.fallback_url, mode };\n}\n\ntype DropOffKey = (typeof LEAD_FORM_ROLE_SPECS)[\"drop_off\"][\"options\"][number][\"key\"];\n\nconst DROP_OFF_KEYS: Record<BookingDropOff, DropOffKey> = {\n waitForIt: \"wait-for-it\",\n dropOff: \"drop-off\",\n};\n\ninterface BookingField {\n name: string;\n type: string;\n label: string;\n value: JsonValue;\n role: (typeof LEAD_FORM_ROLES)[number];\n}\n\n/** A contract-typed `booking` submit (ADR-0024): option keys, never labels; blanks omitted. */\nfunction formSubmitPayload(\n input: BookingInput,\n formSubmit: BookingFormSubmit,\n runtime: TrackingConfigRuntime | null,\n): FormSubmitMetadata {\n const field = (\n role: BookingField[\"role\"],\n name: string,\n type: string,\n label: string,\n value: JsonValue | undefined,\n ) => ({ name, type, label, value, role });\n const text = (value: string | null | undefined) => value?.trim() || undefined;\n const { vehicle, dropOff } = input;\n const entries = [\n field(\"given_name\", \"first_name\", \"text\", \"First name\", text(input.firstName)),\n field(\"family_name\", \"last_name\", \"text\", \"Last name\", text(input.lastName)),\n // E.164 on the wire: it keys the customer and enhanced-conversion matching.\n field(\"phone\", \"phone\", \"tel\", \"Phone\", toE164(input.phone) ?? input.phone),\n field(\"email\", \"email\", \"email\", \"Email\", text(input.email)),\n field(\"service\", \"service\", \"select\", \"Service\", [...input.services]),\n field(\n \"vehicle\",\n \"vehicle\",\n \"text\",\n \"Vehicle\",\n vehicle ? `${vehicle.year} ${vehicle.make} ${vehicle.model}` : undefined,\n ),\n field(\n \"drop_off\",\n \"drop_off\",\n \"select\",\n \"Drop-off\",\n dropOff ? DROP_OFF_KEYS[dropOff] : undefined,\n ),\n field(\"preferred_date\", \"preferred_date\", \"date\", \"Date\", input.startDate),\n field(\"preferred_time\", \"preferred_time\", \"time\", \"Time\", {\n start: `${String(input.startHour).padStart(2, \"0\")}:00`,\n end: null,\n }),\n field(\"note\", \"note\", \"textarea\", \"Notes\", text(input.notes)),\n ];\n // Reserved even when blank, so an extra field can never stand in for one.\n const reported = new Set(entries.map((item) => item.name));\n const fields = entries.filter((item): item is BookingField => item.value !== undefined);\n const extra = (formSubmit.extraFields ?? []).filter((item) => !reported.has(item.name));\n const settings = typedClientSettings(formSubmit.tracking);\n const { violations, hash } = judgeLeadForm(\n \"booking\",\n formSubmit.formId,\n fields.map((item): [string, JsonValue] => [item.role, item.value]),\n formSubmit.contracts,\n { ...settings, trackingConfig: settings.trackingConfig ?? runtime?.ref },\n );\n return {\n form: {\n id: formSubmit.formId,\n action: \"/tracking/bookings\",\n fields: [...fields, ...extra],\n kind: \"booking\" satisfies LeadFormKind,\n contract_version: LEAD_FORM_REGISTRY_VERSION,\n ...(hash ? { contract_hash: hash } : {}),\n ...(violations.length > 0\n ? { contract_violations: violations.slice(0, MAX_CONTRACT_VIOLATIONS) }\n : {}),\n },\n page: { path: typeof window === \"undefined\" ? \"/\" : window.location.pathname },\n };\n}\n\ntype Firing = Pick<ConversionConfigStore, \"getFiring\"> | TrackingConfigRuntime | undefined;\n\n/** The runtime config the sales client was given, when it is the live runtime. */\nfunction runtimeOf(firing: Firing): TrackingConfigRuntime | null {\n return firing && \"integrations\" in firing ? firing : null;\n}\n\nfunction isLocalUrl(url: string): boolean {\n try {\n return [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(new URL(url).hostname);\n } catch {\n return false;\n }\n}\n\n/**\n * A local API with the production CDN books against prod's config_version and slots: the\n * local API refuses it as stale or unavailable. Config and API must come from one backend.\n */\nexport function bookingSetupMismatch(\n endpoint: string,\n runtime: Pick<TrackingConfigRuntime, \"ref\"> | null,\n): string | null {\n if (!runtime || !isLocalUrl(endpoint)) return null;\n const configUrl = resolveTrackingConfigUrl(runtime.ref);\n if (isLocalUrl(configUrl)) return null;\n return (\n `[aranova] Bookings go to the local API (${endpoint}) but the runtime config comes from ` +\n `${configUrl}. Set NEXT_PUBLIC_ARANOVA_CDN_BASE_URL to the local bucket ` +\n \"(http://localhost:9100/aranova-demos) so both come from one backend.\"\n );\n}\n\nexport function createSalesBookingsClient(\n config: SalesTransportConfig,\n firing: Firing,\n): SalesBookingsClient {\n const submittedForms = new Set<string>();\n const staleAttempts = new Set<string>();\n let isSetupChecked = false;\n\n function warnOnSetupMismatch(runtime: TrackingConfigRuntime | null): void {\n if (isSetupChecked) return;\n isSetupChecked = true;\n const warning = bookingSetupMismatch(config.endpoint, runtime);\n if (warning) console.warn(warning);\n }\n\n function handoff(input: BookingInput): string | null {\n const booking = getBookingIntegration(runtimeOf(firing)?.integrations());\n if (!booking) return null;\n return buildBookingHandoffUrl(booking.fallbackUrl, {\n firstName: input.firstName,\n lastName: input.lastName,\n phone: input.phone,\n email: input.email,\n });\n }\n\n /** Never reached Aranova: the mode is the one the runtime config announced. */\n function failedBeforeSend(input: BookingInput): BookingOutcome {\n const mode = getBookingIntegration(runtimeOf(firing)?.integrations())?.mode ?? \"live\";\n return { status: \"failed\", id: null, fallbackUrl: handoff(input), mode };\n }\n\n function withHandoff(outcome: BookingOutcome, input: BookingInput): BookingOutcome {\n if (outcome.status === \"failed\" && !outcome.fallbackUrl) {\n return { ...outcome, fallbackUrl: handoff(input) };\n }\n return outcome;\n }\n\n async function poll(id: string, mode: BookingMode): Promise<BookingOutcome> {\n let outcome: BookingOutcome = { status: \"pending\", id, mode };\n for (const delay of POLL_DELAYS_MS) {\n await sleep(delay);\n try {\n const path = `/bookings/${encodeURIComponent(id)}`;\n outcome = outcomeOf(await salesRequest<BookingResponseBody>(config, \"GET\", path));\n } catch {\n continue;\n }\n if (outcome.status !== \"pending\") return outcome;\n }\n // Still being sent after ~30 s: the workflow keeps going and alerts the owner if it fails.\n return outcome;\n }\n\n async function rejected(\n error: unknown,\n input: BookingInput,\n attemptKey: string,\n runtime: TrackingConfigRuntime | null,\n ): Promise<BookingOutcome> {\n if (!(error instanceof AranovaApiError)) {\n return failedBeforeSend(input);\n }\n if (error.code === BOOKING_ERROR_CODES.stale) {\n // A second stale answer means the published config has not caught up: hand off instead.\n if (staleAttempts.has(attemptKey)) return failedBeforeSend(input);\n staleAttempts.add(attemptKey);\n await runtime?.revalidate();\n return { status: \"stale\", message: error.message };\n }\n if (error.code === BOOKING_ERROR_CODES.rejected) {\n const reasons = error.context?.reasons;\n return {\n status: \"invalid\",\n message: error.message,\n reasons: Array.isArray(reasons) ? reasons.map(String) : [],\n };\n }\n if (error.code === \"validation_error\") {\n return { status: \"invalid\", message: INVALID_DETAILS, reasons: [] };\n }\n // Unavailable, rate-limited, offline or a server fault: the shop's own page still works.\n return failedBeforeSend(input);\n }\n\n return {\n async submit(input, { attemptKey, formSubmit, simulate, onPending }) {\n registerCapability(\"booking_integration\");\n const runtime = runtimeOf(firing);\n warnOnSetupMismatch(runtime);\n const booking = getBookingIntegration(runtime?.integrations());\n if (!booking?.acceptsBookings || booking.configVersion === null) {\n return failedBeforeSend(input);\n }\n if (formSubmit && !submittedForms.has(attemptKey)) {\n submittedForms.add(attemptKey);\n formSubmit.tracking.trackEvent(\n \"form_submit\",\n formSubmitPayload(input, formSubmit, runtime),\n );\n }\n const body: BookingRequestBody = {\n idempotency_key: attemptKey,\n // The tracking client's own visit, so the booking and its form_submit share one session.\n session_id:\n formSubmit?.tracking.getSessionId() ?? peekSessionId() ?? `no-session-${attemptKey}`,\n config_version: booking.configVersion,\n start_date: input.startDate,\n start_hour: input.startHour,\n first_name: input.firstName.trim(),\n last_name: input.lastName.trim(),\n phone: input.phone,\n email: input.email?.trim() || null,\n vehicle: input.vehicle ?? null,\n drop_off: input.dropOff ?? null,\n services: [...input.services],\n notes: input.notes?.trim() || null,\n marketing_sms_consent: input.marketingSmsConsent === true,\n page_path: typeof window === \"undefined\" ? null : window.location.pathname,\n };\n let response: BookingResponseBody;\n try {\n const headers = simulate ? { [BOOKING_SIMULATE_HEADER]: simulate } : undefined;\n response = await salesRequest<BookingResponseBody>(\n config,\n \"POST\",\n \"/bookings\",\n body,\n headers,\n );\n } catch (error) {\n return rejected(error, input, attemptKey, runtime);\n }\n const outcome = outcomeOf(response);\n if (outcome.status !== \"pending\") return withHandoff(outcome, input);\n onPending?.(outcome);\n return withHandoff(await poll(outcome.id, outcome.mode), input);\n },\n\n async updateBooking(bookingId, details, { attemptKey }) {\n const body: BookingDetailsRequestBody = {\n idempotency_key: attemptKey,\n vehicle: details.vehicle ?? null,\n notes: details.notes?.trim() || null,\n heard_via: details.heardVia?.trim() || null,\n };\n try {\n const response = await salesRequest<BookingDetailsResponseBody>(\n config,\n \"POST\",\n `/bookings/${encodeURIComponent(bookingId)}/details`,\n body,\n );\n return { status: response.status, id: response.id };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { status: detailsFailure(error), id: bookingId, message };\n }\n },\n\n async status(id) {\n return outcomeOf(\n await salesRequest<BookingResponseBody>(\n config,\n \"GET\",\n `/bookings/${encodeURIComponent(id)}`,\n ),\n );\n },\n };\n}\n","\"use client\";\n\nimport { useEffect, useMemo } from \"react\";\n\nimport {\n bootstrapGoogleAdsTracking,\n bootstrapMetaPixel,\n bootstrapMultipleGtags,\n bootstrapMultiplePixels,\n getTrackingConfigRuntime,\n registerCapability,\n resolveTrackingConfigUrl,\n type GtagEnvironmentMap,\n type MetaPixelEnvironmentMap,\n type TrackingConfigReference,\n} from \"../../tracking-core/src/index\";\n\n/**\n * Props for the combined ad-platform tag loader.\n *\n * Every field is optional and independent: pass the Google fields, the Meta\n * fields, or both. For each platform a labelled `*Ids` map (ALL loaded) takes\n * precedence over the single `*Id` shortcut.\n */\nexport interface AdPlatformTrackingProps {\n /** Single Google Ads tag id, e.g. `AW-123456789`. */\n gtagId?: string;\n /** Labelled Google Ads tag map — ALL loaded; wins over `gtagId`. */\n gtagIds?: GtagEnvironmentMap;\n /** R2-authoritative Google tracking config. When set, static gtagId(s) are ignored. */\n trackingConfig?: TrackingConfigReference;\n /** Fire a Google page view from this standalone loader. Leave false when using TrackingProvider. */\n standalonePageView?: boolean;\n /** Single Meta Pixel id, e.g. `123456789012345`. */\n metaPixelId?: string;\n /** Labelled Meta Pixel map — ALL loaded; wins over `metaPixelId`. */\n metaPixelIds?: MetaPixelEnvironmentMap;\n}\n\n/**\n * Client component that loads the configured ad-platform tags — the Google tag\n * (`gtag`) and/or the Meta Pixel (`fbq`) — each with the visitor's effective\n * consent applied as the default (opt-out model). One mount handles both\n * platforms; omit a platform's props to skip it. Renders nothing.\n *\n * On React you usually don't need this at all — `<TrackingProvider>` already\n * accepts the same `gtagId/gtagIds` + `metaPixelId/metaPixelIds` props. Use this\n * standalone component when you want the ad tags WITHOUT the event-ingest SDK.\n */\nexport function AdPlatformTracking({\n gtagId,\n gtagIds,\n trackingConfig,\n standalonePageView = false,\n metaPixelId,\n metaPixelIds,\n}: AdPlatformTrackingProps) {\n const trackingConfigKey = trackingConfig\n ? `${resolveTrackingConfigUrl(trackingConfig)}:${trackingConfig.businessId}:${trackingConfig.siteId ?? \"\"}:${trackingConfig.environment}`\n : \"\";\n // Stabilize object references so the effects don't re-fire when the parent\n // passes inline object literals on every render.\n const gtagIdsKey = useMemo(() => (gtagIds ? JSON.stringify(gtagIds) : \"\"), [gtagIds]);\n const metaPixelIdsKey = useMemo(\n () => (metaPixelIds ? JSON.stringify(metaPixelIds) : \"\"),\n [metaPixelIds],\n );\n\n if (trackingConfig || gtagId || (gtagIds && Object.keys(gtagIds).length > 0)) {\n registerCapability(\"ad_tags_google\");\n }\n if (metaPixelId || (metaPixelIds && Object.keys(metaPixelIds).length > 0)) {\n registerCapability(\"ad_tags_meta\");\n }\n\n useEffect(() => {\n if (trackingConfig) {\n const runtime = getTrackingConfigRuntime(trackingConfig);\n if (standalonePageView) runtime.queuePageView();\n else void runtime.revalidate();\n } else 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 // eslint-disable-next-line react-hooks/exhaustive-deps -- trackingConfigKey is the stable proxy for trackingConfig\n }, [gtagId, gtagIdsKey, trackingConfigKey, standalonePageView]);\n\n useEffect(() => {\n if (metaPixelIds && Object.keys(metaPixelIds).length > 0) {\n bootstrapMultiplePixels(metaPixelIds);\n } else if (metaPixelId) {\n bootstrapMetaPixel(metaPixelId);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- metaPixelIdsKey is the stable proxy for metaPixelIds\n }, [metaPixelId, metaPixelIdsKey]);\n\n return null;\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 with the visitor's effective\n * consent applied as the Consent Mode default (opt-out model — granted unless\n * an unexpired stored decline exists).\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(() => (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 return null;\n}\n","\"use client\";\n\nimport { createContext, useContext, useEffect, useMemo, type ReactNode } from \"react\";\n\nimport { version } from \"../package.json\";\nimport {\n attachAutoPageView,\n attachClientCapturesOnce,\n attachCtaClickCapture,\n attachFormStart,\n attachMultiPageSession,\n attachPageExit,\n attachPhoneClickCapture,\n attachScrollDepth,\n attachSiteLinkCapture,\n attachSpecificPageVisit,\n attachTimeOnSite,\n bootstrapGoogleAdsTracking,\n bootstrapMetaPixel,\n bootstrapMultipleGtags,\n bootstrapMultiplePixels,\n createConversionAutoFire,\n createTypedClient,\n decorateCrossSiteUrl,\n getOrCreateTrackingClient,\n getTrackingConfigRuntime,\n registerCapability,\n resolveConversionConfig,\n setDefaultTrackingConfig,\n stripLinkParamFromLocation,\n withConversionAutoFire,\n type ConversionConfig,\n type GtagEnvironmentMap,\n type MetaPixelEnvironmentMap,\n type PhoneConfig,\n type TrackingConfigReference,\n type TrackingEnvironment,\n type TriggerRegistryConfig,\n type TypedTrackingClient,\n} from \"../../tracking-core/src/index\";\nimport { PhoneConfigProvider } from \"../../tracking-core/src/phone-react\";\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, `trackEvent()` metadata that fails its Zod schema throws. When\n * off, the invalid event is dropped with a console warning. Turn on in dev\n * to catch shape bugs early.\n */\n debug?: boolean;\n /**\n * Phone-field display + default-country config, read by `usePhoneField` /\n * `<PhoneField>`. Display is customizable; the transmitted value is always E.164.\n */\n phone?: PhoneConfig;\n /**\n * GAP28 / unified-goal: enable real-time on-site conversion firing. When set, the SDK\n * fetches the per-business config from `cdnUrl` (optional offline `baked` fallback) and\n * AUTOMATICALLY fires `gtag('event','conversion')` for any automatic event-goal\n * (scroll/time/page-view/…) whose trigger threshold a detector crosses. Omit to keep events\n * analytics-only. (Sale + manual-event firing lives in the server/browser sales client.)\n */\n conversionConfig?: { cdnUrl: string; baked?: ConversionConfig | null };\n /**\n * R2-authoritative config reference — `ARANOVA_TRACKING_CONFIG` from\n * `tracking-cli gen`. Wins over `conversionConfig`. The object URL is composed\n * from `businessId` + `environment` against the production CDN; spread in a\n * `cdnBaseUrl` to read it from somewhere else, e.g.\n * `{ ...ARANOVA_TRACKING_CONFIG, cdnBaseUrl: import.meta.env.VITE_ARANOVA_CDN_BASE_URL }`.\n */\n trackingConfig?: TrackingConfigReference;\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 /** Optional Meta Pixel id, for example `123456789012345`. */\n metaPixelId?: string;\n /**\n * Labelled map of Meta Pixel IDs. ALL are loaded simultaneously.\n * When provided, `metaPixelId` is ignored.\n */\n metaPixelIds?: MetaPixelEnvironmentMap;\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 * Append the cross-site identity token to a URL that points at a sibling\n * site of this business, for navigations the linker cannot see. Returns the\n * URL unchanged for any other target.\n */\n decorateUrl: (url: string) => string;\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 {\n apiKey,\n endpoint,\n triggers,\n environment,\n debug,\n phone,\n conversionConfig,\n trackingConfig,\n } = options;\n\n registerCapability(\"base_tracking\");\n setDefaultTrackingConfig(trackingConfig);\n // The R2 config reference is what wires the conversion auto-fire below, so\n // passing it IS the automatic-goal capability — no sales client involved.\n if (trackingConfig) registerCapability(\"conversion_goals_auto\");\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 // Still publish phone config so usePhoneField/<PhoneField> work even when\n // tracking is disabled (missing apiKey/endpoint).\n TrackingProvider: ({ children }: TrackingProviderProps) => (\n <PhoneConfigProvider value={phone ?? null}>{children}</PhoneConfigProvider>\n ),\n useTracking: () => noopTyped,\n decorateUrl: (url: string) => url,\n };\n }\n\n const TrackingContext = createContext<TypedTrackingClient<TRegistry> | null>(null);\n\n function TrackingProvider({\n gtagId,\n gtagIds,\n metaPixelId,\n metaPixelIds,\n children,\n }: TrackingProviderProps): ReactNode {\n // Stabilize object references when callers pass inline ID maps.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const gtagIdsKey = useMemo(() => (gtagIds ? JSON.stringify(gtagIds) : \"\"), [gtagIds]);\n\n // Resolve gtag IDs map for context and heartbeat.\n const resolvedGtagIds = useMemo(\n () =>\n gtagIds\n ? (Object.fromEntries(\n Object.entries(gtagIds).filter((e): e is [string, string] => e[1] != null),\n ) as Record<string, string>)\n : gtagId\n ? { default: gtagId }\n : undefined,\n // eslint-disable-next-line react-hooks/exhaustive-deps -- gtagIdsKey is stable proxy\n [gtagId, gtagIdsKey],\n );\n\n // Page-level singleton raw client. Surviving strict-mode unmount +\n // remount is handled by getOrCreateTrackingClient itself.\n const rawClient = useMemo(() => {\n const created = getOrCreateTrackingClient({\n apiKey,\n endpoint,\n surface: \"react\",\n packageName: \"@aranova/tracking-react\",\n sdkVersion: version,\n triggers,\n environment,\n activeGtagIds: resolvedGtagIds,\n configSiteId: trackingConfig?.siteId ?? null,\n debug,\n });\n // Before attachAutoPageView snapshots the URL (the client already consumed the token).\n stripLinkParamFromLocation();\n return created;\n }, [resolvedGtagIds]);\n const conversionStore = useMemo(\n () =>\n trackingConfig\n ? getTrackingConfigRuntime(trackingConfig)\n : conversionConfig\n ? resolveConversionConfig({\n cdnUrl: conversionConfig.cdnUrl,\n baked: conversionConfig.baked,\n })\n : null,\n [],\n );\n const conversionClient = useMemo(\n () =>\n conversionStore\n ? withConversionAutoFire(rawClient, createConversionAutoFire(conversionStore))\n : rawClient,\n [rawClient, conversionStore],\n );\n const client = useMemo(\n () =>\n createTypedClient<TRegistry>(conversionClient, triggers, {\n debug,\n environment,\n trackingConfig,\n }),\n [conversionClient],\n );\n\n useEffect(() => {\n if (trackingConfig) {\n getTrackingConfigRuntime(trackingConfig).start();\n } else 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 // eslint-disable-next-line react-hooks/exhaustive-deps\n const metaPixelIdsKey = useMemo(\n () => (metaPixelIds ? JSON.stringify(metaPixelIds) : \"\"),\n [metaPixelIds],\n );\n\n useEffect(() => {\n if (metaPixelIds && Object.keys(metaPixelIds).length > 0) {\n bootstrapMultiplePixels(metaPixelIds);\n } else if (metaPixelId) {\n bootstrapMetaPixel(metaPixelId);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps -- metaPixelIdsKey is the stable proxy for metaPixelIds\n }, [metaPixelId, metaPixelIdsKey]);\n\n // Attach the automatic triggers declared in the registry. These are all\n // document/window/pushState listeners, so they must attach ONCE per singleton\n // client, not once per provider mount — otherwise a page with multiple\n // <TrackingProvider>s stacks duplicate listeners and every interaction/navigation\n // emits its event N times. `attachClientCapturesOnce` ref-counts against the\n // singleton client: the first mount runs `build`, later mounts are no-ops, and the\n // last unmount runs the LIFO detach (so layered pushState patches restore cleanly).\n useEffect(() => {\n return attachClientCapturesOnce(rawClient, () => {\n const detachers: Array<() => void> = [];\n\n // GAP28 / unified-goal: when conversion firing is on, wrap the client so every\n // automatic detector event ALSO auto-fires a matching event-goal's conversion\n // (best-effort, never breaks ingest). Built inside `build` so it only resolves on\n // the mount that actually attaches (stale-while-revalidate against the CDN).\n const detectorClient = conversionStore ? conversionClient : rawClient;\n const pageClient =\n trackingConfig && conversionStore && \"queuePageView\" in conversionStore\n ? {\n ...detectorClient,\n trackEvent: (input: Parameters<typeof detectorClient.trackEvent>[0]) => {\n detectorClient.trackEvent(input);\n if (input.eventType === \"page_view\") conversionStore.queuePageView();\n },\n }\n : detectorClient;\n\n // page_view is always present (required in the type). page_exit is\n // SDK-internal: always attached, so dwell/exit analytics need no config.\n detachers.push(attachAutoPageView(pageClient));\n detachers.push(attachPageExit(detectorClient));\n\n const timeOnSite = triggers.automatic.time_on_site;\n if (timeOnSite) {\n detachers.push(attachTimeOnSite(detectorClient, timeOnSite));\n }\n\n detachers.push(\n attachSiteLinkCapture(detectorClient, {\n linkedHostnames: () => conversionStore?.linkedHostnames() ?? [],\n }),\n );\n\n const specificPageVisit = triggers.automatic.specific_page_visit;\n if (specificPageVisit) {\n detachers.push(attachSpecificPageVisit(detectorClient, specificPageVisit));\n }\n\n const scrollDepth = triggers.automatic.scroll_depth;\n if (scrollDepth) {\n detachers.push(attachScrollDepth(detectorClient, scrollDepth));\n }\n\n const multiPageSession = triggers.automatic.multi_page_session;\n if (multiPageSession) {\n detachers.push(attachMultiPageSession(detectorClient, multiPageSession));\n }\n\n const formStart = triggers.automatic.form_start;\n if (formStart) {\n detachers.push(attachFormStart(detectorClient, formStart));\n }\n\n const ctaClick = triggers.manual?.cta_click;\n if (ctaClick) {\n // No-ops unless the config opts into autoCapture.\n detachers.push(attachCtaClickCapture(detectorClient, ctaClick));\n }\n\n const phoneClick = triggers.manual?.phone_click;\n if (phoneClick) {\n // No-ops unless the config opts into autoCapture. A captured tel: tap also\n // drives conversion auto-fire for a linked phone_click goal.\n detachers.push(attachPhoneClickCapture(detectorClient, phoneClick));\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 }, [conversionClient, conversionStore, rawClient]);\n\n return (\n <TrackingContext.Provider value={client}>\n <PhoneConfigProvider value={phone ?? null}>{children}</PhoneConfigProvider>\n </TrackingContext.Provider>\n );\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 {\n TrackingProvider,\n useTracking,\n decorateUrl: (url: string) =>\n decorateCrossSiteUrl(\n url,\n trackingConfig ? getTrackingConfigRuntime(trackingConfig).linkedHostnames() : [],\n ),\n };\n}\n","{\n \"name\": \"@aranova/tracking-react\",\n \"version\": \"1.20.0\",\n \"private\": false,\n \"type\": \"commonjs\",\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 \"typesVersions\": {\n \"*\": {\n \"client\": [\n \"./dist/client.d.ts\"\n ]\n }\n },\n \"sideEffects\": false,\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 \"import\": {\n \"types\": \"./dist/index.d.mts\",\n \"default\": \"./dist/index.mjs\"\n },\n \"require\": {\n \"types\": \"./dist/index.d.ts\",\n \"default\": \"./dist/index.js\"\n }\n },\n \"./client\": {\n \"import\": {\n \"types\": \"./dist/client.d.mts\",\n \"default\": \"./dist/client.mjs\"\n },\n \"require\": {\n \"types\": \"./dist/client.d.ts\",\n \"default\": \"./dist/client.js\"\n }\n },\n \"./sales\": {\n \"import\": {\n \"types\": \"./dist/sales.d.mts\",\n \"default\": \"./dist/sales.mjs\"\n },\n \"require\": {\n \"types\": \"./dist/sales.d.ts\",\n \"default\": \"./dist/sales.js\"\n }\n },\n \"./phone\": {\n \"import\": {\n \"types\": \"./dist/phone.d.mts\",\n \"default\": \"./dist/phone.mjs\"\n },\n \"require\": {\n \"types\": \"./dist/phone.d.ts\",\n \"default\": \"./dist/phone.js\"\n }\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 \"libphonenumber-js\": \"^1.11.0\",\n \"zod\": \"^4.6.2\"\n },\n \"devDependencies\": {\n \"@types/react\": \"^19.2.2\",\n \"@types/react-dom\": \"^19.2.2\",\n \"esbuild\": \"^0.27.7\",\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","\"use client\";\n\n// Layer 2 — React phone field. Authored ONCE here in core and re-exported by\n// tracking-react / tracking-next (the existing core→framework edge). This module\n// is a SEPARATE `'use client'` subpath and is intentionally NOT exported from\n// core's main `index.ts`, so non-React consumers (browser IIFE, server) never\n// pull React. `react` is an OPTIONAL peer dependency.\n\nimport {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useMemo,\n useState,\n type ChangeEvent,\n type Context,\n type FocusEvent,\n type InputHTMLAttributes,\n type ReactNode,\n} from \"react\";\n\nimport { registerCapability } from \"./capabilities\";\nimport {\n DEFAULT_PHONE_COUNTRY,\n formatPhone,\n formatPhoneAsTyped,\n parsePhone,\n type CountryCode,\n type ParsedPhone,\n type PhoneDisplayFormat,\n} from \"./phone\";\nimport type { PhoneConfig } from \"./phone-field\";\n\n/** Carries `createTracking({ phone })` config to `usePhoneField`. Separate from the\n * tracking client context so `useTracking()`'s type is untouched.\n *\n * Created lazily on first access — NOT at module scope — so that importing a\n * barrel which re-exports this module from a React Server Component graph has no\n * eval-time side effect. A module-scope `createContext()` runs during server\n * module evaluation and throws \"createContext only works in Client Components\"\n * (Next 15/16), even when the importer only wanted a server-safe symbol bundled\n * alongside it. Behind a getter the module stays inert until a client component\n * actually renders the provider/hook.\n *\n * Caveat: this module is bundled into BOTH `@aranova/tracking-react` and\n * `@aranova/tracking-next`, so each package has its own context instance at\n * runtime. Within one installed package the provider + hook share it (correct);\n * mixing `createTracking` from one package with `usePhoneField` from the other\n * won't share config (the hook falls back to the `CA`/`national` defaults). Use a\n * single tracking package per app. */\nlet _phoneConfigContext: Context<PhoneConfig | null> | undefined;\nfunction phoneConfigContext(): Context<PhoneConfig | null> {\n return (_phoneConfigContext ??= createContext<PhoneConfig | null>(null));\n}\n\n/** Provides `createTracking({ phone })` config to `usePhoneField` / `<PhoneField>`.\n * Wired internally by each package's `TrackingProvider`; consumers configure phone\n * via `createTracking({ phone })` rather than mounting this directly. Replaces the\n * former raw `PhoneConfigContext` export so the context is never created at module\n * load (see the getter above). */\nexport function PhoneConfigProvider({\n value,\n children,\n}: {\n value: PhoneConfig | null;\n children: ReactNode;\n}): ReactNode {\n const Ctx = phoneConfigContext();\n return <Ctx.Provider value={value}>{children}</Ctx.Provider>;\n}\n\n/** Resolve the effective phone config (provider value or built-in defaults). */\nexport function usePhoneConfig(): { defaultCountry: CountryCode; display: PhoneDisplayFormat } {\n const ctx = useContext(phoneConfigContext());\n return {\n defaultCountry: ctx?.defaultCountry ?? DEFAULT_PHONE_COUNTRY,\n display: ctx?.display ?? \"national\",\n };\n}\n\nexport interface UsePhoneFieldOptions {\n defaultValue?: string;\n /** Overrides the provider's `defaultCountry`. */\n country?: CountryCode;\n /** Overrides the provider's `display` (applied to the settled value on blur). */\n display?: PhoneDisplayFormat;\n /** Notified with the canonical E.164 (or `null`) on every change. */\n onValueChange?: (e164: string | null) => void;\n}\n\nexport interface PhoneInputProps {\n value: string;\n onChange: (event: ChangeEvent<HTMLInputElement>) => void;\n onBlur: (event: FocusEvent<HTMLInputElement>) => void;\n type: \"tel\";\n inputMode: \"tel\";\n autoComplete: \"tel\";\n}\n\nexport interface PhoneFieldApi {\n /** Display value for the `<input>` (live `AsYouType` while typing). */\n value: string;\n /** Canonical E.164 — what gets transmitted. `null` while invalid/incomplete. */\n e164: string | null;\n isValid: boolean;\n /** Validation message, surfaced only after blur with non-empty invalid input. */\n error: string | null;\n parsed: ParsedPhone;\n /** Spread onto an `<input>`: pre-wires value/onChange/onBlur/type/inputMode/autoComplete. */\n inputProps: PhoneInputProps;\n}\n\n/** Headless phone field — the client owns the markup. */\nexport function usePhoneField(opts: UsePhoneFieldOptions = {}): PhoneFieldApi {\n // Reported from the hook rather than from `createTracking({ phone })`:\n // config presence only says the option was set, not that a field renders.\n // `<PhoneField>` is built on this hook, so both surfaces are covered here.\n registerCapability(\"phone_fields\");\n const cfg = usePhoneConfig();\n const country = opts.country ?? cfg.defaultCountry;\n const display = opts.display ?? cfg.display;\n const { onValueChange } = opts;\n\n const [value, setValue] = useState(() => formatPhoneAsTyped(opts.defaultValue ?? \"\", country));\n const [touched, setTouched] = useState(false);\n\n const parsed = useMemo(() => parsePhone(value, country), [value, country]);\n\n const onChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>) => {\n const next = formatPhoneAsTyped(event.target.value, country);\n setValue(next);\n onValueChange?.(parsePhone(next, country).e164);\n },\n [country, onValueChange],\n );\n\n const onBlur = useCallback(\n (_event: FocusEvent<HTMLInputElement>) => {\n setTouched(true);\n // Settle the field to the configured display format once it's valid.\n setValue((current) => {\n const p = parsePhone(current, country);\n return p.isValid ? formatPhone(current, display, country) : current;\n });\n },\n [country, display],\n );\n\n const error =\n touched && value.length > 0 && !parsed.isValid ? \"Enter a valid phone number\" : null;\n\n return {\n value,\n e164: parsed.e164,\n isValid: parsed.isValid,\n error,\n parsed,\n inputProps: { value, onChange, onBlur, type: \"tel\", inputMode: \"tel\", autoComplete: \"tel\" },\n };\n}\n\nexport interface PhoneFieldProps extends Omit<\n InputHTMLAttributes<HTMLInputElement>,\n \"type\" | \"value\" | \"onChange\"\n> {\n country?: CountryCode;\n /** Controlled display value. */\n value?: string;\n /** Uncontrolled initial value. */\n defaultValue?: string;\n /** Receives the native change event (RHF `register().onChange` or your own); the\n * event's `target.value` is already `AsYouType`-formatted. */\n onChange?: (event: ChangeEvent<HTMLInputElement>) => void;\n /** Receives the canonical E.164 (or `null`) on every change. */\n onE164Change?: (e164: string | null) => void;\n}\n\n/**\n * Batteries-included phone input. Composes identically with react-hook-form\n * `{...register('phone')}` and with controlled state — the `AsYouType` +\n * mutate-`e.target.value`-before-`onChange` technique lives inside, so RHF and\n * controlled parents both receive the formatted value, and the wire value stays\n * E.164.\n */\nexport const PhoneField = forwardRef<HTMLInputElement, PhoneFieldProps>(function PhoneField(\n { country, value, defaultValue, onChange, onE164Change, ...rest },\n ref,\n) {\n const cfg = usePhoneConfig();\n const resolvedCountry = country ?? cfg.defaultCountry;\n const isControlled = value !== undefined;\n const [internal, setInternal] = useState(() =>\n formatPhoneAsTyped(defaultValue ?? \"\", resolvedCountry),\n );\n\n const handleChange = (event: ChangeEvent<HTMLInputElement>): void => {\n const formatted = formatPhoneAsTyped(event.target.value, resolvedCountry);\n // Mutate the target BEFORE forwarding so RHF/controlled consumers see the\n // formatted value (and the input renders it).\n event.target.value = formatted;\n onE164Change?.(parsePhone(formatted, resolvedCountry).e164);\n if (!isControlled) setInternal(formatted);\n onChange?.(event);\n };\n\n const shown = isControlled ? formatPhoneAsTyped(value, resolvedCountry) : internal;\n\n return (\n <input\n {...rest}\n ref={ref}\n type=\"tel\"\n inputMode=\"tel\"\n autoComplete=\"tel\"\n value={shown}\n onChange={handleChange}\n />\n );\n});\n","\"use client\";\n\nimport type { AnchorHTMLAttributes, ReactNode } from \"react\";\nimport { useCallback, useMemo, useRef, useState, useSyncExternalStore } from \"react\";\n\nimport { registerCapability } from \"./capabilities\";\nimport {\n bookableSlots,\n buildBookingHandoffUrl,\n getBookingIntegration,\n type BookableDay,\n type BookingHandoffPrefill,\n type BookingIntegration,\n} from \"./integrations-public\";\nimport {\n newBookingAttemptKey,\n type BookingDetailsInput,\n type BookingDetailsOutcome,\n type BookingFormSubmit,\n type BookingInput,\n type BookingInputFor,\n type BookingOutcome,\n type BookingSimulation,\n type SalesBookingsClient,\n} from \"./resources/sales/bookings\";\nimport type { LeadFormContracts } from \"./lead-forms/submit\";\nimport type { ConversionIntegrations } from \"./resources/conversion-config\";\nimport {\n getTrackingConfigRuntime,\n type TrackingConfigReference,\n} from \"./resources/tracking-config-runtime\";\n\nfunction serverSnapshot(): ConversionIntegrations | null {\n return null;\n}\n\n/**\n * The site's booking provider from the live runtime config, or null before one loads.\n *\n * Pass the same `ARANOVA_TRACKING_CONFIG` reference given to `createTracking`; the config\n * runtime is shared, so this adds no fetch.\n */\nexport function useBookingIntegration(\n trackingConfig: TrackingConfigReference | undefined,\n): BookingIntegration | null {\n registerCapability(\"booking_handoff\");\n const subscribe = useCallback(\n (listener: () => void) =>\n trackingConfig ? getTrackingConfigRuntime(trackingConfig).subscribe(listener) : () => {},\n [trackingConfig],\n );\n const integrations = useSyncExternalStore(\n subscribe,\n () => (trackingConfig ? getTrackingConfigRuntime(trackingConfig).integrations() : null),\n serverSnapshot,\n );\n return useMemo(() => getBookingIntegration(integrations), [integrations]);\n}\n\nexport interface BookingHandoffOptions {\n /** Baked copy of the booking link, used when no runtime config has loaded. */\n fallbackUrl?: string;\n prefill?: BookingHandoffPrefill;\n}\n\n/**\n * The prefilled handoff URL, or null when neither the runtime config nor `fallbackUrl` names a\n * usable link. For sites that render the link with their own button component.\n */\nexport function useBookingHandoffUrl(\n trackingConfig: TrackingConfigReference | undefined,\n { fallbackUrl, prefill }: BookingHandoffOptions = {},\n): string | null {\n const booking = useBookingIntegration(trackingConfig);\n const base = booking?.fallbackUrl ?? fallbackUrl;\n return base ? buildBookingHandoffUrl(base, prefill) : null;\n}\n\nexport interface BookingHandoffLinkProps\n extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, \"href\">, BookingHandoffOptions {\n trackingConfig?: TrackingConfigReference;\n children: ReactNode;\n}\n\n/**\n * The optional \"pick your time\" step after a form submit: the shop's booking page, prefilled.\n * Renders nothing when neither the runtime config nor `fallbackUrl` names a usable link.\n */\nexport function BookingHandoffLink({\n trackingConfig,\n fallbackUrl,\n prefill,\n children,\n target = \"_blank\",\n rel = \"noopener noreferrer\",\n ...anchorProps\n}: BookingHandoffLinkProps): ReactNode {\n const href = useBookingHandoffUrl(trackingConfig, { fallbackUrl, prefill });\n if (!href) return null;\n return (\n <a {...anchorProps} href={href} target={target} rel={rel}>\n {children}\n </a>\n );\n}\n\n/**\n * Every bookable day and its start hours, split morning / afternoon, from the live rules.\n * Empty until the config loads or when the shop takes no online bookings. Recomputed each\n * render (cheap), so a tab left open past midnight never offers yesterday's notice window.\n */\nexport function useBookingSlots(\n trackingConfig: TrackingConfigReference | undefined,\n): BookableDay[] {\n const booking = useBookingIntegration(trackingConfig);\n if (!booking?.acceptsBookings || !booking.rules) return [];\n return bookableSlots(booking.rules, { timeZone: booking.timezone });\n}\n\nexport type BookingSubmitStatus = \"idle\" | \"submitting\" | BookingOutcome[\"status\"];\n\nexport interface BookedAttempt {\n bookingId: string;\n attemptKey: string;\n}\n\nexport interface BookingSubmitState<I = BookingInput> {\n status: BookingSubmitStatus;\n outcome: BookingOutcome | null;\n /** Send the booking. A retry of an unanswered or refused submit reuses its attempt key. */\n submit(input: I): Promise<BookingOutcome>;\n /**\n * The recorded booking and the key it was sent with; persist it (e.g. `sessionStorage`) to add\n * details after a reload. Null until the server has the booking.\n */\n booked: BookedAttempt | null;\n /**\n * Add the vehicle, notes or \"How did you hear about us?\" to `booked`, or to `target` (a\n * persisted one). Resolves `not_found` when there is neither.\n */\n updateBooking(\n details: BookingDetailsInput,\n target?: BookedAttempt,\n ): Promise<BookingDetailsOutcome>;\n /** Back to `idle` with a fresh attempt key, e.g. \"book another car\". */\n reset(): void;\n}\n\n/**\n * Submit state for a booking form over `salesClient.bookings`. Owns the parts that are easy to\n * get wrong: one attempt key per submit (a double-click never books twice), the visit's\n * `form_submit`, the stale-config refetch, and the fallback link on failure. A `pending` answer\n * shows at once (`status: \"pending\"`) while the final one is polled; the submit stays in flight.\n * Pass the site's `ARANOVA_LEAD_FORMS` as `contracts` to type the input by the shop's rules.\n */\nexport function useBookingSubmit<C extends LeadFormContracts | undefined = undefined>(\n bookings: SalesBookingsClient,\n options: {\n formSubmit?: BookingFormSubmit;\n /** Development only: a simulated create's outcome. The production API ignores it. */\n simulate?: BookingSimulation;\n contracts?: C;\n } = {},\n): BookingSubmitState<C extends LeadFormContracts ? BookingInputFor<C> : BookingInput> {\n registerCapability(\"booking_integration\");\n const [outcome, setOutcome] = useState<BookingOutcome | null>(null);\n const [submitting, setSubmitting] = useState(false);\n const attemptKey = useRef<string | null>(null);\n const [booked, setBooked] = useState<BookedAttempt | null>(null);\n const inFlight = useRef<Promise<BookingOutcome> | null>(null);\n const { simulate, contracts } = options;\n const formSubmit = useMemo(\n () =>\n options.formSubmit && contracts ? { ...options.formSubmit, contracts } : options.formSubmit,\n [options.formSubmit, contracts],\n );\n\n const submit = useCallback(\n (input: BookingInput) => {\n if (inFlight.current) return inFlight.current;\n attemptKey.current ??= newBookingAttemptKey();\n const key = attemptKey.current;\n setSubmitting(true);\n const run = bookings\n .submit(input, {\n attemptKey: key,\n formSubmit,\n simulate,\n // The server has it: show the pending success while the final answer is polled.\n onPending: (pending) => {\n setBooked({ bookingId: pending.id, attemptKey: key });\n setOutcome(pending);\n setSubmitting(false);\n },\n })\n .then((result) => {\n // Keep the key while nothing was recorded (invalid, stale, a POST that never answered),\n // so the retry replays instead of booking twice.\n const unrecorded =\n result.status === \"invalid\" ||\n result.status === \"stale\" ||\n (result.status === \"failed\" && result.id === null);\n if (!unrecorded) attemptKey.current = null;\n if (result.status !== \"invalid\" && result.status !== \"stale\" && result.id) {\n setBooked({ bookingId: result.id, attemptKey: key });\n }\n setOutcome(result);\n return result;\n })\n .finally(() => {\n inFlight.current = null;\n setSubmitting(false);\n });\n inFlight.current = run;\n return run;\n },\n [bookings, formSubmit, simulate],\n );\n\n const updateBooking = useCallback(\n async (\n details: BookingDetailsInput,\n target?: BookedAttempt,\n ): Promise<BookingDetailsOutcome> => {\n const attempt = target ?? booked;\n if (!attempt) return { status: \"not_found\", id: \"\", message: \"Nothing has been booked yet.\" };\n return bookings.updateBooking(attempt.bookingId, details, {\n attemptKey: attempt.attemptKey,\n });\n },\n [bookings, booked],\n );\n\n const reset = useCallback(() => {\n attemptKey.current = null;\n setBooked(null);\n setOutcome(null);\n }, []);\n\n const status: BookingSubmitStatus = submitting ? \"submitting\" : (outcome?.status ?? \"idle\");\n return { status, outcome, booked, submit, updateBooking, reset };\n}\n","\"use client\";\n\nimport type { CSSProperties, KeyboardEvent as ReactKeyboardEvent, ReactNode } from \"react\";\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\n\nimport { registerCapability } from \"./capabilities\";\nimport type { VideoProgressTriggerReason, VideoVariant, VideoWatchRange } from \"./events/video\";\nimport { getActiveTrackingEnv } from \"./ingest-typed\";\nimport type { TrackingClient } from \"./ingest\";\nimport type { ConversionGoal, TrackedVideoConfig } from \"./resources/conversion-config\";\nimport {\n getTrackingConfigRuntime,\n type TrackingConfigReference,\n} from \"./resources/tracking-config-runtime\";\nimport { mergeVideoRanges, roundVideoSeconds, sumVideoRangesSeconds } from \"./video-ranges\";\n\nexport type { VideoVariant, VideoWatchRange, VideoProgressTriggerReason };\n\n/** A rendition height in pixels (e.g. 720, 1080), or \"auto\" for adaptive bitrate. */\nexport type VideoQuality = number | \"auto\";\n\nexport interface TrackedVideoEvent {\n /** Logical video key, e.g. \"qlt-case-study-v6\". */\n id: string;\n /** Variant-qualified DOM id, e.g. \"qlt-case-study-v6-16x9\". */\n videoId: string;\n variant: VideoVariant;\n currentTime: number;\n duration: number;\n watchedSeconds: number;\n watchedPercent: number;\n maxPercent: number;\n ranges: VideoWatchRange[];\n}\n\nexport interface TrackedVideoCallbacks {\n onPlay?: (e: TrackedVideoEvent) => void;\n onPause?: (e: TrackedVideoEvent) => void;\n /** A finished seek: where playback was and where it landed, in seconds. */\n onSeek?: (e: TrackedVideoEvent & { from: number; to: number }) => void;\n onTimeUpdate?: (e: TrackedVideoEvent) => void;\n onEnded?: (e: TrackedVideoEvent) => void;\n onWaiting?: (e: TrackedVideoEvent) => void;\n /** Every quality change: a manual pick or an automatic ABR switch. */\n onQualityChange?: (\n e: TrackedVideoEvent & { from: VideoQuality; to: VideoQuality; source: \"manual\" | \"auto\" },\n ) => void;\n onFullscreenChange?: (e: TrackedVideoEvent & { fullscreen: boolean }) => void;\n /** Fired once per checkpoint (default 25, 50, 75, 90, 100) as playback crosses it. */\n onCheckpoint?: (e: TrackedVideoEvent & { checkpoint: number }) => void;\n}\n\nexport interface TrackedVideoSources {\n /** 16:9 Stream UID for desktop / landscape viewports. */\n desktop: string;\n /** Optional 9:16 Stream UID for mobile / portrait viewports. Falls back to `desktop` if omitted. */\n mobile?: string | null;\n}\n\nexport interface TrackedVideoProps extends TrackedVideoCallbacks {\n /** Logical video key (e.g. `\"qlt-case-study-v6\"`), resolved from R2 `trackingConfig` or `fallbackConfig`. */\n id?: string;\n /** Alias for `id` when referencing a key from `aranova-services.ts` (`videos`). */\n video?: string;\n /** Cloudflare Stream customer subdomain host (e.g. `\"customer-xxxx.cloudflarestream.com\"`). */\n host?: string;\n /** Stream UIDs for the desktop (16:9) and optional mobile (9:16) cuts. */\n sources?: TrackedVideoSources;\n /** Accessible title for the video player. */\n title?: string;\n /** Poster frame timestamp in seconds (Cloudflare Stream thumbnail parameter). Default: `4`. */\n posterTime?: number;\n /** Optional custom poster URL override. */\n posterUrl?: string;\n /** Optional custom renderer for the poster image (e.g. `next/image`). */\n renderPoster?: (props: {\n src: string;\n variant: VideoVariant;\n title: string;\n width: number;\n height: number;\n }) => ReactNode;\n /** Desktop theater state (controlled). */\n theater?: boolean;\n /** Initial desktop theater state when uncontrolled. */\n defaultTheater?: boolean;\n /** Callback when desktop theater expand/shrink is toggled or triggered on first play. */\n onTheaterChange?: (next: boolean) => void;\n /** Desktop expansion mode (`\"theater\"`, `\"inline\"`, or `\"none\"`). */\n expandMode?: \"theater\" | \"inline\" | \"none\";\n /** Whether clicking Play on desktop automatically expands into theater mode. Default: `true`. */\n autoExpandOnPlay?: boolean;\n /** Max width when expanded inline on desktop without an external `<TrackedVideoStage>`. Default: `1100`. */\n expandedMaxWidth?: number | string;\n /** Percentage checkpoints that trigger an immediate `video_progress` flush. Default: `[25, 50, 75, 90, 100]`. */\n checkpoints?: readonly number[];\n /** Optional R2 config reference override (defaults to the one passed to `createTracking`). */\n trackingConfig?: TrackingConfigReference;\n /** Optional baked video config fallback (e.g. `videos[\"qlt-case-study-v6\"]` from `aranova-services.ts`). */\n fallbackConfig?: TrackedVideoConfig | null;\n /** Optional custom tracking client override (defaults to the active provider's client). */\n client?: TrackingClient | null;\n /** Optional custom loader for Media Chrome and HLS player libraries. */\n loadPlayerLibs?: () => Promise<{ hls?: unknown } | void>;\n className?: string;\n style?: CSSProperties;\n}\n\nexport const DEFAULT_VIDEO_CHECKPOINTS: readonly number[] = [25, 50, 75, 90, 100];\n\nexport const streamManifestUrl = (host: string, uid: string): string =>\n `https://${host}/${uid}/manifest/video.m3u8`;\n\nexport const streamPosterUrl = (host: string, uid: string, time = 4): string =>\n `https://${host}/${uid}/thumbnails/thumbnail.jpg?time=${time}s`;\n\nconst TV_SKIN_STYLE_ATTR = \"data-aranova-tv-skin\";\n\nconst TV_SKIN_CSS = `\n.tv-skin {\n container-type: inline-size;\n position: relative;\n overflow: hidden;\n border-radius: 18px;\n border: 1px solid rgba(255, 255, 255, 0.12);\n background: #0a0e1a;\n box-shadow: 0 18px 48px rgba(0, 0, 0, 0.32);\n}\n.tv-skin-desktop {\n display: none;\n aspect-ratio: 16 / 9;\n width: 100%;\n}\n.tv-skin-mobile {\n display: block;\n margin-left: auto;\n margin-right: auto;\n aspect-ratio: 9 / 16;\n width: 100%;\n max-width: 420px;\n}\n@media (min-width: 768px) {\n .tv-skin-desktop {\n display: block;\n }\n .tv-skin-mobile {\n display: none;\n }\n}\n.tv-skin media-controller {\n display: block;\n width: 100%;\n height: 100%;\n background: #000;\n --media-primary-color: #ffffff;\n --media-text-color: rgba(255, 255, 255, 0.82);\n --media-icon-color: #ffffff;\n --media-secondary-color: transparent;\n --media-control-background: transparent;\n --media-control-hover-background: transparent;\n --media-control-padding: 0;\n --media-control-height: 40px;\n --media-tooltip-display: none;\n --media-font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);\n --media-font-size: 11.5px;\n --media-focus-box-shadow: 0 0 0 2px #74a0ff;\n --media-button-icon-width: 20px;\n --media-button-icon-height: 20px;\n --media-range-padding: 0;\n --media-range-bar-color: #ffffff;\n --media-range-track-background: rgba(255, 255, 255, 0.25);\n --media-range-track-border-radius: 3px;\n --media-time-range-buffered-color: transparent;\n --media-time-range-hover-display: none;\n --media-preview-time-background: transparent;\n --media-range-thumb-background: #ffffff;\n --media-range-thumb-width: 12px;\n --media-range-thumb-height: 12px;\n --media-range-thumb-opacity: 0;\n}\n.tv-skin media-controller::part(vertical-layer),\n.tv-skin media-controller::part(centered-layer) {\n background: transparent;\n}\n.tv-chrome {\n position: absolute;\n inset: auto 0 0 0;\n padding-top: 56px;\n background: linear-gradient(to top, rgba(0, 0, 0, 0.62), rgba(0, 0, 0, 0));\n transition: opacity 0.25s ease;\n}\n.tv-skin media-controller[userinactive]:not([mediapaused]) .tv-chrome {\n opacity: 0;\n pointer-events: none;\n}\n.tv-row {\n display: flex;\n align-items: center;\n gap: 2px;\n padding: 0 4px;\n}\n.tv-spacer {\n flex: 1 1 auto;\n}\n.tv-ctl,\n.tv-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n padding: 0;\n border: 0;\n border-radius: 0;\n background: transparent;\n box-shadow: none;\n color: rgba(255, 255, 255, 0.92);\n cursor: pointer;\n transition: color 0.15s;\n}\n.tv-ctl:hover,\n.tv-btn:hover {\n background: transparent;\n color: #ffffff;\n}\n.tv-ctl:focus-visible,\n.tv-btn:focus-visible,\n.tv-menu-item:focus-visible {\n outline: 2px solid #74a0ff;\n outline-offset: -4px;\n box-shadow: none;\n}\n.tv-time {\n width: auto;\n padding: 0 4px 0 6px;\n background: transparent;\n letter-spacing: 0.02em;\n white-space: nowrap;\n --media-control-background: transparent;\n}\n.tv-time-cur {\n display: none;\n}\n.tv-seek {\n display: block;\n width: 100%;\n height: 16px;\n margin: 0;\n padding: 0;\n background: transparent;\n box-shadow: none;\n border: 0;\n --media-range-track-height: 3px;\n --media-control-background: transparent;\n --media-control-hover-background: transparent;\n}\n.tv-seek:hover,\n.tv-seek:focus-visible {\n --media-range-track-height: 6px;\n --media-range-thumb-opacity: 1;\n}\n.tv-vol {\n display: flex;\n align-items: center;\n}\n.tv-volrange {\n width: 64px;\n height: 40px;\n margin-right: 4px;\n padding: 0;\n opacity: 0;\n pointer-events: none;\n transform: scaleX(0.75);\n transform-origin: right center;\n background: transparent;\n transition: transform 0.2s ease, opacity 0.2s ease;\n --media-control-background: transparent;\n --media-range-track-height: 3px;\n}\n.tv-vol:hover .tv-volrange,\n.tv-vol:focus-within .tv-volrange {\n opacity: 1;\n pointer-events: auto;\n transform: scaleX(1);\n}\n@media (hover: none) {\n .tv-volrange {\n display: none;\n }\n}\n@container (max-width: 479px) {\n .tv-vol,\n .tv-time-full {\n display: none;\n }\n .tv-time-cur {\n display: inline-flex;\n }\n}\n@container (max-width: 767px) {\n .tv-theater {\n display: none;\n }\n}\n.tv-menu-wrap {\n position: relative;\n display: flex;\n align-items: center;\n}\n.tv-menu {\n position: absolute;\n right: 0;\n bottom: calc(100% + 8px);\n z-index: 5;\n width: 136px;\n max-height: 232px;\n margin: 0;\n padding: 4px 0;\n overflow-y: auto;\n list-style: none;\n border: 1px solid rgba(255, 255, 255, 0.2);\n border-radius: 10px;\n background: linear-gradient(rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.12)), rgba(10, 14, 26, 0.55);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12), 0 12px 32px rgba(0, 0, 0, 0.35);\n}\n@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {\n .tv-menu {\n -webkit-backdrop-filter: blur(16px) saturate(140%);\n backdrop-filter: blur(16px) saturate(140%);\n }\n}\n.tv-menu-item {\n position: relative;\n display: flex;\n width: 100%;\n height: 32px;\n align-items: center;\n padding: 0 12px 0 24px;\n border: 0;\n background: transparent;\n color: rgba(255, 255, 255, 0.75);\n font-family: var(--font-sans, system-ui, -apple-system, sans-serif);\n font-size: 13px;\n font-weight: 500;\n text-align: left;\n cursor: pointer;\n}\n.tv-menu-item:hover,\n.tv-menu-item:focus-visible {\n background: rgba(255, 255, 255, 0.1);\n color: #ffffff;\n outline: none;\n}\n.tv-menu-item[aria-checked='true'] {\n color: #ffffff;\n font-weight: 600;\n}\n.tv-menu-item[aria-checked='true']::before {\n content: '';\n position: absolute;\n left: 12px;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: #ffffff;\n}\n.tv-menu-sub {\n margin-left: 4px;\n font-size: 11.5px;\n font-weight: 400;\n color: rgba(255, 255, 255, 0.55);\n}\n.tv-menu-note {\n height: 20px;\n padding: 0 12px 0 24px;\n color: rgba(255, 255, 255, 0.55);\n font-family: var(--font-sans, system-ui, -apple-system, sans-serif);\n font-size: 11.5px;\n line-height: 20px;\n}\n.tv-poster-overlay {\n position: absolute;\n inset: 0;\n}\n.tv-poster-img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n}\n.tv-play-trigger {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n border: 0;\n padding: 0;\n background: transparent;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: filter 0.2s ease;\n}\n.tv-play-trigger:hover {\n filter: brightness(1.1);\n}\n.tv-play-trigger:focus-visible {\n outline: 2px solid #74a0ff;\n outline-offset: -4px;\n}\n.tv-play-pill {\n display: flex;\n width: 64px;\n height: 64px;\n align-items: center;\n justify-content: center;\n border-radius: 9999px;\n border: 2px solid rgba(255, 255, 255, 0.72);\n background: rgba(255, 255, 255, 0.15);\n color: #ffffff;\n backdrop-filter: blur(2px);\n -webkit-backdrop-filter: blur(2px);\n transition: transform 0.3s ease;\n}\n.tv-play-trigger:hover .tv-play-pill {\n transform: scale(1.05);\n}\n`;\n\nfunction ensureTvSkinStyles(): void {\n if (typeof document === \"undefined\") return;\n if (document.querySelector(`style[${TV_SKIN_STYLE_ATTR}]`)) return;\n const style = document.createElement(\"style\");\n style.setAttribute(TV_SKIN_STYLE_ATTR, \"1\");\n style.textContent = TV_SKIN_CSS;\n document.head.append(style);\n}\n\nfunction IconPlay({\n slot,\n size = 20,\n filled = false,\n}: {\n slot?: string;\n size?: number;\n filled?: boolean;\n}) {\n return (\n <svg\n slot={slot}\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill={filled ? \"currentColor\" : \"none\"}\n stroke=\"currentColor\"\n strokeWidth={filled ? 0 : 1.75}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n style={filled ? { marginLeft: 3 } : undefined}\n >\n <polygon points=\"6 3 20 12 6 21 6 3\" />\n </svg>\n );\n}\n\nfunction IconPause({ slot }: { slot?: string }) {\n return (\n <svg\n slot={slot}\n width={20}\n height={20}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.75}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\" />\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\" />\n </svg>\n );\n}\n\nfunction IconVolumeHigh({ slot }: { slot?: string }) {\n return (\n <svg\n slot={slot}\n width={20}\n height={20}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.75}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\" />\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\" />\n <path d=\"M19.07 4.93a10 10 0 0 1 0 14.14\" />\n </svg>\n );\n}\n\nfunction IconVolumeLow({ slot }: { slot?: string }) {\n return (\n <svg\n slot={slot}\n width={20}\n height={20}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.75}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\" />\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\" />\n </svg>\n );\n}\n\nfunction IconVolumeOff({ slot }: { slot?: string }) {\n return (\n <svg\n slot={slot}\n width={20}\n height={20}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.75}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\" />\n <line x1=\"22\" y1=\"9\" x2=\"16\" y2=\"15\" />\n <line x1=\"16\" y1=\"9\" x2=\"22\" y2=\"15\" />\n </svg>\n );\n}\n\nfunction IconSettings() {\n return (\n <svg\n width={20}\n height={20}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.75}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n );\n}\n\nfunction IconTheaterExpand() {\n return (\n <svg\n width={20}\n height={20}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.75}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"15 3 21 3 21 9\" />\n <polyline points=\"9 21 3 21 3 15\" />\n <line x1=\"21\" y1=\"3\" x2=\"14\" y2=\"10\" />\n <line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\" />\n </svg>\n );\n}\n\nfunction IconTheaterShrink() {\n return (\n <svg\n width={20}\n height={20}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.75}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"4 14 10 14 10 20\" />\n <polyline points=\"20 10 14 10 14 4\" />\n <line x1=\"14\" y1=\"10\" x2=\"21\" y2=\"3\" />\n <line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\" />\n </svg>\n );\n}\n\nfunction IconFullscreenEnter({ slot }: { slot?: string }) {\n return (\n <svg\n slot={slot}\n width={20}\n height={20}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.75}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M8 3H5a2 2 0 0 0-2 2v3\" />\n <path d=\"M21 8V5a2 2 0 0 0-2-2h-3\" />\n <path d=\"M3 16v3a2 2 0 0 0 2 2h3\" />\n <path d=\"M16 21h3a2 2 0 0 0 2-2v-3\" />\n </svg>\n );\n}\n\nfunction IconFullscreenExit({ slot }: { slot?: string }) {\n return (\n <svg\n slot={slot}\n width={20}\n height={20}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.75}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M8 3v3a2 2 0 0 1-2 2H3\" />\n <path d=\"M21 8h-3a2 2 0 0 1-2-2V3\" />\n <path d=\"M3 16h3a2 2 0 0 1 2 2v3\" />\n <path d=\"M16 21v-3a2 2 0 0 1 2-2h3\" />\n </svg>\n );\n}\n\nconst EMPTY_GOALS: ConversionGoal[] = [];\n\nfunction serverVideoSnapshot(): TrackedVideoConfig | null {\n return null;\n}\n\nfunction serverGoalsSnapshot(): ConversionGoal[] {\n return EMPTY_GOALS;\n}\n\n/** Look up a configured Cloudflare Stream tracked video by key from R2 runtime or `fallbackConfig`. */\nexport function useTrackedVideoConfig(\n videoKey: string | undefined,\n trackingConfig?: TrackingConfigReference,\n fallbackConfig?: TrackedVideoConfig | null,\n): TrackedVideoConfig | null {\n registerCapability(\"video_tracking\");\n const effectiveRef = trackingConfig ?? getActiveTrackingEnv().trackingConfig;\n const subscribe = useCallback(\n (listener: () => void) =>\n effectiveRef ? getTrackingConfigRuntime(effectiveRef).subscribe(listener) : () => {},\n [effectiveRef],\n );\n const live = useSyncExternalStore(\n subscribe,\n () =>\n effectiveRef && videoKey ? getTrackingConfigRuntime(effectiveRef).getVideo(videoKey) : null,\n serverVideoSnapshot,\n );\n return live ?? fallbackConfig ?? null;\n}\n\nfunction useActiveVideoGoals(\n videoId: string,\n trackingConfig?: TrackingConfigReference,\n): Array<{ key: string; metric: \"percent\" | \"seconds\"; threshold: number }> {\n const effectiveRef = trackingConfig ?? getActiveTrackingEnv().trackingConfig;\n const subscribe = useCallback(\n (listener: () => void) =>\n effectiveRef ? getTrackingConfigRuntime(effectiveRef).subscribe(listener) : () => {},\n [effectiveRef],\n );\n const goals = useSyncExternalStore(\n subscribe,\n () => (effectiveRef ? getTrackingConfigRuntime(effectiveRef).listGoals() : EMPTY_GOALS),\n serverGoalsSnapshot,\n );\n return useMemo(() => {\n const out: Array<{ key: string; metric: \"percent\" | \"seconds\"; threshold: number }> = [];\n for (const goal of goals) {\n if (goal.kind !== \"event\" || !goal.trigger) continue;\n if (goal.trigger.event_type !== \"video_progress\") continue;\n if (goal.trigger.video_id && goal.trigger.video_id !== videoId) continue;\n const metric = goal.trigger.metric;\n const threshold = goal.trigger.threshold;\n if ((metric === \"percent\" || metric === \"seconds\") && typeof threshold === \"number\") {\n out.push({ key: goal.key, metric, threshold });\n }\n }\n return out;\n }, [goals, videoId]);\n}\n\ninterface HlsLevelLike {\n width?: number;\n height?: number;\n bitrate: number;\n}\n\ninterface HlsInstanceLike {\n levels: HlsLevelLike[];\n nextLevel: number;\n loadSource: (url: string) => void;\n attachMedia: (el: HTMLVideoElement) => void;\n destroy: () => void;\n on: (event: string, cb: (event: unknown, data: { level: number }) => void) => void;\n}\n\ninterface HlsConstructorLike {\n new (config?: { capLevelToPlayerSize?: boolean }): HlsInstanceLike;\n isSupported: () => boolean;\n Events: {\n MANIFEST_PARSED: string;\n LEVEL_SWITCHED: string;\n ERROR: string;\n };\n}\n\ninterface Rendition {\n height: number;\n index: number;\n}\n\nfunction renditionsOf(h: HlsInstanceLike): Rendition[] {\n const best = new Map<number, Rendition & { bitrate: number }>();\n h.levels.forEach((l, index) => {\n const px = Math.min(l.width || Infinity, l.height || Infinity);\n if (!Number.isFinite(px)) return;\n const cur = best.get(px);\n if (!cur || l.bitrate > cur.bitrate) best.set(px, { height: px, index, bitrate: l.bitrate });\n });\n return [...best.values()]\n .sort((a, b) => b.height - a.height)\n .map(({ height, index }) => ({ height, index }));\n}\n\nconst MEDIA_CHROME_CDN_URL = \"https://cdn.jsdelivr.net/npm/media-chrome@4/+esm\";\nconst HLS_JS_CDN_URL = \"https://cdn.jsdelivr.net/npm/hls.js@1/dist/hls.min.js\";\n\nlet defaultLibsPromise: Promise<{ hls?: HlsConstructorLike }> | null = null;\n\nfunction loadScriptOnce(src: string, attr: string, isModule = false): Promise<void> {\n if (typeof document === \"undefined\") return Promise.resolve();\n const existing = document.querySelector<HTMLScriptElement>(`script[${attr}]`);\n if (existing) return Promise.resolve();\n return new Promise((resolve) => {\n const s = document.createElement(\"script\");\n if (isModule) s.type = \"module\";\n s.async = true;\n s.src = src;\n s.setAttribute(attr, \"1\");\n s.addEventListener(\"load\", () => resolve(), { once: true });\n s.addEventListener(\"error\", () => resolve(), { once: true });\n document.head.append(s);\n // In JSDOM / unit tests where script network loads never fire, resolve on next tick.\n if (typeof navigator !== \"undefined\" && /jsdom/i.test(navigator.userAgent)) {\n setTimeout(resolve, 0);\n }\n });\n}\n\nfunction loadDefaultPlayerLibs(): Promise<{ hls?: HlsConstructorLike }> {\n if (typeof window === \"undefined\") return Promise.resolve({});\n if (defaultLibsPromise) return defaultLibsPromise;\n defaultLibsPromise = Promise.all([\n typeof customElements !== \"undefined\" && customElements.get(\"media-controller\")\n ? Promise.resolve()\n : loadScriptOnce(MEDIA_CHROME_CDN_URL, \"data-aranova-media-chrome\", true),\n (window as unknown as { Hls?: HlsConstructorLike }).Hls\n ? Promise.resolve()\n : loadScriptOnce(HLS_JS_CDN_URL, \"data-aranova-hls-js\", false),\n ]).then(() => ({\n hls: (window as unknown as { Hls?: HlsConstructorLike }).Hls,\n }));\n return defaultLibsPromise;\n}\n\ninterface CutProps extends TrackedVideoCallbacks {\n id: string;\n variant: VideoVariant;\n uid: string;\n host: string;\n title: string;\n posterTime: number;\n posterUrl?: string;\n renderPoster?: TrackedVideoProps[\"renderPoster\"];\n theater?: boolean;\n onTheaterChange?: (next: boolean) => void;\n autoExpandOnPlay: boolean;\n checkpoints: readonly number[];\n videoGoals: ReadonlyArray<{ key: string; metric: \"percent\" | \"seconds\"; threshold: number }>;\n client: TrackingClient | null;\n loadPlayerLibs?: () => Promise<{ hls?: unknown } | void>;\n className?: string;\n}\n\nfunction Cut({\n id,\n variant,\n uid,\n host,\n title,\n posterTime,\n posterUrl,\n renderPoster,\n theater,\n onTheaterChange,\n autoExpandOnPlay,\n checkpoints,\n videoGoals,\n client,\n loadPlayerLibs,\n className,\n ...cb\n}: CutProps) {\n const root = useRef<HTMLDivElement>(null);\n const video = useRef<HTMLVideoElement>(null);\n const hls = useRef<HlsInstanceLike | null>(null);\n const lastTime = useRef(0);\n const seekFrom = useRef(0);\n const cbRef = useRef(cb);\n cbRef.current = cb;\n\n const clientRef = useRef<TrackingClient | null>(client);\n clientRef.current = client;\n\n const goalsRef = useRef(videoGoals);\n goalsRef.current = videoGoals;\n\n // Watch-range & telemetry state (per Cut instance)\n const rangesRef = useRef<VideoWatchRange[]>([]);\n const activeSegmentStartRef = useRef<number | null>(null);\n const furthestSecondsRef = useRef(0);\n const maxPercentRef = useRef(0);\n const durationRef = useRef(0);\n const playCountRef = useRef(0);\n const seekCountRef = useRef(0);\n const firedCheckpointsRef = useRef<Set<number>>(new Set());\n const firedGoalsRef = useRef<Set<string>>(new Set());\n const dirtySinceLastEmitRef = useRef(false);\n\n const [started, setStarted] = useState(false);\n const [ready, setReady] = useState(false);\n const [playing, setPlaying] = useState(false);\n const [renditions, setRenditions] = useState<Rendition[]>([]);\n const [selected, setSelected] = useState<VideoQuality>(\"auto\");\n const [live, setLive] = useState<number | null>(null);\n const [menu, setMenu] = useState(false);\n const [pending, setPending] = useState<VideoQuality | null>(null);\n const pendingRef = useRef<VideoQuality | null>(null);\n const wrap = useRef<HTMLDivElement>(null);\n const gear = useRef<HTMLButtonElement>(null);\n const selectedRef = useRef<VideoQuality>(\"auto\");\n const pendingTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const liveRef = useRef<number | null>(null);\n const videoId = `${id}-${variant}`;\n\n const getCurrentRanges = useCallback((): VideoWatchRange[] => {\n const v = video.current;\n const cur = v ? v.currentTime : lastTime.current;\n const list = [...rangesRef.current];\n if (activeSegmentStartRef.current !== null && cur > activeSegmentStartRef.current) {\n list.push({\n start_s: activeSegmentStartRef.current,\n end_s: cur,\n });\n }\n return mergeVideoRanges(list);\n }, []);\n\n const commitActiveSegment = useCallback((endTime?: number) => {\n const end = endTime ?? video.current?.currentTime ?? lastTime.current;\n if (activeSegmentStartRef.current !== null && end > activeSegmentStartRef.current) {\n rangesRef.current = mergeVideoRanges([\n ...rangesRef.current,\n { start_s: activeSegmentStartRef.current, end_s: end },\n ]);\n }\n activeSegmentStartRef.current = null;\n }, []);\n\n const snap = useCallback((): TrackedVideoEvent => {\n const v = video.current;\n const currentTime = roundVideoSeconds(v?.currentTime ?? lastTime.current);\n const rawDuration = v?.duration;\n const duration =\n typeof rawDuration === \"number\" && Number.isFinite(rawDuration) && rawDuration > 0\n ? roundVideoSeconds(rawDuration)\n : durationRef.current;\n if (duration > 0) durationRef.current = duration;\n if (currentTime > furthestSecondsRef.current) {\n furthestSecondsRef.current = currentTime;\n }\n const ranges = getCurrentRanges();\n const watchedSeconds = sumVideoRangesSeconds(ranges, duration);\n const watchedPercent =\n duration > 0 ? Math.min(100, Math.round((watchedSeconds / duration) * 100)) : 0;\n const curPercent =\n duration > 0 ? Math.min(100, Math.round((furthestSecondsRef.current / duration) * 100)) : 0;\n if (curPercent > maxPercentRef.current) {\n maxPercentRef.current = curPercent;\n }\n return {\n id,\n videoId,\n variant,\n currentTime,\n duration,\n watchedSeconds,\n watchedPercent,\n maxPercent: maxPercentRef.current,\n ranges,\n };\n }, [getCurrentRanges, id, variant, videoId]);\n\n const emitProgress = useCallback(\n (trigger: VideoProgressTriggerReason, snapshotOverride?: TrackedVideoEvent) => {\n const activeClient = clientRef.current ?? getActiveTrackingEnv().rawClient;\n if (!activeClient || playCountRef.current <= 0) return;\n const s = snapshotOverride ?? snap();\n if (s.ranges.length === 0 && s.currentTime <= 0 && trigger !== \"ended\") return;\n dirtySinceLastEmitRef.current = false;\n const path = typeof window === \"undefined\" ? \"/\" : window.location.pathname;\n activeClient.trackEvent({\n eventType: \"video_progress\",\n metadata: {\n video_id: id,\n variant,\n trigger,\n current_s: s.currentTime,\n furthest_s: roundVideoSeconds(furthestSecondsRef.current),\n watched_s: s.watchedSeconds,\n duration_s: s.duration,\n max_percent: s.maxPercent,\n watched_percent: s.watchedPercent,\n play_count: playCountRef.current,\n seek_count: seekCountRef.current,\n ranges: s.ranges,\n page: { path },\n },\n });\n },\n [id, snap, variant],\n );\n\n const clearPending = () => {\n if (pendingTimer.current) clearTimeout(pendingTimer.current);\n pendingTimer.current = null;\n pendingRef.current = null;\n setPending(null);\n };\n\n useEffect(() => {\n ensureTvSkinStyles();\n registerCapability(\"video_tracking\");\n return () => {\n if (pendingTimer.current) clearTimeout(pendingTimer.current);\n hls.current?.destroy();\n hls.current = null;\n };\n }, []);\n\n // Register page_exit flush so watching visitors who close the tab before pausing still flush their ranges.\n useEffect(() => {\n if (!started || typeof window === \"undefined\") return;\n const flushExitProgress = (useBeacon: boolean) => {\n if (playCountRef.current <= 0 || !dirtySinceLastEmitRef.current) return;\n commitActiveSegment();\n emitProgress(\"page_exit\");\n if (useBeacon) {\n const activeClient = clientRef.current ?? getActiveTrackingEnv().rawClient;\n activeClient?.flushBeacon();\n }\n };\n\n const activeClient = clientRef.current ?? getActiveTrackingEnv().rawClient;\n const unsubscribeBeforeUnload = activeClient?.onBeforeUnload?.(() => {\n flushExitProgress(false);\n });\n\n const onVisibilityChange = () => {\n if (document.visibilityState === \"hidden\") {\n flushExitProgress(true);\n }\n };\n const onPageHide = () => {\n flushExitProgress(true);\n };\n\n document.addEventListener(\"visibilitychange\", onVisibilityChange);\n window.addEventListener(\"pagehide\", onPageHide);\n return () => {\n unsubscribeBeforeUnload?.();\n document.removeEventListener(\"visibilitychange\", onVisibilityChange);\n window.removeEventListener(\"pagehide\", onPageHide);\n };\n }, [started, commitActiveSegment, emitProgress]);\n\n // Fullscreen detection across desktop, Android, and iOS native player.\n useEffect(() => {\n if (!started) return;\n let was = false;\n const report = (fullscreen: boolean) => {\n if (fullscreen === was) return;\n was = fullscreen;\n cbRef.current.onFullscreenChange?.({ ...snap(), fullscreen });\n };\n const onDoc = () => {\n const el = root.current;\n const fe =\n document.fullscreenElement ??\n (document as unknown as { webkitFullscreenElement?: Element }).webkitFullscreenElement;\n report(Boolean(el && fe && el.contains(fe)));\n };\n const v = video.current;\n const begin = () => report(true);\n const end = () => report(false);\n document.addEventListener(\"fullscreenchange\", onDoc);\n document.addEventListener(\"webkitfullscreenchange\", onDoc);\n v?.addEventListener(\"webkitbeginfullscreen\", begin);\n v?.addEventListener(\"webkitendfullscreen\", end);\n return () => {\n document.removeEventListener(\"fullscreenchange\", onDoc);\n document.removeEventListener(\"webkitfullscreenchange\", onDoc);\n v?.removeEventListener(\"webkitbeginfullscreen\", begin);\n v?.removeEventListener(\"webkitendfullscreen\", end);\n };\n }, [started, snap]);\n\n // Quality menu outside-click close\n useEffect(() => {\n if (!menu) return;\n const outside = (e: PointerEvent) => {\n if (wrap.current && !wrap.current.contains(e.target as Node)) setMenu(false);\n };\n document.addEventListener(\"pointerdown\", outside);\n return () => document.removeEventListener(\"pointerdown\", outside);\n }, [menu]);\n\n const closeMenu = (refocus: boolean) => {\n setMenu(false);\n if (refocus) gear.current?.focus();\n };\n\n const onMenuKey = (e: ReactKeyboardEvent) => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n e.stopPropagation();\n closeMenu(true);\n } else if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\") {\n e.preventDefault();\n const items = [\n ...(wrap.current?.querySelectorAll<HTMLButtonElement>('[role=\"menuitemradio\"]') ?? []),\n ];\n const i = items.indexOf(document.activeElement as HTMLButtonElement);\n items[(i + (e.key === \"ArrowDown\" ? 1 : items.length - 1)) % items.length]?.focus();\n }\n };\n\n const emitQuality = (from: VideoQuality, to: VideoQuality, source: \"manual\" | \"auto\") =>\n cbRef.current.onQualityChange?.({ ...snap(), from, to, source });\n\n const start = async () => {\n const v = video.current;\n if (!v || started) return;\n setStarted(true);\n if (variant === \"16x9\" && autoExpandOnPlay) {\n onTheaterChange?.(true);\n }\n const src = streamManifestUrl(host, uid);\n try {\n const loaded = await (loadPlayerLibs ? loadPlayerLibs() : loadDefaultPlayerLibs());\n setReady(true);\n const rawHls =\n (loaded && typeof loaded === \"object\" && \"hls\" in loaded ? loaded.hls : null) ??\n (typeof window !== \"undefined\"\n ? (window as unknown as { Hls?: HlsConstructorLike }).Hls\n : undefined);\n const HlsLib =\n rawHls && typeof rawHls === \"object\" && \"default\" in rawHls\n ? (rawHls as { default?: HlsConstructorLike }).default\n : (rawHls as HlsConstructorLike | undefined);\n if (!HlsLib || !HlsLib.isSupported()) {\n v.src = src;\n } else {\n const h = new HlsLib({ capLevelToPlayerSize: true });\n hls.current = h;\n h.on(HlsLib.Events.MANIFEST_PARSED, () => setRenditions(renditionsOf(h)));\n h.on(HlsLib.Events.LEVEL_SWITCHED, (_e, data) => {\n const lv = h.levels[data.level];\n const height = lv ? Math.min(lv.width || Infinity, lv.height || Infinity) : null;\n const from = liveRef.current;\n liveRef.current = height;\n setLive(height);\n const want = pendingRef.current;\n if (want !== null && want === height) clearPending();\n if (selectedRef.current === \"auto\" && height && from && from !== height) {\n emitQuality(from, height, \"auto\");\n }\n });\n h.on(HlsLib.Events.ERROR, () => clearPending());\n h.loadSource(src);\n h.attachMedia(v);\n }\n } catch {\n setReady(true);\n v.src = src;\n }\n try {\n await v.play();\n } catch {\n // Browser blocked autoplay; controls are now ready for a direct click.\n }\n };\n\n const pick = (q: VideoQuality) => {\n const h = hls.current;\n const from = selectedRef.current;\n setMenu(false);\n if (!h || q === from) return;\n if (q === \"auto\") h.nextLevel = -1;\n else {\n const r = renditions.find((x) => x.height === q);\n if (r) h.nextLevel = r.index;\n }\n selectedRef.current = q;\n setSelected(q);\n clearPending();\n if (typeof q === \"number\" && q !== liveRef.current) {\n pendingRef.current = q;\n setPending(q);\n pendingTimer.current = setTimeout(clearPending, 4000);\n }\n emitQuality(from, q, \"manual\");\n };\n\n const qualityLabel = selected === \"auto\" ? (live ? `Auto (${live}p)` : \"Auto\") : `${selected}p`;\n const resolvedPosterUrl = posterUrl ?? streamPosterUrl(host, uid, posterTime);\n const posterWidth = variant === \"16x9\" ? 1280 : 720;\n const posterHeight = variant === \"16x9\" ? 720 : 1280;\n\n return (\n <div\n ref={root}\n className={`tv-skin ${variant === \"16x9\" ? \"tv-skin-desktop\" : \"tv-skin-mobile\"} ${className ?? \"\"}`}\n data-video-id={videoId}\n >\n {/* @ts-expect-error media-controller is a custom element registered by media-chrome */}\n <media-controller class=\"absolute inset-0 block\" autohide={menu ? \"-1\" : \"2\"}>\n <video\n ref={video}\n slot=\"media\"\n style={{ width: \"100%\", height: \"100%\", backgroundColor: \"#000\", objectFit: \"contain\" }}\n preload=\"none\"\n playsInline\n title={title}\n aria-label={title}\n data-video-id={videoId}\n onPlay={() => {\n const v = video.current;\n const cur = roundVideoSeconds(v?.currentTime ?? 0);\n activeSegmentStartRef.current = cur;\n playCountRef.current += 1;\n dirtySinceLastEmitRef.current = true;\n const s = snap();\n const activeClient = clientRef.current ?? getActiveTrackingEnv().rawClient;\n if (activeClient) {\n const path = typeof window === \"undefined\" ? \"/\" : window.location.pathname;\n activeClient.trackEvent({\n eventType: \"video_play\",\n metadata: {\n video_id: id,\n variant,\n current_s: s.currentTime,\n duration_s: s.duration,\n play_sequence: playCountRef.current,\n page: { path },\n },\n });\n }\n cbRef.current.onPlay?.(s);\n }}\n onPlaying={() => setPlaying(true)}\n onPause={() => {\n const v = video.current;\n // Ignore the implicit pause fired right before `ended`.\n if (v?.ended) return;\n commitActiveSegment(v?.currentTime);\n const s = snap();\n emitProgress(\"pause\", s);\n cbRef.current.onPause?.(s);\n }}\n onEnded={() => {\n const v = video.current;\n const dur =\n typeof v?.duration === \"number\" && Number.isFinite(v.duration)\n ? v.duration\n : durationRef.current;\n commitActiveSegment(dur > 0 ? dur : v?.currentTime);\n maxPercentRef.current = 100;\n const s = snap();\n if (!firedCheckpointsRef.current.has(100) && checkpoints.includes(100)) {\n firedCheckpointsRef.current.add(100);\n cbRef.current.onCheckpoint?.({ ...s, checkpoint: 100 });\n }\n emitProgress(\"ended\", s);\n cbRef.current.onEnded?.(s);\n }}\n onWaiting={() => cbRef.current.onWaiting?.(snap())}\n onTimeUpdate={() => {\n const v = video.current;\n if (v && !v.seeking) {\n if (activeSegmentStartRef.current === null && !v.paused) {\n activeSegmentStartRef.current = lastTime.current;\n }\n lastTime.current = v.currentTime;\n dirtySinceLastEmitRef.current = true;\n }\n const s = snap();\n let hitCheckpoint = false;\n let hitGoal = false;\n\n for (const goal of goalsRef.current) {\n if (firedGoalsRef.current.has(goal.key)) continue;\n const met =\n goal.metric === \"seconds\"\n ? s.watchedSeconds >= goal.threshold\n : s.watchedPercent >= goal.threshold;\n if (met) {\n firedGoalsRef.current.add(goal.key);\n hitGoal = true;\n }\n }\n\n const newlyFiredCheckpoints: number[] = [];\n for (const cp of checkpoints) {\n if (firedCheckpointsRef.current.has(cp)) continue;\n if (s.watchedPercent >= cp || s.maxPercent >= cp) {\n firedCheckpointsRef.current.add(cp);\n newlyFiredCheckpoints.push(cp);\n hitCheckpoint = true;\n }\n }\n\n if (hitCheckpoint || hitGoal) {\n commitActiveSegment(v?.currentTime);\n if (v && !v.paused) activeSegmentStartRef.current = v.currentTime;\n const emittedSnap = snap();\n emitProgress(hitCheckpoint ? \"checkpoint\" : \"goal\", emittedSnap);\n for (const cp of newlyFiredCheckpoints) {\n cbRef.current.onCheckpoint?.({ ...emittedSnap, checkpoint: cp });\n }\n }\n\n cbRef.current.onTimeUpdate?.(s);\n }}\n onSeeking={() => {\n seekFrom.current = lastTime.current;\n commitActiveSegment(seekFrom.current);\n }}\n onSeeked={() => {\n const v = video.current;\n const to = roundVideoSeconds(v?.currentTime ?? 0);\n const from = roundVideoSeconds(seekFrom.current);\n lastTime.current = to;\n if (v && !v.paused) {\n activeSegmentStartRef.current = to;\n }\n if (Math.abs(to - from) >= 1) {\n seekCountRef.current += 1;\n const s = snap();\n emitProgress(\"seek\", s);\n cbRef.current.onSeek?.({ ...s, from, to });\n }\n }}\n >\n <track kind=\"captions\" />\n </video>\n {ready && (\n <>\n {/* @ts-expect-error media-loading-indicator is registered by media-chrome */}\n <media-loading-indicator slot=\"centered-chrome\" />\n <div className=\"tv-chrome\">\n <div className=\"tv-row\">\n {/* @ts-expect-error media-play-button is registered by media-chrome */}\n <media-play-button class=\"tv-ctl\">\n <IconPlay slot=\"play\" />\n <IconPause slot=\"pause\" />\n {/* @ts-expect-error media-play-button closing tag */}\n </media-play-button>\n {/* @ts-expect-error media-time-display is registered by media-chrome */}\n <media-time-display class=\"tv-time tv-time-full\" showduration />\n {/* @ts-expect-error media-time-display is registered by media-chrome */}\n <media-time-display class=\"tv-time tv-time-cur\" />\n <span className=\"tv-spacer\" />\n <div className=\"tv-vol\">\n {/* @ts-expect-error media-mute-button is registered by media-chrome */}\n <media-mute-button class=\"tv-ctl\">\n <IconVolumeHigh slot=\"high\" />\n <IconVolumeLow slot=\"medium\" />\n <IconVolumeLow slot=\"low\" />\n <IconVolumeOff slot=\"off\" />\n {/* @ts-expect-error media-mute-button closing tag */}\n </media-mute-button>\n {/* @ts-expect-error media-volume-range is registered by media-chrome */}\n <media-volume-range class=\"tv-volrange\" />\n </div>\n {renditions.length > 1 && (\n <div className=\"tv-menu-wrap\" ref={wrap}>\n <button\n ref={gear}\n type=\"button\"\n className=\"tv-btn\"\n aria-haspopup=\"menu\"\n aria-expanded={menu}\n aria-label={`Settings, quality ${qualityLabel}`}\n title=\"Settings\"\n onClick={() => setMenu((m) => !m)}\n onKeyDown={onMenuKey}\n >\n <IconSettings />\n </button>\n <span role=\"status\" className=\"sr-only\">\n {pending !== null ? `Switching to ${pending}p` : \"\"}\n </span>\n {menu && (\n <ul\n role=\"menu\"\n aria-label=\"Quality\"\n className=\"tv-menu\"\n onKeyDown={onMenuKey}\n >\n {([\"auto\", ...renditions.map((r) => r.height)] as VideoQuality[]).map(\n (q) => (\n <li key={String(q)} role=\"none\">\n <button\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={selected === q}\n className=\"tv-menu-item\"\n onClick={() => pick(q)}\n >\n {q === \"auto\" ? (\n <>\n Auto\n {live ? <span className=\"tv-menu-sub\"> {live}p</span> : null}\n </>\n ) : (\n `${q}p`\n )}\n </button>\n </li>\n ),\n )}\n <li aria-hidden=\"true\" className=\"tv-menu-note\">\n {pending !== null ? `Switching to ${pending}p…` : \"\"}\n </li>\n </ul>\n )}\n </div>\n )}\n {variant === \"16x9\" && onTheaterChange && (\n <button\n type=\"button\"\n className=\"tv-btn tv-theater\"\n aria-pressed={theater}\n aria-label={theater ? \"Shrink the player\" : \"Expand the player\"}\n title={theater ? \"Shrink\" : \"Expand\"}\n onClick={() => onTheaterChange(!theater)}\n >\n {theater ? <IconTheaterShrink /> : <IconTheaterExpand />}\n </button>\n )}\n {/* @ts-expect-error media-fullscreen-button is registered by media-chrome */}\n <media-fullscreen-button class=\"tv-ctl\">\n <IconFullscreenEnter slot=\"enter\" />\n <IconFullscreenExit slot=\"exit\" />\n {/* @ts-expect-error media-fullscreen-button closing tag */}\n </media-fullscreen-button>\n </div>\n {/* @ts-expect-error media-time-range is registered by media-chrome */}\n <media-time-range class=\"tv-seek\" />\n </div>\n </>\n )}\n {/* @ts-expect-error media-controller closing tag */}\n </media-controller>\n {!playing && (\n <div className=\"tv-poster-overlay\">\n {renderPoster ? (\n renderPoster({\n src: resolvedPosterUrl,\n variant,\n title,\n width: posterWidth,\n height: posterHeight,\n })\n ) : (\n <img\n src={resolvedPosterUrl}\n alt=\"\"\n width={posterWidth}\n height={posterHeight}\n loading=\"lazy\"\n decoding=\"async\"\n className=\"tv-poster-img\"\n />\n )}\n {!started && (\n <button\n type=\"button\"\n onClick={start}\n aria-label={`Play video: ${title}`}\n className=\"tv-play-trigger\"\n >\n <span className=\"tv-play-pill\">\n <IconPlay size={26} filled />\n </span>\n </button>\n )}\n </div>\n )}\n </div>\n );\n}\n\n/** Cloudflare Stream HLS player with watch-range telemetry, checkpoint flushing, and Theater mode. */\nexport function TrackedVideo(props: TrackedVideoProps): ReactNode {\n const {\n id,\n video,\n host,\n sources,\n title,\n posterTime,\n posterUrl,\n renderPoster,\n theater: controlledTheater,\n defaultTheater = false,\n onTheaterChange,\n expandMode = \"inline\",\n autoExpandOnPlay = true,\n expandedMaxWidth = 1100,\n checkpoints = DEFAULT_VIDEO_CHECKPOINTS,\n trackingConfig,\n fallbackConfig,\n client = null,\n loadPlayerLibs,\n className = \"\",\n style,\n ...callbacks\n } = props;\n\n const videoKey = id ?? video ?? fallbackConfig?.key ?? \"\";\n const r2Video = useTrackedVideoConfig(videoKey || undefined, trackingConfig, fallbackConfig);\n const videoGoals = useActiveVideoGoals(videoKey, trackingConfig);\n\n const [internalTheater, setInternalTheater] = useState(defaultTheater);\n const isTheaterControlled = controlledTheater !== undefined;\n const activeTheater = isTheaterControlled ? controlledTheater : internalTheater;\n\n const handleTheaterChange = useCallback(\n (next: boolean) => {\n if (!isTheaterControlled) {\n setInternalTheater(next);\n }\n onTheaterChange?.(next);\n },\n [isTheaterControlled, onTheaterChange],\n );\n\n const resolvedHost = host ?? r2Video?.host ?? \"\";\n const desktopUid = sources?.desktop ?? r2Video?.sources.desktop ?? \"\";\n const mobileUid = sources?.mobile ?? r2Video?.sources.mobile ?? desktopUid;\n const resolvedTitle = title ?? r2Video?.title ?? videoKey;\n const resolvedPosterTime = posterTime ?? r2Video?.poster_time ?? r2Video?.poster_time_s ?? 4;\n\n if (!videoKey || !resolvedHost || !desktopUid) {\n return null;\n }\n\n const showTheaterControl =\n expandMode !== \"none\" && (expandMode === \"theater\" || Boolean(onTheaterChange));\n\n const wrapperStyle: CSSProperties | undefined =\n !isTheaterControlled && expandMode === \"theater\" && activeTheater\n ? {\n maxWidth:\n typeof expandedMaxWidth === \"number\" ? `${expandedMaxWidth}px` : expandedMaxWidth,\n marginLeft: \"auto\",\n marginRight: \"auto\",\n width: \"100%\",\n transition: \"max-width 360ms cubic-bezier(0.2, 0.7, 0.2, 1)\",\n ...style,\n }\n : style;\n\n return (\n <div className={className} style={wrapperStyle} data-tracked-video={videoKey}>\n <Cut\n {...callbacks}\n id={videoKey}\n variant=\"16x9\"\n uid={desktopUid}\n host={resolvedHost}\n title={resolvedTitle}\n posterTime={resolvedPosterTime}\n posterUrl={posterUrl}\n renderPoster={renderPoster}\n theater={activeTheater}\n onTheaterChange={showTheaterControl ? handleTheaterChange : undefined}\n autoExpandOnPlay={autoExpandOnPlay}\n checkpoints={checkpoints}\n videoGoals={videoGoals}\n client={client}\n loadPlayerLibs={loadPlayerLibs}\n />\n <Cut\n {...callbacks}\n id={videoKey}\n variant=\"9x16\"\n uid={mobileUid || desktopUid}\n host={resolvedHost}\n title={resolvedTitle}\n posterTime={resolvedPosterTime}\n posterUrl={posterUrl}\n renderPoster={renderPoster}\n theater={false}\n onTheaterChange={undefined}\n autoExpandOnPlay={false}\n checkpoints={checkpoints}\n videoGoals={videoGoals}\n client={client}\n loadPlayerLibs={loadPlayerLibs}\n />\n </div>\n );\n}\n\nconst STAGE_DURATION_MS = 360;\nconst STAGE_EASE = \"cubic-bezier(0.2, 0.7, 0.2, 1)\";\n\nexport interface TrackedVideoStageProps {\n /** Props forwarded to the `<TrackedVideo>` inside the stage. */\n video: TrackedVideoProps;\n /** Accompanying copy/content rendered beside the player at rest and below it in theater mode. */\n children: ReactNode;\n /** Maximum width of the expanded theater player on desktop. Default: `1100`. */\n expandedMaxWidth?: number | string;\n /** Maximum width of the accompanying copy when stacked below the theater player. Default: `760`. */\n collapsedTextMaxWidth?: number | string;\n className?: string;\n}\n\n/** Two-column desktop stage that FLIP-animates `<TrackedVideo>` into full-width theater layout. */\nexport function TrackedVideoStage({\n video,\n children,\n expandedMaxWidth = 1100,\n collapsedTextMaxWidth = 760,\n className = \"\",\n}: TrackedVideoStageProps): ReactNode {\n const [theater, setTheater] = useState(video.defaultTheater ?? false);\n const videoBox = useRef<HTMLDivElement>(null);\n const textBox = useRef<HTMLDivElement>(null);\n const first = useRef<{ v: DOMRect; t: DOMRect } | null>(null);\n\n const activeTheater = video.theater !== undefined ? video.theater : theater;\n\n const change = useCallback(\n (next: boolean) => {\n if (next === activeTheater) return;\n if (\n next &&\n typeof window !== \"undefined\" &&\n !window.matchMedia(\"(min-width: 768px)\").matches\n ) {\n return;\n }\n if (videoBox.current && textBox.current) {\n first.current = {\n v: videoBox.current.getBoundingClientRect(),\n t: textBox.current.getBoundingClientRect(),\n };\n }\n if (video.theater === undefined) {\n setTheater(next);\n }\n video.onTheaterChange?.(next);\n },\n [activeTheater, video],\n );\n\n useLayoutEffect(() => {\n if (typeof window === \"undefined\") return;\n const f = first.current;\n const vb = videoBox.current;\n const tb = textBox.current;\n first.current = null;\n if (!f || !vb || !tb) return;\n const reduce = window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n const lv = vb.getBoundingClientRect();\n const lt = tb.getBoundingClientRect();\n if (!reduce && typeof vb.animate === \"function\" && lv.width > 0) {\n const s = f.v.width / lv.width;\n vb.animate(\n [\n {\n transformOrigin: \"top left\",\n transform: `translate(${f.v.left - lv.left}px, ${f.v.top - lv.top}px) scale(${s})`,\n },\n { transformOrigin: \"top left\", transform: \"none\" },\n ],\n { duration: STAGE_DURATION_MS, easing: STAGE_EASE },\n );\n tb.animate(\n [\n {\n transform: `translate(${f.t.left - lt.left}px, ${f.t.top - lt.top}px)`,\n opacity: 0.4,\n },\n { transform: \"none\", opacity: 1 },\n ],\n { duration: STAGE_DURATION_MS, easing: STAGE_EASE },\n );\n }\n if (lv.top < 80 || lv.bottom > window.innerHeight - 24) {\n const top = window.scrollY + lv.top - Math.max(96, (window.innerHeight - lv.height) / 2);\n window.scrollTo({ top, behavior: reduce ? \"auto\" : \"smooth\" });\n }\n }, [activeTheater]);\n\n const maxVideoWidthCss =\n typeof expandedMaxWidth === \"number\" ? `${expandedMaxWidth}px` : expandedMaxWidth;\n const maxTextWidthCss =\n typeof collapsedTextMaxWidth === \"number\"\n ? `${collapsedTextMaxWidth}px`\n : collapsedTextMaxWidth;\n\n return (\n <div\n className={\n activeTheater\n ? `grid gap-10 md:gap-12 ${className}`\n : `grid items-start gap-10 lg:grid-cols-[1fr_1fr] lg:gap-16 ${className}`\n }\n >\n <div\n ref={textBox}\n className={activeTheater ? \"md:order-2\" : \"\"}\n style={activeTheater ? { maxWidth: maxTextWidthCss } : undefined}\n >\n {children}\n </div>\n <div\n ref={videoBox}\n className={activeTheater ? \"md:order-1 md:mx-auto md:w-full\" : \"\"}\n style={activeTheater ? { maxWidth: maxVideoWidthCss, width: \"100%\" } : undefined}\n >\n <TrackedVideo\n {...video}\n theater={activeTheater}\n onTheaterChange={change}\n className={video.className ?? \"w-full\"}\n />\n </div>\n </div>\n );\n}\n"],"mappings":"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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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","names":["import_react","getScriptMarker","record","fallbackKey","listeners","CACHE_PREFIX","cacheKey","readCache","writeCache","z","MAX_BUFFERED_EVENTS","import_mini","z","z","z","z","z","z","z","z","z","z","z","z","z","z","valid","import_mini","isRecord","currentPath","check","currentPath","STORAGE_KEY","check","currentPath","currentPath","currentPath","z","import_mini","currentPath","DEFAULT_CHOICE","import_react","trackingConfigKey","import_react","import_react","import_react","import_jsx_runtime","PhoneField","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","EMPTY_GOALS","activeClient"]}
|