@aranova/tracking-react 0.13.0 → 0.14.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.mjs CHANGED
@@ -1,3 +1,87 @@
1
+ // ../tracking-core/src/consent.ts
2
+ var CONSENT_STATE_KEY = "consent_state";
3
+ var grantedListeners = /* @__PURE__ */ new Set();
4
+ function onConsentGranted(listener) {
5
+ grantedListeners.add(listener);
6
+ return () => {
7
+ grantedListeners.delete(listener);
8
+ };
9
+ }
10
+ function getConsentState() {
11
+ if (typeof window === "undefined") return "pending";
12
+ try {
13
+ const storedState = window.localStorage.getItem(CONSENT_STATE_KEY);
14
+ if (storedState === "granted" || storedState === "denied") return storedState;
15
+ } catch {
16
+ }
17
+ return "pending";
18
+ }
19
+
20
+ // ../tracking-core/src/gtag.ts
21
+ var SEND_TO_RE = /^AW-[A-Za-z0-9]+\/[A-Za-z0-9_-]+$/;
22
+ function isValidSendTo(sendTo) {
23
+ return SEND_TO_RE.test(sendTo);
24
+ }
25
+ function fireGtagConversion(input) {
26
+ if (typeof window === "undefined" || typeof window.gtag !== "function") return false;
27
+ if (!isValidSendTo(input.sendTo)) return false;
28
+ const params = { send_to: input.sendTo };
29
+ if (input.value != null) params.value = input.value;
30
+ if (input.currency) params.currency = input.currency;
31
+ if (input.transactionId) params.transaction_id = input.transactionId;
32
+ try {
33
+ window.gtag("event", "conversion", params);
34
+ return true;
35
+ } catch {
36
+ return false;
37
+ }
38
+ }
39
+
40
+ // ../tracking-core/src/resources/conversion-firing.ts
41
+ var DEDUP_PREFIX = "_aranova_conv_";
42
+ var MAX_PENDING = 100;
43
+ var pendingQueue = [];
44
+ function dedupKey(input) {
45
+ return `${DEDUP_PREFIX}${input.transactionId ?? ""}:${input.sendTo}`;
46
+ }
47
+ function alreadyFired(input) {
48
+ if (!input.transactionId || typeof window === "undefined") return false;
49
+ try {
50
+ return window.sessionStorage.getItem(dedupKey(input)) !== null;
51
+ } catch {
52
+ return false;
53
+ }
54
+ }
55
+ function markFired(input) {
56
+ if (!input.transactionId || typeof window === "undefined") return;
57
+ try {
58
+ window.sessionStorage.setItem(dedupKey(input), "1");
59
+ } catch {
60
+ }
61
+ }
62
+ function fireOnce(input) {
63
+ if (alreadyFired(input)) return;
64
+ if (fireGtagConversion(input)) markFired(input);
65
+ }
66
+ function fireConversionWithConsent(input) {
67
+ const state = getConsentState();
68
+ if (state === "denied") return;
69
+ if (state === "pending") {
70
+ if (pendingQueue.length >= MAX_PENDING) pendingQueue.shift();
71
+ pendingQueue.push(input);
72
+ return;
73
+ }
74
+ fireOnce(input);
75
+ }
76
+ function flushPendingConversions() {
77
+ if (getConsentState() !== "granted") return;
78
+ while (pendingQueue.length > 0) {
79
+ const input = pendingQueue.shift();
80
+ if (input) fireOnce(input);
81
+ }
82
+ }
83
+ if (typeof window !== "undefined") onConsentGranted(flushPendingConversions);
84
+
1
85
  // ../tracking-core/src/events/page-view.ts
2
86
  import { z } from "zod";
3
87
  var pageViewMetadataSchema = z.object({
@@ -81,22 +165,98 @@ async function salesRequest(config, method, path, body) {
81
165
  return await response.json();
82
166
  }
83
167
 
168
+ // ../tracking-core/src/resources/sales/money.ts
169
+ var MINOR_UNIT_EXPONENT = {
170
+ USD: 2,
171
+ CAD: 2
172
+ };
173
+ function exponentFor(currency) {
174
+ return MINOR_UNIT_EXPONENT[currency] ?? 2;
175
+ }
176
+ function toMinor(amount, currency) {
177
+ return Math.round(amount * 10 ** exponentFor(currency));
178
+ }
179
+ function fromMinor(cents, currency) {
180
+ return cents / 10 ** exponentFor(currency);
181
+ }
182
+ function formatMoney(cents, currency, locale) {
183
+ return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
184
+ fromMinor(cents, currency)
185
+ );
186
+ }
187
+ function formatDateInTz(iso, timeZone, opts, locale) {
188
+ const date = new Date(iso);
189
+ if (Number.isNaN(date.getTime())) return iso;
190
+ return new Intl.DateTimeFormat(locale, {
191
+ year: "numeric",
192
+ month: "short",
193
+ day: "2-digit",
194
+ hour: "2-digit",
195
+ minute: "2-digit",
196
+ ...opts,
197
+ timeZone
198
+ }).format(date);
199
+ }
200
+
84
201
  // ../tracking-core/src/resources/sales/client.ts
202
+ function fireRecordedConversions(firing, input, recorded, sale, currency) {
203
+ if (!firing) return;
204
+ const txnBase = input.external_id ?? sale.id;
205
+ for (const item of recorded) {
206
+ if (!item.service) continue;
207
+ const config = firing.getFiring(item.service);
208
+ if (!config) continue;
209
+ const cents = item.amount_cents ?? config.value_cents ?? null;
210
+ fireConversionWithConsent({
211
+ sendTo: config.send_to,
212
+ value: cents != null ? fromMinor(cents, currency) : null,
213
+ currency: config.currency ?? currency,
214
+ transactionId: `${txnBase}:${item.service}`
215
+ });
216
+ }
217
+ }
85
218
  function createSalesClient(config) {
86
- return {
87
- async record(input) {
88
- const currency = input.currency ?? config.defaultCurrency;
89
- if (!currency) {
90
- throw new Error(
91
- "record: `currency` is required (pass it on the sale or set config.defaultCurrency)"
92
- );
219
+ async function record(input) {
220
+ const currency = input.currency ?? config.defaultCurrency;
221
+ if (!currency) {
222
+ throw new Error(
223
+ "record: `currency` is required (pass it on the sale or set config.defaultCurrency)"
224
+ );
225
+ }
226
+ const body = {
227
+ ...input,
228
+ currency,
229
+ occurred_at: input.occurred_at ?? (/* @__PURE__ */ new Date()).toISOString()
230
+ };
231
+ const sale = await salesRequest(config, "POST", "/sales", body);
232
+ const recorded = input.services?.length ? input.services.map((s) => ({
233
+ service: s.service,
234
+ amount_cents: s.amount_cents
235
+ })) : [
236
+ {
237
+ service: input.service,
238
+ amount_cents: input.amount_total_cents ?? null
93
239
  }
94
- const body = {
95
- ...input,
240
+ ];
241
+ fireRecordedConversions(config.firing, input, recorded, sale, currency);
242
+ return sale;
243
+ }
244
+ return {
245
+ record,
246
+ // recordSale is the intent-revealing alias — same behavior, clearer call site.
247
+ recordSale: record,
248
+ trackConversion(key, options) {
249
+ const firing = config.firing?.getFiring(key);
250
+ if (!firing) return;
251
+ const currency = firing.currency ?? options?.currency ?? config.defaultCurrency ?? null;
252
+ const cents = firing.value_cents ?? null;
253
+ const value = options?.value ?? (cents != null && currency ? fromMinor(cents, currency) : null);
254
+ fireConversionWithConsent({
255
+ sendTo: firing.send_to,
256
+ value,
96
257
  currency,
97
- occurred_at: input.occurred_at ?? (/* @__PURE__ */ new Date()).toISOString()
98
- };
99
- return salesRequest(config, "POST", "/sales", body);
258
+ transactionId: options?.transactionId ?? null
259
+ });
100
260
  },
101
261
  async list(query) {
102
262
  const { cursor, limit, sort, order, want_total, ...filters } = query ?? {};
@@ -190,39 +350,6 @@ function createSalesClient(config) {
190
350
  };
191
351
  }
192
352
 
193
- // ../tracking-core/src/resources/sales/money.ts
194
- var MINOR_UNIT_EXPONENT = {
195
- USD: 2,
196
- CAD: 2
197
- };
198
- function exponentFor(currency) {
199
- return MINOR_UNIT_EXPONENT[currency] ?? 2;
200
- }
201
- function toMinor(amount, currency) {
202
- return Math.round(amount * 10 ** exponentFor(currency));
203
- }
204
- function fromMinor(cents, currency) {
205
- return cents / 10 ** exponentFor(currency);
206
- }
207
- function formatMoney(cents, currency, locale) {
208
- return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
209
- fromMinor(cents, currency)
210
- );
211
- }
212
- function formatDateInTz(iso, timeZone, opts, locale) {
213
- const date = new Date(iso);
214
- if (Number.isNaN(date.getTime())) return iso;
215
- return new Intl.DateTimeFormat(locale, {
216
- year: "numeric",
217
- month: "short",
218
- day: "2-digit",
219
- hour: "2-digit",
220
- minute: "2-digit",
221
- ...opts,
222
- timeZone
223
- }).format(date);
224
- }
225
-
226
353
  // ../tracking-core/src/resources/sales/schema.ts
227
354
  import { z as z2 } from "zod";
228
355
  var SUPPORTED_CURRENCIES = ["USD", "CAD"];