@aranova/tracking-react 0.18.1 → 0.19.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/sales.js CHANGED
@@ -38,6 +38,79 @@ __export(sales_exports, {
38
38
  });
39
39
  module.exports = __toCommonJS(sales_exports);
40
40
 
41
+ // ../tracking-core/src/phone.ts
42
+ var import_libphonenumber_js = require("libphonenumber-js");
43
+ var DEFAULT_PHONE_COUNTRY = "CA";
44
+ function parsePhone(raw, country) {
45
+ const region = country ?? DEFAULT_PHONE_COUNTRY;
46
+ const parsed = (0, import_libphonenumber_js.parsePhoneNumberFromString)(raw ?? "", region);
47
+ if (!parsed) {
48
+ return { e164: null, national: "", international: "", country: region, isValid: false };
49
+ }
50
+ const isValid = parsed.isValid();
51
+ return {
52
+ // E.164 is only surfaced for a *valid* number — a possible-but-invalid input
53
+ // (e.g. too few digits) still parses but must not be transmitted.
54
+ e164: isValid ? parsed.number : null,
55
+ national: parsed.formatNational(),
56
+ international: parsed.formatInternational(),
57
+ country: parsed.country ?? region,
58
+ isValid
59
+ };
60
+ }
61
+ function toE164(raw, country) {
62
+ return parsePhone(raw, country).e164;
63
+ }
64
+
65
+ // ../tracking-core/src/user-data.ts
66
+ var EMAIL_SHAPE = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
67
+ var stash = { email: null, phoneNumber: null };
68
+ function normalizeEmail(raw) {
69
+ if (typeof raw !== "string") return null;
70
+ const cleaned = raw.trim().toLowerCase();
71
+ return EMAIL_SHAPE.test(cleaned) ? cleaned : null;
72
+ }
73
+ function stashUserData(data, country) {
74
+ const email = normalizeEmail(data.email);
75
+ let phoneNumber = null;
76
+ if (typeof data.phone === "string" && data.phone.length > 0) {
77
+ try {
78
+ phoneNumber = toE164(data.phone, country);
79
+ } catch {
80
+ phoneNumber = null;
81
+ }
82
+ }
83
+ stash = {
84
+ email: email ?? stash.email,
85
+ phoneNumber: phoneNumber ?? stash.phoneNumber
86
+ };
87
+ }
88
+ function applyUserDataForConversion(explicit, country) {
89
+ if (typeof window === "undefined" || typeof window.gtag !== "function") return false;
90
+ let email = stash.email;
91
+ let phoneNumber = stash.phoneNumber;
92
+ if (explicit) {
93
+ const normalizedEmail = normalizeEmail(explicit.email);
94
+ if (normalizedEmail) email = normalizedEmail;
95
+ if (typeof explicit.phone === "string" && explicit.phone.length > 0) {
96
+ try {
97
+ phoneNumber = toE164(explicit.phone, country) ?? phoneNumber;
98
+ } catch {
99
+ }
100
+ }
101
+ }
102
+ if (email === null && phoneNumber === null) return false;
103
+ try {
104
+ window.gtag("set", "user_data", {
105
+ ...email !== null ? { email } : {},
106
+ ...phoneNumber !== null ? { phone_number: phoneNumber } : {}
107
+ });
108
+ return true;
109
+ } catch {
110
+ return false;
111
+ }
112
+ }
113
+
41
114
  // ../tracking-core/src/consent.ts
42
115
  var CONSENT_STATE_KEY = "consent_state";
43
116
  var CONSENT_TIMESTAMP_KEY = "consent_timestamp";
@@ -117,13 +190,18 @@ function markFired(input) {
117
190
  } catch {
118
191
  }
119
192
  }
120
- function fireOnce(input) {
121
- if (alreadyFired(input)) return;
122
- if (fireGtagConversion(input)) markFired(input);
123
- }
124
- function fireConversionWithConsent(input) {
125
- if (getConsentState() === "denied") return;
126
- fireOnce(input);
193
+ function fireConversionWithConsent(input, options) {
194
+ if (getConsentState() === "denied") return "denied";
195
+ if (alreadyFired(input)) return "duplicate";
196
+ if (!isValidSendTo(input.sendTo)) return "invalid";
197
+ try {
198
+ applyUserDataForConversion(options?.userData);
199
+ if (!fireGtagConversion(input)) return "retryable";
200
+ markFired(input);
201
+ return "fired";
202
+ } catch {
203
+ return "retryable";
204
+ }
127
205
  }
128
206
 
129
207
  // ../tracking-core/src/events/page-view.ts
@@ -245,6 +323,8 @@ function formatDateInTz(iso, timeZone, opts, locale) {
245
323
  // ../tracking-core/src/resources/sales/client.ts
246
324
  function fireRecordedConversions(firing, input, recorded, sale, currency) {
247
325
  if (!firing) return;
326
+ const userData = input.customer_email || input.customer_phone ? { email: input.customer_email ?? null, phone: input.customer_phone ?? null } : null;
327
+ if (userData && getConsentState() !== "denied") stashUserData(userData);
248
328
  const txnBase = input.external_id ?? sale.id;
249
329
  for (const item of recorded) {
250
330
  if (!item.service) continue;
@@ -259,12 +339,15 @@ function fireRecordedConversions(firing, input, recorded, sale, currency) {
259
339
  const config = firing.getFiring(item.service);
260
340
  if (!config) continue;
261
341
  const cents = item.amount_cents ?? config.value_cents ?? null;
262
- fireConversionWithConsent({
263
- sendTo: config.send_to,
264
- value: cents != null ? fromMinor(cents, currency) : null,
265
- currency: config.currency ?? currency,
266
- transactionId: `${txnBase}:${item.service}`
267
- });
342
+ fireConversionWithConsent(
343
+ {
344
+ sendTo: config.send_to,
345
+ value: cents != null ? fromMinor(cents, currency) : null,
346
+ currency: config.currency ?? currency,
347
+ transactionId: `${txnBase}:${item.service}`
348
+ },
349
+ { userData }
350
+ );
268
351
  }
269
352
  }
270
353
  function createSalesClient(config) {
@@ -299,6 +382,7 @@ function createSalesClient(config) {
299
382
  recordSale: record,
300
383
  trackConversion(key, options) {
301
384
  if (config.firing && "fireConversion" in config.firing) {
385
+ if (options?.userData && getConsentState() !== "denied") stashUserData(options.userData);
302
386
  config.firing.fireConversion(key, {
303
387
  value: options?.value ?? void 0,
304
388
  currency: options?.currency ?? config.defaultCurrency ?? void 0,
@@ -311,12 +395,15 @@ function createSalesClient(config) {
311
395
  const currency = firing.currency ?? options?.currency ?? config.defaultCurrency ?? null;
312
396
  const cents = firing.value_cents ?? null;
313
397
  const value = options?.value ?? (cents != null && currency ? fromMinor(cents, currency) : null);
314
- fireConversionWithConsent({
315
- sendTo: firing.send_to,
316
- value,
317
- currency,
318
- transactionId: options?.transactionId ?? null
319
- });
398
+ fireConversionWithConsent(
399
+ {
400
+ sendTo: firing.send_to,
401
+ value,
402
+ currency,
403
+ transactionId: options?.transactionId ?? null
404
+ },
405
+ { userData: options?.userData }
406
+ );
320
407
  },
321
408
  async list(query) {
322
409
  const { cursor, limit, sort, order, want_total, ...filters } = query ?? {};