@aranova/tracking-react 0.22.0 → 0.22.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/dist/index.mjs CHANGED
@@ -1679,6 +1679,9 @@ function buildHeartbeatMetadata(surface, sdkVersion, packageName, triggers, gtag
1679
1679
  var DEFAULT_FLUSH_INTERVAL_MS = 2e3;
1680
1680
  var DEFAULT_MAX_QUEUE_SIZE = 10;
1681
1681
  var HARD_MAX_BATCH = 50;
1682
+ var MAX_RETRY_BACKOFF_MS = 6e4;
1683
+ var MAX_BUFFERED_EVENTS2 = 200;
1684
+ var RETRY_BUFFER_KEY_PREFIX = "aranova_tracking_pending_v1";
1682
1685
  var API_KEY_HEADER = "X-Aranova-Api-Key";
1683
1686
  var SDK_VERSION_HEADER = "X-Aranova-Sdk-Version";
1684
1687
  var SDK_PACKAGE_HEADER = "X-Aranova-Sdk-Package";
@@ -1721,9 +1724,9 @@ function consentSnapshot() {
1721
1724
  }
1722
1725
  }
1723
1726
  async function postWithFetch(url, body, apiKey, identity, keepalive) {
1724
- if (typeof fetch !== "function") return;
1727
+ if (typeof fetch !== "function") return "drop";
1725
1728
  try {
1726
- await fetch(url, {
1729
+ const response = await fetch(url, {
1727
1730
  method: "POST",
1728
1731
  headers: {
1729
1732
  "Content-Type": "application/json",
@@ -1739,9 +1742,49 @@ async function postWithFetch(url, body, apiKey, identity, keepalive) {
1739
1742
  credentials: "omit",
1740
1743
  mode: "cors"
1741
1744
  });
1745
+ if (response.ok) return "ok";
1746
+ if (response.status === 408 || response.status === 429 || response.status >= 500) {
1747
+ return "retry";
1748
+ }
1749
+ return "drop";
1742
1750
  } catch {
1751
+ return "retry";
1743
1752
  }
1744
1753
  }
1754
+ function retryBufferKey(apiKey, endpoint) {
1755
+ return `${RETRY_BUFFER_KEY_PREFIX}:${apiKey}:${endpoint}`;
1756
+ }
1757
+ function readPendingBatches(key) {
1758
+ if (typeof window === "undefined") return [];
1759
+ try {
1760
+ const raw = window.localStorage.getItem(key);
1761
+ if (!raw) return [];
1762
+ const parsed = JSON.parse(raw);
1763
+ if (!Array.isArray(parsed)) return [];
1764
+ return parsed.filter(
1765
+ (entry) => typeof entry === "object" && entry !== null && "session" in entry && Array.isArray(entry.events)
1766
+ );
1767
+ } catch {
1768
+ return [];
1769
+ }
1770
+ }
1771
+ function writePendingBatches(key, batches) {
1772
+ if (typeof window === "undefined") return;
1773
+ try {
1774
+ if (batches.length === 0) window.localStorage.removeItem(key);
1775
+ else window.localStorage.setItem(key, JSON.stringify(batches));
1776
+ } catch {
1777
+ }
1778
+ }
1779
+ function trimToBufferCap(batches) {
1780
+ let total = batches.reduce((sum, batch) => sum + batch.events.length, 0);
1781
+ const trimmed = batches.slice();
1782
+ while (total > MAX_BUFFERED_EVENTS2 && trimmed.length > 1) {
1783
+ const dropped = trimmed.shift();
1784
+ total -= dropped ? dropped.events.length : 0;
1785
+ }
1786
+ return trimmed;
1787
+ }
1745
1788
  var globalClient = null;
1746
1789
  var globalClientKey = null;
1747
1790
  function clientConfigKey(config) {
@@ -1797,6 +1840,10 @@ function createTrackingClient(config) {
1797
1840
  };
1798
1841
  let queue = [];
1799
1842
  let flushTimer = null;
1843
+ const bufferKey = retryBufferKey(config.apiKey, endpointBase);
1844
+ let pending = readPendingBatches(bufferKey);
1845
+ let flushChain = Promise.resolve();
1846
+ let retryAttempt = 0;
1800
1847
  let firstPage = null;
1801
1848
  let initialParams = createEmptyTrackingParams();
1802
1849
  let destroyed = false;
@@ -1833,6 +1880,9 @@ function createTrackingClient(config) {
1833
1880
  if (initialSession.isNew) {
1834
1881
  enqueueHeartbeat();
1835
1882
  }
1883
+ if (pending.length > 0) {
1884
+ scheduleFlush();
1885
+ }
1836
1886
  function buildSessionPayload() {
1837
1887
  const rotated = getOrRotateSessionId();
1838
1888
  if (rotated.isNew && rotated.id !== sessionId) {
@@ -1871,12 +1921,12 @@ function createTrackingClient(config) {
1871
1921
  context
1872
1922
  };
1873
1923
  }
1874
- function scheduleFlush() {
1924
+ function scheduleFlush(delayMs = flushIntervalMs) {
1875
1925
  if (flushTimer !== null || destroyed) return;
1876
1926
  flushTimer = setTimeout(() => {
1877
1927
  flushTimer = null;
1878
1928
  void flush();
1879
- }, flushIntervalMs);
1929
+ }, delayMs);
1880
1930
  }
1881
1931
  function clearScheduledFlush() {
1882
1932
  if (flushTimer !== null) {
@@ -1884,17 +1934,50 @@ function createTrackingClient(config) {
1884
1934
  flushTimer = null;
1885
1935
  }
1886
1936
  }
1887
- async function flush() {
1937
+ function bufferQueued() {
1888
1938
  if (queue.length === 0) return;
1889
1939
  const events = queue.slice(0, HARD_MAX_BATCH);
1890
1940
  queue = queue.slice(events.length);
1941
+ pending = trimToBufferCap([...pending, { session: buildSessionPayload(), events }]);
1942
+ writePendingBatches(bufferKey, pending);
1943
+ }
1944
+ function flush() {
1945
+ flushChain = flushChain.then(runFlush).catch(() => {
1946
+ });
1947
+ return flushChain;
1948
+ }
1949
+ async function runFlush() {
1950
+ if (destroyed) return;
1891
1951
  clearScheduledFlush();
1892
- const body = {
1893
- session: buildSessionPayload(),
1894
- events
1895
- };
1896
- const serialized = JSON.stringify(body);
1897
- await postWithFetch(eventsUrl, serialized, config.apiKey, identityHeaders2, false);
1952
+ bufferQueued();
1953
+ if (pending.length === 0) return;
1954
+ try {
1955
+ while (pending.length > 0) {
1956
+ const batch = pending[0];
1957
+ const body = { session: batch.session, events: batch.events };
1958
+ const outcome = await postWithFetch(
1959
+ eventsUrl,
1960
+ JSON.stringify(body),
1961
+ config.apiKey,
1962
+ identityHeaders2,
1963
+ false
1964
+ );
1965
+ if (outcome === "retry") {
1966
+ retryAttempt += 1;
1967
+ return;
1968
+ }
1969
+ pending = pending.slice(1);
1970
+ writePendingBatches(bufferKey, pending);
1971
+ retryAttempt = 0;
1972
+ }
1973
+ } finally {
1974
+ if (pending.length > 0) {
1975
+ const backoff = Math.min(flushIntervalMs * 2 ** retryAttempt, MAX_RETRY_BACKOFF_MS);
1976
+ scheduleFlush(backoff);
1977
+ } else if (queue.length > 0) {
1978
+ scheduleFlush();
1979
+ }
1980
+ }
1898
1981
  }
1899
1982
  function trackEvent(input) {
1900
1983
  if (destroyed) return;
@@ -1920,16 +2003,14 @@ function createTrackingClient(config) {
1920
2003
  }
1921
2004
  }
1922
2005
  function flushOnUnload() {
1923
- if (queue.length === 0) return;
1924
- const events = queue.slice(0, HARD_MAX_BATCH);
1925
- queue = queue.slice(events.length);
2006
+ bufferQueued();
1926
2007
  clearScheduledFlush();
1927
- const body = {
1928
- session: buildSessionPayload(),
1929
- events
1930
- };
1931
- const serialized = JSON.stringify(body);
1932
- void postWithFetch(eventsUrl, serialized, config.apiKey, identityHeaders2, true);
2008
+ if (pending.length === 0) return;
2009
+ const batch = pending[0];
2010
+ pending = pending.slice(1);
2011
+ writePendingBatches(bufferKey, pending);
2012
+ const body = { session: batch.session, events: batch.events };
2013
+ void postWithFetch(eventsUrl, JSON.stringify(body), config.apiKey, identityHeaders2, true);
1933
2014
  }
1934
2015
  if (typeof window !== "undefined") {
1935
2016
  window.addEventListener("pagehide", flushOnUnload);
@@ -3251,6 +3332,9 @@ var saleUpdateSchema = z12.object({
3251
3332
  items: z12.array(saleItemSchema).optional(),
3252
3333
  metadata: metadataSchema.nullable().optional(),
3253
3334
  customer_name: customerNameSchema.nullable().optional(),
3335
+ // Re-attest when changing a filler-looking name; the server clears the
3336
+ // prior attestation whenever `customer_name` changes.
3337
+ customer_name_placeholder_confirmed: z12.boolean().optional(),
3254
3338
  customer_phone: customerPhoneSchema.nullable().optional(),
3255
3339
  customer_email: customerEmailSchema.nullable().optional(),
3256
3340
  // NOT NULL server-side: omit to leave unchanged (explicit null is rejected).
@@ -3612,7 +3696,7 @@ function GoogleAdsTracking(props) {
3612
3696
  import { createContext as createContext2, useContext as useContext2, useEffect as useEffect5, useMemo as useMemo4 } from "react";
3613
3697
 
3614
3698
  // package.json
3615
- var version = "0.22.0";
3699
+ var version = "0.22.2";
3616
3700
 
3617
3701
  // ../tracking-core/src/phone-react.tsx
3618
3702
  import {