@esri/telemetry-amazon 6.0.0-beta.1 → 6.0.0-beta.10

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.
Files changed (83) hide show
  1. package/README.md +7 -7
  2. package/dist/esm/auth.js +22 -31
  3. package/dist/esm/auth.js.map +1 -1
  4. package/dist/esm/create-event-log.js +15 -15
  5. package/dist/esm/create-event-log.js.map +1 -1
  6. package/dist/esm/create-event-log.test.js +50 -8
  7. package/dist/esm/create-event-log.test.js.map +1 -1
  8. package/dist/esm/create-page-view-log.js +0 -5
  9. package/dist/esm/create-page-view-log.js.map +1 -1
  10. package/dist/esm/format-telemetry-attributes.js +8 -4
  11. package/dist/esm/format-telemetry-attributes.js.map +1 -1
  12. package/dist/esm/index.js +41 -25
  13. package/dist/esm/index.js.map +1 -1
  14. package/dist/esm/index.test.js +90 -16
  15. package/dist/esm/index.test.js.map +1 -1
  16. package/dist/esm/plugins/esri.js +346 -65
  17. package/dist/esm/plugins/esri.js.map +1 -1
  18. package/dist/esm/plugins/esri.test.js +498 -36
  19. package/dist/esm/plugins/esri.test.js.map +1 -1
  20. package/dist/esm/plugins/utils/browser.js +97 -0
  21. package/dist/esm/plugins/utils/browser.js.map +1 -0
  22. package/dist/esm/plugins/utils/browser.test.js +130 -0
  23. package/dist/esm/plugins/utils/browser.test.js.map +1 -0
  24. package/dist/esm/plugins/utils/os.js +140 -0
  25. package/dist/esm/plugins/utils/os.js.map +1 -0
  26. package/dist/esm/plugins/utils/os.test.js +54 -0
  27. package/dist/esm/plugins/utils/os.test.js.map +1 -0
  28. package/dist/esm/plugins/{shared.js → utils/shared.js} +1 -1
  29. package/dist/esm/plugins/utils/shared.js.map +1 -0
  30. package/dist/esm/types.js +97 -1
  31. package/dist/esm/types.js.map +1 -1
  32. package/dist/node/auth.js +22 -31
  33. package/dist/node/auth.js.map +1 -1
  34. package/dist/node/create-event-log.js +15 -15
  35. package/dist/node/create-event-log.js.map +1 -1
  36. package/dist/node/create-event-log.test.js +50 -8
  37. package/dist/node/create-event-log.test.js.map +1 -1
  38. package/dist/node/create-page-view-log.js +0 -5
  39. package/dist/node/create-page-view-log.js.map +1 -1
  40. package/dist/node/format-telemetry-attributes.js +8 -4
  41. package/dist/node/format-telemetry-attributes.js.map +1 -1
  42. package/dist/node/index.js +42 -25
  43. package/dist/node/index.js.map +1 -1
  44. package/dist/node/index.test.js +91 -16
  45. package/dist/node/index.test.js.map +1 -1
  46. package/dist/node/plugins/esri.js +346 -65
  47. package/dist/node/plugins/esri.js.map +1 -1
  48. package/dist/node/plugins/esri.test.js +499 -36
  49. package/dist/node/plugins/esri.test.js.map +1 -1
  50. package/dist/node/plugins/utils/browser.js +101 -0
  51. package/dist/node/plugins/utils/browser.js.map +1 -0
  52. package/dist/node/plugins/utils/browser.test.js +132 -0
  53. package/dist/node/plugins/utils/browser.test.js.map +1 -0
  54. package/dist/node/plugins/utils/os.js +144 -0
  55. package/dist/node/plugins/utils/os.js.map +1 -0
  56. package/dist/node/plugins/utils/os.test.js +56 -0
  57. package/dist/node/plugins/utils/os.test.js.map +1 -0
  58. package/dist/node/plugins/{shared.js → utils/shared.js} +3 -2
  59. package/dist/node/plugins/utils/shared.js.map +1 -0
  60. package/dist/node/types.js +98 -0
  61. package/dist/node/types.js.map +1 -1
  62. package/dist/types/create-event-log.d.ts +2 -2
  63. package/dist/types/format-telemetry-attributes.d.ts +3 -3
  64. package/dist/types/index.d.ts +7 -3
  65. package/dist/types/plugins/esri.d.ts +5 -9
  66. package/dist/types/plugins/utils/browser.d.ts +12 -0
  67. package/dist/types/plugins/utils/browser.test.d.ts +1 -0
  68. package/dist/types/plugins/utils/os.d.ts +5 -0
  69. package/dist/types/plugins/utils/os.test.d.ts +1 -0
  70. package/dist/types/plugins/{shared.d.ts → utils/shared.d.ts} +1 -0
  71. package/dist/types/types.d.ts +108 -18
  72. package/dist/umd/telemetry-amazon.js +915 -368
  73. package/dist/umd/telemetry-amazon.js.map +1 -1
  74. package/dist/umd/telemetry-amazon.min.js +915 -368
  75. package/dist/umd/telemetry-amazon.min.js.map +1 -1
  76. package/package.json +4 -2
  77. package/dist/esm/plugins/pinpoint.js +0 -163
  78. package/dist/esm/plugins/pinpoint.js.map +0 -1
  79. package/dist/esm/plugins/shared.js.map +0 -1
  80. package/dist/node/plugins/pinpoint.js +0 -167
  81. package/dist/node/plugins/pinpoint.js.map +0 -1
  82. package/dist/node/plugins/shared.js.map +0 -1
  83. package/dist/types/plugins/pinpoint.d.ts +0 -11
@@ -1,16 +1,27 @@
1
1
  import { Amazon } from '.';
2
- import { createPinpointPlugin } from './plugins/pinpoint';
2
+ import Analytics from 'analytics';
3
+ import awsPinpointPlugin from '@analytics/aws-pinpoint';
3
4
  import { createEsriPlugin } from './plugins/esri';
4
- jest.mock('./plugins/pinpoint', () => ({
5
- createPinpointPlugin: jest.fn(() => ({ track: jest.fn(() => true) })),
5
+ jest.mock('analytics', () => ({
6
+ __esModule: true,
7
+ default: jest.fn(() => ({ track: jest.fn(() => true) })),
8
+ }));
9
+ jest.mock('@analytics/aws-pinpoint', () => ({
10
+ __esModule: true,
11
+ default: jest.fn(() => ({
12
+ name: 'aws-pinpoint',
13
+ })),
6
14
  }));
7
15
  jest.mock('./plugins/esri', () => ({
8
16
  createEsriPlugin: jest.fn(() => ({ track: jest.fn(() => true) })),
9
17
  }));
10
- const createPinpointPluginMock = createPinpointPlugin;
18
+ const AnalyticsMock = Analytics;
19
+ const awsPinpointPluginMock = awsPinpointPlugin;
11
20
  const createEsriPluginMock = createEsriPlugin;
21
+ const PINPOINT_RETIREMENT_CUTOFF_UTC_ISO = '2026-10-31T00:00:00.000Z';
12
22
  describe('Amazon', () => {
13
23
  afterEach(() => {
24
+ jest.useRealTimers();
14
25
  jest.clearAllMocks();
15
26
  });
16
27
  test('Amazon constructor and permissions', () => {
@@ -29,9 +40,12 @@ describe('Amazon', () => {
29
40
  mode: 'pinpoint',
30
41
  }));
31
42
  });
32
- test('pinpoint mode initializes pinpoint plugin with mapped options', () => {
33
- const pinpointPlugin = { track: jest.fn(() => true) };
34
- createPinpointPluginMock.mockReturnValue(pinpointPlugin);
43
+ test('pinpoint mode initializes upstream analytics pinpoint plugin', () => {
44
+ jest
45
+ .useFakeTimers('modern')
46
+ .setSystemTime(new Date('2026-10-30T23:59:59.000Z').getTime());
47
+ const analyticsInstance = { track: jest.fn(() => true) };
48
+ AnalyticsMock.mockReturnValue(analyticsInstance);
35
49
  new Amazon({
36
50
  userPoolID: 'pool-id',
37
51
  app: {
@@ -42,16 +56,44 @@ describe('Amazon', () => {
42
56
  mode: 'pinpoint',
43
57
  fips: true,
44
58
  });
45
- expect(createPinpointPluginMock).toHaveBeenCalledWith({
59
+ expect(awsPinpointPluginMock).toHaveBeenCalledWith({
60
+ fips: true,
61
+ pinpointAppId: '1234',
62
+ getCredentials: expect.any(Function),
63
+ });
64
+ expect(AnalyticsMock).toHaveBeenCalledWith({
65
+ app: 'test',
66
+ version: '1.0.0',
67
+ plugins: [expect.any(Object)],
68
+ });
69
+ expect(createEsriPluginMock).not.toHaveBeenCalled();
70
+ });
71
+ test('pinpoint mode falls back to esri mode on and after retirement cutoff', () => {
72
+ jest
73
+ .useFakeTimers('modern')
74
+ .setSystemTime(new Date(PINPOINT_RETIREMENT_CUTOFF_UTC_ISO));
75
+ new Amazon({
76
+ userPoolID: 'pool-id',
77
+ app: {
78
+ name: 'test',
79
+ id: '1234',
80
+ version: '1.0.0',
81
+ },
82
+ mode: 'pinpoint',
83
+ fips: true,
84
+ });
85
+ expect(awsPinpointPluginMock).not.toHaveBeenCalled();
86
+ expect(AnalyticsMock).not.toHaveBeenCalled();
87
+ expect(createEsriPluginMock).toHaveBeenCalledWith({
88
+ trackerName: 'amazon',
46
89
  appName: 'test',
47
90
  appVersion: '1.0.0',
48
91
  appId: '1234',
49
92
  userPoolID: 'pool-id',
50
93
  fips: true,
51
94
  });
52
- expect(createEsriPluginMock).not.toHaveBeenCalled();
53
95
  });
54
- test('esri mode initializes esri plugin with url/headers', () => {
96
+ test('esri mode initializes esri plugin with internal endpoint config', () => {
55
97
  const esriPlugin = { track: jest.fn(() => true) };
56
98
  createEsriPluginMock.mockReturnValue(esriPlugin);
57
99
  new Amazon({
@@ -61,19 +103,51 @@ describe('Amazon', () => {
61
103
  id: '1234',
62
104
  },
63
105
  mode: 'esri',
64
- endpoint: {
65
- url: 'https://example.com/events',
66
- headers: { 'x-test': '1' },
106
+ });
107
+ expect(createEsriPluginMock).toHaveBeenCalledWith({
108
+ trackerName: 'amazon',
109
+ appName: 'test',
110
+ appVersion: undefined,
111
+ appId: '1234',
112
+ userPoolID: 'pool-id',
113
+ fips: undefined,
114
+ });
115
+ expect(AnalyticsMock).not.toHaveBeenCalled();
116
+ });
117
+ test('esri mode does not throw when userPoolID is missing at runtime', () => {
118
+ new Amazon({
119
+ mode: 'esri',
120
+ app: {
121
+ name: 'test',
122
+ id: '1234',
67
123
  },
68
124
  });
69
125
  expect(createEsriPluginMock).toHaveBeenCalledWith({
70
- url: 'https://example.com/events',
71
- headers: { 'x-test': '1' },
72
126
  trackerName: 'amazon',
73
127
  appName: 'test',
74
128
  appVersion: undefined,
129
+ appId: '1234',
130
+ userPoolID: undefined,
131
+ fips: undefined,
132
+ });
133
+ expect(AnalyticsMock).not.toHaveBeenCalled();
134
+ });
135
+ test('logEvent forwards immediate track option to analytics.track', () => {
136
+ const track = jest.fn(() => true);
137
+ createEsriPluginMock.mockReturnValue({ track });
138
+ const telemetry = new Amazon({
139
+ userPoolID: 'pool-id',
140
+ app: {
141
+ name: 'test',
142
+ id: '1234',
143
+ },
144
+ mode: 'esri',
75
145
  });
76
- expect(createPinpointPluginMock).not.toHaveBeenCalled();
146
+ const result = telemetry.logEvent({ eventType: 'cta_click', category: 'nav' }, { immediate: true });
147
+ const trackMock = track;
148
+ expect(result).toBe(true);
149
+ expect(trackMock).toHaveBeenCalledTimes(1);
150
+ expect(trackMock.mock.calls[0][2]).toEqual({ immediate: true });
77
151
  });
78
152
  });
79
153
  //# sourceMappingURL=index.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/index.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/index.test.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,19 @@
1
- import { debugLog, warnLog, getEndpointId } from './shared';
2
- function getString(value, fallback = '') {
3
- if (value === undefined || value === null) {
4
- return fallback;
5
- }
6
- return String(value);
7
- }
1
+ import { AwsClient } from 'aws4fetch';
2
+ import { getCredentials } from '../auth';
3
+ import { debugLog, warnLog, getEndpointId, getBrowserClientInfo, } from './utils/shared';
4
+ const ESRI_SIGNING_SERVICE = 'execute-api';
5
+ const ESRI_SIGNING_REGION = 'us-east-1';
6
+ const ESRI_ENDPOINT_URL = (appId) => `https://tedev.arcgis.com/v1/apps/${appId}/events`;
7
+ const SESSION_START_EVENT = '_session.start';
8
+ const SESSION_STOP_EVENT = '_session.stop';
9
+ const DEFAULT_IDLE_TIMEOUT_MS = 30 * 60 * 1000;
10
+ const DEFAULT_DEBOUNCE_MS = 5000;
11
+ /** Converts a string to a number, guarded against invalid values */
8
12
  function getNumber(value, fallback = 0) {
9
13
  const numeric = Number(value);
10
14
  return Number.isFinite(numeric) ? numeric : fallback;
11
15
  }
16
+ /** Splits an EsriBatchEventPayload into attributes and metrics */
12
17
  function splitEventData(payload) {
13
18
  const attributes = {};
14
19
  const metrics = {};
@@ -45,93 +50,369 @@ function splitEventData(payload) {
45
50
  metrics,
46
51
  };
47
52
  }
48
- function buildBatchEventPayload({ eventName, payload, trackerName, appName, appVersion, }) {
53
+ /** Constructs an event payload in the shape expected by the endpoint. */
54
+ function buildBatchEventPayload({ queuedEvents, trackerName, appName, appVersion, }) {
55
+ var _a, _b, _c, _d, _e, _f;
56
+ const endpointEvent = queuedEvents[queuedEvents.length - 1] ||
57
+ {
58
+ payload: {},
59
+ browserInfo: {},
60
+ };
61
+ const payload = endpointEvent.payload || {};
62
+ const browserInfo = endpointEvent.browserInfo || {};
49
63
  const endpointId = getEndpointId();
50
- const sessionId = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
51
- const timestamp = new Date().toISOString();
52
- const { attributes, metrics } = splitEventData(payload);
64
+ const location = {};
65
+ if (payload === null || payload === void 0 ? void 0 : payload.city) {
66
+ location.City = payload.city;
67
+ }
68
+ if (payload === null || payload === void 0 ? void 0 : payload.country) {
69
+ location.Country = payload.country;
70
+ }
71
+ if ((payload === null || payload === void 0 ? void 0 : payload.lat) !== undefined && (payload === null || payload === void 0 ? void 0 : payload.lat) !== null) {
72
+ location.Lat = getNumber(payload.lat);
73
+ }
74
+ if ((payload === null || payload === void 0 ? void 0 : payload.lon) !== undefined && (payload === null || payload === void 0 ? void 0 : payload.lon) !== null) {
75
+ location.Lon = getNumber(payload.lon);
76
+ }
53
77
  return {
54
78
  BatchEvent: {
55
79
  Endpoint: {
56
80
  Id: endpointId,
57
- Device: {
58
- Make: getString(payload === null || payload === void 0 ? void 0 : payload.deviceMake),
59
- Model: getString(payload === null || payload === void 0 ? void 0 : payload.deviceModel),
60
- },
61
- Location: {
62
- City: getString(payload === null || payload === void 0 ? void 0 : payload.city),
63
- Country: getString(payload === null || payload === void 0 ? void 0 : payload.country),
64
- Lat: getNumber(payload === null || payload === void 0 ? void 0 : payload.lat),
65
- Lon: getNumber(payload === null || payload === void 0 ? void 0 : payload.lon),
66
- },
81
+ Device: Object.assign({ Locale: (browserInfo === null || browserInfo === void 0 ? void 0 : browserInfo.language) || '', Make: (payload === null || payload === void 0 ? void 0 : payload.deviceMake) || (browserInfo === null || browserInfo === void 0 ? void 0 : browserInfo.make) || '', Model: (payload === null || payload === void 0 ? void 0 : payload.deviceModel) || (browserInfo === null || browserInfo === void 0 ? void 0 : browserInfo.model) || '', ModelVersion: (browserInfo === null || browserInfo === void 0 ? void 0 : browserInfo.version) || '', Platform: ((_a = browserInfo === null || browserInfo === void 0 ? void 0 : browserInfo.os) === null || _a === void 0 ? void 0 : _a.name) || (browserInfo === null || browserInfo === void 0 ? void 0 : browserInfo.platform) || '' }, (((_b = browserInfo === null || browserInfo === void 0 ? void 0 : browserInfo.os) === null || _b === void 0 ? void 0 : _b.version)
82
+ ? { PlatformVersion: browserInfo.os.version }
83
+ : {})),
84
+ Location: location,
67
85
  Application: {
68
- AppTitle: getString(payload === null || payload === void 0 ? void 0 : payload.appTitle, appName || trackerName),
69
- AppVersion: getString(payload === null || payload === void 0 ? void 0 : payload.appVersion, appVersion),
70
- ClientSdkVersion: getString(payload === null || payload === void 0 ? void 0 : payload.clientSdkVersion, trackerName),
86
+ AppTitle: (_d = (_c = payload === null || payload === void 0 ? void 0 : payload.appTitle) !== null && _c !== void 0 ? _c : appName) !== null && _d !== void 0 ? _d : trackerName,
87
+ AppVersion: (_e = payload === null || payload === void 0 ? void 0 : payload.appVersion) !== null && _e !== void 0 ? _e : appVersion,
88
+ ClientSdkVersion: (_f = payload === null || payload === void 0 ? void 0 : payload.clientSdkVersion) !== null && _f !== void 0 ? _f : trackerName,
71
89
  },
72
90
  },
73
- Events: [
74
- {
75
- EventType: eventName,
91
+ Events: queuedEvents.map((queuedEvent) => {
92
+ const timestamp = queuedEvent.timestamp;
93
+ const { attributes, metrics } = splitEventData(queuedEvent.payload);
94
+ const sessionContext = queuedEvent.session ||
95
+ {
96
+ id: `${queuedEvent.unixMs}-${Math.random().toString(16).slice(2)}`,
97
+ startTimestamp: timestamp,
98
+ startUnixMs: queuedEvent.unixMs,
99
+ };
100
+ const isStopEvent = queuedEvent.eventName === SESSION_STOP_EVENT;
101
+ return {
102
+ EventType: queuedEvent.eventName,
76
103
  Timestamp: timestamp,
77
104
  Session: {
78
- Id: sessionId,
79
- StartTimestamp: timestamp,
105
+ Id: sessionContext.id,
106
+ StartTimestamp: sessionContext.startTimestamp,
80
107
  EndTimestamp: timestamp,
81
- Duration: 0,
108
+ Duration: isStopEvent
109
+ ? Math.max(0, queuedEvent.unixMs - sessionContext.startUnixMs)
110
+ : 0,
82
111
  },
83
112
  Attributes: attributes,
84
113
  Metrics: metrics,
85
- },
86
- ],
114
+ };
115
+ }),
87
116
  },
88
117
  };
89
118
  }
90
- export function createEsriPlugin({ url, headers = {}, trackerName, appName, appVersion, }) {
119
+ export function createEsriPlugin({ trackerName, appName, appId, appVersion, userPoolID, fips, debounceMs = DEFAULT_DEBOUNCE_MS, }) {
120
+ const url = ESRI_ENDPOINT_URL(appId);
121
+ let isDisabled = false;
122
+ let hasWarned = false;
123
+ let hasSessionStarted = false;
124
+ let sessionStopSent = false;
125
+ let activeSession;
126
+ let idleTimerId;
127
+ let debounceTimerId;
128
+ let lifecycleListenersAttached = false;
129
+ const removeLifecycleListeners = [];
130
+ let pendingEvents = [];
131
+ const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
132
+ const warnOnce = (message, details) => {
133
+ if (hasWarned) {
134
+ return;
135
+ }
136
+ hasWarned = true;
137
+ warnLog(message, details);
138
+ };
91
139
  debugLog('createEsriPlugin initialized', {
92
140
  trackerName,
93
141
  hasUrl: Boolean(url),
94
- headerCount: Object.keys(headers).length,
95
142
  });
96
- return {
97
- track: (eventName, payload) => {
98
- if (!url || typeof fetch !== 'function') {
99
- debugLog('Skipping endpoint track call', {
100
- eventName,
101
- reason: !url ? 'missing-url' : 'fetch-unavailable',
102
- });
103
- return false;
104
- }
105
- debugLog('Sending endpoint event', {
106
- eventName,
107
- trackerName,
108
- url,
143
+ if (!url) {
144
+ isDisabled = true;
145
+ warnOnce('Esri telemetry disabled due to missing internal endpoint URL', {
146
+ trackerName,
147
+ });
148
+ }
149
+ if (!userPoolID) {
150
+ isDisabled = true;
151
+ warnOnce('Esri telemetry disabled due to missing userPoolID', {
152
+ trackerName,
153
+ });
154
+ }
155
+ if (typeof fetch !== 'function') {
156
+ isDisabled = true;
157
+ warnOnce('Esri telemetry disabled because fetch is unavailable', {
158
+ trackerName,
159
+ });
160
+ }
161
+ const clearIdleTimer = () => {
162
+ if (!idleTimerId) {
163
+ return;
164
+ }
165
+ clearTimeout(idleTimerId);
166
+ idleTimerId = undefined;
167
+ };
168
+ const clearDebounceTimer = () => {
169
+ if (!debounceTimerId) {
170
+ return;
171
+ }
172
+ clearTimeout(debounceTimerId);
173
+ debounceTimerId = undefined;
174
+ };
175
+ const clearPendingEvents = () => {
176
+ pendingEvents = [];
177
+ };
178
+ const enqueueEvent = (event) => {
179
+ pendingEvents.push(event);
180
+ };
181
+ const dequeuePendingEvents = () => {
182
+ if (pendingEvents.length === 0) {
183
+ return [];
184
+ }
185
+ const queuedEvents = pendingEvents;
186
+ pendingEvents = [];
187
+ return queuedEvents;
188
+ };
189
+ const sendQueuedEvents = (queuedEvents) => {
190
+ var _a;
191
+ if (queuedEvents.length === 0) {
192
+ return;
193
+ }
194
+ if (isDisabled || !url || !userPoolID) {
195
+ debugLog('Skipping esri track call', {
196
+ eventName: (_a = queuedEvents[queuedEvents.length - 1]) === null || _a === void 0 ? void 0 : _a.eventName,
197
+ reason: 'plugin-disabled',
109
198
  });
110
- void fetch(url, {
111
- method: 'POST',
112
- headers: Object.assign({ 'Content-Type': 'application/json' }, headers),
113
- body: JSON.stringify(buildBatchEventPayload({
114
- eventName,
115
- payload,
116
- trackerName,
117
- appName,
118
- appVersion,
119
- })),
120
- })
121
- .then(() => {
199
+ return;
200
+ }
201
+ debugLog('Sending esri endpoint batch event', {
202
+ trackerName,
203
+ url,
204
+ eventCount: queuedEvents.length,
205
+ });
206
+ void (async () => {
207
+ var _a;
208
+ try {
209
+ const credentials = await getCredentials(userPoolID, { fips });
210
+ const accessKeyId = (credentials === null || credentials === void 0 ? void 0 : credentials.AccessKeyId) || (credentials === null || credentials === void 0 ? void 0 : credentials.accessKeyId);
211
+ const secretAccessKey = (credentials === null || credentials === void 0 ? void 0 : credentials.SecretKey) || (credentials === null || credentials === void 0 ? void 0 : credentials.secretAccessKey);
212
+ const sessionToken = (credentials === null || credentials === void 0 ? void 0 : credentials.SessionToken) || (credentials === null || credentials === void 0 ? void 0 : credentials.sessionToken);
213
+ if (!accessKeyId || !secretAccessKey) {
214
+ throw new Error('Signed endpoint credentials are missing required key fields.');
215
+ }
216
+ const client = new AwsClient({
217
+ accessKeyId,
218
+ secretAccessKey,
219
+ sessionToken,
220
+ service: ESRI_SIGNING_SERVICE,
221
+ region: ESRI_SIGNING_REGION,
222
+ });
223
+ await client.fetch(url, {
224
+ method: 'POST',
225
+ headers: {
226
+ 'Content-Type': 'application/json',
227
+ },
228
+ body: JSON.stringify(buildBatchEventPayload({
229
+ queuedEvents,
230
+ trackerName,
231
+ appName,
232
+ appVersion,
233
+ })),
234
+ });
122
235
  debugLog('Endpoint event sent', {
123
- eventName,
124
236
  trackerName,
237
+ eventCount: queuedEvents.length,
125
238
  });
126
- })
127
- .catch((error) => {
128
- warnLog('Endpoint track failed', {
129
- eventName,
239
+ }
240
+ catch (error) {
241
+ isDisabled = true;
242
+ clearIdleTimer();
243
+ clearDebounceTimer();
244
+ clearPendingEvents();
245
+ removeAllLifecycleListeners();
246
+ warnOnce('Esri telemetry disabled after endpoint track failure', {
247
+ eventName: (_a = queuedEvents[queuedEvents.length - 1]) === null || _a === void 0 ? void 0 : _a.eventName,
130
248
  trackerName,
131
249
  message: (error === null || error === void 0 ? void 0 : error.message) || String(error),
132
250
  });
251
+ }
252
+ })();
253
+ };
254
+ const flushPendingEvents = (eventToAppend) => {
255
+ clearDebounceTimer();
256
+ const queuedEvents = dequeuePendingEvents();
257
+ if (eventToAppend) {
258
+ queuedEvents.push(eventToAppend);
259
+ }
260
+ sendQueuedEvents(queuedEvents);
261
+ };
262
+ const scheduleDebounceFlush = () => {
263
+ clearDebounceTimer();
264
+ debounceTimerId = setTimeout(() => {
265
+ flushPendingEvents();
266
+ }, debounceMs);
267
+ };
268
+ const removeAllLifecycleListeners = () => {
269
+ removeLifecycleListeners.splice(0).forEach((removeListener) => {
270
+ removeListener();
271
+ });
272
+ lifecycleListenersAttached = false;
273
+ };
274
+ const createSession = () => {
275
+ const now = Date.now();
276
+ return {
277
+ id: `${now}-${Math.random().toString(16).slice(2)}`,
278
+ startTimestamp: new Date(now).toISOString(),
279
+ startUnixMs: now,
280
+ };
281
+ };
282
+ const sendEvent = (eventName, payload, sessionOverride, options, forceImmediate = false) => {
283
+ if (isDisabled || !url || !userPoolID) {
284
+ debugLog('Skipping esri track call', {
285
+ eventName,
286
+ reason: 'plugin-disabled',
133
287
  });
134
- return true;
288
+ return false;
289
+ }
290
+ debugLog('Queueing esri endpoint event', {
291
+ eventName,
292
+ trackerName,
293
+ immediate: Boolean((options === null || options === void 0 ? void 0 : options.immediate) || forceImmediate),
294
+ });
295
+ const queuedEvent = {
296
+ eventName,
297
+ payload: payload,
298
+ session: sessionOverride || activeSession,
299
+ browserInfo: getBrowserClientInfo(),
300
+ timestamp: new Date().toISOString(),
301
+ unixMs: Date.now(),
302
+ };
303
+ if ((options === null || options === void 0 ? void 0 : options.immediate) || forceImmediate) {
304
+ flushPendingEvents(queuedEvent);
305
+ }
306
+ else {
307
+ enqueueEvent(queuedEvent);
308
+ scheduleDebounceFlush();
309
+ }
310
+ return true;
311
+ };
312
+ const sendSessionLifecycleEvent = (eventName, reason, sessionOverride) => {
313
+ return sendEvent(eventName, {
314
+ reason,
315
+ }, sessionOverride, undefined, true);
316
+ };
317
+ const stopSession = (reason) => {
318
+ if (!hasSessionStarted || sessionStopSent) {
319
+ return false;
320
+ }
321
+ const sessionToStop = activeSession;
322
+ const didStop = sendSessionLifecycleEvent(SESSION_STOP_EVENT, reason, sessionToStop);
323
+ if (didStop) {
324
+ sessionStopSent = true;
325
+ hasSessionStarted = false;
326
+ clearIdleTimer();
327
+ activeSession = undefined;
328
+ }
329
+ return didStop;
330
+ };
331
+ const startSession = (reason) => {
332
+ if (hasSessionStarted) {
333
+ return false;
334
+ }
335
+ activeSession = activeSession || createSession();
336
+ const sessionToStart = activeSession;
337
+ const didStart = sendSessionLifecycleEvent(SESSION_START_EVENT, reason, sessionToStart);
338
+ if (didStart) {
339
+ hasSessionStarted = true;
340
+ sessionStopSent = false;
341
+ }
342
+ else {
343
+ activeSession = undefined;
344
+ }
345
+ return didStart;
346
+ };
347
+ const resetIdleTimer = () => {
348
+ if (!isBrowser || isDisabled || !hasSessionStarted) {
349
+ return;
350
+ }
351
+ clearIdleTimer();
352
+ idleTimerId = setTimeout(() => {
353
+ stopSession('idle');
354
+ }, DEFAULT_IDLE_TIMEOUT_MS);
355
+ };
356
+ const handleActivity = () => {
357
+ if (isDisabled) {
358
+ return;
359
+ }
360
+ if (!hasSessionStarted) {
361
+ startSession('wakeup');
362
+ }
363
+ resetIdleTimer();
364
+ };
365
+ const addLifecycleListener = (target, eventName, listener) => {
366
+ target.addEventListener(eventName, listener);
367
+ removeLifecycleListeners.push(() => {
368
+ target.removeEventListener(eventName, listener);
369
+ });
370
+ };
371
+ const attachLifecycleListeners = () => {
372
+ if (!isBrowser || lifecycleListenersAttached || isDisabled) {
373
+ return;
374
+ }
375
+ const activityEvents = ['pointerdown', 'keydown', 'scroll', 'focus'];
376
+ activityEvents.forEach((eventName) => {
377
+ addLifecycleListener(window, eventName, handleActivity);
378
+ });
379
+ addLifecycleListener(window, 'beforeunload', () => {
380
+ stopSession('beforeunload');
381
+ });
382
+ lifecycleListenersAttached = true;
383
+ };
384
+ return {
385
+ track: (eventName, payload, options) => {
386
+ void options;
387
+ attachLifecycleListeners();
388
+ const isSessionStopEvent = eventName === SESSION_STOP_EVENT;
389
+ if (!hasSessionStarted &&
390
+ eventName !== SESSION_START_EVENT &&
391
+ !isSessionStopEvent) {
392
+ startSession('init');
393
+ }
394
+ if (eventName === SESSION_START_EVENT) {
395
+ activeSession = activeSession || createSession();
396
+ hasSessionStarted = true;
397
+ sessionStopSent = false;
398
+ }
399
+ if (isSessionStopEvent) {
400
+ sessionStopSent = true;
401
+ hasSessionStarted = false;
402
+ clearIdleTimer();
403
+ }
404
+ if (hasSessionStarted && !activeSession && !isSessionStopEvent) {
405
+ activeSession = createSession();
406
+ }
407
+ if (hasSessionStarted && !isSessionStopEvent) {
408
+ resetIdleTimer();
409
+ }
410
+ const sessionForEvent = activeSession;
411
+ const didSend = sendEvent(eventName, payload, sessionForEvent, options, eventName === SESSION_START_EVENT || eventName === SESSION_STOP_EVENT);
412
+ if (isSessionStopEvent && didSend) {
413
+ activeSession = undefined;
414
+ }
415
+ return didSend;
135
416
  },
136
417
  };
137
418
  }