@esri/telemetry-amazon 6.0.0-beta.0 → 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.
- package/README.md +26 -9
- package/dist/esm/auth.js +22 -31
- package/dist/esm/auth.js.map +1 -1
- package/dist/esm/create-event-log.js +15 -15
- package/dist/esm/create-event-log.js.map +1 -1
- package/dist/esm/create-event-log.test.js +50 -8
- package/dist/esm/create-event-log.test.js.map +1 -1
- package/dist/esm/create-page-view-log.js +1 -6
- package/dist/esm/create-page-view-log.js.map +1 -1
- package/dist/esm/format-telemetry-attributes.js +8 -4
- package/dist/esm/format-telemetry-attributes.js.map +1 -1
- package/dist/esm/index.js +43 -19
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.test.js +106 -160
- package/dist/esm/index.test.js.map +1 -1
- package/dist/esm/plugins/esri.js +419 -0
- package/dist/esm/plugins/esri.js.map +1 -0
- package/dist/esm/plugins/esri.test.js +556 -0
- package/dist/esm/plugins/esri.test.js.map +1 -0
- package/dist/esm/plugins/utils/browser.js +97 -0
- package/dist/esm/plugins/utils/browser.js.map +1 -0
- package/dist/esm/plugins/utils/browser.test.js +130 -0
- package/dist/esm/plugins/utils/browser.test.js.map +1 -0
- package/dist/esm/plugins/utils/os.js +140 -0
- package/dist/esm/plugins/utils/os.js.map +1 -0
- package/dist/esm/plugins/utils/os.test.js +54 -0
- package/dist/esm/plugins/utils/os.test.js.map +1 -0
- package/dist/esm/plugins/utils/shared.js +74 -0
- package/dist/esm/plugins/utils/shared.js.map +1 -0
- package/dist/esm/types.js +97 -1
- package/dist/esm/types.js.map +1 -1
- package/dist/node/auth.js +22 -31
- package/dist/node/auth.js.map +1 -1
- package/dist/node/create-event-log.js +15 -15
- package/dist/node/create-event-log.js.map +1 -1
- package/dist/node/create-event-log.test.js +50 -8
- package/dist/node/create-event-log.test.js.map +1 -1
- package/dist/node/create-page-view-log.js +1 -6
- package/dist/node/create-page-view-log.js.map +1 -1
- package/dist/node/format-telemetry-attributes.js +8 -4
- package/dist/node/format-telemetry-attributes.js.map +1 -1
- package/dist/node/index.js +43 -19
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.test.js +106 -160
- package/dist/node/index.test.js.map +1 -1
- package/dist/node/plugins/esri.js +423 -0
- package/dist/node/plugins/esri.js.map +1 -0
- package/dist/node/plugins/esri.test.js +559 -0
- package/dist/node/plugins/esri.test.js.map +1 -0
- package/dist/node/plugins/utils/browser.js +101 -0
- package/dist/node/plugins/utils/browser.js.map +1 -0
- package/dist/node/plugins/utils/browser.test.js +132 -0
- package/dist/node/plugins/utils/browser.test.js.map +1 -0
- package/dist/node/plugins/utils/os.js +144 -0
- package/dist/node/plugins/utils/os.js.map +1 -0
- package/dist/node/plugins/utils/os.test.js +56 -0
- package/dist/node/plugins/utils/os.test.js.map +1 -0
- package/dist/node/plugins/utils/shared.js +83 -0
- package/dist/node/plugins/utils/shared.js.map +1 -0
- package/dist/node/types.js +98 -0
- package/dist/node/types.js.map +1 -1
- package/dist/types/create-event-log.d.ts +2 -2
- package/dist/types/create-page-view-log.d.ts +1 -1
- package/dist/types/format-telemetry-attributes.d.ts +3 -3
- package/dist/types/index.d.ts +14 -7
- package/dist/types/plugins/esri.d.ts +6 -0
- package/dist/types/plugins/esri.test.d.ts +1 -0
- package/dist/types/plugins/utils/browser.d.ts +12 -0
- package/dist/types/plugins/utils/browser.test.d.ts +1 -0
- package/dist/types/plugins/utils/os.d.ts +5 -0
- package/dist/types/plugins/utils/os.test.d.ts +1 -0
- package/dist/types/plugins/utils/shared.d.ts +12 -0
- package/dist/types/types.d.ts +114 -19
- package/dist/umd/telemetry-amazon.js +1253 -141
- package/dist/umd/telemetry-amazon.js.map +1 -1
- package/dist/umd/telemetry-amazon.min.js +1253 -141
- package/dist/umd/telemetry-amazon.min.js.map +1 -1
- package/package.json +4 -3
|
@@ -8,115 +8,86 @@
|
|
|
8
8
|
|
|
9
9
|
var awsPinpointPlugin__default = /*#__PURE__*/_interopDefaultLegacy(awsPinpointPlugin);
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
11
|
+
const AMAZON_EVENT_ATTRIBUTE_KEYS = [
|
|
12
|
+
'action',
|
|
13
|
+
'attribute',
|
|
14
|
+
'category',
|
|
15
|
+
'datasetId',
|
|
16
|
+
'details',
|
|
17
|
+
'error',
|
|
18
|
+
'facetValue',
|
|
19
|
+
'hostname',
|
|
20
|
+
'json',
|
|
21
|
+
'label',
|
|
22
|
+
'lastLogin',
|
|
23
|
+
'objectId',
|
|
24
|
+
'org',
|
|
25
|
+
'pageUrl',
|
|
26
|
+
'path',
|
|
27
|
+
'pageName',
|
|
28
|
+
'previousPageName',
|
|
29
|
+
'previousPageUrl',
|
|
30
|
+
'referrer',
|
|
31
|
+
'searchQuery',
|
|
32
|
+
'serviceQuery',
|
|
33
|
+
'statusCode',
|
|
34
|
+
'user',
|
|
35
|
+
'userSince',
|
|
36
|
+
'urlRequested',
|
|
37
|
+
'internalUser',
|
|
38
|
+
'accountType',
|
|
39
|
+
'workflowId',
|
|
40
|
+
'browserWebCapabilities',
|
|
41
|
+
/**
|
|
42
|
+
* Original plan was to union this in as `AmazonDimensionKey`, but it's strictly defined in the schema that there are 20 dimensions
|
|
43
|
+
* so a little non-DRY is fine for readability's sake
|
|
44
|
+
*/
|
|
45
|
+
'dimension1',
|
|
46
|
+
'dimension2',
|
|
47
|
+
'dimension3',
|
|
48
|
+
'dimension4',
|
|
49
|
+
'dimension5',
|
|
50
|
+
'dimension6',
|
|
51
|
+
'dimension7',
|
|
52
|
+
'dimension8',
|
|
53
|
+
'dimension9',
|
|
54
|
+
'dimension10',
|
|
55
|
+
'dimension11',
|
|
56
|
+
'dimension12',
|
|
57
|
+
'dimension13',
|
|
58
|
+
'dimension14',
|
|
59
|
+
'dimension15',
|
|
60
|
+
'dimension16',
|
|
61
|
+
'dimension17',
|
|
62
|
+
'dimension18',
|
|
63
|
+
'dimension19',
|
|
64
|
+
'dimension20',
|
|
65
|
+
/**
|
|
66
|
+
* NOTE: `eventType` is NOT an attribute, but instead a core property on the telemetry payload. Many apps have been treating it
|
|
67
|
+
* as an attribute though, so it's included here for backwards compatibility. Internally it is re-mapped to `eventType`, where all other attributes
|
|
68
|
+
* are mapped to `a_attributeName` in the telemetry payload. This is a known issue and will be addressed in a future release.
|
|
69
|
+
*/
|
|
70
|
+
'eventType',
|
|
71
|
+
/**
|
|
72
|
+
* NOTE: `pathname` is NOT an attribute, nor an official property at all. It is internally mapped to `path`, but is used interchangeably by consuming apps.
|
|
73
|
+
* It's included here for backwards compatibility, but should not be used in new telemetry events. If `path` is set, this will never be used.
|
|
74
|
+
*/
|
|
75
|
+
'pathname',
|
|
76
|
+
];
|
|
63
77
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const fipsSubdomainSuffix = options.fips === true ? '-fips' : '';
|
|
67
|
-
const COGNITO_URL = `https://cognito-identity${fipsSubdomainSuffix}.us-east-1.amazonaws.com/`;
|
|
68
|
-
let cached = storage.get(COGNITO_KEY);
|
|
69
|
-
if (cached && Date.now() / 1000 < cached.Expiration)
|
|
70
|
-
return Promise.resolve(cached);
|
|
71
|
-
const fetchOptions = {
|
|
72
|
-
method: 'POST',
|
|
73
|
-
headers: {
|
|
74
|
-
'Content-type': 'application/x-amz-json-1.1',
|
|
75
|
-
'X-Amz-Target': 'AWSCognitoIdentityService.GetId',
|
|
76
|
-
},
|
|
77
|
-
body: JSON.stringify({ IdentityPoolId }),
|
|
78
|
-
};
|
|
79
|
-
return fetch(COGNITO_URL, fetchOptions)
|
|
80
|
-
.then((response) => {
|
|
81
|
-
if (!response.ok) {
|
|
82
|
-
throw new Error(response.statusText);
|
|
83
|
-
}
|
|
84
|
-
return response.json();
|
|
85
|
-
})
|
|
86
|
-
.then((response) => {
|
|
87
|
-
const { IdentityId } = response;
|
|
88
|
-
const options = {
|
|
89
|
-
method: 'POST',
|
|
90
|
-
headers: {
|
|
91
|
-
'Content-type': 'application/x-amz-json-1.1',
|
|
92
|
-
'X-Amz-Target': 'AWSCognitoIdentityService.GetCredentialsForIdentity',
|
|
93
|
-
},
|
|
94
|
-
body: JSON.stringify({ IdentityId }),
|
|
95
|
-
};
|
|
96
|
-
return fetch(COGNITO_URL, options);
|
|
97
|
-
})
|
|
98
|
-
.then((response) => {
|
|
99
|
-
if (!response.ok) {
|
|
100
|
-
throw new Error(response.statusText);
|
|
101
|
-
}
|
|
102
|
-
return response.json();
|
|
103
|
-
})
|
|
104
|
-
.then(({ Credentials }) => {
|
|
105
|
-
storage.set(COGNITO_KEY, Credentials);
|
|
106
|
-
return Credentials;
|
|
107
|
-
});
|
|
78
|
+
function isAmazonEventAttributeKey(key) {
|
|
79
|
+
return AMAZON_EVENT_ATTRIBUTE_KEYS.includes(key);
|
|
108
80
|
}
|
|
109
|
-
|
|
110
|
-
function formatTelemetryAttributes({ telemetryData, dimensionLookup = {}, excludeKeys = [], }) {
|
|
81
|
+
function formatTelemetryAttributes({ telemetryData, dimensionLookup = {}, }) {
|
|
111
82
|
return Object.keys(telemetryData)
|
|
112
83
|
.filter((key) => {
|
|
113
|
-
return
|
|
84
|
+
return isAmazonEventAttributeKey(key);
|
|
114
85
|
})
|
|
115
86
|
.map((key) => {
|
|
116
87
|
if (dimensionLookup[key]) {
|
|
117
88
|
return {
|
|
118
89
|
key: getCustomDimensionKey(dimensionLookup, key),
|
|
119
|
-
value: telemetryData
|
|
90
|
+
value: getValue(telemetryData, key),
|
|
120
91
|
};
|
|
121
92
|
}
|
|
122
93
|
return {
|
|
@@ -133,7 +104,7 @@
|
|
|
133
104
|
if (key === 'json') {
|
|
134
105
|
return data[key] ? JSON.stringify(data[key]) : 'null';
|
|
135
106
|
}
|
|
136
|
-
return data[key] === undefined ? 'null' : data[key]
|
|
107
|
+
return data[key] === undefined ? 'null' : String(data[key]);
|
|
137
108
|
}
|
|
138
109
|
function getCustomDimensionKey(lookup, key) {
|
|
139
110
|
return `dimension${lookup[key]}`;
|
|
@@ -167,31 +138,30 @@
|
|
|
167
138
|
}
|
|
168
139
|
|
|
169
140
|
function createEventLog({ event = {}, dimensionLookup = {}, metricLookup = {}, }) {
|
|
170
|
-
var _a, _b;
|
|
141
|
+
var _a, _b, _c;
|
|
142
|
+
const acceptedAttributeKeys = new Set(AMAZON_EVENT_ATTRIBUTE_KEYS);
|
|
171
143
|
const { pathname: eventPathname, eventType = 'other' } = event;
|
|
172
144
|
const metrics = formatTelemetryMetrics(event, metricLookup);
|
|
173
|
-
const
|
|
174
|
-
telemetryData: Object.assign({
|
|
145
|
+
const formattedEventAttributes = formatTelemetryAttributes({
|
|
146
|
+
telemetryData: Object.assign({}, event),
|
|
175
147
|
dimensionLookup,
|
|
176
|
-
excludeKeys: [
|
|
177
|
-
'workflow',
|
|
178
|
-
'pathname',
|
|
179
|
-
...Object.keys(metrics),
|
|
180
|
-
...Object.keys(metricLookup),
|
|
181
|
-
],
|
|
182
148
|
});
|
|
183
|
-
const
|
|
149
|
+
const eventAttributes = Object.fromEntries(Object.entries(formattedEventAttributes).filter(([key]) => {
|
|
150
|
+
return acceptedAttributeKeys.has(key);
|
|
151
|
+
}));
|
|
152
|
+
const telemetryPayload = Object.assign(Object.assign(Object.assign({}, eventAttributes), metrics), { name: eventType });
|
|
184
153
|
// Set browser properties if available
|
|
185
|
-
if (typeof document !== 'undefined' &&
|
|
154
|
+
if (typeof document !== 'undefined' &&
|
|
155
|
+
document.referrer &&
|
|
156
|
+
!telemetryPayload.referrer) {
|
|
186
157
|
telemetryPayload.referrer = document.referrer;
|
|
187
158
|
}
|
|
188
159
|
if (typeof window !== 'undefined' && !telemetryPayload.hostname) {
|
|
189
160
|
telemetryPayload.hostname = (_a = window.location) === null || _a === void 0 ? void 0 : _a.hostname;
|
|
190
161
|
}
|
|
191
|
-
telemetryPayload.path =
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
: eventPathname;
|
|
162
|
+
(_b = telemetryPayload.path) !== null && _b !== void 0 ? _b : (telemetryPayload.path = typeof window !== 'undefined' && window.location.pathname
|
|
163
|
+
? (_c = window.location) === null || _c === void 0 ? void 0 : _c.pathname
|
|
164
|
+
: eventPathname);
|
|
195
165
|
return telemetryPayload;
|
|
196
166
|
}
|
|
197
167
|
|
|
@@ -200,16 +170,11 @@
|
|
|
200
170
|
const attributes = formatTelemetryAttributes({
|
|
201
171
|
telemetryData: options,
|
|
202
172
|
dimensionLookup,
|
|
203
|
-
excludeKeys: [
|
|
204
|
-
'workflow',
|
|
205
|
-
...Object.keys(metrics),
|
|
206
|
-
...Object.keys(metricLookup),
|
|
207
|
-
],
|
|
208
173
|
});
|
|
209
174
|
const { referrer, title } = document || {};
|
|
210
175
|
const { hostname, pathname } = window && window.location ? window.location : {};
|
|
211
176
|
return Object.assign(Object.assign({ name: 'pageView', referrer,
|
|
212
|
-
hostname, path: page || pathname, pageUrl: page || pathname, pageName: title, previousPageUrl: previousPage.pageUrl, previousPageName: previousPage.pageName }, attributes), metrics);
|
|
177
|
+
hostname, path: page || pathname, pageUrl: page || pathname, pageName: title, previousPageUrl: previousPage === null || previousPage === void 0 ? void 0 : previousPage.pageUrl, previousPageName: previousPage === null || previousPage === void 0 ? void 0 : previousPage.pageName }, attributes), metrics);
|
|
213
178
|
}
|
|
214
179
|
|
|
215
180
|
var dlv_umd = {exports: {}};
|
|
@@ -288,6 +253,96 @@
|
|
|
288
253
|
return De(_objectSpread2(_objectSpread2({}, defaultSettings), opts));
|
|
289
254
|
}
|
|
290
255
|
|
|
256
|
+
const storage = {
|
|
257
|
+
storage: {},
|
|
258
|
+
memory: true,
|
|
259
|
+
get(key) {
|
|
260
|
+
let stored;
|
|
261
|
+
try {
|
|
262
|
+
stored =
|
|
263
|
+
(window.localStorage && window.localStorage.getItem(key)) ||
|
|
264
|
+
this.storage[key];
|
|
265
|
+
}
|
|
266
|
+
catch (e) {
|
|
267
|
+
stored = this.storage[key];
|
|
268
|
+
}
|
|
269
|
+
if (stored) {
|
|
270
|
+
try {
|
|
271
|
+
return JSON.parse(stored);
|
|
272
|
+
}
|
|
273
|
+
catch (e) {
|
|
274
|
+
return undefined;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
set(key, value) {
|
|
282
|
+
// handle Safari private mode (setItem is not allowed)
|
|
283
|
+
const valueToString = JSON.stringify(value);
|
|
284
|
+
try {
|
|
285
|
+
window.localStorage.setItem(key, valueToString);
|
|
286
|
+
}
|
|
287
|
+
catch (e) {
|
|
288
|
+
if (!this.memory) {
|
|
289
|
+
console.error('setting local storage failed, falling back to in-memory storage');
|
|
290
|
+
this.memory = true;
|
|
291
|
+
}
|
|
292
|
+
this.storage[key] = value;
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
delete(key) {
|
|
296
|
+
try {
|
|
297
|
+
window.localStorage.removeItem(key);
|
|
298
|
+
}
|
|
299
|
+
catch (e) {
|
|
300
|
+
if (!this.memory) {
|
|
301
|
+
console.error('setting local storage failed, falling back to in-memory storage');
|
|
302
|
+
this.memory = true;
|
|
303
|
+
}
|
|
304
|
+
delete this.storage[key];
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const COGNITO_KEY = 'TELEMETRY_COGNITO_CREDENTIALS';
|
|
310
|
+
async function getCredentials(IdentityPoolId, options = {}) {
|
|
311
|
+
const fipsSubdomainSuffix = options.fips === true ? '-fips' : '';
|
|
312
|
+
const COGNITO_URL = `https://cognito-identity${fipsSubdomainSuffix}.us-east-1.amazonaws.com/`;
|
|
313
|
+
let cached = storage.get(COGNITO_KEY);
|
|
314
|
+
if (cached && Date.now() / 1000 < cached.Expiration)
|
|
315
|
+
return cached;
|
|
316
|
+
const fetchOptions = {
|
|
317
|
+
method: 'POST',
|
|
318
|
+
headers: {
|
|
319
|
+
'Content-type': 'application/x-amz-json-1.1',
|
|
320
|
+
'X-Amz-Target': 'AWSCognitoIdentityService.GetId',
|
|
321
|
+
},
|
|
322
|
+
body: JSON.stringify({ IdentityPoolId }),
|
|
323
|
+
};
|
|
324
|
+
const getIdResponse = await fetch(COGNITO_URL, fetchOptions);
|
|
325
|
+
if (!getIdResponse.ok) {
|
|
326
|
+
throw new Error(getIdResponse.statusText);
|
|
327
|
+
}
|
|
328
|
+
const { IdentityId } = await getIdResponse.json();
|
|
329
|
+
const credentialsOptions = {
|
|
330
|
+
method: 'POST',
|
|
331
|
+
headers: {
|
|
332
|
+
'Content-type': 'application/x-amz-json-1.1',
|
|
333
|
+
'X-Amz-Target': 'AWSCognitoIdentityService.GetCredentialsForIdentity',
|
|
334
|
+
},
|
|
335
|
+
body: JSON.stringify({ IdentityId }),
|
|
336
|
+
};
|
|
337
|
+
const getCredentialsResponse = await fetch(COGNITO_URL, credentialsOptions);
|
|
338
|
+
if (!getCredentialsResponse.ok) {
|
|
339
|
+
throw new Error(getCredentialsResponse.statusText);
|
|
340
|
+
}
|
|
341
|
+
const { Credentials } = await getCredentialsResponse.json();
|
|
342
|
+
storage.set(COGNITO_KEY, Credentials);
|
|
343
|
+
return Credentials;
|
|
344
|
+
}
|
|
345
|
+
|
|
291
346
|
/******************************************************************************
|
|
292
347
|
Copyright (c) Microsoft Corporation.
|
|
293
348
|
|
|
@@ -1259,6 +1314,26 @@
|
|
|
1259
1314
|
function bboxToString(extent) {
|
|
1260
1315
|
return extent.join(",");
|
|
1261
1316
|
}
|
|
1317
|
+
/**
|
|
1318
|
+
* Returns the full thumbnail URL for an item.
|
|
1319
|
+
*
|
|
1320
|
+
* @param item - The item to decorate
|
|
1321
|
+
* @param portal - The portal URL
|
|
1322
|
+
* @param token - Optional token to append if the item is private
|
|
1323
|
+
* @returns The item with decorated thumbnail
|
|
1324
|
+
*/
|
|
1325
|
+
function decorateThumbnail(item, portal, token) {
|
|
1326
|
+
if (!item)
|
|
1327
|
+
return item;
|
|
1328
|
+
let thumbnailUrl = null;
|
|
1329
|
+
if (typeof item.thumbnail === "string") {
|
|
1330
|
+
thumbnailUrl = `${portal}/content/items/${item.id}/info/${item.thumbnail}`;
|
|
1331
|
+
if (thumbnailUrl && item.access !== "public" && token) {
|
|
1332
|
+
thumbnailUrl += `?${token}`;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
return Object.assign(Object.assign({}, item), (thumbnailUrl ? { thumbnailUrl } : {}));
|
|
1336
|
+
}
|
|
1262
1337
|
|
|
1263
1338
|
/* Copyright (c) 2018 Environmental Systems Research Institute, Inc.
|
|
1264
1339
|
* Apache-2.0 */
|
|
@@ -1295,6 +1370,16 @@
|
|
|
1295
1370
|
if (requestOptions.params.extent && isBBox(requestOptions.params.extent)) {
|
|
1296
1371
|
requestOptions.params.extent = bboxToString(requestOptions.params.extent);
|
|
1297
1372
|
}
|
|
1373
|
+
const portal = getPortalUrl(requestOptions);
|
|
1374
|
+
const decoratedThumbnail = decorateThumbnail(Object.assign(Object.assign({}, requestOptions.item), { access: requestOptions.item.access }), portal);
|
|
1375
|
+
const paramsThumbnailUrl = requestOptions.params.thumbnailUrl;
|
|
1376
|
+
if (typeof decoratedThumbnail.thumbnailUrl === "string" &&
|
|
1377
|
+
typeof paramsThumbnailUrl === "string") {
|
|
1378
|
+
if (decoratedThumbnail.thumbnailUrl.split("?")[0] ===
|
|
1379
|
+
paramsThumbnailUrl.split("?")[0]) {
|
|
1380
|
+
delete requestOptions.params.thumbnailUrl;
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1298
1383
|
return request(url, requestOptions);
|
|
1299
1384
|
});
|
|
1300
1385
|
}
|
|
@@ -1321,7 +1406,22 @@
|
|
|
1321
1406
|
const url = getItemBaseUrl(id, requestOptions);
|
|
1322
1407
|
// default to a GET request
|
|
1323
1408
|
const options = Object.assign({ httpMethod: "GET" }, requestOptions);
|
|
1324
|
-
return request(url, options)
|
|
1409
|
+
return request(url, options).then(async (item) => {
|
|
1410
|
+
const portal = (requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.portal) || getPortalUrl(requestOptions);
|
|
1411
|
+
let token;
|
|
1412
|
+
// if authentication is provided and it’s an authentication manager and the item is not public then request a token for secure access
|
|
1413
|
+
if ((requestOptions === null || requestOptions === void 0 ? void 0 : requestOptions.authentication) &&
|
|
1414
|
+
typeof requestOptions.authentication !== "string" &&
|
|
1415
|
+
item.access !== "public") {
|
|
1416
|
+
try {
|
|
1417
|
+
token = await requestOptions.authentication.getToken(url);
|
|
1418
|
+
}
|
|
1419
|
+
catch (e) {
|
|
1420
|
+
// Ignore token acquisition errors because `getToken()` can fail for some authentication types, but we still want to return the item info when possible since the browser may still provide thumbnail access via an auth cookie.
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
return decorateThumbnail(item, portal, token);
|
|
1424
|
+
});
|
|
1325
1425
|
}
|
|
1326
1426
|
/**
|
|
1327
1427
|
* Get the fully qualified base URL to the REST end point for an item.
|
|
@@ -1376,29 +1476,1039 @@
|
|
|
1376
1476
|
return { access };
|
|
1377
1477
|
}
|
|
1378
1478
|
|
|
1479
|
+
/**
|
|
1480
|
+
* @license MIT <https://opensource.org/licenses/MIT>
|
|
1481
|
+
* @copyright Michael Hart 2024
|
|
1482
|
+
*/
|
|
1483
|
+
const encoder = new TextEncoder();
|
|
1484
|
+
const HOST_SERVICES = {
|
|
1485
|
+
appstream2: 'appstream',
|
|
1486
|
+
cloudhsmv2: 'cloudhsm',
|
|
1487
|
+
email: 'ses',
|
|
1488
|
+
marketplace: 'aws-marketplace',
|
|
1489
|
+
mobile: 'AWSMobileHubService',
|
|
1490
|
+
pinpoint: 'mobiletargeting',
|
|
1491
|
+
queue: 'sqs',
|
|
1492
|
+
'git-codecommit': 'codecommit',
|
|
1493
|
+
'mturk-requester-sandbox': 'mturk-requester',
|
|
1494
|
+
'personalize-runtime': 'personalize',
|
|
1495
|
+
};
|
|
1496
|
+
const UNSIGNABLE_HEADERS = new Set([
|
|
1497
|
+
'authorization',
|
|
1498
|
+
'content-type',
|
|
1499
|
+
'content-length',
|
|
1500
|
+
'user-agent',
|
|
1501
|
+
'presigned-expires',
|
|
1502
|
+
'expect',
|
|
1503
|
+
'x-amzn-trace-id',
|
|
1504
|
+
'range',
|
|
1505
|
+
'connection',
|
|
1506
|
+
]);
|
|
1507
|
+
class AwsClient {
|
|
1508
|
+
constructor({ accessKeyId, secretAccessKey, sessionToken, service, region, cache, retries, initRetryMs }) {
|
|
1509
|
+
if (accessKeyId == null) throw new TypeError('accessKeyId is a required option')
|
|
1510
|
+
if (secretAccessKey == null) throw new TypeError('secretAccessKey is a required option')
|
|
1511
|
+
this.accessKeyId = accessKeyId;
|
|
1512
|
+
this.secretAccessKey = secretAccessKey;
|
|
1513
|
+
this.sessionToken = sessionToken;
|
|
1514
|
+
this.service = service;
|
|
1515
|
+
this.region = region;
|
|
1516
|
+
this.cache = cache || new Map();
|
|
1517
|
+
this.retries = retries != null ? retries : 10;
|
|
1518
|
+
this.initRetryMs = initRetryMs || 50;
|
|
1519
|
+
}
|
|
1520
|
+
async sign(input, init) {
|
|
1521
|
+
if (input instanceof Request) {
|
|
1522
|
+
const { method, url, headers, body } = input;
|
|
1523
|
+
init = Object.assign({ method, url, headers }, init);
|
|
1524
|
+
if (init.body == null && headers.has('Content-Type')) {
|
|
1525
|
+
init.body = body != null && headers.has('X-Amz-Content-Sha256') ? body : await input.clone().arrayBuffer();
|
|
1526
|
+
}
|
|
1527
|
+
input = url;
|
|
1528
|
+
}
|
|
1529
|
+
const signer = new AwsV4Signer(Object.assign({ url: input.toString() }, init, this, init && init.aws));
|
|
1530
|
+
const signed = Object.assign({}, init, await signer.sign());
|
|
1531
|
+
delete signed.aws;
|
|
1532
|
+
try {
|
|
1533
|
+
return new Request(signed.url.toString(), signed)
|
|
1534
|
+
} catch (e) {
|
|
1535
|
+
if (e instanceof TypeError) {
|
|
1536
|
+
return new Request(signed.url.toString(), Object.assign({ duplex: 'half' }, signed))
|
|
1537
|
+
}
|
|
1538
|
+
throw e
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
async fetch(input, init) {
|
|
1542
|
+
for (let i = 0; i <= this.retries; i++) {
|
|
1543
|
+
const fetched = fetch(await this.sign(input, init));
|
|
1544
|
+
if (i === this.retries) {
|
|
1545
|
+
return fetched
|
|
1546
|
+
}
|
|
1547
|
+
const res = await fetched;
|
|
1548
|
+
if (res.status < 500 && res.status !== 429) {
|
|
1549
|
+
return res
|
|
1550
|
+
}
|
|
1551
|
+
await new Promise(resolve => setTimeout(resolve, Math.random() * this.initRetryMs * Math.pow(2, i)));
|
|
1552
|
+
}
|
|
1553
|
+
throw new Error('An unknown error occurred, ensure retries is not negative')
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
class AwsV4Signer {
|
|
1557
|
+
constructor({ method, url, headers, body, accessKeyId, secretAccessKey, sessionToken, service, region, cache, datetime, signQuery, appendSessionToken, allHeaders, singleEncode }) {
|
|
1558
|
+
if (url == null) throw new TypeError('url is a required option')
|
|
1559
|
+
if (accessKeyId == null) throw new TypeError('accessKeyId is a required option')
|
|
1560
|
+
if (secretAccessKey == null) throw new TypeError('secretAccessKey is a required option')
|
|
1561
|
+
this.method = method || (body ? 'POST' : 'GET');
|
|
1562
|
+
this.url = new URL(url);
|
|
1563
|
+
this.headers = new Headers(headers || {});
|
|
1564
|
+
this.body = body;
|
|
1565
|
+
this.accessKeyId = accessKeyId;
|
|
1566
|
+
this.secretAccessKey = secretAccessKey;
|
|
1567
|
+
this.sessionToken = sessionToken;
|
|
1568
|
+
let guessedService, guessedRegion;
|
|
1569
|
+
if (!service || !region) {
|
|
1570
|
+
[guessedService, guessedRegion] = guessServiceRegion(this.url, this.headers);
|
|
1571
|
+
}
|
|
1572
|
+
this.service = service || guessedService || '';
|
|
1573
|
+
this.region = region || guessedRegion || 'us-east-1';
|
|
1574
|
+
this.cache = cache || new Map();
|
|
1575
|
+
this.datetime = datetime || new Date().toISOString().replace(/[:-]|\.\d{3}/g, '');
|
|
1576
|
+
this.signQuery = signQuery;
|
|
1577
|
+
this.appendSessionToken = appendSessionToken || this.service === 'iotdevicegateway';
|
|
1578
|
+
this.headers.delete('Host');
|
|
1579
|
+
if (this.service === 's3' && !this.signQuery && !this.headers.has('X-Amz-Content-Sha256')) {
|
|
1580
|
+
this.headers.set('X-Amz-Content-Sha256', 'UNSIGNED-PAYLOAD');
|
|
1581
|
+
}
|
|
1582
|
+
const params = this.signQuery ? this.url.searchParams : this.headers;
|
|
1583
|
+
params.set('X-Amz-Date', this.datetime);
|
|
1584
|
+
if (this.sessionToken && !this.appendSessionToken) {
|
|
1585
|
+
params.set('X-Amz-Security-Token', this.sessionToken);
|
|
1586
|
+
}
|
|
1587
|
+
this.signableHeaders = ['host', ...this.headers.keys()]
|
|
1588
|
+
.filter(header => allHeaders || !UNSIGNABLE_HEADERS.has(header))
|
|
1589
|
+
.sort();
|
|
1590
|
+
this.signedHeaders = this.signableHeaders.join(';');
|
|
1591
|
+
this.canonicalHeaders = this.signableHeaders
|
|
1592
|
+
.map(header => header + ':' + (header === 'host' ? this.url.host : (this.headers.get(header) || '').replace(/\s+/g, ' ')))
|
|
1593
|
+
.join('\n');
|
|
1594
|
+
this.credentialString = [this.datetime.slice(0, 8), this.region, this.service, 'aws4_request'].join('/');
|
|
1595
|
+
if (this.signQuery) {
|
|
1596
|
+
if (this.service === 's3' && !params.has('X-Amz-Expires')) {
|
|
1597
|
+
params.set('X-Amz-Expires', '86400');
|
|
1598
|
+
}
|
|
1599
|
+
params.set('X-Amz-Algorithm', 'AWS4-HMAC-SHA256');
|
|
1600
|
+
params.set('X-Amz-Credential', this.accessKeyId + '/' + this.credentialString);
|
|
1601
|
+
params.set('X-Amz-SignedHeaders', this.signedHeaders);
|
|
1602
|
+
}
|
|
1603
|
+
if (this.service === 's3') {
|
|
1604
|
+
try {
|
|
1605
|
+
this.encodedPath = decodeURIComponent(this.url.pathname.replace(/\+/g, ' '));
|
|
1606
|
+
} catch (e) {
|
|
1607
|
+
this.encodedPath = this.url.pathname;
|
|
1608
|
+
}
|
|
1609
|
+
} else {
|
|
1610
|
+
this.encodedPath = this.url.pathname.replace(/\/+/g, '/');
|
|
1611
|
+
}
|
|
1612
|
+
if (!singleEncode) {
|
|
1613
|
+
this.encodedPath = encodeURIComponent(this.encodedPath).replace(/%2F/g, '/');
|
|
1614
|
+
}
|
|
1615
|
+
this.encodedPath = encodeRfc3986(this.encodedPath);
|
|
1616
|
+
const seenKeys = new Set();
|
|
1617
|
+
this.encodedSearch = [...this.url.searchParams]
|
|
1618
|
+
.filter(([k]) => {
|
|
1619
|
+
if (!k) return false
|
|
1620
|
+
if (this.service === 's3') {
|
|
1621
|
+
if (seenKeys.has(k)) return false
|
|
1622
|
+
seenKeys.add(k);
|
|
1623
|
+
}
|
|
1624
|
+
return true
|
|
1625
|
+
})
|
|
1626
|
+
.map(pair => pair.map(p => encodeRfc3986(encodeURIComponent(p))))
|
|
1627
|
+
.sort(([k1, v1], [k2, v2]) => k1 < k2 ? -1 : k1 > k2 ? 1 : v1 < v2 ? -1 : v1 > v2 ? 1 : 0)
|
|
1628
|
+
.map(pair => pair.join('='))
|
|
1629
|
+
.join('&');
|
|
1630
|
+
}
|
|
1631
|
+
async sign() {
|
|
1632
|
+
if (this.signQuery) {
|
|
1633
|
+
this.url.searchParams.set('X-Amz-Signature', await this.signature());
|
|
1634
|
+
if (this.sessionToken && this.appendSessionToken) {
|
|
1635
|
+
this.url.searchParams.set('X-Amz-Security-Token', this.sessionToken);
|
|
1636
|
+
}
|
|
1637
|
+
} else {
|
|
1638
|
+
this.headers.set('Authorization', await this.authHeader());
|
|
1639
|
+
}
|
|
1640
|
+
return {
|
|
1641
|
+
method: this.method,
|
|
1642
|
+
url: this.url,
|
|
1643
|
+
headers: this.headers,
|
|
1644
|
+
body: this.body,
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
async authHeader() {
|
|
1648
|
+
return [
|
|
1649
|
+
'AWS4-HMAC-SHA256 Credential=' + this.accessKeyId + '/' + this.credentialString,
|
|
1650
|
+
'SignedHeaders=' + this.signedHeaders,
|
|
1651
|
+
'Signature=' + (await this.signature()),
|
|
1652
|
+
].join(', ')
|
|
1653
|
+
}
|
|
1654
|
+
async signature() {
|
|
1655
|
+
const date = this.datetime.slice(0, 8);
|
|
1656
|
+
const cacheKey = [this.secretAccessKey, date, this.region, this.service].join();
|
|
1657
|
+
let kCredentials = this.cache.get(cacheKey);
|
|
1658
|
+
if (!kCredentials) {
|
|
1659
|
+
const kDate = await hmac('AWS4' + this.secretAccessKey, date);
|
|
1660
|
+
const kRegion = await hmac(kDate, this.region);
|
|
1661
|
+
const kService = await hmac(kRegion, this.service);
|
|
1662
|
+
kCredentials = await hmac(kService, 'aws4_request');
|
|
1663
|
+
this.cache.set(cacheKey, kCredentials);
|
|
1664
|
+
}
|
|
1665
|
+
return buf2hex(await hmac(kCredentials, await this.stringToSign()))
|
|
1666
|
+
}
|
|
1667
|
+
async stringToSign() {
|
|
1668
|
+
return [
|
|
1669
|
+
'AWS4-HMAC-SHA256',
|
|
1670
|
+
this.datetime,
|
|
1671
|
+
this.credentialString,
|
|
1672
|
+
buf2hex(await hash(await this.canonicalString())),
|
|
1673
|
+
].join('\n')
|
|
1674
|
+
}
|
|
1675
|
+
async canonicalString() {
|
|
1676
|
+
return [
|
|
1677
|
+
this.method.toUpperCase(),
|
|
1678
|
+
this.encodedPath,
|
|
1679
|
+
this.encodedSearch,
|
|
1680
|
+
this.canonicalHeaders + '\n',
|
|
1681
|
+
this.signedHeaders,
|
|
1682
|
+
await this.hexBodyHash(),
|
|
1683
|
+
].join('\n')
|
|
1684
|
+
}
|
|
1685
|
+
async hexBodyHash() {
|
|
1686
|
+
let hashHeader = this.headers.get('X-Amz-Content-Sha256') || (this.service === 's3' && this.signQuery ? 'UNSIGNED-PAYLOAD' : null);
|
|
1687
|
+
if (hashHeader == null) {
|
|
1688
|
+
if (this.body && typeof this.body !== 'string' && !('byteLength' in this.body)) {
|
|
1689
|
+
throw new Error('body must be a string, ArrayBuffer or ArrayBufferView, unless you include the X-Amz-Content-Sha256 header')
|
|
1690
|
+
}
|
|
1691
|
+
hashHeader = buf2hex(await hash(this.body || ''));
|
|
1692
|
+
}
|
|
1693
|
+
return hashHeader
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
async function hmac(key, string) {
|
|
1697
|
+
const cryptoKey = await crypto.subtle.importKey(
|
|
1698
|
+
'raw',
|
|
1699
|
+
typeof key === 'string' ? encoder.encode(key) : key,
|
|
1700
|
+
{ name: 'HMAC', hash: { name: 'SHA-256' } },
|
|
1701
|
+
false,
|
|
1702
|
+
['sign'],
|
|
1703
|
+
);
|
|
1704
|
+
return crypto.subtle.sign('HMAC', cryptoKey, encoder.encode(string))
|
|
1705
|
+
}
|
|
1706
|
+
async function hash(content) {
|
|
1707
|
+
return crypto.subtle.digest('SHA-256', typeof content === 'string' ? encoder.encode(content) : content)
|
|
1708
|
+
}
|
|
1709
|
+
const HEX_CHARS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
|
|
1710
|
+
function buf2hex(arrayBuffer) {
|
|
1711
|
+
const buffer = new Uint8Array(arrayBuffer);
|
|
1712
|
+
let out = '';
|
|
1713
|
+
for (let idx = 0; idx < buffer.length; idx++) {
|
|
1714
|
+
const n = buffer[idx];
|
|
1715
|
+
out += HEX_CHARS[(n >>> 4) & 0xF];
|
|
1716
|
+
out += HEX_CHARS[n & 0xF];
|
|
1717
|
+
}
|
|
1718
|
+
return out
|
|
1719
|
+
}
|
|
1720
|
+
function encodeRfc3986(urlEncodedStr) {
|
|
1721
|
+
return urlEncodedStr.replace(/[!'()*]/g, c => '%' + c.charCodeAt(0).toString(16).toUpperCase())
|
|
1722
|
+
}
|
|
1723
|
+
function guessServiceRegion(url, headers) {
|
|
1724
|
+
const { hostname, pathname } = url;
|
|
1725
|
+
if (hostname.endsWith('.on.aws')) {
|
|
1726
|
+
const match = hostname.match(/^[^.]{1,63}\.lambda-url\.([^.]{1,63})\.on\.aws$/);
|
|
1727
|
+
return match != null ? ['lambda', match[1] || ''] : ['', '']
|
|
1728
|
+
}
|
|
1729
|
+
if (hostname.endsWith('.r2.cloudflarestorage.com')) {
|
|
1730
|
+
return ['s3', 'auto']
|
|
1731
|
+
}
|
|
1732
|
+
if (hostname.endsWith('.backblazeb2.com')) {
|
|
1733
|
+
const match = hostname.match(/^(?:[^.]{1,63}\.)?s3\.([^.]{1,63})\.backblazeb2\.com$/);
|
|
1734
|
+
return match != null ? ['s3', match[1] || ''] : ['', '']
|
|
1735
|
+
}
|
|
1736
|
+
const match = hostname.replace('dualstack.', '').match(/([^.]{1,63})\.(?:([^.]{0,63})\.)?amazonaws\.com(?:\.cn)?$/);
|
|
1737
|
+
let service = (match && match[1]) || '';
|
|
1738
|
+
let region = match && match[2];
|
|
1739
|
+
if (region === 'us-gov') {
|
|
1740
|
+
region = 'us-gov-west-1';
|
|
1741
|
+
} else if (region === 's3' || region === 's3-accelerate') {
|
|
1742
|
+
region = 'us-east-1';
|
|
1743
|
+
service = 's3';
|
|
1744
|
+
} else if (service === 'iot') {
|
|
1745
|
+
if (hostname.startsWith('iot.')) {
|
|
1746
|
+
service = 'execute-api';
|
|
1747
|
+
} else if (hostname.startsWith('data.jobs.iot.')) {
|
|
1748
|
+
service = 'iot-jobs-data';
|
|
1749
|
+
} else {
|
|
1750
|
+
service = pathname === '/mqtt' ? 'iotdevicegateway' : 'iotdata';
|
|
1751
|
+
}
|
|
1752
|
+
} else if (service === 'autoscaling') {
|
|
1753
|
+
const targetPrefix = (headers.get('X-Amz-Target') || '').split('.')[0];
|
|
1754
|
+
if (targetPrefix === 'AnyScaleFrontendService') {
|
|
1755
|
+
service = 'application-autoscaling';
|
|
1756
|
+
} else if (targetPrefix === 'AnyScaleScalingPlannerFrontendService') {
|
|
1757
|
+
service = 'autoscaling-plans';
|
|
1758
|
+
}
|
|
1759
|
+
} else if (region == null && service.startsWith('s3-')) {
|
|
1760
|
+
region = service.slice(3).replace(/^fips-|^external-1/, '');
|
|
1761
|
+
service = 's3';
|
|
1762
|
+
} else if (service.endsWith('-fips')) {
|
|
1763
|
+
service = service.slice(0, -5);
|
|
1764
|
+
} else if (region && /-\d$/.test(service) && !/-\d$/.test(region)) {
|
|
1765
|
+
[service, region] = [region, service];
|
|
1766
|
+
}
|
|
1767
|
+
return [HOST_SERVICES[service] || service, region || '']
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
const LINUX_DISTRO_TOKENS = [
|
|
1771
|
+
'ubuntu',
|
|
1772
|
+
'debian',
|
|
1773
|
+
'opensuse',
|
|
1774
|
+
'suse',
|
|
1775
|
+
'gentoo',
|
|
1776
|
+
'arch linux',
|
|
1777
|
+
'arch',
|
|
1778
|
+
'fedora',
|
|
1779
|
+
'centos',
|
|
1780
|
+
'red hat',
|
|
1781
|
+
'rhel',
|
|
1782
|
+
'rocky linux',
|
|
1783
|
+
'rocky',
|
|
1784
|
+
'almalinux',
|
|
1785
|
+
'alma',
|
|
1786
|
+
'amazon linux',
|
|
1787
|
+
'amazonlinux',
|
|
1788
|
+
'amzn',
|
|
1789
|
+
'manjaro',
|
|
1790
|
+
'linux mint',
|
|
1791
|
+
'mint',
|
|
1792
|
+
'pop!_os',
|
|
1793
|
+
'pop_os',
|
|
1794
|
+
'kali',
|
|
1795
|
+
'alpine',
|
|
1796
|
+
'nixos',
|
|
1797
|
+
'linux',
|
|
1798
|
+
];
|
|
1799
|
+
function escapeRegExp(value) {
|
|
1800
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
1801
|
+
}
|
|
1802
|
+
const LINUX_DISTRO_TOKEN_SOURCE = [...LINUX_DISTRO_TOKENS]
|
|
1803
|
+
.sort((a, b) => b.length - a.length)
|
|
1804
|
+
.map((token) => escapeRegExp(token))
|
|
1805
|
+
.join('|');
|
|
1806
|
+
const LINUX_DISTRO_TOKEN_REGEX = new RegExp(`\\b(${LINUX_DISTRO_TOKEN_SOURCE})\\b`, 'i');
|
|
1807
|
+
const LINUX_DISTRO_VERSION_REGEX = new RegExp(`\\b(?:${LINUX_DISTRO_TOKEN_SOURCE})[\\s\\/_-]*([\\d\\.]+)`, 'i');
|
|
1808
|
+
const LINUX_DISTRO_NAME_MAP = {
|
|
1809
|
+
ubuntu: 'Ubuntu',
|
|
1810
|
+
debian: 'Debian',
|
|
1811
|
+
opensuse: 'openSUSE',
|
|
1812
|
+
suse: 'SUSE',
|
|
1813
|
+
gentoo: 'Gentoo',
|
|
1814
|
+
arch: 'Arch Linux',
|
|
1815
|
+
'arch linux': 'Arch Linux',
|
|
1816
|
+
fedora: 'Fedora',
|
|
1817
|
+
centos: 'CentOS',
|
|
1818
|
+
'red hat': 'Red Hat',
|
|
1819
|
+
rhel: 'Red Hat',
|
|
1820
|
+
rocky: 'Rocky Linux',
|
|
1821
|
+
'rocky linux': 'Rocky Linux',
|
|
1822
|
+
alma: 'AlmaLinux',
|
|
1823
|
+
almalinux: 'AlmaLinux',
|
|
1824
|
+
'amazon linux': 'Amazon Linux',
|
|
1825
|
+
amazonlinux: 'Amazon Linux',
|
|
1826
|
+
amzn: 'Amazon Linux',
|
|
1827
|
+
manjaro: 'Manjaro',
|
|
1828
|
+
'linux mint': 'Linux Mint',
|
|
1829
|
+
mint: 'Linux Mint',
|
|
1830
|
+
'pop!_os': 'Pop!_OS',
|
|
1831
|
+
pop_os: 'Pop!_OS',
|
|
1832
|
+
kali: 'Kali Linux',
|
|
1833
|
+
alpine: 'Alpine Linux',
|
|
1834
|
+
nixos: 'NixOS',
|
|
1835
|
+
linux: 'Linux',
|
|
1836
|
+
};
|
|
1837
|
+
function resolveLinuxDistroName(userAgent) {
|
|
1838
|
+
const token = (userAgent.match(LINUX_DISTRO_TOKEN_REGEX) || [])[1];
|
|
1839
|
+
if (!token) {
|
|
1840
|
+
return undefined;
|
|
1841
|
+
}
|
|
1842
|
+
return LINUX_DISTRO_NAME_MAP[token.toLowerCase()];
|
|
1843
|
+
}
|
|
1844
|
+
function extractVersion(userAgent, versionRegex) {
|
|
1845
|
+
if (!versionRegex) {
|
|
1846
|
+
return undefined;
|
|
1847
|
+
}
|
|
1848
|
+
const version = (userAgent.match(versionRegex) || [])[1];
|
|
1849
|
+
if (!version) {
|
|
1850
|
+
return undefined;
|
|
1851
|
+
}
|
|
1852
|
+
return version.replace(/_/g, '.');
|
|
1853
|
+
}
|
|
1854
|
+
function parseOS(userAgent) {
|
|
1855
|
+
if (!userAgent) {
|
|
1856
|
+
return {};
|
|
1857
|
+
}
|
|
1858
|
+
const normalizedAgent = userAgent.toLowerCase();
|
|
1859
|
+
const osMatchers = [
|
|
1860
|
+
{
|
|
1861
|
+
name: 'Windows Phone',
|
|
1862
|
+
regex: /windows phone|windows mobile/i,
|
|
1863
|
+
versionRegex: /windows (?:phone(?: os)?|mobile)[\s\/]*([\d\.]+)/i,
|
|
1864
|
+
},
|
|
1865
|
+
{
|
|
1866
|
+
name: 'Windows',
|
|
1867
|
+
regex: /windows nt|windows\s\d+/i,
|
|
1868
|
+
versionRegex: /windows nt[\s\/]*([\d\.]+)/i,
|
|
1869
|
+
},
|
|
1870
|
+
{
|
|
1871
|
+
name: 'iOS',
|
|
1872
|
+
regex: /iphone|ipad|ipod|cpu (?:iphone )?os /i,
|
|
1873
|
+
versionRegex: /os\s([\d_\.]+)/i,
|
|
1874
|
+
},
|
|
1875
|
+
{
|
|
1876
|
+
name: 'Android',
|
|
1877
|
+
regex: /android/i,
|
|
1878
|
+
versionRegex: /android[\s\/]*([\d\.]+)/i,
|
|
1879
|
+
},
|
|
1880
|
+
{
|
|
1881
|
+
name: 'ChromeOS',
|
|
1882
|
+
regex: /\bcros\b/i,
|
|
1883
|
+
versionRegex: /\bcros\b[^\)]*?([\d]+(?:\.[\d]+)+)/i,
|
|
1884
|
+
},
|
|
1885
|
+
{
|
|
1886
|
+
name: 'MacOS',
|
|
1887
|
+
regex: /mac os x|macos|macintosh|macintel|darwin/i,
|
|
1888
|
+
versionRegex: /(?:mac os x|os x|macos)[\s\/]*([\d_\.]+)/i,
|
|
1889
|
+
},
|
|
1890
|
+
{
|
|
1891
|
+
name: 'Linux',
|
|
1892
|
+
regex: LINUX_DISTRO_TOKEN_REGEX,
|
|
1893
|
+
versionRegex: LINUX_DISTRO_VERSION_REGEX,
|
|
1894
|
+
},
|
|
1895
|
+
];
|
|
1896
|
+
const matched = osMatchers.find((entry) => entry.regex.test(normalizedAgent));
|
|
1897
|
+
if (!matched) {
|
|
1898
|
+
return {};
|
|
1899
|
+
}
|
|
1900
|
+
const version = extractVersion(normalizedAgent, matched.versionRegex);
|
|
1901
|
+
const name = matched.name === 'Linux'
|
|
1902
|
+
? resolveLinuxDistroName(normalizedAgent) || 'Linux'
|
|
1903
|
+
: matched.name;
|
|
1904
|
+
return {
|
|
1905
|
+
name,
|
|
1906
|
+
version,
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
function getNavigatorSafe() {
|
|
1911
|
+
if (typeof window === 'undefined' || !window.navigator) {
|
|
1912
|
+
return undefined;
|
|
1913
|
+
}
|
|
1914
|
+
return window.navigator;
|
|
1915
|
+
}
|
|
1916
|
+
function getLanguageInfo(nav) {
|
|
1917
|
+
if (!nav) {
|
|
1918
|
+
return '';
|
|
1919
|
+
}
|
|
1920
|
+
const lang = nav.language ||
|
|
1921
|
+
nav.browserLanguage ||
|
|
1922
|
+
(Array.isArray(nav.languages) && nav.languages.length > 0
|
|
1923
|
+
? nav.languages[0]
|
|
1924
|
+
: 'en_US') ||
|
|
1925
|
+
'';
|
|
1926
|
+
return String(lang).toLowerCase().replace('-', '_');
|
|
1927
|
+
}
|
|
1928
|
+
function getBrowserTimezone() {
|
|
1929
|
+
const tzMatch = /\(([A-Za-z\s].*)\)/.exec(new Date().toString());
|
|
1930
|
+
return tzMatch ? tzMatch[1] || '' : '';
|
|
1931
|
+
}
|
|
1932
|
+
function parseBrowserType(userAgent) {
|
|
1933
|
+
const operaMatch = /.+(Opera[\s[A-Z]*|OPR[\sA-Z]*)\/([0-9\.]+).*/i.exec(userAgent);
|
|
1934
|
+
if (operaMatch) {
|
|
1935
|
+
return {
|
|
1936
|
+
type: operaMatch[1],
|
|
1937
|
+
version: operaMatch[2],
|
|
1938
|
+
};
|
|
1939
|
+
}
|
|
1940
|
+
const edgeOrTridentMatch = /.+(Trident|Edge)\/([0-9\.]+).*/i.exec(userAgent);
|
|
1941
|
+
if (edgeOrTridentMatch) {
|
|
1942
|
+
return {
|
|
1943
|
+
type: edgeOrTridentMatch[1],
|
|
1944
|
+
version: edgeOrTridentMatch[2],
|
|
1945
|
+
};
|
|
1946
|
+
}
|
|
1947
|
+
const headlessMatch = /(headlesschrome)(?:\/([\w\.]+)| )/i.exec(userAgent);
|
|
1948
|
+
if (headlessMatch) {
|
|
1949
|
+
return {
|
|
1950
|
+
type: headlessMatch[1],
|
|
1951
|
+
version: headlessMatch[2],
|
|
1952
|
+
};
|
|
1953
|
+
}
|
|
1954
|
+
const browserMatch = /.+(Chrome|Firefox|FxiOS)\/([0-9\.]+).*/i.exec(userAgent);
|
|
1955
|
+
if (browserMatch) {
|
|
1956
|
+
return {
|
|
1957
|
+
type: browserMatch[1],
|
|
1958
|
+
version: browserMatch[2],
|
|
1959
|
+
};
|
|
1960
|
+
}
|
|
1961
|
+
const safariMatch = /.+(Safari)\/([0-9\.]+).*/i.exec(userAgent);
|
|
1962
|
+
if (safariMatch) {
|
|
1963
|
+
return {
|
|
1964
|
+
type: safariMatch[1],
|
|
1965
|
+
version: safariMatch[2],
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
const webkitMatch = /.+(AppleWebKit)\/([0-9\.]+).*/i.exec(userAgent);
|
|
1969
|
+
if (webkitMatch) {
|
|
1970
|
+
return {
|
|
1971
|
+
type: webkitMatch[1],
|
|
1972
|
+
version: webkitMatch[2],
|
|
1973
|
+
};
|
|
1974
|
+
}
|
|
1975
|
+
const anyMatch = /.*([A-Z]+)\/([0-9\.]+).*/i.exec(userAgent);
|
|
1976
|
+
const fallback = anyMatch || ['', 'NA', '0.0.0'];
|
|
1977
|
+
return {
|
|
1978
|
+
type: fallback[1],
|
|
1979
|
+
version: fallback[2],
|
|
1980
|
+
};
|
|
1981
|
+
}
|
|
1982
|
+
function getBrowserClientInfo() {
|
|
1983
|
+
const nav = getNavigatorSafe();
|
|
1984
|
+
if (!nav) {
|
|
1985
|
+
return {};
|
|
1986
|
+
}
|
|
1987
|
+
const userAgent = nav.userAgent || '';
|
|
1988
|
+
const browser = parseBrowserType(userAgent);
|
|
1989
|
+
const isBrave = (nav.brave && nav.brave.isBrave) || false;
|
|
1990
|
+
const type = isBrave ? 'Brave' : browser.type;
|
|
1991
|
+
const os = parseOS(userAgent);
|
|
1992
|
+
const vendorToken = (nav.vendor || '').split(' ').filter(Boolean)[0];
|
|
1993
|
+
const make = type === 'Brave' ? type : vendorToken || nav.product || '';
|
|
1994
|
+
return {
|
|
1995
|
+
language: getLanguageInfo(nav),
|
|
1996
|
+
make,
|
|
1997
|
+
model: type,
|
|
1998
|
+
version: browser.version,
|
|
1999
|
+
name: [type, browser.version].filter(Boolean).join('/'),
|
|
2000
|
+
timezone: getBrowserTimezone(),
|
|
2001
|
+
platform: nav.platform,
|
|
2002
|
+
os,
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
const PINPOINT_ENDPOINT_KEY = 'TELEMETRY_PINPOINT_ENDPOINT_ID';
|
|
2007
|
+
const LOG_PREFIX = '[telemetry-amazon]';
|
|
2008
|
+
function isDebugEnabled() {
|
|
2009
|
+
var _a;
|
|
2010
|
+
const processEnv = typeof globalThis !== 'undefined'
|
|
2011
|
+
? (_a = globalThis.process) === null || _a === void 0 ? void 0 : _a.env
|
|
2012
|
+
: undefined;
|
|
2013
|
+
return ((processEnv === null || processEnv === void 0 ? void 0 : processEnv.TELEMETRY_AMAZON_DEBUG) === 'true' ||
|
|
2014
|
+
(processEnv === null || processEnv === void 0 ? void 0 : processEnv.NODE_ENV) === 'development');
|
|
2015
|
+
}
|
|
2016
|
+
function debugLog(message, details) {
|
|
2017
|
+
if (!isDebugEnabled() || typeof console === 'undefined') {
|
|
2018
|
+
return;
|
|
2019
|
+
}
|
|
2020
|
+
if (details && typeof console.debug === 'function') {
|
|
2021
|
+
console.debug(`${LOG_PREFIX} ${message}`, details);
|
|
2022
|
+
return;
|
|
2023
|
+
}
|
|
2024
|
+
if (typeof console.debug === 'function') {
|
|
2025
|
+
console.debug(`${LOG_PREFIX} ${message}`);
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
function warnLog(message, details) {
|
|
2029
|
+
if (typeof console === 'undefined' || typeof console.warn !== 'function') {
|
|
2030
|
+
return;
|
|
2031
|
+
}
|
|
2032
|
+
if (details) {
|
|
2033
|
+
console.warn(`${LOG_PREFIX} ${message}`, details);
|
|
2034
|
+
return;
|
|
2035
|
+
}
|
|
2036
|
+
console.warn(`${LOG_PREFIX} ${message}`);
|
|
2037
|
+
}
|
|
2038
|
+
function getEndpointId() {
|
|
2039
|
+
const cached = storage.get(PINPOINT_ENDPOINT_KEY);
|
|
2040
|
+
if (cached && typeof cached === 'object' && cached.endpointId) {
|
|
2041
|
+
return cached.endpointId;
|
|
2042
|
+
}
|
|
2043
|
+
if (typeof cached === 'string') {
|
|
2044
|
+
return cached;
|
|
2045
|
+
}
|
|
2046
|
+
const generated = `telemetry-${Date.now()}-${Math.random()
|
|
2047
|
+
.toString(16)
|
|
2048
|
+
.slice(2)}`;
|
|
2049
|
+
storage.set(PINPOINT_ENDPOINT_KEY, { endpointId: generated });
|
|
2050
|
+
return generated;
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
const ESRI_SIGNING_SERVICE = 'execute-api';
|
|
2054
|
+
const ESRI_SIGNING_REGION = 'us-east-1';
|
|
2055
|
+
const ESRI_ENDPOINT_URL = (appId) => `https://tedev.arcgis.com/v1/apps/${appId}/events`;
|
|
2056
|
+
const SESSION_START_EVENT = '_session.start';
|
|
2057
|
+
const SESSION_STOP_EVENT = '_session.stop';
|
|
2058
|
+
const DEFAULT_IDLE_TIMEOUT_MS = 30 * 60 * 1000;
|
|
2059
|
+
const DEFAULT_DEBOUNCE_MS = 5000;
|
|
2060
|
+
/** Converts a string to a number, guarded against invalid values */
|
|
2061
|
+
function getNumber(value, fallback = 0) {
|
|
2062
|
+
const numeric = Number(value);
|
|
2063
|
+
return Number.isFinite(numeric) ? numeric : fallback;
|
|
2064
|
+
}
|
|
2065
|
+
/** Splits an EsriBatchEventPayload into attributes and metrics */
|
|
2066
|
+
function splitEventData(payload) {
|
|
2067
|
+
const attributes = {};
|
|
2068
|
+
const metrics = {};
|
|
2069
|
+
const excludedKeys = new Set([
|
|
2070
|
+
'name',
|
|
2071
|
+
'appTitle',
|
|
2072
|
+
'appVersion',
|
|
2073
|
+
'clientSdkVersion',
|
|
2074
|
+
'deviceMake',
|
|
2075
|
+
'deviceModel',
|
|
2076
|
+
'city',
|
|
2077
|
+
'country',
|
|
2078
|
+
'lat',
|
|
2079
|
+
'lon',
|
|
2080
|
+
]);
|
|
2081
|
+
Object.entries(payload || {}).forEach(([key, value]) => {
|
|
2082
|
+
if (excludedKeys.has(key) || value === undefined || value === null) {
|
|
2083
|
+
return;
|
|
2084
|
+
}
|
|
2085
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
2086
|
+
metrics[key] = value;
|
|
2087
|
+
return;
|
|
2088
|
+
}
|
|
2089
|
+
if (typeof value === 'string' || typeof value === 'boolean') {
|
|
2090
|
+
attributes[key] = String(value);
|
|
2091
|
+
return;
|
|
2092
|
+
}
|
|
2093
|
+
if (Array.isArray(value) || typeof value === 'object') {
|
|
2094
|
+
attributes[key] = JSON.stringify(value);
|
|
2095
|
+
}
|
|
2096
|
+
});
|
|
2097
|
+
return {
|
|
2098
|
+
attributes,
|
|
2099
|
+
metrics,
|
|
2100
|
+
};
|
|
2101
|
+
}
|
|
2102
|
+
/** Constructs an event payload in the shape expected by the endpoint. */
|
|
2103
|
+
function buildBatchEventPayload({ queuedEvents, trackerName, appName, appVersion, }) {
|
|
2104
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2105
|
+
const endpointEvent = queuedEvents[queuedEvents.length - 1] ||
|
|
2106
|
+
{
|
|
2107
|
+
payload: {},
|
|
2108
|
+
browserInfo: {},
|
|
2109
|
+
};
|
|
2110
|
+
const payload = endpointEvent.payload || {};
|
|
2111
|
+
const browserInfo = endpointEvent.browserInfo || {};
|
|
2112
|
+
const endpointId = getEndpointId();
|
|
2113
|
+
const location = {};
|
|
2114
|
+
if (payload === null || payload === void 0 ? void 0 : payload.city) {
|
|
2115
|
+
location.City = payload.city;
|
|
2116
|
+
}
|
|
2117
|
+
if (payload === null || payload === void 0 ? void 0 : payload.country) {
|
|
2118
|
+
location.Country = payload.country;
|
|
2119
|
+
}
|
|
2120
|
+
if ((payload === null || payload === void 0 ? void 0 : payload.lat) !== undefined && (payload === null || payload === void 0 ? void 0 : payload.lat) !== null) {
|
|
2121
|
+
location.Lat = getNumber(payload.lat);
|
|
2122
|
+
}
|
|
2123
|
+
if ((payload === null || payload === void 0 ? void 0 : payload.lon) !== undefined && (payload === null || payload === void 0 ? void 0 : payload.lon) !== null) {
|
|
2124
|
+
location.Lon = getNumber(payload.lon);
|
|
2125
|
+
}
|
|
2126
|
+
return {
|
|
2127
|
+
BatchEvent: {
|
|
2128
|
+
Endpoint: {
|
|
2129
|
+
Id: endpointId,
|
|
2130
|
+
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)
|
|
2131
|
+
? { PlatformVersion: browserInfo.os.version }
|
|
2132
|
+
: {})),
|
|
2133
|
+
Location: location,
|
|
2134
|
+
Application: {
|
|
2135
|
+
AppTitle: (_d = (_c = payload === null || payload === void 0 ? void 0 : payload.appTitle) !== null && _c !== void 0 ? _c : appName) !== null && _d !== void 0 ? _d : trackerName,
|
|
2136
|
+
AppVersion: (_e = payload === null || payload === void 0 ? void 0 : payload.appVersion) !== null && _e !== void 0 ? _e : appVersion,
|
|
2137
|
+
ClientSdkVersion: (_f = payload === null || payload === void 0 ? void 0 : payload.clientSdkVersion) !== null && _f !== void 0 ? _f : trackerName,
|
|
2138
|
+
},
|
|
2139
|
+
},
|
|
2140
|
+
Events: queuedEvents.map((queuedEvent) => {
|
|
2141
|
+
const timestamp = queuedEvent.timestamp;
|
|
2142
|
+
const { attributes, metrics } = splitEventData(queuedEvent.payload);
|
|
2143
|
+
const sessionContext = queuedEvent.session ||
|
|
2144
|
+
{
|
|
2145
|
+
id: `${queuedEvent.unixMs}-${Math.random().toString(16).slice(2)}`,
|
|
2146
|
+
startTimestamp: timestamp,
|
|
2147
|
+
startUnixMs: queuedEvent.unixMs,
|
|
2148
|
+
};
|
|
2149
|
+
const isStopEvent = queuedEvent.eventName === SESSION_STOP_EVENT;
|
|
2150
|
+
return {
|
|
2151
|
+
EventType: queuedEvent.eventName,
|
|
2152
|
+
Timestamp: timestamp,
|
|
2153
|
+
Session: {
|
|
2154
|
+
Id: sessionContext.id,
|
|
2155
|
+
StartTimestamp: sessionContext.startTimestamp,
|
|
2156
|
+
EndTimestamp: timestamp,
|
|
2157
|
+
Duration: isStopEvent
|
|
2158
|
+
? Math.max(0, queuedEvent.unixMs - sessionContext.startUnixMs)
|
|
2159
|
+
: 0,
|
|
2160
|
+
},
|
|
2161
|
+
Attributes: attributes,
|
|
2162
|
+
Metrics: metrics,
|
|
2163
|
+
};
|
|
2164
|
+
}),
|
|
2165
|
+
},
|
|
2166
|
+
};
|
|
2167
|
+
}
|
|
2168
|
+
function createEsriPlugin({ trackerName, appName, appId, appVersion, userPoolID, fips, debounceMs = DEFAULT_DEBOUNCE_MS, }) {
|
|
2169
|
+
const url = ESRI_ENDPOINT_URL(appId);
|
|
2170
|
+
let isDisabled = false;
|
|
2171
|
+
let hasWarned = false;
|
|
2172
|
+
let hasSessionStarted = false;
|
|
2173
|
+
let sessionStopSent = false;
|
|
2174
|
+
let activeSession;
|
|
2175
|
+
let idleTimerId;
|
|
2176
|
+
let debounceTimerId;
|
|
2177
|
+
let lifecycleListenersAttached = false;
|
|
2178
|
+
const removeLifecycleListeners = [];
|
|
2179
|
+
let pendingEvents = [];
|
|
2180
|
+
const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
2181
|
+
const warnOnce = (message, details) => {
|
|
2182
|
+
if (hasWarned) {
|
|
2183
|
+
return;
|
|
2184
|
+
}
|
|
2185
|
+
hasWarned = true;
|
|
2186
|
+
warnLog(message, details);
|
|
2187
|
+
};
|
|
2188
|
+
debugLog('createEsriPlugin initialized', {
|
|
2189
|
+
trackerName,
|
|
2190
|
+
hasUrl: Boolean(url),
|
|
2191
|
+
});
|
|
2192
|
+
if (!url) {
|
|
2193
|
+
isDisabled = true;
|
|
2194
|
+
warnOnce('Esri telemetry disabled due to missing internal endpoint URL', {
|
|
2195
|
+
trackerName,
|
|
2196
|
+
});
|
|
2197
|
+
}
|
|
2198
|
+
if (!userPoolID) {
|
|
2199
|
+
isDisabled = true;
|
|
2200
|
+
warnOnce('Esri telemetry disabled due to missing userPoolID', {
|
|
2201
|
+
trackerName,
|
|
2202
|
+
});
|
|
2203
|
+
}
|
|
2204
|
+
if (typeof fetch !== 'function') {
|
|
2205
|
+
isDisabled = true;
|
|
2206
|
+
warnOnce('Esri telemetry disabled because fetch is unavailable', {
|
|
2207
|
+
trackerName,
|
|
2208
|
+
});
|
|
2209
|
+
}
|
|
2210
|
+
const clearIdleTimer = () => {
|
|
2211
|
+
if (!idleTimerId) {
|
|
2212
|
+
return;
|
|
2213
|
+
}
|
|
2214
|
+
clearTimeout(idleTimerId);
|
|
2215
|
+
idleTimerId = undefined;
|
|
2216
|
+
};
|
|
2217
|
+
const clearDebounceTimer = () => {
|
|
2218
|
+
if (!debounceTimerId) {
|
|
2219
|
+
return;
|
|
2220
|
+
}
|
|
2221
|
+
clearTimeout(debounceTimerId);
|
|
2222
|
+
debounceTimerId = undefined;
|
|
2223
|
+
};
|
|
2224
|
+
const clearPendingEvents = () => {
|
|
2225
|
+
pendingEvents = [];
|
|
2226
|
+
};
|
|
2227
|
+
const enqueueEvent = (event) => {
|
|
2228
|
+
pendingEvents.push(event);
|
|
2229
|
+
};
|
|
2230
|
+
const dequeuePendingEvents = () => {
|
|
2231
|
+
if (pendingEvents.length === 0) {
|
|
2232
|
+
return [];
|
|
2233
|
+
}
|
|
2234
|
+
const queuedEvents = pendingEvents;
|
|
2235
|
+
pendingEvents = [];
|
|
2236
|
+
return queuedEvents;
|
|
2237
|
+
};
|
|
2238
|
+
const sendQueuedEvents = (queuedEvents) => {
|
|
2239
|
+
var _a;
|
|
2240
|
+
if (queuedEvents.length === 0) {
|
|
2241
|
+
return;
|
|
2242
|
+
}
|
|
2243
|
+
if (isDisabled || !url || !userPoolID) {
|
|
2244
|
+
debugLog('Skipping esri track call', {
|
|
2245
|
+
eventName: (_a = queuedEvents[queuedEvents.length - 1]) === null || _a === void 0 ? void 0 : _a.eventName,
|
|
2246
|
+
reason: 'plugin-disabled',
|
|
2247
|
+
});
|
|
2248
|
+
return;
|
|
2249
|
+
}
|
|
2250
|
+
debugLog('Sending esri endpoint batch event', {
|
|
2251
|
+
trackerName,
|
|
2252
|
+
url,
|
|
2253
|
+
eventCount: queuedEvents.length,
|
|
2254
|
+
});
|
|
2255
|
+
void (async () => {
|
|
2256
|
+
var _a;
|
|
2257
|
+
try {
|
|
2258
|
+
const credentials = await getCredentials(userPoolID, { fips });
|
|
2259
|
+
const accessKeyId = (credentials === null || credentials === void 0 ? void 0 : credentials.AccessKeyId) || (credentials === null || credentials === void 0 ? void 0 : credentials.accessKeyId);
|
|
2260
|
+
const secretAccessKey = (credentials === null || credentials === void 0 ? void 0 : credentials.SecretKey) || (credentials === null || credentials === void 0 ? void 0 : credentials.secretAccessKey);
|
|
2261
|
+
const sessionToken = (credentials === null || credentials === void 0 ? void 0 : credentials.SessionToken) || (credentials === null || credentials === void 0 ? void 0 : credentials.sessionToken);
|
|
2262
|
+
if (!accessKeyId || !secretAccessKey) {
|
|
2263
|
+
throw new Error('Signed endpoint credentials are missing required key fields.');
|
|
2264
|
+
}
|
|
2265
|
+
const client = new AwsClient({
|
|
2266
|
+
accessKeyId,
|
|
2267
|
+
secretAccessKey,
|
|
2268
|
+
sessionToken,
|
|
2269
|
+
service: ESRI_SIGNING_SERVICE,
|
|
2270
|
+
region: ESRI_SIGNING_REGION,
|
|
2271
|
+
});
|
|
2272
|
+
await client.fetch(url, {
|
|
2273
|
+
method: 'POST',
|
|
2274
|
+
headers: {
|
|
2275
|
+
'Content-Type': 'application/json',
|
|
2276
|
+
},
|
|
2277
|
+
body: JSON.stringify(buildBatchEventPayload({
|
|
2278
|
+
queuedEvents,
|
|
2279
|
+
trackerName,
|
|
2280
|
+
appName,
|
|
2281
|
+
appVersion,
|
|
2282
|
+
})),
|
|
2283
|
+
});
|
|
2284
|
+
debugLog('Endpoint event sent', {
|
|
2285
|
+
trackerName,
|
|
2286
|
+
eventCount: queuedEvents.length,
|
|
2287
|
+
});
|
|
2288
|
+
}
|
|
2289
|
+
catch (error) {
|
|
2290
|
+
isDisabled = true;
|
|
2291
|
+
clearIdleTimer();
|
|
2292
|
+
clearDebounceTimer();
|
|
2293
|
+
clearPendingEvents();
|
|
2294
|
+
removeAllLifecycleListeners();
|
|
2295
|
+
warnOnce('Esri telemetry disabled after endpoint track failure', {
|
|
2296
|
+
eventName: (_a = queuedEvents[queuedEvents.length - 1]) === null || _a === void 0 ? void 0 : _a.eventName,
|
|
2297
|
+
trackerName,
|
|
2298
|
+
message: (error === null || error === void 0 ? void 0 : error.message) || String(error),
|
|
2299
|
+
});
|
|
2300
|
+
}
|
|
2301
|
+
})();
|
|
2302
|
+
};
|
|
2303
|
+
const flushPendingEvents = (eventToAppend) => {
|
|
2304
|
+
clearDebounceTimer();
|
|
2305
|
+
const queuedEvents = dequeuePendingEvents();
|
|
2306
|
+
if (eventToAppend) {
|
|
2307
|
+
queuedEvents.push(eventToAppend);
|
|
2308
|
+
}
|
|
2309
|
+
sendQueuedEvents(queuedEvents);
|
|
2310
|
+
};
|
|
2311
|
+
const scheduleDebounceFlush = () => {
|
|
2312
|
+
clearDebounceTimer();
|
|
2313
|
+
debounceTimerId = setTimeout(() => {
|
|
2314
|
+
flushPendingEvents();
|
|
2315
|
+
}, debounceMs);
|
|
2316
|
+
};
|
|
2317
|
+
const removeAllLifecycleListeners = () => {
|
|
2318
|
+
removeLifecycleListeners.splice(0).forEach((removeListener) => {
|
|
2319
|
+
removeListener();
|
|
2320
|
+
});
|
|
2321
|
+
lifecycleListenersAttached = false;
|
|
2322
|
+
};
|
|
2323
|
+
const createSession = () => {
|
|
2324
|
+
const now = Date.now();
|
|
2325
|
+
return {
|
|
2326
|
+
id: `${now}-${Math.random().toString(16).slice(2)}`,
|
|
2327
|
+
startTimestamp: new Date(now).toISOString(),
|
|
2328
|
+
startUnixMs: now,
|
|
2329
|
+
};
|
|
2330
|
+
};
|
|
2331
|
+
const sendEvent = (eventName, payload, sessionOverride, options, forceImmediate = false) => {
|
|
2332
|
+
if (isDisabled || !url || !userPoolID) {
|
|
2333
|
+
debugLog('Skipping esri track call', {
|
|
2334
|
+
eventName,
|
|
2335
|
+
reason: 'plugin-disabled',
|
|
2336
|
+
});
|
|
2337
|
+
return false;
|
|
2338
|
+
}
|
|
2339
|
+
debugLog('Queueing esri endpoint event', {
|
|
2340
|
+
eventName,
|
|
2341
|
+
trackerName,
|
|
2342
|
+
immediate: Boolean((options === null || options === void 0 ? void 0 : options.immediate) || forceImmediate),
|
|
2343
|
+
});
|
|
2344
|
+
const queuedEvent = {
|
|
2345
|
+
eventName,
|
|
2346
|
+
payload: payload,
|
|
2347
|
+
session: sessionOverride || activeSession,
|
|
2348
|
+
browserInfo: getBrowserClientInfo(),
|
|
2349
|
+
timestamp: new Date().toISOString(),
|
|
2350
|
+
unixMs: Date.now(),
|
|
2351
|
+
};
|
|
2352
|
+
if ((options === null || options === void 0 ? void 0 : options.immediate) || forceImmediate) {
|
|
2353
|
+
flushPendingEvents(queuedEvent);
|
|
2354
|
+
}
|
|
2355
|
+
else {
|
|
2356
|
+
enqueueEvent(queuedEvent);
|
|
2357
|
+
scheduleDebounceFlush();
|
|
2358
|
+
}
|
|
2359
|
+
return true;
|
|
2360
|
+
};
|
|
2361
|
+
const sendSessionLifecycleEvent = (eventName, reason, sessionOverride) => {
|
|
2362
|
+
return sendEvent(eventName, {
|
|
2363
|
+
reason,
|
|
2364
|
+
}, sessionOverride, undefined, true);
|
|
2365
|
+
};
|
|
2366
|
+
const stopSession = (reason) => {
|
|
2367
|
+
if (!hasSessionStarted || sessionStopSent) {
|
|
2368
|
+
return false;
|
|
2369
|
+
}
|
|
2370
|
+
const sessionToStop = activeSession;
|
|
2371
|
+
const didStop = sendSessionLifecycleEvent(SESSION_STOP_EVENT, reason, sessionToStop);
|
|
2372
|
+
if (didStop) {
|
|
2373
|
+
sessionStopSent = true;
|
|
2374
|
+
hasSessionStarted = false;
|
|
2375
|
+
clearIdleTimer();
|
|
2376
|
+
activeSession = undefined;
|
|
2377
|
+
}
|
|
2378
|
+
return didStop;
|
|
2379
|
+
};
|
|
2380
|
+
const startSession = (reason) => {
|
|
2381
|
+
if (hasSessionStarted) {
|
|
2382
|
+
return false;
|
|
2383
|
+
}
|
|
2384
|
+
activeSession = activeSession || createSession();
|
|
2385
|
+
const sessionToStart = activeSession;
|
|
2386
|
+
const didStart = sendSessionLifecycleEvent(SESSION_START_EVENT, reason, sessionToStart);
|
|
2387
|
+
if (didStart) {
|
|
2388
|
+
hasSessionStarted = true;
|
|
2389
|
+
sessionStopSent = false;
|
|
2390
|
+
}
|
|
2391
|
+
else {
|
|
2392
|
+
activeSession = undefined;
|
|
2393
|
+
}
|
|
2394
|
+
return didStart;
|
|
2395
|
+
};
|
|
2396
|
+
const resetIdleTimer = () => {
|
|
2397
|
+
if (!isBrowser || isDisabled || !hasSessionStarted) {
|
|
2398
|
+
return;
|
|
2399
|
+
}
|
|
2400
|
+
clearIdleTimer();
|
|
2401
|
+
idleTimerId = setTimeout(() => {
|
|
2402
|
+
stopSession('idle');
|
|
2403
|
+
}, DEFAULT_IDLE_TIMEOUT_MS);
|
|
2404
|
+
};
|
|
2405
|
+
const handleActivity = () => {
|
|
2406
|
+
if (isDisabled) {
|
|
2407
|
+
return;
|
|
2408
|
+
}
|
|
2409
|
+
if (!hasSessionStarted) {
|
|
2410
|
+
startSession('wakeup');
|
|
2411
|
+
}
|
|
2412
|
+
resetIdleTimer();
|
|
2413
|
+
};
|
|
2414
|
+
const addLifecycleListener = (target, eventName, listener) => {
|
|
2415
|
+
target.addEventListener(eventName, listener);
|
|
2416
|
+
removeLifecycleListeners.push(() => {
|
|
2417
|
+
target.removeEventListener(eventName, listener);
|
|
2418
|
+
});
|
|
2419
|
+
};
|
|
2420
|
+
const attachLifecycleListeners = () => {
|
|
2421
|
+
if (!isBrowser || lifecycleListenersAttached || isDisabled) {
|
|
2422
|
+
return;
|
|
2423
|
+
}
|
|
2424
|
+
const activityEvents = ['pointerdown', 'keydown', 'scroll', 'focus'];
|
|
2425
|
+
activityEvents.forEach((eventName) => {
|
|
2426
|
+
addLifecycleListener(window, eventName, handleActivity);
|
|
2427
|
+
});
|
|
2428
|
+
addLifecycleListener(window, 'beforeunload', () => {
|
|
2429
|
+
stopSession('beforeunload');
|
|
2430
|
+
});
|
|
2431
|
+
lifecycleListenersAttached = true;
|
|
2432
|
+
};
|
|
2433
|
+
return {
|
|
2434
|
+
track: (eventName, payload, options) => {
|
|
2435
|
+
attachLifecycleListeners();
|
|
2436
|
+
const isSessionStopEvent = eventName === SESSION_STOP_EVENT;
|
|
2437
|
+
if (!hasSessionStarted &&
|
|
2438
|
+
eventName !== SESSION_START_EVENT &&
|
|
2439
|
+
!isSessionStopEvent) {
|
|
2440
|
+
startSession('init');
|
|
2441
|
+
}
|
|
2442
|
+
if (eventName === SESSION_START_EVENT) {
|
|
2443
|
+
activeSession = activeSession || createSession();
|
|
2444
|
+
hasSessionStarted = true;
|
|
2445
|
+
sessionStopSent = false;
|
|
2446
|
+
}
|
|
2447
|
+
if (isSessionStopEvent) {
|
|
2448
|
+
sessionStopSent = true;
|
|
2449
|
+
hasSessionStarted = false;
|
|
2450
|
+
clearIdleTimer();
|
|
2451
|
+
}
|
|
2452
|
+
if (hasSessionStarted && !activeSession && !isSessionStopEvent) {
|
|
2453
|
+
activeSession = createSession();
|
|
2454
|
+
}
|
|
2455
|
+
if (hasSessionStarted && !isSessionStopEvent) {
|
|
2456
|
+
resetIdleTimer();
|
|
2457
|
+
}
|
|
2458
|
+
const sessionForEvent = activeSession;
|
|
2459
|
+
const didSend = sendEvent(eventName, payload, sessionForEvent, options, eventName === SESSION_START_EVENT || eventName === SESSION_STOP_EVENT);
|
|
2460
|
+
if (isSessionStopEvent && didSend) {
|
|
2461
|
+
activeSession = undefined;
|
|
2462
|
+
}
|
|
2463
|
+
return didSend;
|
|
2464
|
+
},
|
|
2465
|
+
};
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
// October 31, 2026
|
|
2469
|
+
const PINPOINT_RETIREMENT_CUTOFF_UTC_MS = Date.parse('2026-10-31T00:00:00.000Z');
|
|
1379
2470
|
/**
|
|
1380
2471
|
* Amazon Telemetry
|
|
1381
|
-
* Supports
|
|
2472
|
+
* Supports Pinpoint and Esri endpoint modes
|
|
1382
2473
|
*/
|
|
1383
2474
|
class Amazon {
|
|
1384
2475
|
constructor(options) {
|
|
1385
2476
|
this.name = 'amazon';
|
|
2477
|
+
this.previousPage = {};
|
|
1386
2478
|
this.isInitialized = false;
|
|
1387
|
-
|
|
1388
|
-
this.
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
plugins: [
|
|
1392
|
-
awsPinpointPlugin__default["default"]({
|
|
1393
|
-
fips,
|
|
1394
|
-
pinpointAppId: app.id,
|
|
1395
|
-
getCredentials: () => {
|
|
1396
|
-
return getCredentials(userPoolID, { fips });
|
|
1397
|
-
},
|
|
1398
|
-
}),
|
|
1399
|
-
],
|
|
1400
|
-
});
|
|
2479
|
+
this.appConfig = {};
|
|
2480
|
+
this.isPinpointRetired = (nowMs = Date.now()) => nowMs >= PINPOINT_RETIREMENT_CUTOFF_UTC_MS;
|
|
2481
|
+
// default to `esri` mode if not specified
|
|
2482
|
+
const { app = {}, fips, userPoolID, mode = 'esri' } = options;
|
|
1401
2483
|
Object.assign(this, options);
|
|
2484
|
+
this.appConfig = app;
|
|
2485
|
+
this.userPoolIDConfig = userPoolID;
|
|
2486
|
+
this.fipsConfig = fips;
|
|
2487
|
+
/** @deprecated Pinpoint is retiring on October 31, 2026. After this point all traffic will be routed to the in-house endpoint */
|
|
2488
|
+
if (mode === 'pinpoint' && !this.isPinpointRetired()) {
|
|
2489
|
+
this.mode = 'pinpoint';
|
|
2490
|
+
this.analytics = analyticsLib({
|
|
2491
|
+
app: app.name,
|
|
2492
|
+
version: app.version,
|
|
2493
|
+
plugins: [
|
|
2494
|
+
awsPinpointPlugin__default["default"]({
|
|
2495
|
+
fips,
|
|
2496
|
+
pinpointAppId: app.id,
|
|
2497
|
+
getCredentials: () => getCredentials(userPoolID, { fips }),
|
|
2498
|
+
}),
|
|
2499
|
+
],
|
|
2500
|
+
});
|
|
2501
|
+
return;
|
|
2502
|
+
}
|
|
2503
|
+
this.mode = 'esri';
|
|
2504
|
+
this.analytics = createEsriPlugin({
|
|
2505
|
+
trackerName: this.name,
|
|
2506
|
+
appName: this.appConfig.name,
|
|
2507
|
+
appVersion: this.appConfig.version,
|
|
2508
|
+
appId: this.appConfig.id,
|
|
2509
|
+
userPoolID: this.userPoolIDConfig,
|
|
2510
|
+
fips: this.fipsConfig,
|
|
2511
|
+
});
|
|
1402
2512
|
}
|
|
1403
2513
|
init() {
|
|
1404
2514
|
// if already initialized, return
|
|
@@ -1429,16 +2539,18 @@
|
|
|
1429
2539
|
});
|
|
1430
2540
|
const { pageUrl, pageName } = telemetryPayload;
|
|
1431
2541
|
this.previousPage = { pageUrl, pageName };
|
|
1432
|
-
this.analytics.track('pageView', telemetryPayload);
|
|
2542
|
+
const result = this.analytics.track('pageView', telemetryPayload);
|
|
2543
|
+
return typeof result === 'boolean' ? result : true;
|
|
1433
2544
|
}
|
|
1434
|
-
logEvent(event = {}) {
|
|
2545
|
+
logEvent(event = {}, trackOptions) {
|
|
1435
2546
|
const telemetryPayload = createEventLog({
|
|
1436
2547
|
event,
|
|
1437
2548
|
dimensionLookup: this.dimensions,
|
|
1438
2549
|
metricLookup: this.metrics,
|
|
1439
2550
|
});
|
|
1440
2551
|
const { name } = telemetryPayload;
|
|
1441
|
-
this.analytics.track(name, telemetryPayload);
|
|
2552
|
+
const result = this.analytics.track(name, telemetryPayload, trackOptions);
|
|
2553
|
+
return typeof result === 'boolean' ? result : true;
|
|
1442
2554
|
}
|
|
1443
2555
|
}
|
|
1444
2556
|
|