@aranova/tracking-next 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
@@ -1620,6 +1620,9 @@ function buildHeartbeatMetadata(surface, sdkVersion, packageName, triggers, gtag
1620
1620
  var DEFAULT_FLUSH_INTERVAL_MS = 2e3;
1621
1621
  var DEFAULT_MAX_QUEUE_SIZE = 10;
1622
1622
  var HARD_MAX_BATCH = 50;
1623
+ var MAX_RETRY_BACKOFF_MS = 6e4;
1624
+ var MAX_BUFFERED_EVENTS2 = 200;
1625
+ var RETRY_BUFFER_KEY_PREFIX = "aranova_tracking_pending_v1";
1623
1626
  var API_KEY_HEADER = "X-Aranova-Api-Key";
1624
1627
  var SDK_VERSION_HEADER = "X-Aranova-Sdk-Version";
1625
1628
  var SDK_PACKAGE_HEADER = "X-Aranova-Sdk-Package";
@@ -1662,9 +1665,9 @@ function consentSnapshot() {
1662
1665
  }
1663
1666
  }
1664
1667
  async function postWithFetch(url, body, apiKey, identity, keepalive) {
1665
- if (typeof fetch !== "function") return;
1668
+ if (typeof fetch !== "function") return "drop";
1666
1669
  try {
1667
- await fetch(url, {
1670
+ const response = await fetch(url, {
1668
1671
  method: "POST",
1669
1672
  headers: {
1670
1673
  "Content-Type": "application/json",
@@ -1680,9 +1683,49 @@ async function postWithFetch(url, body, apiKey, identity, keepalive) {
1680
1683
  credentials: "omit",
1681
1684
  mode: "cors"
1682
1685
  });
1686
+ if (response.ok) return "ok";
1687
+ if (response.status === 408 || response.status === 429 || response.status >= 500) {
1688
+ return "retry";
1689
+ }
1690
+ return "drop";
1683
1691
  } catch {
1692
+ return "retry";
1684
1693
  }
1685
1694
  }
1695
+ function retryBufferKey(apiKey, endpoint) {
1696
+ return `${RETRY_BUFFER_KEY_PREFIX}:${apiKey}:${endpoint}`;
1697
+ }
1698
+ function readPendingBatches(key) {
1699
+ if (typeof window === "undefined") return [];
1700
+ try {
1701
+ const raw = window.localStorage.getItem(key);
1702
+ if (!raw) return [];
1703
+ const parsed = JSON.parse(raw);
1704
+ if (!Array.isArray(parsed)) return [];
1705
+ return parsed.filter(
1706
+ (entry) => typeof entry === "object" && entry !== null && "session" in entry && Array.isArray(entry.events)
1707
+ );
1708
+ } catch {
1709
+ return [];
1710
+ }
1711
+ }
1712
+ function writePendingBatches(key, batches) {
1713
+ if (typeof window === "undefined") return;
1714
+ try {
1715
+ if (batches.length === 0) window.localStorage.removeItem(key);
1716
+ else window.localStorage.setItem(key, JSON.stringify(batches));
1717
+ } catch {
1718
+ }
1719
+ }
1720
+ function trimToBufferCap(batches) {
1721
+ let total = batches.reduce((sum, batch) => sum + batch.events.length, 0);
1722
+ const trimmed = batches.slice();
1723
+ while (total > MAX_BUFFERED_EVENTS2 && trimmed.length > 1) {
1724
+ const dropped = trimmed.shift();
1725
+ total -= dropped ? dropped.events.length : 0;
1726
+ }
1727
+ return trimmed;
1728
+ }
1686
1729
  var globalClient = null;
1687
1730
  var globalClientKey = null;
1688
1731
  function clientConfigKey(config) {
@@ -1738,6 +1781,10 @@ function createTrackingClient(config) {
1738
1781
  };
1739
1782
  let queue = [];
1740
1783
  let flushTimer = null;
1784
+ const bufferKey = retryBufferKey(config.apiKey, endpointBase);
1785
+ let pending = readPendingBatches(bufferKey);
1786
+ let flushChain = Promise.resolve();
1787
+ let retryAttempt = 0;
1741
1788
  let firstPage = null;
1742
1789
  let initialParams = createEmptyTrackingParams();
1743
1790
  let destroyed = false;
@@ -1774,6 +1821,9 @@ function createTrackingClient(config) {
1774
1821
  if (initialSession.isNew) {
1775
1822
  enqueueHeartbeat();
1776
1823
  }
1824
+ if (pending.length > 0) {
1825
+ scheduleFlush();
1826
+ }
1777
1827
  function buildSessionPayload() {
1778
1828
  const rotated = getOrRotateSessionId();
1779
1829
  if (rotated.isNew && rotated.id !== sessionId) {
@@ -1812,12 +1862,12 @@ function createTrackingClient(config) {
1812
1862
  context
1813
1863
  };
1814
1864
  }
1815
- function scheduleFlush() {
1865
+ function scheduleFlush(delayMs = flushIntervalMs) {
1816
1866
  if (flushTimer !== null || destroyed) return;
1817
1867
  flushTimer = setTimeout(() => {
1818
1868
  flushTimer = null;
1819
1869
  void flush();
1820
- }, flushIntervalMs);
1870
+ }, delayMs);
1821
1871
  }
1822
1872
  function clearScheduledFlush() {
1823
1873
  if (flushTimer !== null) {
@@ -1825,17 +1875,50 @@ function createTrackingClient(config) {
1825
1875
  flushTimer = null;
1826
1876
  }
1827
1877
  }
1828
- async function flush() {
1878
+ function bufferQueued() {
1829
1879
  if (queue.length === 0) return;
1830
1880
  const events = queue.slice(0, HARD_MAX_BATCH);
1831
1881
  queue = queue.slice(events.length);
1882
+ pending = trimToBufferCap([...pending, { session: buildSessionPayload(), events }]);
1883
+ writePendingBatches(bufferKey, pending);
1884
+ }
1885
+ function flush() {
1886
+ flushChain = flushChain.then(runFlush).catch(() => {
1887
+ });
1888
+ return flushChain;
1889
+ }
1890
+ async function runFlush() {
1891
+ if (destroyed) return;
1832
1892
  clearScheduledFlush();
1833
- const body = {
1834
- session: buildSessionPayload(),
1835
- events
1836
- };
1837
- const serialized = JSON.stringify(body);
1838
- await postWithFetch(eventsUrl, serialized, config.apiKey, identityHeaders2, false);
1893
+ bufferQueued();
1894
+ if (pending.length === 0) return;
1895
+ try {
1896
+ while (pending.length > 0) {
1897
+ const batch = pending[0];
1898
+ const body = { session: batch.session, events: batch.events };
1899
+ const outcome = await postWithFetch(
1900
+ eventsUrl,
1901
+ JSON.stringify(body),
1902
+ config.apiKey,
1903
+ identityHeaders2,
1904
+ false
1905
+ );
1906
+ if (outcome === "retry") {
1907
+ retryAttempt += 1;
1908
+ return;
1909
+ }
1910
+ pending = pending.slice(1);
1911
+ writePendingBatches(bufferKey, pending);
1912
+ retryAttempt = 0;
1913
+ }
1914
+ } finally {
1915
+ if (pending.length > 0) {
1916
+ const backoff = Math.min(flushIntervalMs * 2 ** retryAttempt, MAX_RETRY_BACKOFF_MS);
1917
+ scheduleFlush(backoff);
1918
+ } else if (queue.length > 0) {
1919
+ scheduleFlush();
1920
+ }
1921
+ }
1839
1922
  }
1840
1923
  function trackEvent(input) {
1841
1924
  if (destroyed) return;
@@ -1861,16 +1944,14 @@ function createTrackingClient(config) {
1861
1944
  }
1862
1945
  }
1863
1946
  function flushOnUnload() {
1864
- if (queue.length === 0) return;
1865
- const events = queue.slice(0, HARD_MAX_BATCH);
1866
- queue = queue.slice(events.length);
1947
+ bufferQueued();
1867
1948
  clearScheduledFlush();
1868
- const body = {
1869
- session: buildSessionPayload(),
1870
- events
1871
- };
1872
- const serialized = JSON.stringify(body);
1873
- void postWithFetch(eventsUrl, serialized, config.apiKey, identityHeaders2, true);
1949
+ if (pending.length === 0) return;
1950
+ const batch = pending[0];
1951
+ pending = pending.slice(1);
1952
+ writePendingBatches(bufferKey, pending);
1953
+ const body = { session: batch.session, events: batch.events };
1954
+ void postWithFetch(eventsUrl, JSON.stringify(body), config.apiKey, identityHeaders2, true);
1874
1955
  }
1875
1956
  if (typeof window !== "undefined") {
1876
1957
  window.addEventListener("pagehide", flushOnUnload);
@@ -3057,6 +3138,9 @@ var saleUpdateSchema = z12.object({
3057
3138
  items: z12.array(saleItemSchema).optional(),
3058
3139
  metadata: metadataSchema.nullable().optional(),
3059
3140
  customer_name: customerNameSchema.nullable().optional(),
3141
+ // Re-attest when changing a filler-looking name; the server clears the
3142
+ // prior attestation whenever `customer_name` changes.
3143
+ customer_name_placeholder_confirmed: z12.boolean().optional(),
3060
3144
  customer_phone: customerPhoneSchema.nullable().optional(),
3061
3145
  customer_email: customerEmailSchema.nullable().optional(),
3062
3146
  // NOT NULL server-side: omit to leave unchanged (explicit null is rejected).
@@ -3431,7 +3515,7 @@ function GoogleAdsTracking(props) {
3431
3515
  import { usePathname, useSearchParams } from "next/navigation";
3432
3516
 
3433
3517
  // package.json
3434
- var version = "0.22.0";
3518
+ var version = "0.22.2";
3435
3519
 
3436
3520
  // src/factory.tsx
3437
3521
  import {