@esri/telemetry-amazon 6.0.0-beta.3 → 6.0.0-beta.4

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.
@@ -1718,6 +1718,9 @@
1718
1718
  const ESRI_SIGNING_SERVICE = 'execute-api';
1719
1719
  const ESRI_SIGNING_REGION = 'us-east-1';
1720
1720
  const ESRI_ENDPOINT_URL = (appId) => `https://tedev.arcgis.com/v1/apps/${appId}/events`;
1721
+ const SESSION_START_EVENT = '_session.start';
1722
+ const SESSION_STOP_EVENT = '_session.stop';
1723
+ const DEFAULT_IDLE_TIMEOUT_MS = 30 * 60 * 1000;
1721
1724
  /** Converts a string to a number, guarded against invalid values */
1722
1725
  function getNumber(value, fallback = 0) {
1723
1726
  const numeric = Number(value);
@@ -1759,11 +1762,17 @@
1759
1762
  metrics,
1760
1763
  };
1761
1764
  }
1762
- function buildBatchEventPayload({ eventName, payload, trackerName, appName, appVersion, }) {
1765
+ function buildBatchEventPayload({ eventName, payload, trackerName, appName, appVersion, session, }) {
1763
1766
  var _a, _b, _c, _d;
1764
1767
  const endpointId = getEndpointId();
1765
- const sessionId = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
1766
1768
  const timestamp = new Date().toISOString();
1769
+ const sessionContext = session ||
1770
+ {
1771
+ id: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
1772
+ startTimestamp: timestamp,
1773
+ startUnixMs: Date.now(),
1774
+ };
1775
+ const isStopEvent = eventName === SESSION_STOP_EVENT;
1767
1776
  const { attributes, metrics } = splitEventData(payload);
1768
1777
  return {
1769
1778
  BatchEvent: {
@@ -1790,10 +1799,12 @@
1790
1799
  EventType: eventName,
1791
1800
  Timestamp: timestamp,
1792
1801
  Session: {
1793
- Id: sessionId,
1794
- StartTimestamp: timestamp,
1802
+ Id: sessionContext.id,
1803
+ StartTimestamp: sessionContext.startTimestamp,
1795
1804
  EndTimestamp: timestamp,
1796
- Duration: 0,
1805
+ Duration: isStopEvent
1806
+ ? Math.max(0, Date.now() - sessionContext.startUnixMs)
1807
+ : 0,
1797
1808
  },
1798
1809
  Attributes: attributes,
1799
1810
  Metrics: metrics,
@@ -1806,6 +1817,13 @@
1806
1817
  const url = ESRI_ENDPOINT_URL(appId);
1807
1818
  let isDisabled = false;
1808
1819
  let hasWarned = false;
1820
+ let hasSessionStarted = false;
1821
+ let sessionStopSent = false;
1822
+ let activeSession;
1823
+ let idleTimerId;
1824
+ let lifecycleListenersAttached = false;
1825
+ const removeLifecycleListeners = [];
1826
+ const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
1809
1827
  const warnOnce = (message, details) => {
1810
1828
  if (hasWarned) {
1811
1829
  return;
@@ -1835,64 +1853,191 @@
1835
1853
  trackerName,
1836
1854
  });
1837
1855
  }
1838
- return {
1839
- track: (eventName, payload) => {
1840
- if (isDisabled || !url || !userPoolID) {
1841
- debugLog('Skipping esri track call', {
1842
- eventName,
1843
- reason: 'plugin-disabled',
1844
- });
1845
- return false;
1846
- }
1847
- debugLog('Sending esri endpoint event', {
1856
+ const clearIdleTimer = () => {
1857
+ if (!idleTimerId) {
1858
+ return;
1859
+ }
1860
+ clearTimeout(idleTimerId);
1861
+ idleTimerId = undefined;
1862
+ };
1863
+ const removeAllLifecycleListeners = () => {
1864
+ removeLifecycleListeners.splice(0).forEach((removeListener) => {
1865
+ removeListener();
1866
+ });
1867
+ lifecycleListenersAttached = false;
1868
+ };
1869
+ const createSession = () => {
1870
+ const now = Date.now();
1871
+ return {
1872
+ id: `${now}-${Math.random().toString(16).slice(2)}`,
1873
+ startTimestamp: new Date(now).toISOString(),
1874
+ startUnixMs: now,
1875
+ };
1876
+ };
1877
+ const sendEvent = (eventName, payload, sessionOverride) => {
1878
+ if (isDisabled || !url || !userPoolID) {
1879
+ debugLog('Skipping esri track call', {
1848
1880
  eventName,
1849
- trackerName,
1850
- url,
1881
+ reason: 'plugin-disabled',
1851
1882
  });
1852
- void getCredentials(userPoolID, { fips })
1853
- .then((credentials) => {
1854
- const accessKeyId = (credentials === null || credentials === void 0 ? void 0 : credentials.AccessKeyId) || (credentials === null || credentials === void 0 ? void 0 : credentials.accessKeyId);
1855
- const secretAccessKey = (credentials === null || credentials === void 0 ? void 0 : credentials.SecretKey) || (credentials === null || credentials === void 0 ? void 0 : credentials.secretAccessKey);
1856
- const sessionToken = (credentials === null || credentials === void 0 ? void 0 : credentials.SessionToken) || (credentials === null || credentials === void 0 ? void 0 : credentials.sessionToken);
1857
- if (!accessKeyId || !secretAccessKey) {
1858
- throw new Error('Signed endpoint credentials are missing required key fields.');
1859
- }
1860
- const client = new AwsClient({
1861
- accessKeyId,
1862
- secretAccessKey,
1863
- sessionToken,
1864
- service: ESRI_SIGNING_SERVICE,
1865
- region: ESRI_SIGNING_REGION,
1866
- });
1867
- return client.fetch(url, {
1868
- method: 'POST',
1869
- headers: {
1870
- 'Content-Type': 'application/json',
1871
- },
1872
- body: JSON.stringify(buildBatchEventPayload({
1873
- eventName,
1874
- payload: payload,
1875
- trackerName,
1876
- appName,
1877
- appVersion,
1878
- })),
1879
- });
1880
- })
1881
- .then(() => {
1882
- debugLog('Endpoint event sent', {
1883
- eventName,
1884
- trackerName,
1885
- });
1886
- })
1887
- .catch((error) => {
1888
- isDisabled = true;
1889
- warnOnce('Esri telemetry disabled after endpoint track failure', {
1883
+ return false;
1884
+ }
1885
+ debugLog('Sending esri endpoint event', {
1886
+ eventName,
1887
+ trackerName,
1888
+ url,
1889
+ });
1890
+ void getCredentials(userPoolID, { fips })
1891
+ .then((credentials) => {
1892
+ const accessKeyId = (credentials === null || credentials === void 0 ? void 0 : credentials.AccessKeyId) || (credentials === null || credentials === void 0 ? void 0 : credentials.accessKeyId);
1893
+ const secretAccessKey = (credentials === null || credentials === void 0 ? void 0 : credentials.SecretKey) || (credentials === null || credentials === void 0 ? void 0 : credentials.secretAccessKey);
1894
+ const sessionToken = (credentials === null || credentials === void 0 ? void 0 : credentials.SessionToken) || (credentials === null || credentials === void 0 ? void 0 : credentials.sessionToken);
1895
+ if (!accessKeyId || !secretAccessKey) {
1896
+ throw new Error('Signed endpoint credentials are missing required key fields.');
1897
+ }
1898
+ const client = new AwsClient({
1899
+ accessKeyId,
1900
+ secretAccessKey,
1901
+ sessionToken,
1902
+ service: ESRI_SIGNING_SERVICE,
1903
+ region: ESRI_SIGNING_REGION,
1904
+ });
1905
+ return client.fetch(url, {
1906
+ method: 'POST',
1907
+ headers: {
1908
+ 'Content-Type': 'application/json',
1909
+ },
1910
+ body: JSON.stringify(buildBatchEventPayload({
1890
1911
  eventName,
1912
+ payload: payload,
1891
1913
  trackerName,
1892
- message: (error === null || error === void 0 ? void 0 : error.message) || String(error),
1893
- });
1914
+ appName,
1915
+ appVersion,
1916
+ session: sessionOverride || activeSession,
1917
+ })),
1918
+ });
1919
+ })
1920
+ .then(() => {
1921
+ debugLog('Endpoint event sent', {
1922
+ eventName,
1923
+ trackerName,
1924
+ });
1925
+ })
1926
+ .catch((error) => {
1927
+ isDisabled = true;
1928
+ clearIdleTimer();
1929
+ removeAllLifecycleListeners();
1930
+ warnOnce('Esri telemetry disabled after endpoint track failure', {
1931
+ eventName,
1932
+ trackerName,
1933
+ message: (error === null || error === void 0 ? void 0 : error.message) || String(error),
1894
1934
  });
1895
- return true;
1935
+ });
1936
+ return true;
1937
+ };
1938
+ const sendSessionLifecycleEvent = (eventName, reason, sessionOverride) => {
1939
+ return sendEvent(eventName, {
1940
+ reason,
1941
+ }, sessionOverride);
1942
+ };
1943
+ const stopSession = (reason) => {
1944
+ if (!hasSessionStarted || sessionStopSent) {
1945
+ return false;
1946
+ }
1947
+ const sessionToStop = activeSession;
1948
+ const didStop = sendSessionLifecycleEvent(SESSION_STOP_EVENT, reason, sessionToStop);
1949
+ if (didStop) {
1950
+ sessionStopSent = true;
1951
+ hasSessionStarted = false;
1952
+ clearIdleTimer();
1953
+ activeSession = undefined;
1954
+ }
1955
+ return didStop;
1956
+ };
1957
+ const startSession = (reason) => {
1958
+ if (hasSessionStarted) {
1959
+ return false;
1960
+ }
1961
+ activeSession = activeSession || createSession();
1962
+ const sessionToStart = activeSession;
1963
+ const didStart = sendSessionLifecycleEvent(SESSION_START_EVENT, reason, sessionToStart);
1964
+ if (didStart) {
1965
+ hasSessionStarted = true;
1966
+ sessionStopSent = false;
1967
+ }
1968
+ else {
1969
+ activeSession = undefined;
1970
+ }
1971
+ return didStart;
1972
+ };
1973
+ const resetIdleTimer = () => {
1974
+ if (!isBrowser || isDisabled || !hasSessionStarted) {
1975
+ return;
1976
+ }
1977
+ clearIdleTimer();
1978
+ idleTimerId = setTimeout(() => {
1979
+ stopSession('idle');
1980
+ }, DEFAULT_IDLE_TIMEOUT_MS);
1981
+ };
1982
+ const handleActivity = () => {
1983
+ if (isDisabled) {
1984
+ return;
1985
+ }
1986
+ if (!hasSessionStarted) {
1987
+ startSession('wakeup');
1988
+ }
1989
+ resetIdleTimer();
1990
+ };
1991
+ const addLifecycleListener = (target, eventName, listener) => {
1992
+ target.addEventListener(eventName, listener);
1993
+ removeLifecycleListeners.push(() => {
1994
+ target.removeEventListener(eventName, listener);
1995
+ });
1996
+ };
1997
+ const attachLifecycleListeners = () => {
1998
+ if (!isBrowser || lifecycleListenersAttached || isDisabled) {
1999
+ return;
2000
+ }
2001
+ const activityEvents = ['pointerdown', 'keydown', 'scroll', 'focus'];
2002
+ activityEvents.forEach((eventName) => {
2003
+ addLifecycleListener(window, eventName, handleActivity);
2004
+ });
2005
+ addLifecycleListener(window, 'beforeunload', () => {
2006
+ stopSession('beforeunload');
2007
+ });
2008
+ lifecycleListenersAttached = true;
2009
+ };
2010
+ return {
2011
+ track: (eventName, payload) => {
2012
+ attachLifecycleListeners();
2013
+ const isSessionStopEvent = eventName === SESSION_STOP_EVENT;
2014
+ if (!hasSessionStarted &&
2015
+ eventName !== SESSION_START_EVENT &&
2016
+ !isSessionStopEvent) {
2017
+ startSession('init');
2018
+ }
2019
+ if (eventName === SESSION_START_EVENT) {
2020
+ activeSession = activeSession || createSession();
2021
+ hasSessionStarted = true;
2022
+ sessionStopSent = false;
2023
+ }
2024
+ if (isSessionStopEvent) {
2025
+ sessionStopSent = true;
2026
+ hasSessionStarted = false;
2027
+ clearIdleTimer();
2028
+ }
2029
+ if (hasSessionStarted && !activeSession && !isSessionStopEvent) {
2030
+ activeSession = createSession();
2031
+ }
2032
+ if (hasSessionStarted && !isSessionStopEvent) {
2033
+ resetIdleTimer();
2034
+ }
2035
+ const sessionForEvent = activeSession;
2036
+ const didSend = sendEvent(eventName, payload, sessionForEvent);
2037
+ if (isSessionStopEvent && didSend) {
2038
+ activeSession = undefined;
2039
+ }
2040
+ return didSend;
1896
2041
  },
1897
2042
  };
1898
2043
  }