@aranova/tracking-react 0.14.2 → 0.16.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/README.md CHANGED
@@ -119,6 +119,49 @@ tracking.trackEvent("phone_click", {
119
119
  });
120
120
  ```
121
121
 
122
+ ### CTA clicks — manual or auto-capture
123
+
124
+ Fire `cta_click` yourself for full control over the name:
125
+
126
+ ```tsx
127
+ tracking.trackEvent("cta_click", {
128
+ cta_name: "Book appointment",
129
+ section: "hero",
130
+ destination_url: "/booking",
131
+ page: { path: window.location.pathname },
132
+ });
133
+ ```
134
+
135
+ Or opt into **auto-capture** and skip per-button code. Add `autoCapture` to the
136
+ `cta_click` registration — this attaches a single delegated click listener:
137
+
138
+ ```tsx
139
+ manual: {
140
+ cta_click: { autoCapture: {} }, // default selector: [data-aranova-cta]
141
+ // or target existing classes: { autoCapture: { selector: "a.cta, .btn-primary" } }
142
+ },
143
+ ```
144
+
145
+ Then tag your CTAs in markup — no imports, no handlers:
146
+
147
+ ```tsx
148
+ <a href="/booking" data-aranova-cta="Book now" data-aranova-section="hero">Book now</a>
149
+ <button data-aranova-cta="Get a quote">Get a quote</button>
150
+ ```
151
+
152
+ `cta_name` resolves to the `data-aranova-cta` value, else the element's trimmed text
153
+ (capped 120 chars), else `tag#id`. `section` comes from an optional `data-aranova-section`;
154
+ `href`/`destination_url` and a short `element` descriptor are captured automatically. Point
155
+ the selector at real CTAs (an explicit attribute is the safe default) — every matching click
156
+ is counted, including ones that later `stopPropagation`.
157
+
158
+ ### Automatic dwell + exit (`page_exit`)
159
+
160
+ `page_exit` is captured automatically — no registration. On SPA navigation, tab hide, and
161
+ page unload it records the **active** (visible) time spent on the page plus the max scroll
162
+ depth reached, delivered via a keepalive beacon so the final page still counts. This powers
163
+ per-page dwell and drop-off in the dashboard Analytics tab; there is nothing to configure.
164
+
122
165
  ## Phone Fields
123
166
 
124
167
  Bundled `libphonenumber-js`: parse/format utils + a React input. Display is configurable; the
@@ -255,69 +298,43 @@ Each call fires **only** the WEBPAGE conversion for that goal (no `/sales` write
255
298
 
256
299
  > **Before any manual goal exists,** `gen` emits an empty `ARANOVA_CONVERSIONS`, so `AranovaConversion` resolves to `never` — binding it (`createSalesClient<AranovaService, AranovaConversion>`) then makes **every** `trackConversion` call a compile error. Until you've mapped at least one manual goal and re-run `gen`, bind only the service generic (`createSalesClient<AranovaService>(…)`); the conversion key defaults back to `string`.
257
300
 
258
- ## Consent UI
301
+ ## Consent (v2 — opt-out model)
259
302
 
260
- The bundled `<ConsentBanner />` renders a non-blocking bottom-docked banner while consent is `pending`, persists the visitor's choice to `localStorage`, and propagates it to Google Consent Mode v2 when gtag is loaded. **Inline-styled** — no Tailwind or CSS imports required at the consumer.
303
+ Tracking is **on by default**: gtag, the Meta Pixel, and on-site conversion firing all run from first paint unless the visitor has stored an explicit, unexpired decline. A decline is honored for **90 days** (then the visitor reverts to default-granted); an explicit grant never expires. There is no `pending` state and the packages ship **no consent UI** — each site provides its own footer "cookie preferences" control and discloses tracking in its privacy policy (that notice is what makes the opt-out model defensible).
261
304
 
262
- ```tsx
263
- import { ConsentBanner } from "@aranova/tracking-react";
305
+ ### Footer control — `useCookiePreferences()`
264
306
 
265
- // Drop-in, defaults work everywhere
266
- <ConsentBanner />;
267
- ```
307
+ ```tsx
308
+ import { useCookiePreferences } from "@aranova/tracking-react";
268
309
 
269
- All props are optional:
310
+ function CookiePreferences() {
311
+ const { isDenied, isDefault, optOut, optIn } = useCookiePreferences();
270
312
 
271
- ```tsx
272
- <ConsentBanner
273
- title="Cookies"
274
- message="We use cookies to track ad performance."
275
- acceptLabel="Sure"
276
- declineLabel="No thanks"
277
- policyHref="/privacy"
278
- policyLabel="Privacy policy" // default: "Learn more"
279
- onAccept={() => track("consent_accepted")}
280
- onDecline={() => track("consent_declined")}
281
- position="bottom" // or "top"
282
- theme="light" // "light" | "dark" | "auto"
283
- className="my-extra-classes"
284
- style={{ background: "#fafafa" }} // wins over the theme defaults
285
- />
313
+ return isDenied ? (
314
+ <button onClick={optIn}>Enable ad measurement</button>
315
+ ) : (
316
+ <button onClick={optOut}>Opt out of ad measurement</button>
317
+ );
318
+ }
286
319
  ```
287
320
 
288
- ### Fully custom UI `useConsent()`
321
+ The hook returns the effective `state` (`"granted" | "denied"`), its `source` (`"default"` = no explicit choice, `"explicit"`), boolean helpers (`isDefault` / `isGranted` / `isDenied`), the choice's `updatedAt` / `expiresAt`, and the `optOut()` / `optIn()` / `reset()` actions. It stays in sync with other components in the same tab (via `onConsentChange`) and other tabs (via `storage` events). `optOut` accepts a custom TTL via the hook's `{ declineTtlDays }` option.
289
322
 
290
- For a bespoke banner, skip the component and drive your own UI with the headless hook:
323
+ For non-component contexts the same primitives are exported as plain functions: `optOut()`, `optIn()`, `resetConsent()`, `getConsentChoice()`, and `onConsentChange()`.
291
324
 
292
- ```tsx
293
- import { useConsent } from "@aranova/tracking-react";
294
-
295
- function CookieBar() {
296
- const { state, accept, decline, reset, isPending } = useConsent();
325
+ ### Deprecated opt-in flow
297
326
 
298
- if (!isPending) {
299
- // Footer link: re-open the banner if they change their mind.
300
- return <button onClick={reset}>Cookie preferences</button>;
301
- }
302
- return (
303
- <MyBespokeBanner>
304
- <button onClick={decline}>No thanks</button>
305
- <button onClick={accept}>Sure</button>
306
- </MyBespokeBanner>
307
- );
308
- }
309
- ```
327
+ `<ConsentBanner />`, `useConsent()`, and `useConsentState()` are `@deprecated` and scheduled for removal. The banner is **permanently inert** — it rendered only while consent was `pending`, which no longer occurs — so leaving it mounted is harmless but pointless. `useConsent()` still works as a shim (`isPending` is always `false`; `accept`/`decline` map to `optIn`/`optOut`).
310
328
 
311
- The hook handles localStorage persistence, gtag sync, and cross-tab propagation same machinery the default banner uses. `resetConsent()` is also exported as a standalone for non-component contexts.
329
+ **Migrating from the banner flow:** delete `<ConsentBanner />`, add a footer control built on `useCookiePreferences`, and mention the tracking + opt-out in your privacy policy. Existing visitors' stored grants stay granted; stored declines stay denied for 90 days from their first visit after the upgrade.
312
330
 
313
331
  ## Exports
314
332
 
315
333
  - `createTracking()`
316
334
  - `TrackingProvider` and scoped `useTracking`
317
335
  - `GoogleAdsTracking`
318
- - `ConsentBanner` + `ConsentBannerProps`
319
- - Consent hooks: `useConsent()` + `UseConsentResult` (headless); `useConsentState()` (read-only alias)
320
- - Standalone consent helpers: `getConsentState()`, `setConsentState()`, `resetConsent()`
336
+ - Consent (v2): `useCookiePreferences()` + `UseCookiePreferencesResult`; standalone `optIn()`, `optOut()`, `resetConsent()`, `getConsentChoice()`, `onConsentChange()`, `getConsentState()`, `setConsentState()`
337
+ - Deprecated consent shims: `ConsentBanner` + `ConsentBannerProps`, `useConsent()` + `UseConsentResult`, `useConsentState()`
321
338
  - Attribution hooks: `useTrackingParams()`, `useGclid()`
322
339
  - `createSalesClient()` (isomorphic — public key writes; secret key reads/CRUD, `summary`, `customers.*`, `business.config`) + money/date helpers (`toMinor`/`fromMinor`/`formatMoney`/`formatDateInTz`). Also available React-free at **`@aranova/tracking-react/sales`** (with all sale/customer/config types) — the recommended import for server/serverless code.
323
340
  - Phone: `parsePhone`/`toE164`/`formatPhone`/`formatPhoneAsTyped`/`phoneField`, `usePhoneField`, `PhoneField` (utils also at `/phone`)
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, CSSProperties, InputHTMLAttributes, ChangeEvent, FocusEvent } from 'react';
3
- import { C as ConsentState, T as TrackingInstallSurface, a as TrackingEnvironment, b as TrackingClientContext, c as TrackingParams, d as TrackingEventCreatePayload, e as TrackingSessionUpsertPayload, F as FormSubmitConfig, f as FormSubmitMetadata, G as GtagEnvironmentMap, M as MetaPixelEnvironmentMap, P as PhoneConfig, g as ParsedPhone, h as PhoneDisplayFormat } from './phone-utils-Dyk0F14_.mjs';
4
- export { D as DEFAULT_PHONE_COUNTRY, J as JsonValue, i as TrackedField, j as TrackingInitConfig, k as formSubmitConfigSchema, l as formSubmitMetadataSchema, m as formatPhone, n as formatPhoneAsTyped, o as jsonValueSchema, p as parsePhone, q as phoneField, t as toE164 } from './phone-utils-Dyk0F14_.mjs';
3
+ import { T as TrackingParams, a as TrackingInstallSurface, b as TrackingEnvironment, c as TrackingClientContext, d as TrackingEventCreatePayload, e as TrackingSessionUpsertPayload, F as FormSubmitConfig, f as FormSubmitMetadata, G as GtagEnvironmentMap, M as MetaPixelEnvironmentMap, C as ConsentState, g as ConsentChoiceState, h as ConsentSource, P as PhoneConfig, i as ParsedPhone, j as PhoneDisplayFormat } from './phone-utils-DlAQK-gU.mjs';
4
+ export { k as ConsentChoice, D as DEFAULT_DECLINE_TTL_DAYS, l as DEFAULT_PHONE_COUNTRY, J as JsonValue, S as SetConsentOptions, m as TrackedField, n as TrackingInitConfig, o as formSubmitConfigSchema, p as formSubmitMetadataSchema, q as formatPhone, r as formatPhoneAsTyped, s as getConsentChoice, t as getConsentState, u as jsonValueSchema, v as onConsentChange, w as optIn, x as optOut, y as parsePhone, z as phoneField, A as resetConsent, B as setConsentState, E as toE164 } from './phone-utils-DlAQK-gU.mjs';
5
5
  import { C as ConversionConfig } from './sales-Bq7H-Vym.mjs';
6
6
  export { A as AranovaApiError, B as BusinessConfig, a as BusinessConfigFeatures, b as BusinessConfigService, c as CompareTo, d as ConversionConfigStore, e as CurrencyRevenue, f as CustomerCurrencyDelta, g as CustomerCurrencyTotal, h as CustomerGetOptions, i as CustomerGetResult, j as CustomerKpis, k as CustomerKpisDeltas, l as CustomerKpisPrevious, m as CustomerListPage, n as CustomerListQuery, o as CustomerProfile, p as CustomerSegment, q as CustomerSegmentCount, r as CustomerSortField, s as CustomerSummary, t as CustomerSummaryQuery, D as DistinctCustomersByCurrency, G as Granularity, N as NAMED_RANGES, u as NamedRange, P as PublicServiceItem, S as SUPPORTED_CURRENCIES, v as Sale, w as SaleCursorPage, x as SaleFilters, y as SaleInput, z as SaleItem, E as SaleItemInput, F as SaleKeysetSortField, H as SaleListPage, I as SaleListQuery, J as SaleListQueryV2, K as SaleService, L as SaleServiceInput, M as SaleSortField, O as SaleSortOrder, Q as SaleSummary, R as SaleSummaryPrevious, T as SaleSummaryQuery, U as SaleSummaryQueryV2, V as SaleSummaryV2, W as SaleUpdateInput, X as SalesBusinessClient, Y as SalesCategoryBreakdown, Z as SalesClient, _ as SalesClientConfig, $ as SalesCustomersClient, a0 as SalesServiceBreakdown, a1 as SalesTransportConfig, a2 as SalesTrendPoint, a3 as SummaryCurrencyDelta, a4 as SummaryDeltas, a5 as SummaryWindow, a6 as SupportedCurrency, a7 as TRACKING_RANGES, a8 as TrackingOverviewRange, a9 as createSalesClient, aa as fetchServices, ab as formatDateInTz, ac as formatMoney, ad as fromMinor, ae as resolveConversionConfig, af as saleCreateSchema, ag as saleItemSchema, ah as saleServiceSchema, ai as saleUpdateSchema, aj as salesRequest, ak as toMinor } from './sales-Bq7H-Vym.mjs';
7
7
  import * as src from 'src';
@@ -10,37 +10,14 @@ import { CountryCode } from 'libphonenumber-js';
10
10
  export { CountryCode } from 'libphonenumber-js';
11
11
 
12
12
  /**
13
- * Default non-blocking consent banner.
13
+ * Legacy opt-in-era consent banner.
14
14
  *
15
- * Renders only while consent is `pending`; collapses to `null` once the
16
- * visitor has chosen.
17
- *
18
- * **Styling is intentionally self-contained** inline styles, zero CSS
19
- * dependencies, no Tailwind required at the consumer. The Tailwind-based
20
- * banner shipped before 0.9.1 rendered as transparent in any consumer that
21
- * didn't configure their content array to scan
22
- * `node_modules/@aranova/tracking-react/dist/**`; this version sidesteps that
23
- * class of bug entirely.
24
- *
25
- * For a fully bespoke banner, skip this component and use {@link useConsent}
26
- * directly to drive your own UI.
27
- *
28
- * @example
29
- * // Drop-in default
30
- * <ConsentBanner />
31
- *
32
- * @example
33
- * // Customized
34
- * <ConsentBanner
35
- * message="We use cookies to learn which ads drive bookings."
36
- * acceptLabel="Sounds good"
37
- * declineLabel="No thanks"
38
- * policyHref="/privacy"
39
- * policyLabel="Privacy policy"
40
- * theme="dark"
41
- * onAccept={() => track('consent_accepted')}
42
- * onDecline={() => track('consent_declined')}
43
- * />
15
+ * @deprecated PERMANENTLY INERT since consent v2 (opt-out model): it renders
16
+ * only while consent is `pending`, and the effective state is never `pending`
17
+ * anymore, so this component always returns `null`. Tracking is on by default;
18
+ * replace the banner with a footer "cookie preferences" control built on
19
+ * {@link useCookiePreferences} (see the package README). Kept exported so
20
+ * existing integrations keep compiling; scheduled for removal.
44
21
  */
45
22
  interface ConsentBannerProps {
46
23
  /** Body text. Defaults to the standard cookies-for-ad-performance message. */
@@ -72,40 +49,26 @@ interface ConsentBannerProps {
72
49
  /** Inline style overrides applied to the outer wrapper after the defaults. */
73
50
  style?: CSSProperties;
74
51
  }
75
- declare function ConsentBanner({ message, title, acceptLabel, declineLabel, policyHref, policyLabel, onAccept, onDecline, position, theme, className, style, }?: ConsentBannerProps): ReactNode;
76
-
77
- /**
78
- * Google Consent Mode value sent to `gtag('consent', 'update', ...)`.
79
- */
80
- type GtagConsentValue = "granted" | "denied";
81
52
  /**
82
- * Read the persisted visitor consent state from localStorage.
83
- *
84
- * Returns `pending` when called during SSR or before the visitor has made a
85
- * choice.
53
+ * @deprecated Permanently inert since consent v2 always renders `null`
54
+ * because the effective consent state is never `pending`. Use a footer
55
+ * control built on {@link useCookiePreferences} instead. See
56
+ * {@link ConsentBannerProps} for details.
86
57
  */
87
- declare function getConsentState(): ConsentState;
58
+ declare function ConsentBanner({ message, title, acceptLabel, declineLabel, policyHref, policyLabel, onAccept, onDecline, position, theme, className, style, }?: ConsentBannerProps): ReactNode;
59
+
88
60
  /**
89
- * Persist a visitor consent choice and update Google Consent Mode when gtag is
90
- * loaded.
61
+ * Attribution query/cookie keys captured by the SDK.
91
62
  */
92
- declare function setConsentState(state: GtagConsentValue): void;
63
+ declare const TRACKING_PARAM_KEYS: readonly ["gclid", "fbclid", "utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"];
64
+ type TrackingParamKey = (typeof TRACKING_PARAM_KEYS)[number];
93
65
  /**
94
- * Clear the stored consent choice so the banner re-appears on next render.
95
- *
96
- * Power a "Cookie preferences" link in a footer so visitors can change their
97
- * mind without losing access to your site:
98
- *
99
- * ```tsx
100
- * const { reset } = useConsent();
101
- * <button onClick={reset}>Cookie preferences</button>
102
- * ```
66
+ * Capture tracking params from a URL, persist them to first-party cookies, and
67
+ * return the current cookie-backed attribution state.
103
68
  *
104
- * Does NOT push an `update` to gtag — there's nothing to update because the
105
- * visitor hasn't chosen anything yet. The next `setConsentState()` call will
106
- * sync gtag once they re-choose.
69
+ * Defaults to `window.location.href` in the browser.
107
70
  */
108
- declare function resetConsent(): void;
71
+ declare function captureTrackingParamsFromLocation(url?: string, maxAgeSeconds?: number): TrackingParams;
109
72
 
110
73
  interface TrackingContextInput {
111
74
  packageName?: string | null;
@@ -127,6 +90,12 @@ interface TrackingSessionInput {
127
90
  firstPage?: string | null;
128
91
  sessionId: string;
129
92
  visitorId?: string | null;
93
+ /**
94
+ * Landing attribution override (ADR-016). When omitted, the session's
95
+ * persisted landing record is used (captured from the current URL if the
96
+ * session has none yet).
97
+ */
98
+ landingParams?: Partial<Record<TrackingParamKey, string>>;
130
99
  }
131
100
  /**
132
101
  * Build runtime context attached to tracking sessions and events.
@@ -158,6 +127,8 @@ declare const ctaClickMetadataSchema: z.ZodObject<{
158
127
  }>;
159
128
  section: z.ZodOptional<z.ZodNullable<z.ZodString>>;
160
129
  destination_url: z.ZodOptional<z.ZodNullable<z.ZodString>>;
130
+ href: z.ZodOptional<z.ZodNullable<z.ZodString>>;
131
+ element: z.ZodOptional<z.ZodNullable<z.ZodString>>;
161
132
  }, "strict", z.ZodTypeAny, {
162
133
  page: {
163
134
  path: string;
@@ -165,6 +136,8 @@ declare const ctaClickMetadataSchema: z.ZodObject<{
165
136
  cta_name: string;
166
137
  section?: string | null | undefined;
167
138
  destination_url?: string | null | undefined;
139
+ href?: string | null | undefined;
140
+ element?: string | null | undefined;
168
141
  }, {
169
142
  page: {
170
143
  path: string;
@@ -172,14 +145,34 @@ declare const ctaClickMetadataSchema: z.ZodObject<{
172
145
  cta_name: string;
173
146
  section?: string | null | undefined;
174
147
  destination_url?: string | null | undefined;
148
+ href?: string | null | undefined;
149
+ element?: string | null | undefined;
175
150
  }>;
176
151
  type CtaClickMetadata = z.infer<typeof ctaClickMetadataSchema>;
177
152
  /**
178
153
  * Registration config for `cta_click`.
179
154
  *
180
- * This event is manual-only and currently has no registration options.
155
+ * The event stays manually fireable; `autoCapture` additionally attaches a
156
+ * delegated click listener that fires it for any element matching `selector`
157
+ * (default `[data-aranova-cta]`) — tag your CTAs, get analytics for free.
181
158
  */
182
- declare const ctaClickConfigSchema: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
159
+ declare const ctaClickConfigSchema: z.ZodObject<{
160
+ autoCapture: z.ZodOptional<z.ZodObject<{
161
+ selector: z.ZodOptional<z.ZodString>;
162
+ }, "strict", z.ZodTypeAny, {
163
+ selector?: string | undefined;
164
+ }, {
165
+ selector?: string | undefined;
166
+ }>>;
167
+ }, "strict", z.ZodTypeAny, {
168
+ autoCapture?: {
169
+ selector?: string | undefined;
170
+ } | undefined;
171
+ }, {
172
+ autoCapture?: {
173
+ selector?: string | undefined;
174
+ } | undefined;
175
+ }>;
183
176
  type CtaClickConfig = z.infer<typeof ctaClickConfigSchema>;
184
177
 
185
178
  /**
@@ -343,6 +336,49 @@ declare const multiPageSessionConfigSchema: z.ZodObject<{
343
336
  }>;
344
337
  type MultiPageSessionConfig = z.infer<typeof multiPageSessionConfigSchema>;
345
338
 
339
+ /**
340
+ * Metadata for the automatic `page_exit` event.
341
+ *
342
+ * Fired when the user leaves a page (SPA navigation away, tab hidden, or
343
+ * pagehide). `dwell_ms` is the ACTIVE (visible) time spent on the page segment
344
+ * being closed — hidden time never counts, matching `time_on_site` semantics.
345
+ * A page revisited after being hidden emits another `page_exit` for the next
346
+ * visible segment, so summing `dwell_ms` per page/session yields total active
347
+ * dwell without double counting.
348
+ */
349
+ declare const pageExitMetadataSchema: z.ZodObject<{
350
+ dwell_ms: z.ZodNumber;
351
+ max_scroll_percent: z.ZodNullable<z.ZodNumber>;
352
+ page: z.ZodObject<{
353
+ path: z.ZodString;
354
+ }, "strict", z.ZodTypeAny, {
355
+ path: string;
356
+ }, {
357
+ path: string;
358
+ }>;
359
+ }, "strict", z.ZodTypeAny, {
360
+ page: {
361
+ path: string;
362
+ };
363
+ dwell_ms: number;
364
+ max_scroll_percent: number | null;
365
+ }, {
366
+ page: {
367
+ path: string;
368
+ };
369
+ dwell_ms: number;
370
+ max_scroll_percent: number | null;
371
+ }>;
372
+ type PageExitMetadata = z.infer<typeof pageExitMetadataSchema>;
373
+ /**
374
+ * Registration config for automatic `page_exit`.
375
+ *
376
+ * SDK-internal: attached unconditionally (like `sdk_heartbeat`), so there are
377
+ * no registration options.
378
+ */
379
+ declare const pageExitConfigSchema: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
380
+ type PageExitConfig = z.infer<typeof pageExitConfigSchema>;
381
+
346
382
  /**
347
383
  * Metadata for the automatic `page_view` event.
348
384
  *
@@ -874,6 +910,33 @@ declare const EVENT_REGISTRY: {
874
910
  }>;
875
911
  readonly configSchema: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
876
912
  };
913
+ readonly page_exit: {
914
+ readonly kind: "automatic";
915
+ readonly metadataSchema: z.ZodObject<{
916
+ dwell_ms: z.ZodNumber;
917
+ max_scroll_percent: z.ZodNullable<z.ZodNumber>;
918
+ page: z.ZodObject<{
919
+ path: z.ZodString;
920
+ }, "strict", z.ZodTypeAny, {
921
+ path: string;
922
+ }, {
923
+ path: string;
924
+ }>;
925
+ }, "strict", z.ZodTypeAny, {
926
+ page: {
927
+ path: string;
928
+ };
929
+ dwell_ms: number;
930
+ max_scroll_percent: number | null;
931
+ }, {
932
+ page: {
933
+ path: string;
934
+ };
935
+ dwell_ms: number;
936
+ max_scroll_percent: number | null;
937
+ }>;
938
+ readonly configSchema: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
939
+ };
877
940
  readonly form_submit: {
878
941
  readonly kind: "manual";
879
942
  readonly metadataSchema: z.ZodObject<{
@@ -993,6 +1056,8 @@ declare const EVENT_REGISTRY: {
993
1056
  }>;
994
1057
  section: z.ZodOptional<z.ZodNullable<z.ZodString>>;
995
1058
  destination_url: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1059
+ href: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1060
+ element: z.ZodOptional<z.ZodNullable<z.ZodString>>;
996
1061
  }, "strict", z.ZodTypeAny, {
997
1062
  page: {
998
1063
  path: string;
@@ -1000,6 +1065,8 @@ declare const EVENT_REGISTRY: {
1000
1065
  cta_name: string;
1001
1066
  section?: string | null | undefined;
1002
1067
  destination_url?: string | null | undefined;
1068
+ href?: string | null | undefined;
1069
+ element?: string | null | undefined;
1003
1070
  }, {
1004
1071
  page: {
1005
1072
  path: string;
@@ -1007,8 +1074,26 @@ declare const EVENT_REGISTRY: {
1007
1074
  cta_name: string;
1008
1075
  section?: string | null | undefined;
1009
1076
  destination_url?: string | null | undefined;
1077
+ href?: string | null | undefined;
1078
+ element?: string | null | undefined;
1079
+ }>;
1080
+ readonly configSchema: z.ZodObject<{
1081
+ autoCapture: z.ZodOptional<z.ZodObject<{
1082
+ selector: z.ZodOptional<z.ZodString>;
1083
+ }, "strict", z.ZodTypeAny, {
1084
+ selector?: string | undefined;
1085
+ }, {
1086
+ selector?: string | undefined;
1087
+ }>>;
1088
+ }, "strict", z.ZodTypeAny, {
1089
+ autoCapture?: {
1090
+ selector?: string | undefined;
1091
+ } | undefined;
1092
+ }, {
1093
+ autoCapture?: {
1094
+ selector?: string | undefined;
1095
+ } | undefined;
1010
1096
  }>;
1011
- readonly configSchema: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
1012
1097
  };
1013
1098
  };
1014
1099
  /**
@@ -1037,6 +1122,7 @@ type MetadataByName = {
1037
1122
  multi_page_session: MultiPageSessionMetadata;
1038
1123
  form_start: FormStartMetadata;
1039
1124
  sdk_heartbeat: SdkHeartbeatMetadata;
1125
+ page_exit: PageExitMetadata;
1040
1126
  form_submit: FormSubmitMetadata;
1041
1127
  phone_click: PhoneClickMetadata;
1042
1128
  cta_click: CtaClickMetadata;
@@ -1049,6 +1135,7 @@ type ConfigByName = {
1049
1135
  multi_page_session: MultiPageSessionConfig;
1050
1136
  form_start: FormStartConfig;
1051
1137
  sdk_heartbeat: SdkHeartbeatConfig;
1138
+ page_exit: PageExitConfig;
1052
1139
  form_submit: FormSubmitConfig;
1053
1140
  phone_click: PhoneClickConfig;
1054
1141
  cta_click: CtaClickConfig;
@@ -1163,8 +1250,14 @@ interface TrackEventInput {
1163
1250
  interface TrackingClient {
1164
1251
  /** Enqueue an event for batched delivery. */
1165
1252
  trackEvent: (input: TrackEventInput) => void;
1166
- /** Flush queued events immediately. */
1253
+ /** Flush queued events immediately (fetch, non-keepalive). */
1167
1254
  flush: () => Promise<void>;
1255
+ /**
1256
+ * Flush queued events through the keepalive transport so the request
1257
+ * survives document unload. Use from `pagehide`/`visibilitychange:hidden`
1258
+ * handlers — a plain `flush()` there is aborted by the browser on unload.
1259
+ */
1260
+ flushBeacon: () => void;
1168
1261
  /** Return the current rolling session id. */
1169
1262
  getSessionId: () => string;
1170
1263
  /** Return the persistent visitor id. */
@@ -1173,18 +1266,6 @@ interface TrackingClient {
1173
1266
  destroy: () => void;
1174
1267
  }
1175
1268
 
1176
- /**
1177
- * Attribution query/cookie keys captured by the SDK.
1178
- */
1179
- declare const TRACKING_PARAM_KEYS: readonly ["gclid", "fbclid", "utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"];
1180
- /**
1181
- * Capture tracking params from a URL, persist them to first-party cookies, and
1182
- * return the current cookie-backed attribution state.
1183
- *
1184
- * Defaults to `window.location.href` in the browser.
1185
- */
1186
- declare function captureTrackingParamsFromLocation(url?: string, maxAgeSeconds?: number): TrackingParams;
1187
-
1188
1269
  interface TypedTrackEventOptions {
1189
1270
  /**
1190
1271
  * Override the page URL associated with this event.
@@ -1248,9 +1329,9 @@ interface AdPlatformTrackingProps {
1248
1329
  }
1249
1330
  /**
1250
1331
  * Client component that loads the configured ad-platform tags — the Google tag
1251
- * (`gtag`) and/or the Meta Pixel (`fbq`) — each with Consent Mode, and restores
1252
- * stored consent. One mount handles both platforms; omit a platform's props to
1253
- * skip it. Renders nothing.
1332
+ * (`gtag`) and/or the Meta Pixel (`fbq`) — each with the visitor's effective
1333
+ * consent applied as the default (opt-out model). One mount handles both
1334
+ * platforms; omit a platform's props to skip it. Renders nothing.
1254
1335
  *
1255
1336
  * On React you usually don't need this at all — `<TrackingProvider>` already
1256
1337
  * accepts the same `gtagId/gtagIds` + `metaPixelId/metaPixelIds` props. Use this
@@ -1272,7 +1353,9 @@ type GoogleAdsTrackingProps = {
1272
1353
  gtagIds: GtagEnvironmentMap;
1273
1354
  };
1274
1355
  /**
1275
- * Client component that loads Google Ads gtag and restores stored consent.
1356
+ * Client component that loads Google Ads gtag with the visitor's effective
1357
+ * consent applied as the Consent Mode default (opt-out model — granted unless
1358
+ * an unexpired stored decline exists).
1276
1359
  *
1277
1360
  * Render once near the application root when the client site runs paid Google
1278
1361
  * Ads. The component renders nothing.
@@ -1291,38 +1374,69 @@ declare function useGclid(): string | null;
1291
1374
  * Values are loaded after mount, so the initial render returns all `null`s.
1292
1375
  */
1293
1376
  declare function useTrackingParams(): TrackingParams;
1377
+ /** Options for {@link useCookiePreferences}. */
1378
+ interface UseCookiePreferencesOptions {
1379
+ /** Days an explicit decline is honored. Defaults to 90. */
1380
+ declineTtlDays?: number;
1381
+ }
1294
1382
  /**
1295
- * Read the current visitor consent state and update when another tab changes
1296
- * the stored value.
1297
- *
1298
- * Prefer {@link useConsent} for new code — it returns the same state plus
1299
- * the `accept` / `decline` / `reset` actions a custom consent UI needs.
1300
- * `useConsentState` is kept as a convenience for callers that only need to
1301
- * read.
1383
+ * The headless cookie-preferences surface returned by
1384
+ * {@link useCookiePreferences}.
1302
1385
  */
1303
- declare function useConsentState(): ConsentState;
1386
+ interface UseCookiePreferencesResult {
1387
+ /** Effective consent — `granted` unless an unexpired explicit decline exists. */
1388
+ state: ConsentChoiceState;
1389
+ /** `default` = no valid explicit choice stored; `explicit` = visitor chose. */
1390
+ source: ConsentSource;
1391
+ /** True when the visitor has made no (valid, unexpired) explicit choice. */
1392
+ isDefault: boolean;
1393
+ isGranted: boolean;
1394
+ isDenied: boolean;
1395
+ /** ISO timestamp of the explicit choice; null for the default state. */
1396
+ updatedAt: string | null;
1397
+ /** ISO expiry of an unexpired decline; null otherwise. */
1398
+ expiresAt: string | null;
1399
+ /** Explicitly opt out of ad tracking (honored for 90 days by default). */
1400
+ optOut: () => void;
1401
+ /** Explicitly opt in (never expires). */
1402
+ optIn: () => void;
1403
+ /** Clear the explicit choice — back to default-granted. */
1404
+ reset: () => void;
1405
+ }
1304
1406
  /**
1305
- * The headless consent surface state + actions in one hook.
1407
+ * Headless cookie-preferences hook for the opt-out consent model (consent v2).
1306
1408
  *
1307
- * Build a fully-custom banner without losing the gtag-sync, localStorage
1308
- * persistence, or cross-tab propagation:
1409
+ * Tracking is ON by default; this hook is how each client site wires its own
1410
+ * footer "Cookie preferences" control (button, dialog, toggle — the packages
1411
+ * ship no consent UI). State stays in sync with actions from other components
1412
+ * in the same tab (via `onConsentChange`) and other tabs (via `storage`
1413
+ * events).
1309
1414
  *
1310
1415
  * ```tsx
1311
- * const { state, accept, decline, reset, isPending } = useConsent();
1312
- *
1313
- * if (!isPending) {
1314
- * return <button onClick={reset}>Cookie preferences</button>;
1416
+ * function CookiePreferences() {
1417
+ * const { isDenied, optOut, optIn } = useCookiePreferences();
1418
+ * return isDenied ? (
1419
+ * <button onClick={optIn}>Enable ad measurement</button>
1420
+ * ) : (
1421
+ * <button onClick={optOut}>Opt out of ad measurement</button>
1422
+ * );
1315
1423
  * }
1316
- * return (
1317
- * <MyBannerStyling>
1318
- * <button onClick={decline}>No thanks</button>
1319
- * <button onClick={accept}>Sure</button>
1320
- * </MyBannerStyling>
1321
- * );
1322
1424
  * ```
1425
+ */
1426
+ declare function useCookiePreferences(options?: UseCookiePreferencesOptions): UseCookiePreferencesResult;
1427
+ /**
1428
+ * Read the current visitor consent state.
1323
1429
  *
1324
- * The boolean helpers (`isPending` / `isGranted` / `isDenied`) are equivalent
1325
- * to comparing `state` directly they're there for readability at call sites.
1430
+ * @deprecated Since consent v2 (opt-out model) the state is never `pending`.
1431
+ * Use {@link useCookiePreferences}it exposes the effective state plus
1432
+ * `source` so you can tell a default grant from an explicit one.
1433
+ */
1434
+ declare function useConsentState(): ConsentState;
1435
+ /**
1436
+ * Result shape of the deprecated {@link useConsent} hook.
1437
+ *
1438
+ * @deprecated Use {@link UseCookiePreferencesResult} via
1439
+ * {@link useCookiePreferences}. `isPending` is always `false` since consent v2.
1326
1440
  */
1327
1441
  interface UseConsentResult {
1328
1442
  state: ConsentState;
@@ -1333,6 +1447,14 @@ interface UseConsentResult {
1333
1447
  decline: () => void;
1334
1448
  reset: () => void;
1335
1449
  }
1450
+ /**
1451
+ * Legacy opt-in-era consent hook.
1452
+ *
1453
+ * @deprecated Since consent v2 tracking defaults ON (opt-out model): the state
1454
+ * is never `pending`, so banner UIs gated on `isPending` never render. Use
1455
+ * {@link useCookiePreferences} for footer "cookie preferences" controls.
1456
+ * `accept` / `decline` still work and map to `optIn` / `optOut`.
1457
+ */
1336
1458
  declare function useConsent(): UseConsentResult;
1337
1459
 
1338
1460
  interface CreateTrackingOptions<TRegistry extends TriggerRegistryConfig> {
@@ -1484,4 +1606,4 @@ interface PhoneFieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "t
1484
1606
  */
1485
1607
  declare const PhoneField: react.ForwardRefExoticComponent<PhoneFieldProps & react.RefAttributes<HTMLInputElement>>;
1486
1608
 
1487
- export { ALL_AUTOMATIC_EVENT_NAMES, ALL_MANUAL_EVENT_NAMES, AdPlatformTracking, type AdPlatformTrackingProps, type AutomaticEventName, ConsentBanner, type ConsentBannerProps, ConsentState, ConversionConfig, type CreateTrackingOptions, type CreateTrackingResult, type CtaClickConfig, type CtaClickMetadata, EVENT_REGISTRY, type EventConfig, type EventKind, type EventMetadata, type EventName, type FormStartConfig, type FormStartMetadata, FormSubmitConfig, FormSubmitMetadata, GoogleAdsTracking, type GoogleAdsTrackingProps, GtagEnvironmentMap, type ManualEventName, MetaPixelEnvironmentMap, type MultiPageSessionConfig, type MultiPageSessionMetadata, type PageViewConfig, type PageViewMetadata, ParsedPhone, type PhoneClickConfig, type PhoneClickMetadata, PhoneConfig, PhoneDisplayFormat, PhoneField, type PhoneFieldApi, type PhoneFieldProps, type PhoneInputProps, type RegisteredAutomaticEvents, type RegisteredManualEvents, SPECIFIC_PAGE_NAMES, type ScrollDepthConfig, type ScrollDepthMetadata, type SdkHeartbeatConfig, type SdkHeartbeatMetadata, type SpecificPageName, type SpecificPageVisitConfig, type SpecificPageVisitMetadata, TRACKING_PARAM_KEYS, type TimeOnSiteConfig, type TimeOnSiteMetadata, type TrackableEvent, type TrackingClient, TrackingClientContext, TrackingEventCreatePayload, TrackingInstallSurface, TrackingParams, type TrackingProviderProps, TrackingSessionUpsertPayload, type TriggerRegistryConfig, type TypedTrackEventOptions, type TypedTrackingClient, type UseConsentResult, type UsePhoneFieldOptions, captureTrackingParamsFromLocation, createTracking, createTrackingClientContext, createTrackingEventCreatePayload, createTrackingSessionUpsertPayload, ctaClickConfigSchema, ctaClickMetadataSchema, formStartConfigSchema, formStartMetadataSchema, getConsentState, getEventDefinition, multiPageSessionConfigSchema, multiPageSessionMetadataSchema, pageViewConfigSchema, pageViewMetadataSchema, phoneClickConfigSchema, phoneClickMetadataSchema, resetConsent, scrollDepthConfigSchema, scrollDepthMetadataSchema, sdkHeartbeatConfigSchema, sdkHeartbeatMetadataSchema, sdkHeartbeatTriggersSchema, setConsentState, specificPageNameSchema, specificPageVisitConfigSchema, specificPageVisitMetadataSchema, timeOnSiteConfigSchema, timeOnSiteMetadataSchema, useConsent, useConsentState, useGclid, usePhoneConfig, usePhoneField, useTrackingParams };
1609
+ export { ALL_AUTOMATIC_EVENT_NAMES, ALL_MANUAL_EVENT_NAMES, AdPlatformTracking, type AdPlatformTrackingProps, type AutomaticEventName, ConsentBanner, type ConsentBannerProps, ConsentChoiceState, ConsentSource, ConsentState, ConversionConfig, type CreateTrackingOptions, type CreateTrackingResult, type CtaClickConfig, type CtaClickMetadata, EVENT_REGISTRY, type EventConfig, type EventKind, type EventMetadata, type EventName, type FormStartConfig, type FormStartMetadata, FormSubmitConfig, FormSubmitMetadata, GoogleAdsTracking, type GoogleAdsTrackingProps, GtagEnvironmentMap, type ManualEventName, MetaPixelEnvironmentMap, type MultiPageSessionConfig, type MultiPageSessionMetadata, type PageExitConfig, type PageExitMetadata, type PageViewConfig, type PageViewMetadata, ParsedPhone, type PhoneClickConfig, type PhoneClickMetadata, PhoneConfig, PhoneDisplayFormat, PhoneField, type PhoneFieldApi, type PhoneFieldProps, type PhoneInputProps, type RegisteredAutomaticEvents, type RegisteredManualEvents, SPECIFIC_PAGE_NAMES, type ScrollDepthConfig, type ScrollDepthMetadata, type SdkHeartbeatConfig, type SdkHeartbeatMetadata, type SpecificPageName, type SpecificPageVisitConfig, type SpecificPageVisitMetadata, TRACKING_PARAM_KEYS, type TimeOnSiteConfig, type TimeOnSiteMetadata, type TrackableEvent, type TrackingClient, TrackingClientContext, TrackingEventCreatePayload, TrackingInstallSurface, TrackingParams, type TrackingProviderProps, TrackingSessionUpsertPayload, type TriggerRegistryConfig, type TypedTrackEventOptions, type TypedTrackingClient, type UseConsentResult, type UseCookiePreferencesOptions, type UseCookiePreferencesResult, type UsePhoneFieldOptions, captureTrackingParamsFromLocation, createTracking, createTrackingClientContext, createTrackingEventCreatePayload, createTrackingSessionUpsertPayload, ctaClickConfigSchema, ctaClickMetadataSchema, formStartConfigSchema, formStartMetadataSchema, getEventDefinition, multiPageSessionConfigSchema, multiPageSessionMetadataSchema, pageExitConfigSchema, pageExitMetadataSchema, pageViewConfigSchema, pageViewMetadataSchema, phoneClickConfigSchema, phoneClickMetadataSchema, scrollDepthConfigSchema, scrollDepthMetadataSchema, sdkHeartbeatConfigSchema, sdkHeartbeatMetadataSchema, sdkHeartbeatTriggersSchema, specificPageNameSchema, specificPageVisitConfigSchema, specificPageVisitMetadataSchema, timeOnSiteConfigSchema, timeOnSiteMetadataSchema, useConsent, useConsentState, useCookiePreferences, useGclid, usePhoneConfig, usePhoneField, useTrackingParams };