@aranova/tracking-react 0.14.0 → 0.14.2

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/README.md CHANGED
@@ -223,8 +223,10 @@ export const { TrackingProvider, useTracking } = createTracking({
223
223
 
224
224
  ```ts
225
225
  import { createSalesClient, resolveConversionConfig, toMinor } from "@aranova/tracking-react";
226
+ import type { AranovaService, AranovaConversion } from "./aranova-services"; // generated by `gen`
226
227
 
227
- const sales = createSalesClient({
228
+ // Bind the generated unions so `service` and `trackConversion` keys are type-checked.
229
+ const sales = createSalesClient<AranovaService, AranovaConversion>({
228
230
  apiKey,
229
231
  endpoint,
230
232
  firing: resolveConversionConfig({ cdnUrl: ARANOVA_CONFIG_URL }),
@@ -235,10 +237,23 @@ await sales.recordSale({
235
237
  amount_total_cents: toMinor(250, "CAD"),
236
238
  service: "tires",
237
239
  }); // records + fires
238
- sales.trackConversion("phone_click"); // fires a manual event-goal only (no sale recorded)
239
240
  ```
240
241
 
241
- Firing is consent-gated, de-duped by `transaction_id`, and no-ops server-side. The config bucket needs an R2 CORS policysee [conversion-config-schema.md](https://github.com/AranovaIO/aranova_internal/blob/master/docs/tracking-package/conversion-config-schema.md).
242
+ **Manual conversions (multiple forms):** register the trigger once, then call `trackConversion(key)` in each form's submit handler. Keys come from the dashboard Goals tab (event-goals with a `form_submit`/`phone_click`/`cta_click` trigger mapped to a WEBPAGE action) and are emitted as the `AranovaConversion` union by `gen` so they autocomplete and reject typos:
243
+
244
+ ```ts
245
+ // createTracking({ …, triggers: { manual: { form_submit: {} } } }) // register once
246
+
247
+ // in the contact form's onSubmit:
248
+ sales.trackConversion("contact_form"); // ✅ typed by AranovaConversion
249
+ // in the demo form's onSubmit:
250
+ sales.trackConversion("demo_request");
251
+ sales.trackConversion("typo"); // ❌ compile error — not a known goal key
252
+ ```
253
+
254
+ Each call fires **only** the WEBPAGE conversion for that goal (no `/sales` write); the SDK no-ops if the goal isn't mapped. Firing is consent-gated, de-duped by `transaction_id`, and no-ops server-side. The config bucket needs an R2 CORS policy — see [conversion-config-schema.md](https://github.com/AranovaIO/aranova_internal/blob/master/docs/tracking-package/conversion-config-schema.md).
255
+
256
+ > **Before any manual goal exists,** `gen` emits an empty `ARANOVA_CONVERSIONS`, so `AranovaConversion` resolves to `never` — binding it (`createSalesClient<AranovaService, AranovaConversion>`) then makes **every** `trackConversion` call a compile error. Until you've mapped at least one manual goal and re-run `gen`, bind only the service generic (`createSalesClient<AranovaService>(…)`); the conversion key defaults back to `string`.
242
257
 
243
258
  ## Consent UI
244
259
 
package/dist/index.d.mts CHANGED
@@ -2,8 +2,8 @@ import * as react from 'react';
2
2
  import { ReactNode, CSSProperties, InputHTMLAttributes, ChangeEvent, FocusEvent } from 'react';
3
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, M as MetaPixelEnvironmentMap, P as PhoneConfig, g as ParsedPhone, h as PhoneDisplayFormat } from './phone-utils-Dyk0F14_.mjs';
4
4
  export { D as DEFAULT_PHONE_COUNTRY, J as JsonValue, i as TrackedField, j as TrackingInitConfig, k as formSubmitConfigSchema, l as formSubmitMetadataSchema, m as formatPhone, n as formatPhoneAsTyped, o as jsonValueSchema, p as parsePhone, q as phoneField, t as toE164 } from './phone-utils-Dyk0F14_.mjs';
5
- import { C as ConversionConfig } from './sales-BsJodmh0.mjs';
6
- export { A as AranovaApiError, B as BusinessConfig, a as BusinessConfigFeatures, b as BusinessConfigService, c as CompareTo, d as ConversionConfigStore, e as CurrencyRevenue, f as CustomerCurrencyDelta, g as CustomerCurrencyTotal, h as CustomerGetOptions, i as CustomerGetResult, j as CustomerKpis, k as CustomerKpisDeltas, l as CustomerKpisPrevious, m as CustomerListPage, n as CustomerListQuery, o as CustomerProfile, p as CustomerSegment, q as CustomerSegmentCount, r as CustomerSortField, s as CustomerSummary, t as CustomerSummaryQuery, D as DistinctCustomersByCurrency, G as Granularity, N as NAMED_RANGES, u as NamedRange, P as PublicServiceItem, S as SUPPORTED_CURRENCIES, v as Sale, w as SaleCursorPage, x as SaleFilters, y as SaleInput, z as SaleItem, E as SaleItemInput, F as SaleKeysetSortField, H as SaleListPage, I as SaleListQuery, J as SaleListQueryV2, K as SaleService, L as SaleServiceInput, M as SaleSortField, O as SaleSortOrder, Q as SaleSummary, R as SaleSummaryPrevious, T as SaleSummaryQuery, U as SaleSummaryQueryV2, V as SaleSummaryV2, W as SaleUpdateInput, X as SalesBusinessClient, Y as SalesCategoryBreakdown, Z as SalesClient, _ as SalesClientConfig, $ as SalesCustomersClient, a0 as SalesServiceBreakdown, a1 as SalesTransportConfig, a2 as SalesTrendPoint, a3 as SummaryCurrencyDelta, a4 as SummaryDeltas, a5 as SummaryWindow, a6 as SupportedCurrency, a7 as TRACKING_RANGES, a8 as TrackingOverviewRange, a9 as createSalesClient, aa as fetchServices, ab as formatDateInTz, ac as formatMoney, ad as fromMinor, ae as resolveConversionConfig, af as saleCreateSchema, ag as saleItemSchema, ah as saleServiceSchema, ai as saleUpdateSchema, aj as salesRequest, ak as toMinor } from './sales-BsJodmh0.mjs';
5
+ import { C as ConversionConfig } from './sales-Bq7H-Vym.mjs';
6
+ export { A as AranovaApiError, B as BusinessConfig, a as BusinessConfigFeatures, b as BusinessConfigService, c as CompareTo, d as ConversionConfigStore, e as CurrencyRevenue, f as CustomerCurrencyDelta, g as CustomerCurrencyTotal, h as CustomerGetOptions, i as CustomerGetResult, j as CustomerKpis, k as CustomerKpisDeltas, l as CustomerKpisPrevious, m as CustomerListPage, n as CustomerListQuery, o as CustomerProfile, p as CustomerSegment, q as CustomerSegmentCount, r as CustomerSortField, s as CustomerSummary, t as CustomerSummaryQuery, D as DistinctCustomersByCurrency, G as Granularity, N as NAMED_RANGES, u as NamedRange, P as PublicServiceItem, S as SUPPORTED_CURRENCIES, v as Sale, w as SaleCursorPage, x as SaleFilters, y as SaleInput, z as SaleItem, E as SaleItemInput, F as SaleKeysetSortField, H as SaleListPage, I as SaleListQuery, J as SaleListQueryV2, K as SaleService, L as SaleServiceInput, M as SaleSortField, O as SaleSortOrder, Q as SaleSummary, R as SaleSummaryPrevious, T as SaleSummaryQuery, U as SaleSummaryQueryV2, V as SaleSummaryV2, W as SaleUpdateInput, X as SalesBusinessClient, Y as SalesCategoryBreakdown, Z as SalesClient, _ as SalesClientConfig, $ as SalesCustomersClient, a0 as SalesServiceBreakdown, a1 as SalesTransportConfig, a2 as SalesTrendPoint, a3 as SummaryCurrencyDelta, a4 as SummaryDeltas, a5 as SummaryWindow, a6 as SupportedCurrency, a7 as TRACKING_RANGES, a8 as TrackingOverviewRange, a9 as createSalesClient, aa as fetchServices, ab as formatDateInTz, ac as formatMoney, ad as fromMinor, ae as resolveConversionConfig, af as saleCreateSchema, ag as saleItemSchema, ah as saleServiceSchema, ai as saleUpdateSchema, aj as salesRequest, ak as toMinor } from './sales-Bq7H-Vym.mjs';
7
7
  import * as src from 'src';
8
8
  import { z } from 'zod';
9
9
  import { CountryCode } from 'libphonenumber-js';
package/dist/index.d.ts CHANGED
@@ -2,8 +2,8 @@ import * as react from 'react';
2
2
  import { ReactNode, CSSProperties, InputHTMLAttributes, ChangeEvent, FocusEvent } from 'react';
3
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, M as MetaPixelEnvironmentMap, P as PhoneConfig, g as ParsedPhone, h as PhoneDisplayFormat } from './phone-utils-Dyk0F14_.js';
4
4
  export { D as DEFAULT_PHONE_COUNTRY, J as JsonValue, i as TrackedField, j as TrackingInitConfig, k as formSubmitConfigSchema, l as formSubmitMetadataSchema, m as formatPhone, n as formatPhoneAsTyped, o as jsonValueSchema, p as parsePhone, q as phoneField, t as toE164 } from './phone-utils-Dyk0F14_.js';
5
- import { C as ConversionConfig } from './sales-BsJodmh0.js';
6
- export { A as AranovaApiError, B as BusinessConfig, a as BusinessConfigFeatures, b as BusinessConfigService, c as CompareTo, d as ConversionConfigStore, e as CurrencyRevenue, f as CustomerCurrencyDelta, g as CustomerCurrencyTotal, h as CustomerGetOptions, i as CustomerGetResult, j as CustomerKpis, k as CustomerKpisDeltas, l as CustomerKpisPrevious, m as CustomerListPage, n as CustomerListQuery, o as CustomerProfile, p as CustomerSegment, q as CustomerSegmentCount, r as CustomerSortField, s as CustomerSummary, t as CustomerSummaryQuery, D as DistinctCustomersByCurrency, G as Granularity, N as NAMED_RANGES, u as NamedRange, P as PublicServiceItem, S as SUPPORTED_CURRENCIES, v as Sale, w as SaleCursorPage, x as SaleFilters, y as SaleInput, z as SaleItem, E as SaleItemInput, F as SaleKeysetSortField, H as SaleListPage, I as SaleListQuery, J as SaleListQueryV2, K as SaleService, L as SaleServiceInput, M as SaleSortField, O as SaleSortOrder, Q as SaleSummary, R as SaleSummaryPrevious, T as SaleSummaryQuery, U as SaleSummaryQueryV2, V as SaleSummaryV2, W as SaleUpdateInput, X as SalesBusinessClient, Y as SalesCategoryBreakdown, Z as SalesClient, _ as SalesClientConfig, $ as SalesCustomersClient, a0 as SalesServiceBreakdown, a1 as SalesTransportConfig, a2 as SalesTrendPoint, a3 as SummaryCurrencyDelta, a4 as SummaryDeltas, a5 as SummaryWindow, a6 as SupportedCurrency, a7 as TRACKING_RANGES, a8 as TrackingOverviewRange, a9 as createSalesClient, aa as fetchServices, ab as formatDateInTz, ac as formatMoney, ad as fromMinor, ae as resolveConversionConfig, af as saleCreateSchema, ag as saleItemSchema, ah as saleServiceSchema, ai as saleUpdateSchema, aj as salesRequest, ak as toMinor } from './sales-BsJodmh0.js';
5
+ import { C as ConversionConfig } from './sales-Bq7H-Vym.js';
6
+ export { A as AranovaApiError, B as BusinessConfig, a as BusinessConfigFeatures, b as BusinessConfigService, c as CompareTo, d as ConversionConfigStore, e as CurrencyRevenue, f as CustomerCurrencyDelta, g as CustomerCurrencyTotal, h as CustomerGetOptions, i as CustomerGetResult, j as CustomerKpis, k as CustomerKpisDeltas, l as CustomerKpisPrevious, m as CustomerListPage, n as CustomerListQuery, o as CustomerProfile, p as CustomerSegment, q as CustomerSegmentCount, r as CustomerSortField, s as CustomerSummary, t as CustomerSummaryQuery, D as DistinctCustomersByCurrency, G as Granularity, N as NAMED_RANGES, u as NamedRange, P as PublicServiceItem, S as SUPPORTED_CURRENCIES, v as Sale, w as SaleCursorPage, x as SaleFilters, y as SaleInput, z as SaleItem, E as SaleItemInput, F as SaleKeysetSortField, H as SaleListPage, I as SaleListQuery, J as SaleListQueryV2, K as SaleService, L as SaleServiceInput, M as SaleSortField, O as SaleSortOrder, Q as SaleSummary, R as SaleSummaryPrevious, T as SaleSummaryQuery, U as SaleSummaryQueryV2, V as SaleSummaryV2, W as SaleUpdateInput, X as SalesBusinessClient, Y as SalesCategoryBreakdown, Z as SalesClient, _ as SalesClientConfig, $ as SalesCustomersClient, a0 as SalesServiceBreakdown, a1 as SalesTransportConfig, a2 as SalesTrendPoint, a3 as SummaryCurrencyDelta, a4 as SummaryDeltas, a5 as SummaryWindow, a6 as SupportedCurrency, a7 as TRACKING_RANGES, a8 as TrackingOverviewRange, a9 as createSalesClient, aa as fetchServices, ab as formatDateInTz, ac as formatMoney, ad as fromMinor, ae as resolveConversionConfig, af as saleCreateSchema, ag as saleItemSchema, ah as saleServiceSchema, ai as saleUpdateSchema, aj as salesRequest, ak as toMinor } from './sales-Bq7H-Vym.js';
7
7
  import * as src from 'src';
8
8
  import { z } from 'zod';
9
9
  import { CountryCode } from 'libphonenumber-js';
package/dist/index.js CHANGED
@@ -246,9 +246,10 @@ function isValidGtagId(id) {
246
246
  function ensureGtagFunction() {
247
247
  window.dataLayer = window.dataLayer || [];
248
248
  if (typeof window.gtag === "function") return window.gtag;
249
- window.gtag = (...args) => {
250
- window.dataLayer?.push(args);
251
- };
249
+ function gtag() {
250
+ window.dataLayer?.push(arguments);
251
+ }
252
+ window.gtag = gtag;
252
253
  return window.gtag;
253
254
  }
254
255
  var SEND_TO_RE = /^AW-[A-Za-z0-9]+\/[A-Za-z0-9_-]+$/;
@@ -626,19 +627,32 @@ function resolveConversionConfig(options) {
626
627
  let current = cached?.config ?? options.baked ?? null;
627
628
  let etag = cached?.etag ?? null;
628
629
  const goalsByKey = /* @__PURE__ */ new Map();
630
+ const resolveListeners = /* @__PURE__ */ new Set();
629
631
  function rebuildIndex() {
630
632
  goalsByKey.clear();
631
633
  for (const goal of current?.goals ?? []) {
632
634
  goalsByKey.set(goal.key, goal);
633
635
  }
634
636
  }
637
+ function notifyResolved() {
638
+ const listeners = [...resolveListeners];
639
+ resolveListeners.clear();
640
+ for (const listener of listeners) {
641
+ try {
642
+ listener();
643
+ } catch {
644
+ }
645
+ }
646
+ }
635
647
  function adopt(next, nextEtag) {
636
648
  if (!next) return;
637
649
  if (current && next.config_version <= current.config_version) return;
650
+ const wasEmpty = current === null;
638
651
  current = next;
639
652
  etag = nextEtag;
640
653
  rebuildIndex();
641
654
  writeCache(options.cdnUrl, { etag, config: next });
655
+ if (wasEmpty) notifyResolved();
642
656
  }
643
657
  async function revalidate() {
644
658
  if (typeof window === "undefined") return;
@@ -663,6 +677,16 @@ function resolveConversionConfig(options) {
663
677
  getGoal: (key) => goalsByKey.get(key) ?? null,
664
678
  listGoals: () => [...goalsByKey.values()],
665
679
  current: () => current,
680
+ isReady: () => current !== null,
681
+ onResolve: (listener) => {
682
+ if (current !== null) {
683
+ listener();
684
+ return () => {
685
+ };
686
+ }
687
+ resolveListeners.add(listener);
688
+ return () => resolveListeners.delete(listener);
689
+ },
666
690
  revalidate
667
691
  };
668
692
  }
@@ -724,22 +748,39 @@ function thresholdMet(goal, eventType, metadata) {
724
748
  function currentPath() {
725
749
  return typeof window === "undefined" ? "" : window.location.pathname;
726
750
  }
751
+ var MAX_BUFFERED_EVENTS = 50;
727
752
  function createConversionAutoFire(store) {
753
+ const pending = [];
754
+ let subscribed = false;
755
+ function fireMatching(eventType, metadata) {
756
+ for (const goal of store.listGoals()) {
757
+ if (goal.kind !== "event" || !goal.firing) continue;
758
+ if (!thresholdMet(goal, eventType, metadata)) continue;
759
+ const firing = goal.firing;
760
+ const cents = firing.value_cents ?? null;
761
+ const currency = firing.currency ?? null;
762
+ fireConversionWithConsent({
763
+ sendTo: firing.send_to,
764
+ value: cents != null && currency ? fromMinor(cents, currency) : null,
765
+ currency,
766
+ // Page-scoped txn id → fire once per (goal, path) per session; engagement conversions
767
+ // shouldn't re-fire as the visitor scrolls back and forth or re-enters a page.
768
+ transactionId: `auto:${goal.key}:${currentPath()}`
769
+ });
770
+ }
771
+ }
728
772
  return {
729
773
  onAutomaticEvent(eventType, metadata) {
730
- for (const goal of store.listGoals()) {
731
- if (goal.kind !== "event" || !goal.firing) continue;
732
- if (!thresholdMet(goal, eventType, metadata)) continue;
733
- const firing = goal.firing;
734
- const cents = firing.value_cents ?? null;
735
- const currency = firing.currency ?? null;
736
- fireConversionWithConsent({
737
- sendTo: firing.send_to,
738
- value: cents != null && currency ? fromMinor(cents, currency) : null,
739
- currency,
740
- // Page-scoped txn id → fire once per (goal, path) per session; engagement conversions
741
- // shouldn't re-fire as the visitor scrolls back and forth or re-enters a page.
742
- transactionId: `auto:${goal.key}:${currentPath()}`
774
+ if (store.isReady()) {
775
+ fireMatching(eventType, metadata);
776
+ return;
777
+ }
778
+ if (pending.length < MAX_BUFFERED_EVENTS) pending.push({ eventType, metadata });
779
+ if (!subscribed) {
780
+ subscribed = true;
781
+ store.onResolve(() => {
782
+ const buffered = pending.splice(0);
783
+ for (const event of buffered) fireMatching(event.eventType, event.metadata);
743
784
  });
744
785
  }
745
786
  }
@@ -2449,7 +2490,7 @@ function GoogleAdsTracking(props) {
2449
2490
  var import_react6 = require("react");
2450
2491
 
2451
2492
  // package.json
2452
- var version = "0.14.0";
2493
+ var version = "0.14.2";
2453
2494
 
2454
2495
  // ../tracking-core/src/phone-react.tsx
2455
2496
  var import_react5 = require("react");