@aranova/tracking-react 0.18.1 → 0.19.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.mts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +193 -58
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +191 -58
- package/dist/index.mjs.map +1 -1
- package/dist/{phone-utils-CVCzW04Z.d.mts → phone-utils-B2vHJkNz.d.mts} +10 -0
- package/dist/{phone-utils-CVCzW04Z.d.ts → phone-utils-B2vHJkNz.d.ts} +10 -0
- package/dist/phone.d.mts +1 -1
- package/dist/phone.d.ts +1 -1
- package/dist/phone.js +40 -40
- package/dist/phone.js.map +1 -1
- package/dist/phone.mjs +40 -40
- package/dist/phone.mjs.map +1 -1
- package/dist/{sales-QqKh6_iy.d.mts → sales--WzOzyiO.d.mts} +18 -1
- package/dist/{sales-QqKh6_iy.d.ts → sales--WzOzyiO.d.ts} +18 -1
- package/dist/sales.d.mts +2 -1
- package/dist/sales.d.ts +2 -1
- package/dist/sales.js +97 -17
- package/dist/sales.js.map +1 -1
- package/dist/sales.mjs +97 -17
- package/dist/sales.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -4,6 +4,140 @@ import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
|
4
4
|
// src/hooks.ts
|
|
5
5
|
import { useCallback, useEffect, useState } from "react";
|
|
6
6
|
|
|
7
|
+
// ../tracking-core/src/phone.ts
|
|
8
|
+
import { AsYouType, parsePhoneNumberFromString } from "libphonenumber-js";
|
|
9
|
+
var DEFAULT_PHONE_COUNTRY = "CA";
|
|
10
|
+
function parsePhone(raw, country) {
|
|
11
|
+
const region = country ?? DEFAULT_PHONE_COUNTRY;
|
|
12
|
+
const parsed = parsePhoneNumberFromString(raw ?? "", region);
|
|
13
|
+
if (!parsed) {
|
|
14
|
+
return { e164: null, national: "", international: "", country: region, isValid: false };
|
|
15
|
+
}
|
|
16
|
+
const isValid = parsed.isValid();
|
|
17
|
+
return {
|
|
18
|
+
// E.164 is only surfaced for a *valid* number — a possible-but-invalid input
|
|
19
|
+
// (e.g. too few digits) still parses but must not be transmitted.
|
|
20
|
+
e164: isValid ? parsed.number : null,
|
|
21
|
+
national: parsed.formatNational(),
|
|
22
|
+
international: parsed.formatInternational(),
|
|
23
|
+
country: parsed.country ?? region,
|
|
24
|
+
isValid
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function toE164(raw, country) {
|
|
28
|
+
return parsePhone(raw, country).e164;
|
|
29
|
+
}
|
|
30
|
+
function formatPhone(value, format = "national", country) {
|
|
31
|
+
const parsed = parsePhone(value, country);
|
|
32
|
+
if (typeof format === "function") return format(parsed);
|
|
33
|
+
switch (format) {
|
|
34
|
+
case "international":
|
|
35
|
+
return parsed.international || value;
|
|
36
|
+
case "e164":
|
|
37
|
+
return parsed.e164 ?? value;
|
|
38
|
+
case "national":
|
|
39
|
+
default:
|
|
40
|
+
return parsed.national || value;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function formatPhoneAsTyped(raw, country) {
|
|
44
|
+
return new AsYouType(country ?? DEFAULT_PHONE_COUNTRY).input(raw ?? "");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ../tracking-core/src/user-data.ts
|
|
48
|
+
var EMAIL_SHAPE = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
|
|
49
|
+
var EMAIL_NAME_HINT = /e[-_]?mail/i;
|
|
50
|
+
var PHONE_NAME_HINT = /(^|[^a-z])(phone|tel|mobile|cell)/i;
|
|
51
|
+
var stash = { email: null, phoneNumber: null };
|
|
52
|
+
function normalizeEmail(raw) {
|
|
53
|
+
if (typeof raw !== "string") return null;
|
|
54
|
+
const cleaned = raw.trim().toLowerCase();
|
|
55
|
+
return EMAIL_SHAPE.test(cleaned) ? cleaned : null;
|
|
56
|
+
}
|
|
57
|
+
function fieldText(field, key) {
|
|
58
|
+
const value = field[key];
|
|
59
|
+
return typeof value === "string" ? value : "";
|
|
60
|
+
}
|
|
61
|
+
function extractUserDataFromFormFields(fields, country) {
|
|
62
|
+
const result = { email: null, phoneNumber: null };
|
|
63
|
+
if (!Array.isArray(fields)) return result;
|
|
64
|
+
const passes = [
|
|
65
|
+
(field, _hint, type) => fieldText(field, "type").toLowerCase() === type,
|
|
66
|
+
(field, hint) => hint.test(fieldText(field, "name")) || hint.test(fieldText(field, "label"))
|
|
67
|
+
];
|
|
68
|
+
for (const matches of passes) {
|
|
69
|
+
for (const raw of fields) {
|
|
70
|
+
if (raw === null || typeof raw !== "object") continue;
|
|
71
|
+
const field = raw;
|
|
72
|
+
if (typeof field.value !== "string" || field.value.length === 0) continue;
|
|
73
|
+
if (result.email === null && matches(field, EMAIL_NAME_HINT, "email")) {
|
|
74
|
+
result.email = normalizeEmail(field.value);
|
|
75
|
+
}
|
|
76
|
+
if (result.phoneNumber === null && matches(field, PHONE_NAME_HINT, "tel")) {
|
|
77
|
+
try {
|
|
78
|
+
result.phoneNumber = toE164(field.value, country);
|
|
79
|
+
} catch {
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (result.email !== null && result.phoneNumber !== null) break;
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
function stashUserData(data, country) {
|
|
88
|
+
const email = normalizeEmail(data.email);
|
|
89
|
+
let phoneNumber = null;
|
|
90
|
+
if (typeof data.phone === "string" && data.phone.length > 0) {
|
|
91
|
+
try {
|
|
92
|
+
phoneNumber = toE164(data.phone, country);
|
|
93
|
+
} catch {
|
|
94
|
+
phoneNumber = null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
stash = {
|
|
98
|
+
email: email ?? stash.email,
|
|
99
|
+
phoneNumber: phoneNumber ?? stash.phoneNumber
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function stashUserDataFromFormFields(fields, country) {
|
|
103
|
+
try {
|
|
104
|
+
const extracted = extractUserDataFromFormFields(fields, country);
|
|
105
|
+
stash = {
|
|
106
|
+
email: extracted.email ?? stash.email,
|
|
107
|
+
phoneNumber: extracted.phoneNumber ?? stash.phoneNumber
|
|
108
|
+
};
|
|
109
|
+
} catch {
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function clearStashedUserData() {
|
|
113
|
+
stash = { email: null, phoneNumber: null };
|
|
114
|
+
}
|
|
115
|
+
function applyUserDataForConversion(explicit, country) {
|
|
116
|
+
if (typeof window === "undefined" || typeof window.gtag !== "function") return false;
|
|
117
|
+
let email = stash.email;
|
|
118
|
+
let phoneNumber = stash.phoneNumber;
|
|
119
|
+
if (explicit) {
|
|
120
|
+
const normalizedEmail = normalizeEmail(explicit.email);
|
|
121
|
+
if (normalizedEmail) email = normalizedEmail;
|
|
122
|
+
if (typeof explicit.phone === "string" && explicit.phone.length > 0) {
|
|
123
|
+
try {
|
|
124
|
+
phoneNumber = toE164(explicit.phone, country) ?? phoneNumber;
|
|
125
|
+
} catch {
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (email === null && phoneNumber === null) return false;
|
|
130
|
+
try {
|
|
131
|
+
window.gtag("set", "user_data", {
|
|
132
|
+
...email !== null ? { email } : {},
|
|
133
|
+
...phoneNumber !== null ? { phone_number: phoneNumber } : {}
|
|
134
|
+
});
|
|
135
|
+
return true;
|
|
136
|
+
} catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
7
141
|
// ../tracking-core/src/consent.ts
|
|
8
142
|
var CONSENT_STATE_KEY = "consent_state";
|
|
9
143
|
var CONSENT_TIMESTAMP_KEY = "consent_timestamp";
|
|
@@ -89,6 +223,13 @@ function setConsentState(state, options) {
|
|
|
89
223
|
} catch {
|
|
90
224
|
}
|
|
91
225
|
pushConsentToPlatforms(state);
|
|
226
|
+
if (state === "denied") {
|
|
227
|
+
clearStashedUserData();
|
|
228
|
+
try {
|
|
229
|
+
if (typeof window.gtag === "function") window.gtag("set", "user_data", null);
|
|
230
|
+
} catch {
|
|
231
|
+
}
|
|
232
|
+
}
|
|
92
233
|
notifyConsentChanged({ state, source: "explicit", updatedAt, expiresAt });
|
|
93
234
|
}
|
|
94
235
|
function optIn() {
|
|
@@ -113,6 +254,11 @@ function resetConsent() {
|
|
|
113
254
|
var TRACKING_COOKIE_MAX_AGE_SECONDS = 7776e3;
|
|
114
255
|
var TRACKING_PARAM_KEYS = [
|
|
115
256
|
"gclid",
|
|
257
|
+
// Google's iOS/Safari replacement click IDs — issued when privacy features
|
|
258
|
+
// withhold gclid (wbraid: web-to-web, gbraid: app-to-web). First-class
|
|
259
|
+
// citizens: captured, persisted, and attributed exactly like gclid.
|
|
260
|
+
"wbraid",
|
|
261
|
+
"gbraid",
|
|
116
262
|
"fbclid",
|
|
117
263
|
"utm_source",
|
|
118
264
|
"utm_medium",
|
|
@@ -123,6 +269,8 @@ var TRACKING_PARAM_KEYS = [
|
|
|
123
269
|
function createEmptyTrackingParams() {
|
|
124
270
|
return {
|
|
125
271
|
gclid: null,
|
|
272
|
+
wbraid: null,
|
|
273
|
+
gbraid: null,
|
|
126
274
|
fbclid: null,
|
|
127
275
|
utm_source: null,
|
|
128
276
|
utm_medium: null,
|
|
@@ -454,6 +602,8 @@ function buildLandingPayloadFields(sessionId, override) {
|
|
|
454
602
|
if (params === null) return {};
|
|
455
603
|
return {
|
|
456
604
|
landing_gclid: params.gclid ?? null,
|
|
605
|
+
landing_wbraid: params.wbraid ?? null,
|
|
606
|
+
landing_gbraid: params.gbraid ?? null,
|
|
457
607
|
landing_fbclid: params.fbclid ?? null,
|
|
458
608
|
landing_utm_source: params.utm_source ?? null,
|
|
459
609
|
landing_utm_medium: params.utm_medium ?? null,
|
|
@@ -481,6 +631,8 @@ function createTrackingSessionUpsertPayload(trackingParams, input, context) {
|
|
|
481
631
|
session_id: input.sessionId,
|
|
482
632
|
visitor_id: input.visitorId ?? null,
|
|
483
633
|
gclid: trackingParams.gclid,
|
|
634
|
+
wbraid: trackingParams.wbraid,
|
|
635
|
+
gbraid: trackingParams.gbraid,
|
|
484
636
|
fbclid: trackingParams.fbclid,
|
|
485
637
|
fbc: getFbcCookie(),
|
|
486
638
|
fbp: getFbpCookie(),
|
|
@@ -502,6 +654,8 @@ function createTrackingEventCreatePayload(trackingParams, input, context) {
|
|
|
502
654
|
session_id: input.sessionId,
|
|
503
655
|
event_type: input.eventType,
|
|
504
656
|
gclid: trackingParams.gclid,
|
|
657
|
+
wbraid: trackingParams.wbraid,
|
|
658
|
+
gbraid: trackingParams.gbraid,
|
|
505
659
|
fbclid: trackingParams.fbclid,
|
|
506
660
|
fbc: getFbcCookie(),
|
|
507
661
|
fbp: getFbpCookie(),
|
|
@@ -531,14 +685,12 @@ function markFired(input) {
|
|
|
531
685
|
} catch {
|
|
532
686
|
}
|
|
533
687
|
}
|
|
534
|
-
function
|
|
688
|
+
function fireConversionWithConsent(input, options) {
|
|
689
|
+
if (getConsentState() === "denied") return;
|
|
535
690
|
if (alreadyFired(input)) return;
|
|
691
|
+
applyUserDataForConversion(options?.userData);
|
|
536
692
|
if (fireGtagConversion(input)) markFired(input);
|
|
537
693
|
}
|
|
538
|
-
function fireConversionWithConsent(input) {
|
|
539
|
-
if (getConsentState() === "denied") return;
|
|
540
|
-
fireOnce(input);
|
|
541
|
-
}
|
|
542
694
|
|
|
543
695
|
// ../tracking-core/src/resources/conversion-config.ts
|
|
544
696
|
function isStringMap(value) {
|
|
@@ -983,6 +1135,7 @@ var TrackingConfigRuntime = class {
|
|
|
983
1135
|
if (!firing || getConsentState() === "denied") continue;
|
|
984
1136
|
const currency = item.currency ?? firing.currency ?? null;
|
|
985
1137
|
const value = item.value ?? (firing.value_cents != null && currency ? fromMinor(firing.value_cents, currency) : null);
|
|
1138
|
+
applyUserDataForConversion();
|
|
986
1139
|
fireGtagConversion({
|
|
987
1140
|
sendTo: firing.send_to,
|
|
988
1141
|
value,
|
|
@@ -1514,6 +1667,8 @@ function createTrackingClient(config) {
|
|
|
1514
1667
|
session_id: sessionId,
|
|
1515
1668
|
visitor_id: visitorId,
|
|
1516
1669
|
gclid: params.gclid,
|
|
1670
|
+
wbraid: params.wbraid,
|
|
1671
|
+
gbraid: params.gbraid,
|
|
1517
1672
|
fbclid: params.fbclid,
|
|
1518
1673
|
fbc: getFbcCookie(),
|
|
1519
1674
|
fbp: getFbpCookie(),
|
|
@@ -1560,6 +1715,13 @@ function createTrackingClient(config) {
|
|
|
1560
1715
|
function trackEvent(input) {
|
|
1561
1716
|
if (destroyed) return;
|
|
1562
1717
|
if (!input || typeof input.eventType !== "string" || input.eventType.length === 0) return;
|
|
1718
|
+
if (input.eventType === "form_submit" && getConsentState() !== "denied") {
|
|
1719
|
+
try {
|
|
1720
|
+
const fields = input.metadata?.form?.fields;
|
|
1721
|
+
if (fields) stashUserDataFromFormFields(fields, config.phone?.defaultCountry);
|
|
1722
|
+
} catch {
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1563
1725
|
const occurredAt = input.occurredAt instanceof Date ? input.occurredAt.toISOString() : typeof input.occurredAt === "string" ? input.occurredAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
1564
1726
|
queue.push({
|
|
1565
1727
|
event_type: input.eventType,
|
|
@@ -2523,46 +2685,6 @@ function attachCtaClickCapture(client, config) {
|
|
|
2523
2685
|
};
|
|
2524
2686
|
}
|
|
2525
2687
|
|
|
2526
|
-
// ../tracking-core/src/phone.ts
|
|
2527
|
-
import { AsYouType, parsePhoneNumberFromString } from "libphonenumber-js";
|
|
2528
|
-
var DEFAULT_PHONE_COUNTRY = "CA";
|
|
2529
|
-
function parsePhone(raw, country) {
|
|
2530
|
-
const region = country ?? DEFAULT_PHONE_COUNTRY;
|
|
2531
|
-
const parsed = parsePhoneNumberFromString(raw ?? "", region);
|
|
2532
|
-
if (!parsed) {
|
|
2533
|
-
return { e164: null, national: "", international: "", country: region, isValid: false };
|
|
2534
|
-
}
|
|
2535
|
-
const isValid = parsed.isValid();
|
|
2536
|
-
return {
|
|
2537
|
-
// E.164 is only surfaced for a *valid* number — a possible-but-invalid input
|
|
2538
|
-
// (e.g. too few digits) still parses but must not be transmitted.
|
|
2539
|
-
e164: isValid ? parsed.number : null,
|
|
2540
|
-
national: parsed.formatNational(),
|
|
2541
|
-
international: parsed.formatInternational(),
|
|
2542
|
-
country: parsed.country ?? region,
|
|
2543
|
-
isValid
|
|
2544
|
-
};
|
|
2545
|
-
}
|
|
2546
|
-
function toE164(raw, country) {
|
|
2547
|
-
return parsePhone(raw, country).e164;
|
|
2548
|
-
}
|
|
2549
|
-
function formatPhone(value, format = "national", country) {
|
|
2550
|
-
const parsed = parsePhone(value, country);
|
|
2551
|
-
if (typeof format === "function") return format(parsed);
|
|
2552
|
-
switch (format) {
|
|
2553
|
-
case "international":
|
|
2554
|
-
return parsed.international || value;
|
|
2555
|
-
case "e164":
|
|
2556
|
-
return parsed.e164 ?? value;
|
|
2557
|
-
case "national":
|
|
2558
|
-
default:
|
|
2559
|
-
return parsed.national || value;
|
|
2560
|
-
}
|
|
2561
|
-
}
|
|
2562
|
-
function formatPhoneAsTyped(raw, country) {
|
|
2563
|
-
return new AsYouType(country ?? DEFAULT_PHONE_COUNTRY).input(raw ?? "");
|
|
2564
|
-
}
|
|
2565
|
-
|
|
2566
2688
|
// ../tracking-core/src/triggers/phone-click-capture.ts
|
|
2567
2689
|
var DEFAULT_TEL_SELECTOR = 'a[href^="tel:"]';
|
|
2568
2690
|
function safeDecodeURIComponent(value) {
|
|
@@ -2672,6 +2794,8 @@ async function salesRequest(config, method, path, body) {
|
|
|
2672
2794
|
// ../tracking-core/src/resources/sales/client.ts
|
|
2673
2795
|
function fireRecordedConversions(firing, input, recorded, sale, currency) {
|
|
2674
2796
|
if (!firing) return;
|
|
2797
|
+
const userData = input.customer_email || input.customer_phone ? { email: input.customer_email ?? null, phone: input.customer_phone ?? null } : null;
|
|
2798
|
+
if (userData && getConsentState() !== "denied") stashUserData(userData);
|
|
2675
2799
|
const txnBase = input.external_id ?? sale.id;
|
|
2676
2800
|
for (const item of recorded) {
|
|
2677
2801
|
if (!item.service) continue;
|
|
@@ -2686,12 +2810,15 @@ function fireRecordedConversions(firing, input, recorded, sale, currency) {
|
|
|
2686
2810
|
const config = firing.getFiring(item.service);
|
|
2687
2811
|
if (!config) continue;
|
|
2688
2812
|
const cents = item.amount_cents ?? config.value_cents ?? null;
|
|
2689
|
-
fireConversionWithConsent(
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2813
|
+
fireConversionWithConsent(
|
|
2814
|
+
{
|
|
2815
|
+
sendTo: config.send_to,
|
|
2816
|
+
value: cents != null ? fromMinor(cents, currency) : null,
|
|
2817
|
+
currency: config.currency ?? currency,
|
|
2818
|
+
transactionId: `${txnBase}:${item.service}`
|
|
2819
|
+
},
|
|
2820
|
+
{ userData }
|
|
2821
|
+
);
|
|
2695
2822
|
}
|
|
2696
2823
|
}
|
|
2697
2824
|
function createSalesClient(config) {
|
|
@@ -2726,6 +2853,7 @@ function createSalesClient(config) {
|
|
|
2726
2853
|
recordSale: record,
|
|
2727
2854
|
trackConversion(key, options) {
|
|
2728
2855
|
if (config.firing && "fireConversion" in config.firing) {
|
|
2856
|
+
if (options?.userData && getConsentState() !== "denied") stashUserData(options.userData);
|
|
2729
2857
|
config.firing.fireConversion(key, {
|
|
2730
2858
|
value: options?.value ?? void 0,
|
|
2731
2859
|
currency: options?.currency ?? config.defaultCurrency ?? void 0,
|
|
@@ -2738,12 +2866,15 @@ function createSalesClient(config) {
|
|
|
2738
2866
|
const currency = firing.currency ?? options?.currency ?? config.defaultCurrency ?? null;
|
|
2739
2867
|
const cents = firing.value_cents ?? null;
|
|
2740
2868
|
const value = options?.value ?? (cents != null && currency ? fromMinor(cents, currency) : null);
|
|
2741
|
-
fireConversionWithConsent(
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2869
|
+
fireConversionWithConsent(
|
|
2870
|
+
{
|
|
2871
|
+
sendTo: firing.send_to,
|
|
2872
|
+
value,
|
|
2873
|
+
currency,
|
|
2874
|
+
transactionId: options?.transactionId ?? null
|
|
2875
|
+
},
|
|
2876
|
+
{ userData: options?.userData }
|
|
2877
|
+
);
|
|
2747
2878
|
},
|
|
2748
2879
|
async list(query) {
|
|
2749
2880
|
const { cursor, limit, sort, order, want_total, ...filters } = query ?? {};
|
|
@@ -3288,7 +3419,7 @@ function GoogleAdsTracking(props) {
|
|
|
3288
3419
|
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect5, useMemo as useMemo4 } from "react";
|
|
3289
3420
|
|
|
3290
3421
|
// package.json
|
|
3291
|
-
var version = "0.
|
|
3422
|
+
var version = "0.19.0";
|
|
3292
3423
|
|
|
3293
3424
|
// ../tracking-core/src/phone-react.tsx
|
|
3294
3425
|
import {
|
|
@@ -3558,6 +3689,7 @@ export {
|
|
|
3558
3689
|
TRACKING_PARAM_KEYS,
|
|
3559
3690
|
TRACKING_RANGES,
|
|
3560
3691
|
captureTrackingParamsFromLocation,
|
|
3692
|
+
clearStashedUserData,
|
|
3561
3693
|
createSalesClient,
|
|
3562
3694
|
createTracking,
|
|
3563
3695
|
createTrackingClientContext,
|
|
@@ -3586,6 +3718,7 @@ export {
|
|
|
3586
3718
|
saleUpdateSchema,
|
|
3587
3719
|
salesRequest,
|
|
3588
3720
|
setConsentState,
|
|
3721
|
+
stashUserData,
|
|
3589
3722
|
toE164,
|
|
3590
3723
|
toMinor,
|
|
3591
3724
|
useConsent,
|