@aranova/tracking-react 0.16.2 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -758,8 +758,8 @@ function thresholdMet(goal, eventType, metadata) {
758
758
  return typeof metadata.page_name === "string" && metadata.page_name === t.page_name;
759
759
  case "page_view":
760
760
  case "form_start":
761
+ case "phone_click":
761
762
  return true;
762
- // no threshold — fire whenever the detector emits
763
763
  default:
764
764
  return false;
765
765
  }
@@ -1418,7 +1418,11 @@ var phoneClickMetadataSchema = z8.object({
1418
1418
  }).strict(),
1419
1419
  section: z8.string().nullable().optional()
1420
1420
  }).strict();
1421
- var phoneClickConfigSchema = z8.object({}).strict();
1421
+ var phoneClickConfigSchema = z8.object({
1422
+ autoCapture: z8.object({
1423
+ selector: z8.string().optional()
1424
+ }).strict().optional()
1425
+ }).strict();
1422
1426
 
1423
1427
  // ../tracking-core/src/events/scroll-depth.ts
1424
1428
  import { z as z9 } from "zod";
@@ -2054,6 +2058,99 @@ function attachCtaClickCapture(client, config) {
2054
2058
  };
2055
2059
  }
2056
2060
 
2061
+ // ../tracking-core/src/phone.ts
2062
+ import { AsYouType, parsePhoneNumberFromString } from "libphonenumber-js";
2063
+ var DEFAULT_PHONE_COUNTRY = "CA";
2064
+ function parsePhone(raw, country) {
2065
+ const region = country ?? DEFAULT_PHONE_COUNTRY;
2066
+ const parsed = parsePhoneNumberFromString(raw ?? "", region);
2067
+ if (!parsed) {
2068
+ return { e164: null, national: "", international: "", country: region, isValid: false };
2069
+ }
2070
+ const isValid = parsed.isValid();
2071
+ return {
2072
+ // E.164 is only surfaced for a *valid* number — a possible-but-invalid input
2073
+ // (e.g. too few digits) still parses but must not be transmitted.
2074
+ e164: isValid ? parsed.number : null,
2075
+ national: parsed.formatNational(),
2076
+ international: parsed.formatInternational(),
2077
+ country: parsed.country ?? region,
2078
+ isValid
2079
+ };
2080
+ }
2081
+ function toE164(raw, country) {
2082
+ return parsePhone(raw, country).e164;
2083
+ }
2084
+ function formatPhone(value, format = "national", country) {
2085
+ const parsed = parsePhone(value, country);
2086
+ if (typeof format === "function") return format(parsed);
2087
+ switch (format) {
2088
+ case "international":
2089
+ return parsed.international || value;
2090
+ case "e164":
2091
+ return parsed.e164 ?? value;
2092
+ case "national":
2093
+ default:
2094
+ return parsed.national || value;
2095
+ }
2096
+ }
2097
+ function formatPhoneAsTyped(raw, country) {
2098
+ return new AsYouType(country ?? DEFAULT_PHONE_COUNTRY).input(raw ?? "");
2099
+ }
2100
+
2101
+ // ../tracking-core/src/triggers/phone-click-capture.ts
2102
+ var DEFAULT_TEL_SELECTOR = 'a[href^="tel:"]';
2103
+ function safeDecodeURIComponent(value) {
2104
+ try {
2105
+ return decodeURIComponent(value);
2106
+ } catch {
2107
+ return value;
2108
+ }
2109
+ }
2110
+ function resolvePhoneNumber(el) {
2111
+ const href = el instanceof HTMLAnchorElement ? el.href : el.getAttribute("href") ?? "";
2112
+ const raw = safeDecodeURIComponent(href.replace(/^tel:/i, "").split(";")[0]).trim();
2113
+ return toE164(raw) ?? raw;
2114
+ }
2115
+ function attachPhoneClickCapture(client, config) {
2116
+ if (typeof window === "undefined" || typeof document === "undefined") {
2117
+ return () => {
2118
+ };
2119
+ }
2120
+ const autoCapture = config.autoCapture;
2121
+ if (!autoCapture) {
2122
+ return () => {
2123
+ };
2124
+ }
2125
+ const selector = autoCapture.selector ?? DEFAULT_TEL_SELECTOR;
2126
+ function onClick(event) {
2127
+ const target = event.target;
2128
+ if (!(target instanceof Element)) return;
2129
+ let matched = null;
2130
+ try {
2131
+ matched = target.closest(selector);
2132
+ } catch {
2133
+ return;
2134
+ }
2135
+ if (matched === null) return;
2136
+ const metadata = {
2137
+ phone_number: resolvePhoneNumber(matched),
2138
+ page: { path: window.location.pathname },
2139
+ section: matched.getAttribute("data-aranova-section")
2140
+ };
2141
+ client.trackEvent({
2142
+ eventType: "phone_click",
2143
+ metadata,
2144
+ pageUrl: window.location.href,
2145
+ occurredAt: null
2146
+ });
2147
+ }
2148
+ document.addEventListener("click", onClick, true);
2149
+ return () => {
2150
+ document.removeEventListener("click", onClick, true);
2151
+ };
2152
+ }
2153
+
2057
2154
  // ../tracking-core/src/resources/sales/errors.ts
2058
2155
  var AranovaApiError = class extends Error {
2059
2156
  constructor(message, options) {
@@ -2341,7 +2438,10 @@ var saleCreateSchema = z12.object({
2341
2438
  metadata: metadataSchema.nullable().optional(),
2342
2439
  customer_name: customerNameSchema.nullable().optional(),
2343
2440
  customer_phone: customerPhoneSchema.nullable().optional(),
2344
- customer_email: customerEmailSchema.nullable().optional()
2441
+ customer_email: customerEmailSchema.nullable().optional(),
2442
+ // CASL consent attestation: the customer agreed to receive SMS. Recorded
2443
+ // with a timestamp server-side; every SMS send path gates on it.
2444
+ sms_consent: z12.boolean().default(false)
2345
2445
  }).strict().superRefine((val, ctx) => refineServiceXor(val, ctx, { requireAmount: true }));
2346
2446
  var saleUpdateSchema = z12.object({
2347
2447
  description: z12.string().nullable().optional(),
@@ -2354,7 +2454,9 @@ var saleUpdateSchema = z12.object({
2354
2454
  metadata: metadataSchema.nullable().optional(),
2355
2455
  customer_name: customerNameSchema.nullable().optional(),
2356
2456
  customer_phone: customerPhoneSchema.nullable().optional(),
2357
- customer_email: customerEmailSchema.nullable().optional()
2457
+ customer_email: customerEmailSchema.nullable().optional(),
2458
+ // NOT NULL server-side: omit to leave unchanged (explicit null is rejected).
2459
+ sms_consent: z12.boolean().optional()
2358
2460
  }).strict().superRefine((val, ctx) => refineServiceXor(val, ctx, { requireAmount: false }));
2359
2461
  var TRACKING_RANGES = ["24h", "7d", "30d"];
2360
2462
  var NAMED_RANGES = [
@@ -2376,46 +2478,6 @@ async function fetchServices(config) {
2376
2478
  return salesRequest(config, "GET", "/services");
2377
2479
  }
2378
2480
 
2379
- // ../tracking-core/src/phone.ts
2380
- import { AsYouType, parsePhoneNumberFromString } from "libphonenumber-js";
2381
- var DEFAULT_PHONE_COUNTRY = "CA";
2382
- function parsePhone(raw, country) {
2383
- const region = country ?? DEFAULT_PHONE_COUNTRY;
2384
- const parsed = parsePhoneNumberFromString(raw ?? "", region);
2385
- if (!parsed) {
2386
- return { e164: null, national: "", international: "", country: region, isValid: false };
2387
- }
2388
- const isValid = parsed.isValid();
2389
- return {
2390
- // E.164 is only surfaced for a *valid* number — a possible-but-invalid input
2391
- // (e.g. too few digits) still parses but must not be transmitted.
2392
- e164: isValid ? parsed.number : null,
2393
- national: parsed.formatNational(),
2394
- international: parsed.formatInternational(),
2395
- country: parsed.country ?? region,
2396
- isValid
2397
- };
2398
- }
2399
- function toE164(raw, country) {
2400
- return parsePhone(raw, country).e164;
2401
- }
2402
- function formatPhone(value, format = "national", country) {
2403
- const parsed = parsePhone(value, country);
2404
- if (typeof format === "function") return format(parsed);
2405
- switch (format) {
2406
- case "international":
2407
- return parsed.international || value;
2408
- case "e164":
2409
- return parsed.e164 ?? value;
2410
- case "national":
2411
- default:
2412
- return parsed.national || value;
2413
- }
2414
- }
2415
- function formatPhoneAsTyped(raw, country) {
2416
- return new AsYouType(country ?? DEFAULT_PHONE_COUNTRY).input(raw ?? "");
2417
- }
2418
-
2419
2481
  // ../tracking-core/src/phone-field.ts
2420
2482
  function phoneField(name, raw, country) {
2421
2483
  return { name, type: "phone", value: toE164(raw, country) };
@@ -2738,7 +2800,7 @@ function GoogleAdsTracking(props) {
2738
2800
  import { createContext as createContext2, useContext as useContext2, useEffect as useEffect5, useMemo as useMemo4 } from "react";
2739
2801
 
2740
2802
  // package.json
2741
- var version = "0.16.2";
2803
+ var version = "0.17.1";
2742
2804
 
2743
2805
  // ../tracking-core/src/phone-react.tsx
2744
2806
  import {
@@ -2951,6 +3013,10 @@ function createTracking(options) {
2951
3013
  if (ctaClick) {
2952
3014
  detachers.push(attachCtaClickCapture(detectorClient, ctaClick));
2953
3015
  }
3016
+ const phoneClick = triggers.manual?.phone_click;
3017
+ if (phoneClick) {
3018
+ detachers.push(attachPhoneClickCapture(detectorClient, phoneClick));
3019
+ }
2954
3020
  return () => {
2955
3021
  for (let i = detachers.length - 1; i >= 0; i--) {
2956
3022
  detachers[i]();