@aranova/tracking-react 0.11.0 → 0.12.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/index.d.ts CHANGED
@@ -1,6 +1,11 @@
1
- import { ReactNode, CSSProperties } from 'react';
1
+ import * as react from 'react';
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, P as PhoneConfig, g as ParsedPhone, h as PhoneDisplayFormat } from './phone-utils-CVX7JmqB.js';
4
+ export { D as DEFAULT_PHONE_COUNTRY, J as JsonValue, i as TrackedField, j as TrackingInitConfig, k as formatPhone, l as formatPhoneAsTyped, p as parsePhone, m as phoneField, t as toE164 } from './phone-utils-CVX7JmqB.js';
2
5
  import * as src from 'src';
3
6
  import { z } from 'zod';
7
+ import { CountryCode } from 'libphonenumber-js';
8
+ export { CountryCode } from 'libphonenumber-js';
4
9
 
5
10
  /**
6
11
  * Default non-blocking consent banner.
@@ -67,166 +72,6 @@ interface ConsentBannerProps {
67
72
  }
68
73
  declare function ConsentBanner({ message, title, acceptLabel, declineLabel, policyHref, policyLabel, onAccept, onDecline, position, theme, className, style, }?: ConsentBannerProps): ReactNode;
69
74
 
70
- /**
71
- * Visitor consent state stored by the SDK.
72
- *
73
- * - `pending`: the visitor has not accepted or declined yet.
74
- * - `granted`: consent was accepted and Google Consent Mode is updated to granted.
75
- * - `denied`: consent was declined and Google Consent Mode is updated to denied.
76
- */
77
- type ConsentState = 'granted' | 'denied' | 'pending';
78
- /**
79
- * Attribution parameters captured from the landing URL and persisted in cookies.
80
- *
81
- * Missing params are represented as `null` so payloads can be serialized
82
- * directly without checking for `undefined`.
83
- */
84
- interface TrackingParams {
85
- /** Google Ads click id. */
86
- gclid: string | null;
87
- /** Meta/Facebook click id. */
88
- fbclid: string | null;
89
- /** UTM source, for example `google` or `newsletter`. */
90
- utm_source: string | null;
91
- /** UTM medium, for example `cpc` or `email`. */
92
- utm_medium: string | null;
93
- /** UTM campaign name. */
94
- utm_campaign: string | null;
95
- /** UTM paid-search term. */
96
- utm_term: string | null;
97
- /** UTM content/ad creative label. */
98
- utm_content: string | null;
99
- }
100
- /**
101
- * Runtime surface that installed the tracking SDK.
102
- *
103
- * Included in ingest payloads and heartbeat events so the dashboard can tell
104
- * whether a site uses the Next, React, or script-tag integration.
105
- */
106
- type TrackingInstallSurface = 'next' | 'react' | 'script';
107
- /**
108
- * Deployment environment label for event tagging.
109
- *
110
- * Used to stamp tracking events with the deployment context so the dashboard
111
- * can distinguish production traffic from dev test traffic. The backend
112
- * enforces this via a Postgres enum, so values are strictly one of
113
- * `'production'` or `'development'`.
114
- */
115
- type TrackingEnvironment = 'production' | 'development';
116
- /**
117
- * Labelled map of Google Ads tag IDs.
118
- *
119
- * ALL entries are loaded simultaneously via `gtag('config', ...)` — the keys
120
- * are human-readable labels (e.g. `production`, `test`) and the values are
121
- * Google Ads tag IDs (e.g. `AW-123456789`).
122
- */
123
- type GtagEnvironmentMap = Record<string, string>;
124
- /**
125
- * Runtime context attached to tracking sessions and events.
126
- */
127
- interface TrackingClientContext {
128
- /** Install surface that created the client. */
129
- surface: TrackingInstallSurface;
130
- /** Package version, when available. */
131
- sdk_version: string | null;
132
- /** Package name, for example `@aranova/tracking-react`. */
133
- package_name: string | null;
134
- /** Browser origin of the tracked site. */
135
- site_origin: string | null;
136
- /** Current document title at client creation time. */
137
- page_title: string | null;
138
- /** Browser document referrer at client creation time. */
139
- referrer: string | null;
140
- /**
141
- * Deployment environment label, e.g. `'production'`, `'development'`.
142
- *
143
- * Always set — defaults to `'production'` when the consumer doesn't
144
- * pass `environment` to the factory. Sending `null` would fail the
145
- * backend's strict enum validation.
146
- */
147
- environment: TrackingEnvironment;
148
- /** All active gtag IDs loaded on this page, keyed by label. */
149
- active_gtag_ids: Record<string, string> | null;
150
- }
151
- /**
152
- * Session payload sent to `POST /tracking/events`.
153
- *
154
- * The backend upserts this by `(business_id, session_id)` before inserting
155
- * individual events.
156
- */
157
- interface TrackingSessionUpsertPayload {
158
- /** Rolling 30-minute client-side session id. */
159
- session_id: string;
160
- /** Persistent client-side visitor id. */
161
- visitor_id: string | null;
162
- gclid: string | null;
163
- fbclid: string | null;
164
- utm_source: string | null;
165
- utm_medium: string | null;
166
- utm_campaign: string | null;
167
- utm_term: string | null;
168
- utm_content: string | null;
169
- first_page: string | null;
170
- consent_state: Record<string, unknown> | null;
171
- context: TrackingClientContext;
172
- }
173
- /**
174
- * Event payload shape before batching into the ingest request.
175
- */
176
- interface TrackingEventCreatePayload {
177
- /** Session id that logically owns the event. */
178
- session_id: string;
179
- /** Registered event name, for example `page_view` or `form_submit`. */
180
- event_type: string;
181
- gclid: string | null;
182
- fbclid: string | null;
183
- /** Full page URL associated with the event, if known. */
184
- page_url: string | null;
185
- /** Event-specific metadata. Runtime shape depends on `event_type`. */
186
- metadata: Record<string, unknown> | null;
187
- context: TrackingClientContext;
188
- }
189
- /**
190
- * Browser-script initialization config passed to `window.AranovaTracking.init()`.
191
- */
192
- interface TrackingInitConfig {
193
- /** Public tracking API key issued from the Aranova dashboard. */
194
- apiKey?: string;
195
- /** Tracking endpoint base URL, usually ending in `/tracking`. */
196
- endpoint?: string;
197
- /** Optional Google Ads tag id, for example `AW-123456789`. */
198
- gtagId?: string;
199
- /**
200
- * Labelled map of Google Ads tag IDs. ALL are loaded simultaneously.
201
- * When provided, `gtagId` is ignored.
202
- */
203
- gtagIds?: GtagEnvironmentMap;
204
- /** Deployment environment label reported in session context. */
205
- environment?: TrackingEnvironment;
206
- /** Whether to capture attribution params from `window.location`. Defaults to true. */
207
- autoCaptureTrackingParams?: boolean;
208
- /** Whether the browser script should inject the default consent banner. */
209
- renderConsentBanner?: boolean;
210
- /** Attribution cookie max age in seconds. Defaults to 90 days. */
211
- cookieMaxAgeSeconds?: number;
212
- /** Override the install surface reported in payload context. */
213
- surface?: TrackingInstallSurface;
214
- }
215
- declare global {
216
- interface Window {
217
- gtag?: (...args: unknown[]) => void;
218
- dataLayer?: unknown[];
219
- AranovaTracking?: {
220
- init: (config: TrackingInitConfig) => void;
221
- captureTrackingParams: () => TrackingParams;
222
- getTrackingParams: () => TrackingParams;
223
- getConsentState: () => ConsentState;
224
- setConsentState: (state: 'granted' | 'denied') => void;
225
- trackEvent: (eventType: string, metadata: Record<string, unknown>) => void;
226
- };
227
- }
228
- }
229
-
230
75
  /**
231
76
  * Google Consent Mode value sent to `gtag('consent', 'update', ...)`.
232
77
  */
@@ -456,131 +301,6 @@ declare const formStartConfigSchema: z.ZodObject<{
456
301
  }>;
457
302
  type FormStartConfig = z.infer<typeof formStartConfigSchema>;
458
303
 
459
- /**
460
- * JSON-serializable value accepted by `form_submit.fields[].value`.
461
- *
462
- * This intentionally excludes `undefined`, functions, symbols, `Date`
463
- * instances, and non-finite numbers. Values are stored in PostgreSQL JSONB, so
464
- * consumers should send only data that has a stable JSON representation.
465
- */
466
- type JsonValue = string | number | boolean | null | JsonValue[] | {
467
- [key: string]: JsonValue;
468
- };
469
- /**
470
- * Metadata for a manually fired `form_submit` event.
471
- *
472
- * Register the event with `manual: { form_submit: {} }`, then call
473
- * `trackEvent('form_submit', metadata)` from the host site's submit handler.
474
- *
475
- * `fields` is optional. If present, each field value must be JSON-serializable
476
- * and should be explicitly allowlisted by the integration. Do not send names,
477
- * emails, visitor phone numbers, addresses, payment data, medical details,
478
- * passwords, file contents, or free-text messages.
479
- *
480
- * @example
481
- * ```ts
482
- * tracking.trackEvent('form_submit', {
483
- * form: {
484
- * id: 'lead-form',
485
- * action: '/api/lead',
486
- * fields: [
487
- * {
488
- * name: 'service_interest',
489
- * type: 'select',
490
- * label: 'Service interest',
491
- * value: 'teeth_whitening',
492
- * },
493
- * ],
494
- * },
495
- * page: { path: window.location.pathname },
496
- * });
497
- * ```
498
- */
499
- declare const formSubmitMetadataSchema: z.ZodObject<{
500
- form: z.ZodObject<{
501
- id: z.ZodString;
502
- action: z.ZodNullable<z.ZodString>;
503
- fields: z.ZodOptional<z.ZodArray<z.ZodObject<{
504
- name: z.ZodString;
505
- type: z.ZodString;
506
- label: z.ZodNullable<z.ZodString>;
507
- value: z.ZodType<JsonValue, z.ZodTypeDef, JsonValue>;
508
- }, "strict", z.ZodTypeAny, {
509
- label: string | null;
510
- value: JsonValue;
511
- type: string;
512
- name: string;
513
- }, {
514
- label: string | null;
515
- value: JsonValue;
516
- type: string;
517
- name: string;
518
- }>, "many">>;
519
- }, "strict", z.ZodTypeAny, {
520
- id: string;
521
- action: string | null;
522
- fields?: {
523
- label: string | null;
524
- value: JsonValue;
525
- type: string;
526
- name: string;
527
- }[] | undefined;
528
- }, {
529
- id: string;
530
- action: string | null;
531
- fields?: {
532
- label: string | null;
533
- value: JsonValue;
534
- type: string;
535
- name: string;
536
- }[] | undefined;
537
- }>;
538
- page: z.ZodObject<{
539
- path: z.ZodString;
540
- }, "strict", z.ZodTypeAny, {
541
- path: string;
542
- }, {
543
- path: string;
544
- }>;
545
- }, "strict", z.ZodTypeAny, {
546
- form: {
547
- id: string;
548
- action: string | null;
549
- fields?: {
550
- label: string | null;
551
- value: JsonValue;
552
- type: string;
553
- name: string;
554
- }[] | undefined;
555
- };
556
- page: {
557
- path: string;
558
- };
559
- }, {
560
- form: {
561
- id: string;
562
- action: string | null;
563
- fields?: {
564
- label: string | null;
565
- value: JsonValue;
566
- type: string;
567
- name: string;
568
- }[] | undefined;
569
- };
570
- page: {
571
- path: string;
572
- };
573
- }>;
574
- type FormSubmitMetadata = z.infer<typeof formSubmitMetadataSchema>;
575
- /**
576
- * Registration config for `form_submit`.
577
- *
578
- * This event is manual-only and currently has no registration options. The
579
- * empty object enables typed `trackEvent('form_submit', ...)` calls.
580
- */
581
- declare const formSubmitConfigSchema: z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>;
582
- type FormSubmitConfig = z.infer<typeof formSubmitConfigSchema>;
583
-
584
304
  /**
585
305
  * Metadata for the automatic `multi_page_session` event.
586
306
  *
@@ -634,14 +354,14 @@ declare const pageViewMetadataSchema: z.ZodObject<{
634
354
  search: z.ZodString;
635
355
  hash: z.ZodString;
636
356
  }, "strict", z.ZodTypeAny, {
357
+ path: string;
637
358
  search: string;
638
359
  title: string | null;
639
- path: string;
640
360
  hash: string;
641
361
  }, {
362
+ path: string;
642
363
  search: string;
643
364
  title: string | null;
644
- path: string;
645
365
  hash: string;
646
366
  }>;
647
367
  referrer: z.ZodNullable<z.ZodString>;
@@ -657,9 +377,9 @@ declare const pageViewMetadataSchema: z.ZodObject<{
657
377
  }>>>;
658
378
  }, "strict", z.ZodTypeAny, {
659
379
  page: {
380
+ path: string;
660
381
  search: string;
661
382
  title: string | null;
662
- path: string;
663
383
  hash: string;
664
384
  };
665
385
  referrer: string | null;
@@ -669,9 +389,9 @@ declare const pageViewMetadataSchema: z.ZodObject<{
669
389
  } | null | undefined;
670
390
  }, {
671
391
  page: {
392
+ path: string;
672
393
  search: string;
673
394
  title: string | null;
674
- path: string;
675
395
  hash: string;
676
396
  };
677
397
  referrer: string | null;
@@ -880,14 +600,14 @@ declare const EVENT_REGISTRY: {
880
600
  search: z.ZodString;
881
601
  hash: z.ZodString;
882
602
  }, "strict", z.ZodTypeAny, {
603
+ path: string;
883
604
  search: string;
884
605
  title: string | null;
885
- path: string;
886
606
  hash: string;
887
607
  }, {
608
+ path: string;
888
609
  search: string;
889
610
  title: string | null;
890
- path: string;
891
611
  hash: string;
892
612
  }>;
893
613
  referrer: z.ZodNullable<z.ZodString>;
@@ -903,9 +623,9 @@ declare const EVENT_REGISTRY: {
903
623
  }>>>;
904
624
  }, "strict", z.ZodTypeAny, {
905
625
  page: {
626
+ path: string;
906
627
  search: string;
907
628
  title: string | null;
908
- path: string;
909
629
  hash: string;
910
630
  };
911
631
  referrer: string | null;
@@ -915,9 +635,9 @@ declare const EVENT_REGISTRY: {
915
635
  } | null | undefined;
916
636
  }, {
917
637
  page: {
638
+ path: string;
918
639
  search: string;
919
640
  title: string | null;
920
- path: string;
921
641
  hash: string;
922
642
  };
923
643
  referrer: string | null;
@@ -1161,33 +881,33 @@ declare const EVENT_REGISTRY: {
1161
881
  label: z.ZodNullable<z.ZodString>;
1162
882
  value: z.ZodType<src.JsonValue, z.ZodTypeDef, src.JsonValue>;
1163
883
  }, "strict", z.ZodTypeAny, {
1164
- label: string | null;
1165
884
  value: src.JsonValue;
1166
885
  type: string;
1167
886
  name: string;
1168
- }, {
1169
887
  label: string | null;
888
+ }, {
1170
889
  value: src.JsonValue;
1171
890
  type: string;
1172
891
  name: string;
892
+ label: string | null;
1173
893
  }>, "many">>;
1174
894
  }, "strict", z.ZodTypeAny, {
1175
895
  id: string;
1176
896
  action: string | null;
1177
897
  fields?: {
1178
- label: string | null;
1179
898
  value: src.JsonValue;
1180
899
  type: string;
1181
900
  name: string;
901
+ label: string | null;
1182
902
  }[] | undefined;
1183
903
  }, {
1184
904
  id: string;
1185
905
  action: string | null;
1186
906
  fields?: {
1187
- label: string | null;
1188
907
  value: src.JsonValue;
1189
908
  type: string;
1190
909
  name: string;
910
+ label: string | null;
1191
911
  }[] | undefined;
1192
912
  }>;
1193
913
  page: z.ZodObject<{
@@ -1202,10 +922,10 @@ declare const EVENT_REGISTRY: {
1202
922
  id: string;
1203
923
  action: string | null;
1204
924
  fields?: {
1205
- label: string | null;
1206
925
  value: src.JsonValue;
1207
926
  type: string;
1208
927
  name: string;
928
+ label: string | null;
1209
929
  }[] | undefined;
1210
930
  };
1211
931
  page: {
@@ -1216,10 +936,10 @@ declare const EVENT_REGISTRY: {
1216
936
  id: string;
1217
937
  action: string | null;
1218
938
  fields?: {
1219
- label: string | null;
1220
939
  value: src.JsonValue;
1221
940
  type: string;
1222
941
  name: string;
942
+ label: string | null;
1223
943
  }[] | undefined;
1224
944
  };
1225
945
  page: {
@@ -1836,7 +1556,11 @@ interface SaleListPage {
1836
1556
  interface SaleCursorPage {
1837
1557
  items: Sale[];
1838
1558
  next_cursor: string | null;
1559
+ /** Populated only when `want_total` was requested (a single indexed COUNT). */
1560
+ total_count?: number;
1561
+ has_more?: boolean;
1839
1562
  }
1563
+ type SaleSortOrder = 'asc' | 'desc';
1840
1564
  /**
1841
1565
  * Comprehensive filter shape mirrored from the backend's `SaleQueryFilters`.
1842
1566
  *
@@ -1849,6 +1573,8 @@ interface SaleFilters {
1849
1573
  business_id?: string;
1850
1574
  external_id?: string;
1851
1575
  service_id?: string;
1576
+ /** E.164 phone — the canonical customer key; filters sales for one customer. */
1577
+ customer_phone?: string;
1852
1578
  currency?: SupportedCurrency;
1853
1579
  environment?: (typeof TRACKING_ENVIRONMENTS)[number];
1854
1580
  since?: string;
@@ -1872,6 +1598,15 @@ interface SaleListQuery extends SaleFilters {
1872
1598
  limit?: number;
1873
1599
  cursor?: string | null;
1874
1600
  }
1601
+ /** Keyset-safe sort columns (NOT-NULL, indexed). */
1602
+ type SaleKeysetSortField = 'occurred_at' | 'created_at' | 'amount_total_cents';
1603
+ /** v2 list query — adds server sort + opt-in total. */
1604
+ interface SaleListQueryV2 extends SaleListQuery {
1605
+ sort?: SaleKeysetSortField;
1606
+ order?: SaleSortOrder;
1607
+ /** Opt-in: a single indexed COUNT over the filtered set. */
1608
+ want_total?: boolean;
1609
+ }
1875
1610
  declare const TRACKING_RANGES: readonly ["24h", "7d", "30d"];
1876
1611
  type TrackingOverviewRange = (typeof TRACKING_RANGES)[number];
1877
1612
  /** Query input for `SalesClient.summary()` — filters + range + options. */
@@ -1927,6 +1662,175 @@ interface SaleSummary {
1927
1662
  by_category: SalesCategoryBreakdown[];
1928
1663
  series: SalesTrendPoint[];
1929
1664
  }
1665
+ declare const NAMED_RANGES: readonly ["today", "yesterday", "wtd", "mtd", "qtd", "ytd", "24h", "7d", "30d", "90d", "custom"];
1666
+ /** Superset of `TrackingOverviewRange` (`24h`/`7d`/`30d` stay valid). */
1667
+ type NamedRange = (typeof NAMED_RANGES)[number];
1668
+ type Granularity = 'hour' | 'day' | 'week' | 'month' | 'auto';
1669
+ type CompareTo = 'previous_period' | 'previous_year' | 'none';
1670
+ interface DistinctCustomersByCurrency {
1671
+ currency: SupportedCurrency;
1672
+ distinct_customers: number;
1673
+ }
1674
+ interface SummaryWindow {
1675
+ since: string;
1676
+ until: string;
1677
+ }
1678
+ interface SummaryCurrencyDelta {
1679
+ currency: SupportedCurrency;
1680
+ revenue_cents_delta: number;
1681
+ /** Ratio (0.12 = +12%); null when previous revenue was 0. */
1682
+ revenue_pct_delta: number | null;
1683
+ sale_count_delta: number;
1684
+ }
1685
+ interface SummaryDeltas {
1686
+ by_currency: SummaryCurrencyDelta[];
1687
+ sale_count_delta: number;
1688
+ distinct_customers_delta: number;
1689
+ }
1690
+ interface SaleSummaryPrevious {
1691
+ window: SummaryWindow;
1692
+ metrics: SaleSummaryV2['metrics'];
1693
+ by_currency: CurrencyRevenue[];
1694
+ series: SalesTrendPoint[];
1695
+ }
1696
+ /** Query input for `SalesClient.summary()` v2 — widened range + tz/compare options. */
1697
+ interface SaleSummaryQueryV2 extends SaleFilters {
1698
+ range?: NamedRange;
1699
+ /** Required when `range === 'custom'`. */
1700
+ since?: string;
1701
+ until?: string;
1702
+ /** IANA tz, e.g. `America/Toronto`. Default = business tz. */
1703
+ timezone?: string;
1704
+ granularity?: Granularity;
1705
+ compare_to?: CompareTo;
1706
+ include_categories?: boolean;
1707
+ include_deleted_services?: boolean;
1708
+ top_n?: number;
1709
+ }
1710
+ /** Superset of `SaleSummary` (assignable to it). */
1711
+ interface SaleSummaryV2 extends Omit<SaleSummary, 'range' | 'metrics'> {
1712
+ range: NamedRange;
1713
+ metrics: SaleSummary['metrics'] & {
1714
+ distinct_customers_by_currency: DistinctCustomersByCurrency[];
1715
+ };
1716
+ timezone: string | null;
1717
+ window: SummaryWindow | null;
1718
+ granularity: string | null;
1719
+ previous: SaleSummaryPrevious | null;
1720
+ deltas: SummaryDeltas | null;
1721
+ }
1722
+ type CustomerSegment = 'new' | 'returning' | 'repeat' | 'lapsed';
1723
+ type CustomerSortField = 'total_spent' | 'last_purchase' | 'purchases' | 'first_purchase';
1724
+ interface CustomerCurrencyTotal {
1725
+ currency: SupportedCurrency;
1726
+ revenue_cents: number;
1727
+ sale_count: number;
1728
+ average_order_value_cents: number;
1729
+ }
1730
+ interface CustomerSummary {
1731
+ /** The E.164 phone — the canonical customer id. */
1732
+ customer_id: string;
1733
+ display_name: string | null;
1734
+ email: string | null;
1735
+ phone: string;
1736
+ first_purchase_at: string;
1737
+ last_purchase_at: string;
1738
+ purchase_count: number;
1739
+ segment: CustomerSegment;
1740
+ totals: CustomerCurrencyTotal[];
1741
+ }
1742
+ type CustomerProfile = CustomerSummary;
1743
+ interface CustomerListQuery extends Omit<SaleFilters, 'external_id'> {
1744
+ segment?: CustomerSegment;
1745
+ sort?: CustomerSortField;
1746
+ order?: SaleSortOrder;
1747
+ cursor?: string | null;
1748
+ limit?: number;
1749
+ want_total?: boolean;
1750
+ }
1751
+ interface CustomerListPage {
1752
+ items: CustomerSummary[];
1753
+ next_cursor: string | null;
1754
+ total_count: number | null;
1755
+ }
1756
+ interface CustomerGetOptions {
1757
+ include_sales?: boolean;
1758
+ limit?: number;
1759
+ cursor?: string | null;
1760
+ }
1761
+ interface CustomerGetResult {
1762
+ customer: CustomerProfile;
1763
+ /** Present only when `include_sales: true` (and a business-scoped key). */
1764
+ sales?: SaleCursorPage | null;
1765
+ }
1766
+ interface CustomerSummaryQuery extends SaleFilters {
1767
+ range?: NamedRange;
1768
+ since?: string;
1769
+ until?: string;
1770
+ timezone?: string;
1771
+ compare_to?: CompareTo;
1772
+ }
1773
+ interface CustomerSegmentCount {
1774
+ segment: CustomerSegment;
1775
+ count: number;
1776
+ }
1777
+ interface CustomerCurrencyDelta {
1778
+ currency: SupportedCurrency;
1779
+ revenue_cents_delta: number;
1780
+ /** Ratio (0.12 = +12%); null when previous revenue was 0. */
1781
+ revenue_pct_delta: number | null;
1782
+ }
1783
+ interface CustomerKpisDeltas {
1784
+ total_customers_delta: number;
1785
+ new_customers_delta: number;
1786
+ returning_customers_delta: number;
1787
+ repeat_rate_delta: number;
1788
+ ltv_by_currency: CustomerCurrencyDelta[];
1789
+ }
1790
+ interface CustomerKpisPrevious {
1791
+ window_since: string;
1792
+ window_until: string;
1793
+ total_customers: number;
1794
+ new_customers: number;
1795
+ returning_customers: number;
1796
+ repeat_rate: number;
1797
+ ltv_by_currency: CustomerCurrencyTotal[];
1798
+ }
1799
+ interface CustomerKpis {
1800
+ range: NamedRange;
1801
+ timezone: string;
1802
+ window_since: string;
1803
+ window_until: string;
1804
+ total_customers: number;
1805
+ new_customers: number;
1806
+ returning_customers: number;
1807
+ repeat_rate: number;
1808
+ by_segment: CustomerSegmentCount[];
1809
+ ltv_by_currency: CustomerCurrencyTotal[];
1810
+ /** Present only when `compare_to` is set. */
1811
+ previous?: CustomerKpisPrevious | null;
1812
+ deltas?: CustomerKpisDeltas | null;
1813
+ }
1814
+ interface BusinessConfigService {
1815
+ key: string;
1816
+ label: string;
1817
+ archived: boolean;
1818
+ }
1819
+ interface BusinessConfigFeatures {
1820
+ customers: boolean;
1821
+ comparisons: boolean;
1822
+ retention: boolean;
1823
+ }
1824
+ interface BusinessConfig {
1825
+ business_id: string;
1826
+ display_name: string;
1827
+ timezone: string;
1828
+ primary_currency: SupportedCurrency;
1829
+ currencies: SupportedCurrency[];
1830
+ default_phone_country: string | null;
1831
+ services: BusinessConfigService[];
1832
+ features: BusinessConfigFeatures;
1833
+ }
1930
1834
 
1931
1835
  /** Shared config for every sales HTTP helper. */
1932
1836
  interface SalesTransportConfig {
@@ -1949,6 +1853,17 @@ interface SalesClientConfig extends SalesTransportConfig {
1949
1853
  /** Applied when an individual `record()` call omits `currency`. */
1950
1854
  defaultCurrency?: SupportedCurrency;
1951
1855
  }
1856
+ /** Phone-keyed customer rollups (sk-only; a public key gets a `403`). */
1857
+ interface SalesCustomersClient {
1858
+ list(query?: CustomerListQuery): Promise<CustomerListPage>;
1859
+ /** `id` is the customer's E.164 phone. */
1860
+ get(id: string, options?: CustomerGetOptions): Promise<CustomerGetResult>;
1861
+ summary(query?: CustomerSummaryQuery): Promise<CustomerKpis>;
1862
+ }
1863
+ /** Business config (low-sensitivity — accepts a public or secret key). */
1864
+ interface SalesBusinessClient {
1865
+ config(): Promise<BusinessConfig>;
1866
+ }
1952
1867
  /**
1953
1868
  * One isomorphic sales client — what a key may *do* is enforced by the backend,
1954
1869
  * not by hiding methods. A **public** key (`aranv_pk_…`) may `record` (the
@@ -1969,13 +1884,14 @@ interface SalesClient<TService extends string = string> {
1969
1884
  currency?: SupportedCurrency;
1970
1885
  occurred_at?: string;
1971
1886
  }): Promise<Sale>;
1972
- list(query?: SaleListQuery): Promise<SaleCursorPage>;
1887
+ /** Keyset list with optional server sort + opt-in `total_count`. */
1888
+ list(query?: SaleListQueryV2): Promise<SaleCursorPage>;
1973
1889
  /**
1974
- * Currency-grouped aggregations for the key's business (revenue per currency,
1975
- * AOV, distinct customers, by-service / by-currency / by-category breakdowns,
1976
- * and a revenue/count trend). Secret key only a public key gets a `403`.
1890
+ * Currency-grouped aggregations for the key's business. Additive v2 options:
1891
+ * calendar/custom ranges, IANA `timezone`, `granularity`, and `compare_to`.
1892
+ * Legacy `24h/7d/30d` keep their exact prior numbers. Secret key only.
1977
1893
  */
1978
- summary(query: SaleSummaryQuery): Promise<SaleSummary>;
1894
+ summary(query: SaleSummaryQueryV2): Promise<SaleSummaryV2>;
1979
1895
  get(id: string): Promise<Sale>;
1980
1896
  update(id: string, patch: Omit<SaleUpdateInput, 'service' | 'services'> & {
1981
1897
  service?: TService | null;
@@ -1985,6 +1901,10 @@ interface SalesClient<TService extends string = string> {
1985
1901
  }>;
1986
1902
  }): Promise<Sale>;
1987
1903
  delete(id: string): Promise<void>;
1904
+ /** Phone-keyed customer rollups (sk-only). */
1905
+ customers: SalesCustomersClient;
1906
+ /** Business config (pk or sk). */
1907
+ business: SalesBusinessClient;
1988
1908
  }
1989
1909
  declare function createSalesClient<TService extends string = string>(config: SalesClientConfig): SalesClient<TService>;
1990
1910
 
@@ -2016,6 +1936,12 @@ declare function fromMinor(cents: number, currency: SupportedCurrency): number;
2016
1936
  * Uses the built-in `Intl.NumberFormat` — no extra dependency.
2017
1937
  */
2018
1938
  declare function formatMoney(cents: number, currency: SupportedCurrency, locale?: string): string;
1939
+ /**
1940
+ * Format an ISO timestamp in a specific IANA time zone (e.g. `America/Toronto`),
1941
+ * so dashboards stop hand-rolling `Intl`. Defaults to a short date-time; pass
1942
+ * `opts` to override fields. The `timeZone` is always forced to the argument.
1943
+ */
1944
+ declare function formatDateInTz(iso: string, timeZone: string, opts?: Intl.DateTimeFormatOptions, locale?: string): string;
2019
1945
 
2020
1946
  /**
2021
1947
  * Error thrown by the sales client (`createSalesClient`) on a
@@ -2144,6 +2070,11 @@ interface CreateTrackingOptions<TRegistry extends TriggerRegistryConfig> {
2144
2070
  * loudly. Leave off in prod; turn on in dev to catch shape bugs early.
2145
2071
  */
2146
2072
  debug?: boolean;
2073
+ /**
2074
+ * Phone-field display + default-country config, read by `usePhoneField` /
2075
+ * `<PhoneField>`. Display is customizable; the transmitted value is always E.164.
2076
+ */
2077
+ phone?: PhoneConfig;
2147
2078
  }
2148
2079
  interface TrackingProviderProps {
2149
2080
  /**
@@ -2179,4 +2110,71 @@ interface CreateTrackingResult<TRegistry extends TriggerRegistryConfig> {
2179
2110
  }
2180
2111
  declare function createTracking<TRegistry extends TriggerRegistryConfig>(options: CreateTrackingOptions<TRegistry>): CreateTrackingResult<TRegistry>;
2181
2112
 
2182
- export { AranovaApiError, type AutomaticEventName, ConsentBanner, type ConsentBannerProps, type ConsentState, type CreateTrackingOptions, type CreateTrackingResult, type CtaClickConfig, type CtaClickMetadata, type CurrencyRevenue, type EventConfig, type EventMetadata, type EventName, type FormStartConfig, type FormStartMetadata, type FormSubmitConfig, type FormSubmitMetadata, GoogleAdsTracking, type GoogleAdsTrackingProps, type JsonValue, type ManualEventName, type MultiPageSessionConfig, type MultiPageSessionMetadata, type PageViewConfig, type PageViewMetadata, type PhoneClickConfig, type PhoneClickMetadata, type PublicServiceItem, type RegisteredAutomaticEvents, type RegisteredManualEvents, type Sale, type SaleCursorPage, type SaleInput, type SaleItem, type SaleItemInput, type SaleListPage, type SaleListQuery, type SaleService, type SaleServiceInput, type SaleSummary, type SaleSummaryQuery, type SaleUpdateInput, type SalesCategoryBreakdown, type SalesClient, type SalesClientConfig, type SalesServiceBreakdown, type SalesTransportConfig, type SalesTrendPoint, type ScrollDepthConfig, type ScrollDepthMetadata, type SpecificPageName, type SpecificPageVisitConfig, type SpecificPageVisitMetadata, type SupportedCurrency, TRACKING_PARAM_KEYS, type TimeOnSiteConfig, type TimeOnSiteMetadata, type TrackingClient, type TrackingClientContext, type TrackingEventCreatePayload, type TrackingInitConfig, type TrackingInstallSurface, type TrackingOverviewRange, type TrackingParams, type TrackingProviderProps, type TrackingSessionUpsertPayload, type TriggerRegistryConfig, type TypedTrackEventOptions, type TypedTrackingClient, type UseConsentResult, captureTrackingParamsFromLocation, createSalesClient, createTracking, createTrackingClientContext, createTrackingEventCreatePayload, createTrackingSessionUpsertPayload, fetchServices, formatMoney, fromMinor, getConsentState, resetConsent, setConsentState, toMinor, useConsent, useConsentState, useGclid, useTrackingParams };
2113
+ /** Carries `createTracking({ phone })` config to `usePhoneField`. Separate from the
2114
+ * tracking client context so `useTracking()`'s type is untouched.
2115
+ *
2116
+ * Caveat: this module is bundled into BOTH `@aranova/tracking-react` and
2117
+ * `@aranova/tracking-next`, so each package has its own context instance at
2118
+ * runtime. Within one installed package the provider + hook share it (correct);
2119
+ * mixing `createTracking` from one package with `usePhoneField` from the other
2120
+ * won't share config (the hook falls back to the `CA`/`national` defaults). Use a
2121
+ * single tracking package per app. */
2122
+ declare const PhoneConfigContext: react.Context<PhoneConfig | null>;
2123
+ /** Resolve the effective phone config (provider value or built-in defaults). */
2124
+ declare function usePhoneConfig(): {
2125
+ defaultCountry: CountryCode;
2126
+ display: PhoneDisplayFormat;
2127
+ };
2128
+ interface UsePhoneFieldOptions {
2129
+ defaultValue?: string;
2130
+ /** Overrides the provider's `defaultCountry`. */
2131
+ country?: CountryCode;
2132
+ /** Overrides the provider's `display` (applied to the settled value on blur). */
2133
+ display?: PhoneDisplayFormat;
2134
+ /** Notified with the canonical E.164 (or `null`) on every change. */
2135
+ onValueChange?: (e164: string | null) => void;
2136
+ }
2137
+ interface PhoneInputProps {
2138
+ value: string;
2139
+ onChange: (event: ChangeEvent<HTMLInputElement>) => void;
2140
+ onBlur: (event: FocusEvent<HTMLInputElement>) => void;
2141
+ type: 'tel';
2142
+ inputMode: 'tel';
2143
+ autoComplete: 'tel';
2144
+ }
2145
+ interface PhoneFieldApi {
2146
+ /** Display value for the `<input>` (live `AsYouType` while typing). */
2147
+ value: string;
2148
+ /** Canonical E.164 — what gets transmitted. `null` while invalid/incomplete. */
2149
+ e164: string | null;
2150
+ isValid: boolean;
2151
+ /** Validation message, surfaced only after blur with non-empty invalid input. */
2152
+ error: string | null;
2153
+ parsed: ParsedPhone;
2154
+ /** Spread onto an `<input>`: pre-wires value/onChange/onBlur/type/inputMode/autoComplete. */
2155
+ inputProps: PhoneInputProps;
2156
+ }
2157
+ /** Headless phone field — the client owns the markup. */
2158
+ declare function usePhoneField(opts?: UsePhoneFieldOptions): PhoneFieldApi;
2159
+ interface PhoneFieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'value' | 'onChange'> {
2160
+ country?: CountryCode;
2161
+ /** Controlled display value. */
2162
+ value?: string;
2163
+ /** Uncontrolled initial value. */
2164
+ defaultValue?: string;
2165
+ /** Receives the native change event (RHF `register().onChange` or your own); the
2166
+ * event's `target.value` is already `AsYouType`-formatted. */
2167
+ onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
2168
+ /** Receives the canonical E.164 (or `null`) on every change. */
2169
+ onE164Change?: (e164: string | null) => void;
2170
+ }
2171
+ /**
2172
+ * Batteries-included phone input. Composes identically with react-hook-form
2173
+ * `{...register('phone')}` and with controlled state — the `AsYouType` +
2174
+ * mutate-`e.target.value`-before-`onChange` technique lives inside, so RHF and
2175
+ * controlled parents both receive the formatted value, and the wire value stays
2176
+ * E.164.
2177
+ */
2178
+ declare const PhoneField: react.ForwardRefExoticComponent<PhoneFieldProps & react.RefAttributes<HTMLInputElement>>;
2179
+
2180
+ export { AranovaApiError, type AutomaticEventName, type BusinessConfig, type BusinessConfigFeatures, type BusinessConfigService, type CompareTo, ConsentBanner, type ConsentBannerProps, ConsentState, type CreateTrackingOptions, type CreateTrackingResult, type CtaClickConfig, type CtaClickMetadata, type CurrencyRevenue, type CustomerCurrencyDelta, type CustomerCurrencyTotal, type CustomerGetOptions, type CustomerGetResult, type CustomerKpis, type CustomerKpisDeltas, type CustomerKpisPrevious, type CustomerListPage, type CustomerListQuery, type CustomerProfile, type CustomerSegment, type CustomerSegmentCount, type CustomerSortField, type CustomerSummary, type CustomerSummaryQuery, type DistinctCustomersByCurrency, type EventConfig, type EventMetadata, type EventName, type FormStartConfig, type FormStartMetadata, FormSubmitConfig, FormSubmitMetadata, GoogleAdsTracking, type GoogleAdsTrackingProps, type Granularity, type ManualEventName, type MultiPageSessionConfig, type MultiPageSessionMetadata, type NamedRange, type PageViewConfig, type PageViewMetadata, ParsedPhone, type PhoneClickConfig, type PhoneClickMetadata, PhoneConfig, PhoneConfigContext, PhoneDisplayFormat, PhoneField, type PhoneFieldApi, type PhoneFieldProps, type PhoneInputProps, type PublicServiceItem, type RegisteredAutomaticEvents, type RegisteredManualEvents, type Sale, type SaleCursorPage, type SaleInput, type SaleItem, type SaleItemInput, type SaleKeysetSortField, type SaleListPage, type SaleListQuery, type SaleListQueryV2, type SaleService, type SaleServiceInput, type SaleSortOrder, type SaleSummary, type SaleSummaryPrevious, type SaleSummaryQuery, type SaleSummaryQueryV2, type SaleSummaryV2, type SaleUpdateInput, type SalesBusinessClient, type SalesCategoryBreakdown, type SalesClient, type SalesClientConfig, type SalesCustomersClient, type SalesServiceBreakdown, type SalesTransportConfig, type SalesTrendPoint, type ScrollDepthConfig, type ScrollDepthMetadata, type SpecificPageName, type SpecificPageVisitConfig, type SpecificPageVisitMetadata, type SummaryCurrencyDelta, type SummaryDeltas, type SummaryWindow, type SupportedCurrency, TRACKING_PARAM_KEYS, type TimeOnSiteConfig, type TimeOnSiteMetadata, type TrackingClient, TrackingClientContext, TrackingEventCreatePayload, TrackingInstallSurface, type TrackingOverviewRange, TrackingParams, type TrackingProviderProps, TrackingSessionUpsertPayload, type TriggerRegistryConfig, type TypedTrackEventOptions, type TypedTrackingClient, type UseConsentResult, type UsePhoneFieldOptions, captureTrackingParamsFromLocation, createSalesClient, createTracking, createTrackingClientContext, createTrackingEventCreatePayload, createTrackingSessionUpsertPayload, fetchServices, formatDateInTz, formatMoney, fromMinor, getConsentState, resetConsent, setConsentState, toMinor, useConsent, useConsentState, useGclid, usePhoneConfig, usePhoneField, useTrackingParams };