@esri/telemetry
Advanced tools
Comparing version
@@ -35,7 +35,26 @@ import sha256 from 'crypto-js/sha256.js'; | ||
this.debug = options.debug; | ||
this.disabled = shouldDisableTracking(options); | ||
this.suppressDisabledWarnings = options.suppressDisabledWarnings; | ||
this.logger = options.logger || console; | ||
if (this.disabled) { | ||
this.logger.warn('Telemetry Disabled'); | ||
// if passed privacy settings, use them | ||
if (options.userPrivacySettings) { | ||
this._userPrivacySettings = options.userPrivacySettings; | ||
} | ||
// if consent is required, but no privacy settings are passed, create them | ||
// this basically defaults to no tracking | ||
if (options.requireConsent && !options.userPrivacySettings) { | ||
this._userPrivacySettings = { | ||
accepted: false, | ||
performance: false, | ||
targeting: false, | ||
functional: false, | ||
id: generateGUID(), | ||
timestamp: Date.now(), | ||
}; | ||
options.userPrivacySettings = this._userPrivacySettings; | ||
} | ||
// Check if tracking should be wholesale disabled | ||
this.disabled = shouldDisableTracking(options); | ||
if (this.disabled && !this.suppressDisabledWarnings) { | ||
this.logger.info('Telemetry Disabled'); | ||
} | ||
const user = ((_a = options.portal) === null || _a === void 0 ? void 0 : _a.user) || options.user; | ||
@@ -50,13 +69,46 @@ if (user) { | ||
initializeTrackers() { | ||
const userSettings = this._userPrivacySettings; | ||
// Reset trackers | ||
this.trackers = []; | ||
// Depending on the user settings, we may not want to load all trackers | ||
if (this.options.plugins) { | ||
this.trackers.push(...this.options.plugins); | ||
if (userSettings) { | ||
// ------------------------------------------------------ | ||
// Rules: | ||
// if settings.performance: true, we can load the Esri Tracker | ||
// if settings.{performance,targeting,functional}: true, we can load the 3rd party trackers | ||
// ------------------------------------------------------ | ||
if (userSettings === null || userSettings === void 0 ? void 0 : userSettings.performance) { | ||
this.trackers.push(...this.options.plugins.filter((tracker) => tracker.name === 'amazon')); | ||
} | ||
if ((userSettings === null || userSettings === void 0 ? void 0 : userSettings.performance) && | ||
(userSettings === null || userSettings === void 0 ? void 0 : userSettings.targeting) && | ||
(userSettings === null || userSettings === void 0 ? void 0 : userSettings.functional)) { | ||
const thirdPartyTrackers = [ | ||
'adobe-analytics', | ||
'google', | ||
'googleAnalytics', | ||
'siteimprove', | ||
]; | ||
this.trackers.push(...this.options.plugins.filter((tracker) => thirdPartyTrackers.includes(tracker.name))); | ||
} | ||
} | ||
else { | ||
// enable all trackers | ||
this.trackers.push(...this.options.plugins); | ||
} | ||
} | ||
if (!this.trackers.length) { | ||
this.logger.error(new Error('No trackers configured')); | ||
// Just log that no trackers are configured | ||
// this is likely due to the user privacy settings | ||
// this.logger.error(new Error('No trackers configured')); | ||
this.logger.info('No trackers configured'); | ||
} | ||
} | ||
getScriptTags() { | ||
return this.trackers.map((tracker) => { | ||
return this.trackers | ||
.map((tracker) => { | ||
return tracker.getScriptTags && tracker.getScriptTags(); | ||
}).join(''); | ||
}) | ||
.join(''); | ||
} | ||
@@ -86,10 +138,39 @@ async init() { | ||
} | ||
/** | ||
* Allow clients to update the user privacy settings | ||
* after the telemetry instance has been created. | ||
* Host app is responsible for persisting the settings | ||
* into localStorage or cookies, depending on their requirements | ||
*/ | ||
async updateUserPrivacySettings(newSettings) { | ||
const { performance, targeting, functional } = this._userPrivacySettings; | ||
// decide if the new settings are more restrictive than the previous ones | ||
// if so we need to return `{reload: true}` so that the page can be reloaded | ||
// to ensure that the trackers are re-initialized | ||
const reload = (!newSettings.performance && performance) || | ||
(!newSettings.targeting && targeting) || | ||
(!newSettings.functional && functional); | ||
// hold into it | ||
this._userPrivacySettings = newSettings; | ||
// we need to re-initialize the trackers based on the new settings | ||
if (!reload) { | ||
// if the new settings are more permissive than the old | ||
// we know that at least some trackers are enabled, | ||
// thus telemetry as a whole is not disabled | ||
this.disabled = false; | ||
// re-initialize the trackers | ||
this.initializeTrackers(); | ||
// and initialize them because it is possible they have not been | ||
await this.init(); | ||
} | ||
return { reload }; | ||
} | ||
logPageView(page, event = {}, options = {}) { | ||
if (this.disabled) { | ||
this.logger.warn('Page view was not logged because telemetry is disabled.'); | ||
if (this.disabled && !this.suppressDisabledWarnings) { | ||
this.logger.info('Page view was not logged because telemetry is disabled.'); | ||
return false; | ||
} | ||
const enabledTrackers = this.trackers.filter(({ disabled }) => !disabled); | ||
const enabledTrackers = this.trackers.filter(({ disabled, hasError }) => !disabled && !hasError); | ||
if (!enabledTrackers.length) { | ||
this.logger.warn('Page view was not logged because no enabled telemetry-plugins are registered.'); | ||
this.logger.info('Page view was not logged because no enabled telemetry-plugins are registered.'); | ||
return false; | ||
@@ -108,9 +189,9 @@ } | ||
logEvent(event, options = {}) { | ||
if (this.disabled) { | ||
this.logger.warn('Event was not logged because telemetry is disabled.'); | ||
if (this.disabled && !this.suppressDisabledWarnings) { | ||
this.logger.info('Event was not logged because telemetry is disabled.'); | ||
return false; | ||
} | ||
const enabledTrackers = this.trackers.filter(({ disabled }) => !disabled); | ||
const enabledTrackers = this.trackers.filter(({ disabled, hasError }) => !disabled && !hasError); | ||
if (!enabledTrackers.length) { | ||
this.logger.warn('Event was not logged because no enabled telemetry-plugins are registered.'); | ||
this.logger.info('Event was not logged because no enabled telemetry-plugins are registered.'); | ||
return false; | ||
@@ -224,6 +305,14 @@ } | ||
} | ||
/** | ||
* Disable a tracker. This does not deterministically unload | ||
* the tracker from the page, but it does prevent events from | ||
* this library from being sent to it. Thus, if you need to ensure | ||
* that an instantiated tracker is fully disabled, you should reload | ||
* the application (thus, the `reload` property on the return value from `updateUserPrivacySettings`) | ||
* @param trackerName | ||
*/ | ||
disableTracker(trackerName) { | ||
var _a; | ||
const tracker = this.trackers.find(({ name }) => name === trackerName); | ||
if (tracker) { | ||
if (tracker && !tracker.hasError) { | ||
tracker.disabled = true; | ||
@@ -233,6 +322,10 @@ (_a = tracker.disable) === null || _a === void 0 ? void 0 : _a.call(tracker); | ||
} | ||
/** | ||
* Enable a specific tracker | ||
* @param trackerName | ||
*/ | ||
enableTracker(trackerName) { | ||
var _a; | ||
const tracker = this.trackers.find(({ name }) => name === trackerName); | ||
if (tracker) { | ||
if (tracker && !tracker.hasError) { | ||
tracker.disabled = false; | ||
@@ -265,2 +358,39 @@ (_a = tracker.enable) === null || _a === void 0 ? void 0 : _a.call(tracker); | ||
} | ||
// /** | ||
// * Get the privacy settings from local storage or use the default values | ||
// * @returns | ||
// */ | ||
// function getPrivacySettings(win?: Window): IPrivacySettings { | ||
// // Default settings, which is the same as if they rejected all tracking | ||
// let settings: IPrivacySettings = { | ||
// accepted: false, | ||
// performance: false, | ||
// targeting: false, | ||
// functional: false, | ||
// id: generateGUID(), | ||
// timestamp: Date.now(), | ||
// }; | ||
// if (win?.localStorage) { | ||
// // try to get the privacy settings from local storage | ||
// const storedSettings = win.localStorage.getItem('esri_privacy_settings'); | ||
// if (storedSettings) { | ||
// settings = JSON.parse(storedSettings); | ||
// } else { | ||
// // store the default settings in local storage | ||
// win.localStorage.setItem( | ||
// 'esri_privacy_settings', | ||
// JSON.stringify(settings), | ||
// ); | ||
// } | ||
// } | ||
// return settings; | ||
// } | ||
// Created by github copilot | ||
function generateGUID() { | ||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { | ||
const r = (Math.random() * 16) | 0; | ||
const v = c === 'x' ? r : (r & 0x3) | 0x8; | ||
return v.toString(16); | ||
}); | ||
} | ||
//# sourceMappingURL=index.js.map |
export function shouldDisableTracking(options = {}) { | ||
const { disabled, portal } = options; | ||
const { disabled, portal, userPrivacySettings } = options; | ||
// if user has turned off all tracking, then we disable tracking | ||
if ((userPrivacySettings === null || userPrivacySettings === void 0 ? void 0 : userPrivacySettings.functional) === false && | ||
(userPrivacySettings === null || userPrivacySettings === void 0 ? void 0 : userPrivacySettings.performance) === false && | ||
(userPrivacySettings === null || userPrivacySettings === void 0 ? void 0 : userPrivacySettings.targeting) === false) { | ||
return true; | ||
} | ||
if (disabled || (portal === null || portal === void 0 ? void 0 : portal.eueiEnabled) === false) { | ||
return true; | ||
} | ||
if (!portal || hasEueiEnabledAndIsMemberOfOrg(portal) || isRegisteredUserWithoutOrgInUSA(portal) || isAnonymousUserInUSA(portal)) { | ||
if (!portal || | ||
hasEueiEnabledAndIsMemberOfOrg(portal) || | ||
isRegisteredUserWithoutOrgInUSA(portal) || | ||
isAnonymousUserInUSA(portal)) { | ||
return false; | ||
@@ -8,0 +17,0 @@ } |
@@ -39,7 +39,26 @@ "use strict"; | ||
this.debug = options.debug; | ||
this.disabled = (0, should_disable_tracking_1.shouldDisableTracking)(options); | ||
this.suppressDisabledWarnings = options.suppressDisabledWarnings; | ||
this.logger = options.logger || console; | ||
if (this.disabled) { | ||
this.logger.warn('Telemetry Disabled'); | ||
// if passed privacy settings, use them | ||
if (options.userPrivacySettings) { | ||
this._userPrivacySettings = options.userPrivacySettings; | ||
} | ||
// if consent is required, but no privacy settings are passed, create them | ||
// this basically defaults to no tracking | ||
if (options.requireConsent && !options.userPrivacySettings) { | ||
this._userPrivacySettings = { | ||
accepted: false, | ||
performance: false, | ||
targeting: false, | ||
functional: false, | ||
id: generateGUID(), | ||
timestamp: Date.now(), | ||
}; | ||
options.userPrivacySettings = this._userPrivacySettings; | ||
} | ||
// Check if tracking should be wholesale disabled | ||
this.disabled = (0, should_disable_tracking_1.shouldDisableTracking)(options); | ||
if (this.disabled && !this.suppressDisabledWarnings) { | ||
this.logger.info('Telemetry Disabled'); | ||
} | ||
const user = ((_a = options.portal) === null || _a === void 0 ? void 0 : _a.user) || options.user; | ||
@@ -54,13 +73,46 @@ if (user) { | ||
initializeTrackers() { | ||
const userSettings = this._userPrivacySettings; | ||
// Reset trackers | ||
this.trackers = []; | ||
// Depending on the user settings, we may not want to load all trackers | ||
if (this.options.plugins) { | ||
this.trackers.push(...this.options.plugins); | ||
if (userSettings) { | ||
// ------------------------------------------------------ | ||
// Rules: | ||
// if settings.performance: true, we can load the Esri Tracker | ||
// if settings.{performance,targeting,functional}: true, we can load the 3rd party trackers | ||
// ------------------------------------------------------ | ||
if (userSettings === null || userSettings === void 0 ? void 0 : userSettings.performance) { | ||
this.trackers.push(...this.options.plugins.filter((tracker) => tracker.name === 'amazon')); | ||
} | ||
if ((userSettings === null || userSettings === void 0 ? void 0 : userSettings.performance) && | ||
(userSettings === null || userSettings === void 0 ? void 0 : userSettings.targeting) && | ||
(userSettings === null || userSettings === void 0 ? void 0 : userSettings.functional)) { | ||
const thirdPartyTrackers = [ | ||
'adobe-analytics', | ||
'google', | ||
'googleAnalytics', | ||
'siteimprove', | ||
]; | ||
this.trackers.push(...this.options.plugins.filter((tracker) => thirdPartyTrackers.includes(tracker.name))); | ||
} | ||
} | ||
else { | ||
// enable all trackers | ||
this.trackers.push(...this.options.plugins); | ||
} | ||
} | ||
if (!this.trackers.length) { | ||
this.logger.error(new Error('No trackers configured')); | ||
// Just log that no trackers are configured | ||
// this is likely due to the user privacy settings | ||
// this.logger.error(new Error('No trackers configured')); | ||
this.logger.info('No trackers configured'); | ||
} | ||
} | ||
getScriptTags() { | ||
return this.trackers.map((tracker) => { | ||
return this.trackers | ||
.map((tracker) => { | ||
return tracker.getScriptTags && tracker.getScriptTags(); | ||
}).join(''); | ||
}) | ||
.join(''); | ||
} | ||
@@ -90,10 +142,39 @@ async init() { | ||
} | ||
/** | ||
* Allow clients to update the user privacy settings | ||
* after the telemetry instance has been created. | ||
* Host app is responsible for persisting the settings | ||
* into localStorage or cookies, depending on their requirements | ||
*/ | ||
async updateUserPrivacySettings(newSettings) { | ||
const { performance, targeting, functional } = this._userPrivacySettings; | ||
// decide if the new settings are more restrictive than the previous ones | ||
// if so we need to return `{reload: true}` so that the page can be reloaded | ||
// to ensure that the trackers are re-initialized | ||
const reload = (!newSettings.performance && performance) || | ||
(!newSettings.targeting && targeting) || | ||
(!newSettings.functional && functional); | ||
// hold into it | ||
this._userPrivacySettings = newSettings; | ||
// we need to re-initialize the trackers based on the new settings | ||
if (!reload) { | ||
// if the new settings are more permissive than the old | ||
// we know that at least some trackers are enabled, | ||
// thus telemetry as a whole is not disabled | ||
this.disabled = false; | ||
// re-initialize the trackers | ||
this.initializeTrackers(); | ||
// and initialize them because it is possible they have not been | ||
await this.init(); | ||
} | ||
return { reload }; | ||
} | ||
logPageView(page, event = {}, options = {}) { | ||
if (this.disabled) { | ||
this.logger.warn('Page view was not logged because telemetry is disabled.'); | ||
if (this.disabled && !this.suppressDisabledWarnings) { | ||
this.logger.info('Page view was not logged because telemetry is disabled.'); | ||
return false; | ||
} | ||
const enabledTrackers = this.trackers.filter(({ disabled }) => !disabled); | ||
const enabledTrackers = this.trackers.filter(({ disabled, hasError }) => !disabled && !hasError); | ||
if (!enabledTrackers.length) { | ||
this.logger.warn('Page view was not logged because no enabled telemetry-plugins are registered.'); | ||
this.logger.info('Page view was not logged because no enabled telemetry-plugins are registered.'); | ||
return false; | ||
@@ -112,9 +193,9 @@ } | ||
logEvent(event, options = {}) { | ||
if (this.disabled) { | ||
this.logger.warn('Event was not logged because telemetry is disabled.'); | ||
if (this.disabled && !this.suppressDisabledWarnings) { | ||
this.logger.info('Event was not logged because telemetry is disabled.'); | ||
return false; | ||
} | ||
const enabledTrackers = this.trackers.filter(({ disabled }) => !disabled); | ||
const enabledTrackers = this.trackers.filter(({ disabled, hasError }) => !disabled && !hasError); | ||
if (!enabledTrackers.length) { | ||
this.logger.warn('Event was not logged because no enabled telemetry-plugins are registered.'); | ||
this.logger.info('Event was not logged because no enabled telemetry-plugins are registered.'); | ||
return false; | ||
@@ -228,6 +309,14 @@ } | ||
} | ||
/** | ||
* Disable a tracker. This does not deterministically unload | ||
* the tracker from the page, but it does prevent events from | ||
* this library from being sent to it. Thus, if you need to ensure | ||
* that an instantiated tracker is fully disabled, you should reload | ||
* the application (thus, the `reload` property on the return value from `updateUserPrivacySettings`) | ||
* @param trackerName | ||
*/ | ||
disableTracker(trackerName) { | ||
var _a; | ||
const tracker = this.trackers.find(({ name }) => name === trackerName); | ||
if (tracker) { | ||
if (tracker && !tracker.hasError) { | ||
tracker.disabled = true; | ||
@@ -237,6 +326,10 @@ (_a = tracker.disable) === null || _a === void 0 ? void 0 : _a.call(tracker); | ||
} | ||
/** | ||
* Enable a specific tracker | ||
* @param trackerName | ||
*/ | ||
enableTracker(trackerName) { | ||
var _a; | ||
const tracker = this.trackers.find(({ name }) => name === trackerName); | ||
if (tracker) { | ||
if (tracker && !tracker.hasError) { | ||
tracker.disabled = false; | ||
@@ -270,2 +363,39 @@ (_a = tracker.enable) === null || _a === void 0 ? void 0 : _a.call(tracker); | ||
} | ||
// /** | ||
// * Get the privacy settings from local storage or use the default values | ||
// * @returns | ||
// */ | ||
// function getPrivacySettings(win?: Window): IPrivacySettings { | ||
// // Default settings, which is the same as if they rejected all tracking | ||
// let settings: IPrivacySettings = { | ||
// accepted: false, | ||
// performance: false, | ||
// targeting: false, | ||
// functional: false, | ||
// id: generateGUID(), | ||
// timestamp: Date.now(), | ||
// }; | ||
// if (win?.localStorage) { | ||
// // try to get the privacy settings from local storage | ||
// const storedSettings = win.localStorage.getItem('esri_privacy_settings'); | ||
// if (storedSettings) { | ||
// settings = JSON.parse(storedSettings); | ||
// } else { | ||
// // store the default settings in local storage | ||
// win.localStorage.setItem( | ||
// 'esri_privacy_settings', | ||
// JSON.stringify(settings), | ||
// ); | ||
// } | ||
// } | ||
// return settings; | ||
// } | ||
// Created by github copilot | ||
function generateGUID() { | ||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { | ||
const r = (Math.random() * 16) | 0; | ||
const v = c === 'x' ? r : (r & 0x3) | 0x8; | ||
return v.toString(16); | ||
}); | ||
} | ||
//# sourceMappingURL=index.js.map |
@@ -5,7 +5,16 @@ "use strict"; | ||
function shouldDisableTracking(options = {}) { | ||
const { disabled, portal } = options; | ||
const { disabled, portal, userPrivacySettings } = options; | ||
// if user has turned off all tracking, then we disable tracking | ||
if ((userPrivacySettings === null || userPrivacySettings === void 0 ? void 0 : userPrivacySettings.functional) === false && | ||
(userPrivacySettings === null || userPrivacySettings === void 0 ? void 0 : userPrivacySettings.performance) === false && | ||
(userPrivacySettings === null || userPrivacySettings === void 0 ? void 0 : userPrivacySettings.targeting) === false) { | ||
return true; | ||
} | ||
if (disabled || (portal === null || portal === void 0 ? void 0 : portal.eueiEnabled) === false) { | ||
return true; | ||
} | ||
if (!portal || hasEueiEnabledAndIsMemberOfOrg(portal) || isRegisteredUserWithoutOrgInUSA(portal) || isAnonymousUserInUSA(portal)) { | ||
if (!portal || | ||
hasEueiEnabledAndIsMemberOfOrg(portal) || | ||
isRegisteredUserWithoutOrgInUSA(portal) || | ||
isAnonymousUserInUSA(portal)) { | ||
return false; | ||
@@ -12,0 +21,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import { Attributes, EventData, TelemetryOptions, User, Workflow } from './types'; | ||
import { Attributes, EventData, IPrivacySettings, TelemetryOptions, User, Workflow } from './types'; | ||
export * from './utils'; | ||
@@ -9,3 +9,5 @@ export declare class Telemetry implements TelemetryCore<TelemetryOptions> { | ||
private disabled; | ||
private suppressDisabledWarnings; | ||
private logger; | ||
private _userPrivacySettings; | ||
constructor(options: TelemetryOptions); | ||
@@ -16,2 +18,11 @@ private initializeTrackers; | ||
setUser(user?: string | User, orgType?: string): void; | ||
/** | ||
* Allow clients to update the user privacy settings | ||
* after the telemetry instance has been created. | ||
* Host app is responsible for persisting the settings | ||
* into localStorage or cookies, depending on their requirements | ||
*/ | ||
updateUserPrivacySettings(newSettings: IPrivacySettings): Promise<{ | ||
reload: boolean; | ||
}>; | ||
logPageView(page: string, event?: EventData, options?: TelemetryOptions): boolean; | ||
@@ -29,3 +40,15 @@ logEvent(event: EventData, options?: TelemetryOptions): boolean; | ||
preProcess(event?: EventData, options?: TelemetryOptions): Record<string, any>; | ||
/** | ||
* Disable a tracker. This does not deterministically unload | ||
* the tracker from the page, but it does prevent events from | ||
* this library from being sent to it. Thus, if you need to ensure | ||
* that an instantiated tracker is fully disabled, you should reload | ||
* the application (thus, the `reload` property on the return value from `updateUserPrivacySettings`) | ||
* @param trackerName | ||
*/ | ||
disableTracker(trackerName: string): void; | ||
/** | ||
* Enable a specific tracker | ||
* @param trackerName | ||
*/ | ||
enableTracker(trackerName: string): void; | ||
@@ -32,0 +55,0 @@ } |
@@ -32,4 +32,14 @@ export declare type GenericObject = { | ||
debug?: boolean; | ||
suppressDisabledWarnings?: boolean; | ||
omitComplexData?: boolean; | ||
logger?: ILogger; | ||
/** | ||
* If true, the system will not log any events until the user has accepted the privacy policy. | ||
*/ | ||
requireConsent?: boolean; | ||
/** | ||
* Any existing privacy settings for the user. The host application is responsible for | ||
* managing these settings via localStorage or cookies. | ||
*/ | ||
userPrivacySettings?: IPrivacySettings; | ||
} | ||
@@ -39,2 +49,3 @@ export interface Tracker { | ||
disabled?: boolean; | ||
hasError?: boolean; | ||
getScriptTags?(): string; | ||
@@ -125,2 +136,10 @@ init(): Promise<void>; | ||
} | ||
export interface IPrivacySettings { | ||
accepted: boolean; | ||
performance: boolean; | ||
targeting: boolean; | ||
functional: boolean; | ||
id: string; | ||
timestamp: number; | ||
} | ||
export {}; |
@@ -1033,7 +1033,16 @@ (function (global, factory) { | ||
function shouldDisableTracking(options = {}) { | ||
const { disabled, portal } = options; | ||
const { disabled, portal, userPrivacySettings } = options; | ||
// if user has turned off all tracking, then we disable tracking | ||
if ((userPrivacySettings === null || userPrivacySettings === void 0 ? void 0 : userPrivacySettings.functional) === false && | ||
(userPrivacySettings === null || userPrivacySettings === void 0 ? void 0 : userPrivacySettings.performance) === false && | ||
(userPrivacySettings === null || userPrivacySettings === void 0 ? void 0 : userPrivacySettings.targeting) === false) { | ||
return true; | ||
} | ||
if (disabled || (portal === null || portal === void 0 ? void 0 : portal.eueiEnabled) === false) { | ||
return true; | ||
} | ||
if (!portal || hasEueiEnabledAndIsMemberOfOrg(portal) || isRegisteredUserWithoutOrgInUSA(portal) || isAnonymousUserInUSA(portal)) { | ||
if (!portal || | ||
hasEueiEnabledAndIsMemberOfOrg(portal) || | ||
isRegisteredUserWithoutOrgInUSA(portal) || | ||
isAnonymousUserInUSA(portal)) { | ||
return false; | ||
@@ -1217,7 +1226,26 @@ } | ||
this.debug = options.debug; | ||
this.disabled = shouldDisableTracking(options); | ||
this.suppressDisabledWarnings = options.suppressDisabledWarnings; | ||
this.logger = options.logger || console; | ||
if (this.disabled) { | ||
this.logger.warn('Telemetry Disabled'); | ||
// if passed privacy settings, use them | ||
if (options.userPrivacySettings) { | ||
this._userPrivacySettings = options.userPrivacySettings; | ||
} | ||
// if consent is required, but no privacy settings are passed, create them | ||
// this basically defaults to no tracking | ||
if (options.requireConsent && !options.userPrivacySettings) { | ||
this._userPrivacySettings = { | ||
accepted: false, | ||
performance: false, | ||
targeting: false, | ||
functional: false, | ||
id: generateGUID(), | ||
timestamp: Date.now(), | ||
}; | ||
options.userPrivacySettings = this._userPrivacySettings; | ||
} | ||
// Check if tracking should be wholesale disabled | ||
this.disabled = shouldDisableTracking(options); | ||
if (this.disabled && !this.suppressDisabledWarnings) { | ||
this.logger.info('Telemetry Disabled'); | ||
} | ||
const user = ((_a = options.portal) === null || _a === void 0 ? void 0 : _a.user) || options.user; | ||
@@ -1232,13 +1260,46 @@ if (user) { | ||
initializeTrackers() { | ||
const userSettings = this._userPrivacySettings; | ||
// Reset trackers | ||
this.trackers = []; | ||
// Depending on the user settings, we may not want to load all trackers | ||
if (this.options.plugins) { | ||
this.trackers.push(...this.options.plugins); | ||
if (userSettings) { | ||
// ------------------------------------------------------ | ||
// Rules: | ||
// if settings.performance: true, we can load the Esri Tracker | ||
// if settings.{performance,targeting,functional}: true, we can load the 3rd party trackers | ||
// ------------------------------------------------------ | ||
if (userSettings === null || userSettings === void 0 ? void 0 : userSettings.performance) { | ||
this.trackers.push(...this.options.plugins.filter((tracker) => tracker.name === 'amazon')); | ||
} | ||
if ((userSettings === null || userSettings === void 0 ? void 0 : userSettings.performance) && | ||
(userSettings === null || userSettings === void 0 ? void 0 : userSettings.targeting) && | ||
(userSettings === null || userSettings === void 0 ? void 0 : userSettings.functional)) { | ||
const thirdPartyTrackers = [ | ||
'adobe-analytics', | ||
'google', | ||
'googleAnalytics', | ||
'siteimprove', | ||
]; | ||
this.trackers.push(...this.options.plugins.filter((tracker) => thirdPartyTrackers.includes(tracker.name))); | ||
} | ||
} | ||
else { | ||
// enable all trackers | ||
this.trackers.push(...this.options.plugins); | ||
} | ||
} | ||
if (!this.trackers.length) { | ||
this.logger.error(new Error('No trackers configured')); | ||
// Just log that no trackers are configured | ||
// this is likely due to the user privacy settings | ||
// this.logger.error(new Error('No trackers configured')); | ||
this.logger.info('No trackers configured'); | ||
} | ||
} | ||
getScriptTags() { | ||
return this.trackers.map((tracker) => { | ||
return this.trackers | ||
.map((tracker) => { | ||
return tracker.getScriptTags && tracker.getScriptTags(); | ||
}).join(''); | ||
}) | ||
.join(''); | ||
} | ||
@@ -1268,10 +1329,39 @@ async init() { | ||
} | ||
/** | ||
* Allow clients to update the user privacy settings | ||
* after the telemetry instance has been created. | ||
* Host app is responsible for persisting the settings | ||
* into localStorage or cookies, depending on their requirements | ||
*/ | ||
async updateUserPrivacySettings(newSettings) { | ||
const { performance, targeting, functional } = this._userPrivacySettings; | ||
// decide if the new settings are more restrictive than the previous ones | ||
// if so we need to return `{reload: true}` so that the page can be reloaded | ||
// to ensure that the trackers are re-initialized | ||
const reload = (!newSettings.performance && performance) || | ||
(!newSettings.targeting && targeting) || | ||
(!newSettings.functional && functional); | ||
// hold into it | ||
this._userPrivacySettings = newSettings; | ||
// we need to re-initialize the trackers based on the new settings | ||
if (!reload) { | ||
// if the new settings are more permissive than the old | ||
// we know that at least some trackers are enabled, | ||
// thus telemetry as a whole is not disabled | ||
this.disabled = false; | ||
// re-initialize the trackers | ||
this.initializeTrackers(); | ||
// and initialize them because it is possible they have not been | ||
await this.init(); | ||
} | ||
return { reload }; | ||
} | ||
logPageView(page, event = {}, options = {}) { | ||
if (this.disabled) { | ||
this.logger.warn('Page view was not logged because telemetry is disabled.'); | ||
if (this.disabled && !this.suppressDisabledWarnings) { | ||
this.logger.info('Page view was not logged because telemetry is disabled.'); | ||
return false; | ||
} | ||
const enabledTrackers = this.trackers.filter(({ disabled }) => !disabled); | ||
const enabledTrackers = this.trackers.filter(({ disabled, hasError }) => !disabled && !hasError); | ||
if (!enabledTrackers.length) { | ||
this.logger.warn('Page view was not logged because no enabled telemetry-plugins are registered.'); | ||
this.logger.info('Page view was not logged because no enabled telemetry-plugins are registered.'); | ||
return false; | ||
@@ -1290,9 +1380,9 @@ } | ||
logEvent(event, options = {}) { | ||
if (this.disabled) { | ||
this.logger.warn('Event was not logged because telemetry is disabled.'); | ||
if (this.disabled && !this.suppressDisabledWarnings) { | ||
this.logger.info('Event was not logged because telemetry is disabled.'); | ||
return false; | ||
} | ||
const enabledTrackers = this.trackers.filter(({ disabled }) => !disabled); | ||
const enabledTrackers = this.trackers.filter(({ disabled, hasError }) => !disabled && !hasError); | ||
if (!enabledTrackers.length) { | ||
this.logger.warn('Event was not logged because no enabled telemetry-plugins are registered.'); | ||
this.logger.info('Event was not logged because no enabled telemetry-plugins are registered.'); | ||
return false; | ||
@@ -1406,6 +1496,14 @@ } | ||
} | ||
/** | ||
* Disable a tracker. This does not deterministically unload | ||
* the tracker from the page, but it does prevent events from | ||
* this library from being sent to it. Thus, if you need to ensure | ||
* that an instantiated tracker is fully disabled, you should reload | ||
* the application (thus, the `reload` property on the return value from `updateUserPrivacySettings`) | ||
* @param trackerName | ||
*/ | ||
disableTracker(trackerName) { | ||
var _a; | ||
const tracker = this.trackers.find(({ name }) => name === trackerName); | ||
if (tracker) { | ||
if (tracker && !tracker.hasError) { | ||
tracker.disabled = true; | ||
@@ -1415,6 +1513,10 @@ (_a = tracker.disable) === null || _a === void 0 ? void 0 : _a.call(tracker); | ||
} | ||
/** | ||
* Enable a specific tracker | ||
* @param trackerName | ||
*/ | ||
enableTracker(trackerName) { | ||
var _a; | ||
const tracker = this.trackers.find(({ name }) => name === trackerName); | ||
if (tracker) { | ||
if (tracker && !tracker.hasError) { | ||
tracker.disabled = false; | ||
@@ -1447,2 +1549,39 @@ (_a = tracker.enable) === null || _a === void 0 ? void 0 : _a.call(tracker); | ||
} | ||
// /** | ||
// * Get the privacy settings from local storage or use the default values | ||
// * @returns | ||
// */ | ||
// function getPrivacySettings(win?: Window): IPrivacySettings { | ||
// // Default settings, which is the same as if they rejected all tracking | ||
// let settings: IPrivacySettings = { | ||
// accepted: false, | ||
// performance: false, | ||
// targeting: false, | ||
// functional: false, | ||
// id: generateGUID(), | ||
// timestamp: Date.now(), | ||
// }; | ||
// if (win?.localStorage) { | ||
// // try to get the privacy settings from local storage | ||
// const storedSettings = win.localStorage.getItem('esri_privacy_settings'); | ||
// if (storedSettings) { | ||
// settings = JSON.parse(storedSettings); | ||
// } else { | ||
// // store the default settings in local storage | ||
// win.localStorage.setItem( | ||
// 'esri_privacy_settings', | ||
// JSON.stringify(settings), | ||
// ); | ||
// } | ||
// } | ||
// return settings; | ||
// } | ||
// Created by github copilot | ||
function generateGUID() { | ||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { | ||
const r = (Math.random() * 16) | 0; | ||
const v = c === 'x' ? r : (r & 0x3) | 0x8; | ||
return v.toString(16); | ||
}); | ||
} | ||
@@ -1449,0 +1588,0 @@ exports.Telemetry = Telemetry; |
@@ -1033,7 +1033,16 @@ (function (global, factory) { | ||
function shouldDisableTracking(options = {}) { | ||
const { disabled, portal } = options; | ||
const { disabled, portal, userPrivacySettings } = options; | ||
// if user has turned off all tracking, then we disable tracking | ||
if ((userPrivacySettings === null || userPrivacySettings === void 0 ? void 0 : userPrivacySettings.functional) === false && | ||
(userPrivacySettings === null || userPrivacySettings === void 0 ? void 0 : userPrivacySettings.performance) === false && | ||
(userPrivacySettings === null || userPrivacySettings === void 0 ? void 0 : userPrivacySettings.targeting) === false) { | ||
return true; | ||
} | ||
if (disabled || (portal === null || portal === void 0 ? void 0 : portal.eueiEnabled) === false) { | ||
return true; | ||
} | ||
if (!portal || hasEueiEnabledAndIsMemberOfOrg(portal) || isRegisteredUserWithoutOrgInUSA(portal) || isAnonymousUserInUSA(portal)) { | ||
if (!portal || | ||
hasEueiEnabledAndIsMemberOfOrg(portal) || | ||
isRegisteredUserWithoutOrgInUSA(portal) || | ||
isAnonymousUserInUSA(portal)) { | ||
return false; | ||
@@ -1217,7 +1226,26 @@ } | ||
this.debug = options.debug; | ||
this.disabled = shouldDisableTracking(options); | ||
this.suppressDisabledWarnings = options.suppressDisabledWarnings; | ||
this.logger = options.logger || console; | ||
if (this.disabled) { | ||
this.logger.warn('Telemetry Disabled'); | ||
// if passed privacy settings, use them | ||
if (options.userPrivacySettings) { | ||
this._userPrivacySettings = options.userPrivacySettings; | ||
} | ||
// if consent is required, but no privacy settings are passed, create them | ||
// this basically defaults to no tracking | ||
if (options.requireConsent && !options.userPrivacySettings) { | ||
this._userPrivacySettings = { | ||
accepted: false, | ||
performance: false, | ||
targeting: false, | ||
functional: false, | ||
id: generateGUID(), | ||
timestamp: Date.now(), | ||
}; | ||
options.userPrivacySettings = this._userPrivacySettings; | ||
} | ||
// Check if tracking should be wholesale disabled | ||
this.disabled = shouldDisableTracking(options); | ||
if (this.disabled && !this.suppressDisabledWarnings) { | ||
this.logger.info('Telemetry Disabled'); | ||
} | ||
const user = ((_a = options.portal) === null || _a === void 0 ? void 0 : _a.user) || options.user; | ||
@@ -1232,13 +1260,46 @@ if (user) { | ||
initializeTrackers() { | ||
const userSettings = this._userPrivacySettings; | ||
// Reset trackers | ||
this.trackers = []; | ||
// Depending on the user settings, we may not want to load all trackers | ||
if (this.options.plugins) { | ||
this.trackers.push(...this.options.plugins); | ||
if (userSettings) { | ||
// ------------------------------------------------------ | ||
// Rules: | ||
// if settings.performance: true, we can load the Esri Tracker | ||
// if settings.{performance,targeting,functional}: true, we can load the 3rd party trackers | ||
// ------------------------------------------------------ | ||
if (userSettings === null || userSettings === void 0 ? void 0 : userSettings.performance) { | ||
this.trackers.push(...this.options.plugins.filter((tracker) => tracker.name === 'amazon')); | ||
} | ||
if ((userSettings === null || userSettings === void 0 ? void 0 : userSettings.performance) && | ||
(userSettings === null || userSettings === void 0 ? void 0 : userSettings.targeting) && | ||
(userSettings === null || userSettings === void 0 ? void 0 : userSettings.functional)) { | ||
const thirdPartyTrackers = [ | ||
'adobe-analytics', | ||
'google', | ||
'googleAnalytics', | ||
'siteimprove', | ||
]; | ||
this.trackers.push(...this.options.plugins.filter((tracker) => thirdPartyTrackers.includes(tracker.name))); | ||
} | ||
} | ||
else { | ||
// enable all trackers | ||
this.trackers.push(...this.options.plugins); | ||
} | ||
} | ||
if (!this.trackers.length) { | ||
this.logger.error(new Error('No trackers configured')); | ||
// Just log that no trackers are configured | ||
// this is likely due to the user privacy settings | ||
// this.logger.error(new Error('No trackers configured')); | ||
this.logger.info('No trackers configured'); | ||
} | ||
} | ||
getScriptTags() { | ||
return this.trackers.map((tracker) => { | ||
return this.trackers | ||
.map((tracker) => { | ||
return tracker.getScriptTags && tracker.getScriptTags(); | ||
}).join(''); | ||
}) | ||
.join(''); | ||
} | ||
@@ -1268,10 +1329,39 @@ async init() { | ||
} | ||
/** | ||
* Allow clients to update the user privacy settings | ||
* after the telemetry instance has been created. | ||
* Host app is responsible for persisting the settings | ||
* into localStorage or cookies, depending on their requirements | ||
*/ | ||
async updateUserPrivacySettings(newSettings) { | ||
const { performance, targeting, functional } = this._userPrivacySettings; | ||
// decide if the new settings are more restrictive than the previous ones | ||
// if so we need to return `{reload: true}` so that the page can be reloaded | ||
// to ensure that the trackers are re-initialized | ||
const reload = (!newSettings.performance && performance) || | ||
(!newSettings.targeting && targeting) || | ||
(!newSettings.functional && functional); | ||
// hold into it | ||
this._userPrivacySettings = newSettings; | ||
// we need to re-initialize the trackers based on the new settings | ||
if (!reload) { | ||
// if the new settings are more permissive than the old | ||
// we know that at least some trackers are enabled, | ||
// thus telemetry as a whole is not disabled | ||
this.disabled = false; | ||
// re-initialize the trackers | ||
this.initializeTrackers(); | ||
// and initialize them because it is possible they have not been | ||
await this.init(); | ||
} | ||
return { reload }; | ||
} | ||
logPageView(page, event = {}, options = {}) { | ||
if (this.disabled) { | ||
this.logger.warn('Page view was not logged because telemetry is disabled.'); | ||
if (this.disabled && !this.suppressDisabledWarnings) { | ||
this.logger.info('Page view was not logged because telemetry is disabled.'); | ||
return false; | ||
} | ||
const enabledTrackers = this.trackers.filter(({ disabled }) => !disabled); | ||
const enabledTrackers = this.trackers.filter(({ disabled, hasError }) => !disabled && !hasError); | ||
if (!enabledTrackers.length) { | ||
this.logger.warn('Page view was not logged because no enabled telemetry-plugins are registered.'); | ||
this.logger.info('Page view was not logged because no enabled telemetry-plugins are registered.'); | ||
return false; | ||
@@ -1290,9 +1380,9 @@ } | ||
logEvent(event, options = {}) { | ||
if (this.disabled) { | ||
this.logger.warn('Event was not logged because telemetry is disabled.'); | ||
if (this.disabled && !this.suppressDisabledWarnings) { | ||
this.logger.info('Event was not logged because telemetry is disabled.'); | ||
return false; | ||
} | ||
const enabledTrackers = this.trackers.filter(({ disabled }) => !disabled); | ||
const enabledTrackers = this.trackers.filter(({ disabled, hasError }) => !disabled && !hasError); | ||
if (!enabledTrackers.length) { | ||
this.logger.warn('Event was not logged because no enabled telemetry-plugins are registered.'); | ||
this.logger.info('Event was not logged because no enabled telemetry-plugins are registered.'); | ||
return false; | ||
@@ -1406,6 +1496,14 @@ } | ||
} | ||
/** | ||
* Disable a tracker. This does not deterministically unload | ||
* the tracker from the page, but it does prevent events from | ||
* this library from being sent to it. Thus, if you need to ensure | ||
* that an instantiated tracker is fully disabled, you should reload | ||
* the application (thus, the `reload` property on the return value from `updateUserPrivacySettings`) | ||
* @param trackerName | ||
*/ | ||
disableTracker(trackerName) { | ||
var _a; | ||
const tracker = this.trackers.find(({ name }) => name === trackerName); | ||
if (tracker) { | ||
if (tracker && !tracker.hasError) { | ||
tracker.disabled = true; | ||
@@ -1415,6 +1513,10 @@ (_a = tracker.disable) === null || _a === void 0 ? void 0 : _a.call(tracker); | ||
} | ||
/** | ||
* Enable a specific tracker | ||
* @param trackerName | ||
*/ | ||
enableTracker(trackerName) { | ||
var _a; | ||
const tracker = this.trackers.find(({ name }) => name === trackerName); | ||
if (tracker) { | ||
if (tracker && !tracker.hasError) { | ||
tracker.disabled = false; | ||
@@ -1447,2 +1549,39 @@ (_a = tracker.enable) === null || _a === void 0 ? void 0 : _a.call(tracker); | ||
} | ||
// /** | ||
// * Get the privacy settings from local storage or use the default values | ||
// * @returns | ||
// */ | ||
// function getPrivacySettings(win?: Window): IPrivacySettings { | ||
// // Default settings, which is the same as if they rejected all tracking | ||
// let settings: IPrivacySettings = { | ||
// accepted: false, | ||
// performance: false, | ||
// targeting: false, | ||
// functional: false, | ||
// id: generateGUID(), | ||
// timestamp: Date.now(), | ||
// }; | ||
// if (win?.localStorage) { | ||
// // try to get the privacy settings from local storage | ||
// const storedSettings = win.localStorage.getItem('esri_privacy_settings'); | ||
// if (storedSettings) { | ||
// settings = JSON.parse(storedSettings); | ||
// } else { | ||
// // store the default settings in local storage | ||
// win.localStorage.setItem( | ||
// 'esri_privacy_settings', | ||
// JSON.stringify(settings), | ||
// ); | ||
// } | ||
// } | ||
// return settings; | ||
// } | ||
// Created by github copilot | ||
function generateGUID() { | ||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { | ||
const r = (Math.random() * 16) | 0; | ||
const v = c === 'x' ? r : (r & 0x3) | 0x8; | ||
return v.toString(16); | ||
}); | ||
} | ||
@@ -1449,0 +1588,0 @@ exports.Telemetry = Telemetry; |
{ | ||
"name": "@esri/telemetry", | ||
"version": "7.0.2", | ||
"version": "7.0.3", | ||
"description": "A JavaScript Implementation of the ArcGIS Telemetry Specification", | ||
@@ -55,3 +55,3 @@ "main": "dist/node/index.js", | ||
}, | ||
"gitHead": "c92b317fe0643958ab784034f71c2ad5fcf098c1" | ||
"gitHead": "d6a2f8ad11b07c45dcd263cad6618e2cd8a713ec" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
383326
16.42%4251
16.37%0
-100%