@microsoft/power-apps-native-common 0.2.2 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/lib/NOTICE.txt +26 -0
  2. package/lib/commonjs/index.js +29 -5
  3. package/lib/commonjs/telemetry/AnonymousTelemetry.js +25 -0
  4. package/lib/commonjs/telemetry/CustomEventsLogger.js +56 -0
  5. package/lib/commonjs/telemetry/OneDsTelemetryProvider.js +47 -5
  6. package/lib/commonjs/telemetry/TelemetrySanitizer.js +25 -4
  7. package/lib/commonjs/telemetry/resolveTelemetryLocation.js +29 -3
  8. package/lib/commonjs/telemetry/stringifyError.js +7 -15
  9. package/lib/commonjs/utils/DeviceIdUtils.js +9 -28
  10. package/lib/module/index.js +3 -3
  11. package/lib/module/telemetry/AnonymousTelemetry.js +21 -0
  12. package/lib/module/telemetry/CustomEventsLogger.js +51 -0
  13. package/lib/module/telemetry/OneDsTelemetryProvider.js +47 -5
  14. package/lib/module/telemetry/TelemetrySanitizer.js +25 -4
  15. package/lib/module/telemetry/resolveTelemetryLocation.js +26 -3
  16. package/lib/module/telemetry/stringifyError.js +7 -16
  17. package/lib/module/utils/DeviceIdUtils.js +8 -28
  18. package/lib/typescript/commonjs/index.d.ts +4 -4
  19. package/lib/typescript/commonjs/telemetry/AnonymousTelemetry.d.ts +4 -0
  20. package/lib/typescript/commonjs/telemetry/CustomEventsLogger.d.ts +21 -0
  21. package/lib/typescript/commonjs/telemetry/OneDsTelemetryProvider.d.ts +8 -1
  22. package/lib/typescript/commonjs/telemetry/TelemetryTypes.d.ts +8 -1
  23. package/lib/typescript/commonjs/telemetry/resolveTelemetryLocation.d.ts +8 -1
  24. package/lib/typescript/commonjs/telemetry/stringifyError.d.ts +3 -3
  25. package/lib/typescript/commonjs/utils/DeviceIdUtils.d.ts +5 -12
  26. package/lib/typescript/module/index.d.ts +4 -4
  27. package/lib/typescript/module/telemetry/AnonymousTelemetry.d.ts +4 -0
  28. package/lib/typescript/module/telemetry/CustomEventsLogger.d.ts +21 -0
  29. package/lib/typescript/module/telemetry/OneDsTelemetryProvider.d.ts +8 -1
  30. package/lib/typescript/module/telemetry/TelemetryTypes.d.ts +8 -1
  31. package/lib/typescript/module/telemetry/resolveTelemetryLocation.d.ts +8 -1
  32. package/lib/typescript/module/telemetry/stringifyError.d.ts +3 -3
  33. package/lib/typescript/module/utils/DeviceIdUtils.d.ts +5 -12
  34. package/package.json +3 -2
  35. package/lib/commonjs/telemetry/CustomerTelemetryLogger.js +0 -56
  36. package/lib/module/telemetry/CustomerTelemetryLogger.js +0 -51
  37. package/lib/typescript/commonjs/telemetry/CustomerTelemetryLogger.d.ts +0 -21
  38. package/lib/typescript/module/telemetry/CustomerTelemetryLogger.d.ts +0 -21
package/lib/NOTICE.txt CHANGED
@@ -30,6 +30,32 @@ SOFTWARE.
30
30
 
31
31
  ========================================================================
32
32
 
33
+ @microsoft/applicationinsights-channel-js 3.4.3
34
+ --
35
+ The MIT License (MIT)
36
+
37
+ Copyright (c) Microsoft Corporation
38
+
39
+ Permission is hereby granted, free of charge, to any person obtaining a copy
40
+ of this software and associated documentation files (the "Software"), to deal
41
+ in the Software without restriction, including without limitation the rights
42
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
43
+ copies of the Software, and to permit persons to whom the Software is
44
+ furnished to do so, subject to the following conditions:
45
+
46
+ The above copyright notice and this permission notice shall be included in all
47
+ copies or substantial portions of the Software.
48
+
49
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
50
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
51
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
52
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
53
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
54
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
55
+ SOFTWARE.
56
+
57
+ ========================================================================
58
+
33
59
  @microsoft/applicationinsights-core-js 3.4.3
34
60
  --
35
61
  The MIT License (MIT)
@@ -57,10 +57,10 @@ Object.defineProperty(exports, "configureAppLogger", {
57
57
  return _AppLogger.configureAppLogger;
58
58
  }
59
59
  });
60
- Object.defineProperty(exports, "configureCustomerTelemetryLogger", {
60
+ Object.defineProperty(exports, "configureCustomEventsLogger", {
61
61
  enumerable: true,
62
62
  get: function () {
63
- return _CustomerTelemetryLogger.configureCustomerTelemetryLogger;
63
+ return _CustomEventsLogger.configureCustomEventsLogger;
64
64
  }
65
65
  });
66
66
  Object.defineProperty(exports, "getAppLogger", {
@@ -69,10 +69,10 @@ Object.defineProperty(exports, "getAppLogger", {
69
69
  return _AppLogger.getAppLogger;
70
70
  }
71
71
  });
72
- Object.defineProperty(exports, "getCustomerTelemetryLogger", {
72
+ Object.defineProperty(exports, "getCustomEventsLogger", {
73
73
  enumerable: true,
74
74
  get: function () {
75
- return _CustomerTelemetryLogger.getCustomerTelemetryLogger;
75
+ return _CustomEventsLogger.getCustomEventsLogger;
76
76
  }
77
77
  });
78
78
  Object.defineProperty(exports, "getHttpCorrelationProperties", {
@@ -87,6 +87,12 @@ Object.defineProperty(exports, "regionToClusterCategory", {
87
87
  return _ClusterCategory.regionToClusterCategory;
88
88
  }
89
89
  });
90
+ Object.defineProperty(exports, "resetDeviceId", {
91
+ enumerable: true,
92
+ get: function () {
93
+ return _DeviceIdUtils.resetDeviceId;
94
+ }
95
+ });
90
96
  Object.defineProperty(exports, "resolveDeviceId", {
91
97
  enumerable: true,
92
98
  get: function () {
@@ -99,6 +105,12 @@ Object.defineProperty(exports, "resolveTelemetryLocation", {
99
105
  return _resolveTelemetryLocation.resolveTelemetryLocation;
100
106
  }
101
107
  });
108
+ Object.defineProperty(exports, "resolveTelemetryLocationFromDataverseRegion", {
109
+ enumerable: true,
110
+ get: function () {
111
+ return _resolveTelemetryLocation.resolveTelemetryLocationFromDataverseRegion;
112
+ }
113
+ });
102
114
  Object.defineProperty(exports, "sanitizeErrorString", {
103
115
  enumerable: true,
104
116
  get: function () {
@@ -129,6 +141,18 @@ Object.defineProperty(exports, "suppressKnownWarnings", {
129
141
  return _suppressKnownWarnings.suppressKnownWarnings;
130
142
  }
131
143
  });
144
+ Object.defineProperty(exports, "tryResolveTelemetryLocationFromDataverseRegion", {
145
+ enumerable: true,
146
+ get: function () {
147
+ return _resolveTelemetryLocation.tryResolveTelemetryLocationFromDataverseRegion;
148
+ }
149
+ });
150
+ Object.defineProperty(exports, "tryResolveTelemetryLocationFromEnvironment", {
151
+ enumerable: true,
152
+ get: function () {
153
+ return _resolveTelemetryLocation.tryResolveTelemetryLocationFromEnvironment;
154
+ }
155
+ });
132
156
  var _ConsoleAdapter = require("./telemetry/ConsoleAdapter.js");
133
157
  var _Logger = require("./telemetry/Logger.js");
134
158
  var _OneDsTelemetryProvider = require("./telemetry/OneDsTelemetryProvider.js");
@@ -140,6 +164,6 @@ var _suppressKnownWarnings = require("./telemetry/suppressKnownWarnings.js");
140
164
  var _HttpClient = require("./http/HttpClient.js");
141
165
  var _DeviceIdUtils = require("./utils/DeviceIdUtils.js");
142
166
  var _AppLogger = require("./telemetry/AppLogger.js");
143
- var _CustomerTelemetryLogger = require("./telemetry/CustomerTelemetryLogger.js");
167
+ var _CustomEventsLogger = require("./telemetry/CustomEventsLogger.js");
144
168
  var _TelemetryTypes = require("./telemetry/TelemetryTypes.js");
145
169
  var _ClusterCategory = require("./types/ClusterCategory.js");
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.buildAnonymousTelemetryData = buildAnonymousTelemetryData;
7
+ function buildAnonymousTelemetryData(event, appName, clusterCategory) {
8
+ const operationName = typeof event.properties.operationName === 'string' ? event.properties.operationName : undefined;
9
+ return {
10
+ app_Name: appName,
11
+ clusterCategory,
12
+ event_Name: event.eventName,
13
+ severity: event.severity,
14
+ timestamp: event.timestamp,
15
+ ...(event.duration !== undefined ? {
16
+ duration: event.duration
17
+ } : {}),
18
+ ...(operationName ? {
19
+ operation_Name: operationName
20
+ } : {}),
21
+ customDimensions: JSON.stringify(operationName ? {
22
+ operationName
23
+ } : {})
24
+ };
25
+ }
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.configureCustomEventsLogger = configureCustomEventsLogger;
7
+ exports.getCustomEventsLogger = getCustomEventsLogger;
8
+ var _Logger = require("./Logger.js");
9
+ var _stringifyError = require("./stringifyError.js");
10
+ /*!
11
+ * Copyright (C) Microsoft Corporation. All rights reserved.
12
+ */
13
+
14
+ class CustomEventsLogger {
15
+ constructor(logger) {
16
+ this.logger = logger;
17
+ }
18
+ trackEvent(name, properties) {
19
+ this.logger.trackActivityEvent(name, this.withCustomEventsSource(properties));
20
+ }
21
+ trackError(name, error, properties) {
22
+ this.logger.trackErrorEvent(name, this.withCustomEventsSource({
23
+ ...properties,
24
+ error: error === undefined ? undefined : (0, _stringifyError.stringifyError)(error)
25
+ }));
26
+ }
27
+ trackScenario(name, properties) {
28
+ return this.logger.trackScenario(name, this.withCustomEventsSource(properties));
29
+ }
30
+ flush() {
31
+ return this.logger.flush();
32
+ }
33
+ withCustomEventsSource(properties) {
34
+ return {
35
+ ...properties,
36
+ telemetry_source: 'custom'
37
+ };
38
+ }
39
+ }
40
+ let customEventsLogger = new CustomEventsLogger(new _Logger.Logger());
41
+
42
+ /**
43
+ * Configures the custom-events singleton. Hosting infrastructure owns
44
+ * this function; application code should use getCustomEventsLogger().
45
+ */
46
+ function configureCustomEventsLogger(logger) {
47
+ customEventsLogger = new CustomEventsLogger(logger);
48
+ }
49
+
50
+ /**
51
+ * Returns the app's custom-events logger. It is a safe no-op until the
52
+ * host configures an Application Insights provider.
53
+ */
54
+ function getCustomEventsLogger() {
55
+ return customEventsLogger;
56
+ }
@@ -8,6 +8,7 @@ var _applicationinsightsCoreJs = require("@microsoft/applicationinsights-core-js
8
8
  var _dsPostJs = require("@microsoft/1ds-post-js");
9
9
  var _GuidUtils = require("../utils/GuidUtils.js");
10
10
  var _PlatformInfoUtils = require("../utils/PlatformInfoUtils.js");
11
+ var _AnonymousTelemetry = require("./AnonymousTelemetry.js");
11
12
  /*!
12
13
  * Copyright (C) Microsoft Corporation. All rights reserved.
13
14
  */
@@ -97,11 +98,14 @@ class OneDsTelemetryProvider {
97
98
  defaultProperties = {};
98
99
  constructor(config) {
99
100
  this._sessionId = (0, _GuidUtils.generateGuid)();
101
+ this.anonymous = config.anonymous ?? false;
102
+ this.telemetryLocation = config.telemetryLocation ?? 'global';
100
103
  this.appName = resolveAppName(config.clusterCategory);
101
104
  this.appVersion = config.appVersion ?? '';
102
105
  this.clientType = config.clientType;
103
106
  this.clusterCategory = config.clusterCategory;
104
107
  this.deviceId = config.deviceId ?? 'react-native';
108
+ this.includeUserIdentity = config.includeUserIdentity ?? false;
105
109
  this.minSeverity = config.minSeverity ?? 'info';
106
110
  const {
107
111
  osName,
@@ -128,15 +132,15 @@ class OneDsTelemetryProvider {
128
132
  if (config.commonProperties) {
129
133
  Object.assign(this.defaultProperties, config.commonProperties);
130
134
  }
131
- const postChannel = new _dsPostJs.PostChannel();
135
+ this.postChannel = new _dsPostJs.PostChannel();
132
136
  const instrumentationKey = config.instrumentationKey ?? resolveInstrumentationKey(config.clusterCategory);
133
- const collectorUrl = resolveCollectorUrl(config.clusterCategory, config.telemetryLocation);
137
+ const collectorUrl = resolveCollectorUrl(config.clusterCategory, this.telemetryLocation);
134
138
  const coreConfig = {
135
139
  instrumentationKey,
136
- channels: [[postChannel]]
140
+ channels: [[this.postChannel]]
137
141
  };
138
142
  coreConfig.extensionConfig = {
139
- [postChannel.identifier]: {
143
+ [this.postChannel.identifier]: {
140
144
  // Override endpoint explicitly — PostChannel reads overrideEndpointUrl
141
145
  // from its own extensionConfig, not from coreConfig.endpointUrl.
142
146
  overrideEndpointUrl: collectorUrl,
@@ -168,6 +172,7 @@ class OneDsTelemetryProvider {
168
172
  const ext = envelope.ext = envelope.ext ?? {};
169
173
  const utc = ext['utc'] = ext['utc'] ?? {};
170
174
  utc['eventFlags'] = (utc['eventFlags'] || 0) | 0x100000;
175
+ if (this.anonymous) return;
171
176
  const app = ext['app'] = ext['app'] ?? {};
172
177
  app['sesId'] = this._sessionId;
173
178
  if (this.appVersion) app['ver'] = this.appVersion; // → AppInfo_Version
@@ -193,6 +198,15 @@ class OneDsTelemetryProvider {
193
198
  this.core.addTelemetryInitializer(initializer);
194
199
  }
195
200
  track(event) {
201
+ if (this.anonymous) {
202
+ this.core.track({
203
+ name: 'event',
204
+ time: event.timestamp,
205
+ baseType: 'custom',
206
+ data: (0, _AnonymousTelemetry.buildAnonymousTelemetryData)(event, this.appName, this.clusterCategory)
207
+ });
208
+ return;
209
+ }
196
210
  const allProperties = {
197
211
  ...this.defaultProperties,
198
212
  ...event.properties
@@ -218,7 +232,7 @@ class OneDsTelemetryProvider {
218
232
  };
219
233
 
220
234
  // User identity promoted to top-level columns directly
221
- if (this.userIdentity) {
235
+ if (this.includeUserIdentity && this.userIdentity) {
222
236
  data.objectId = this.userIdentity.objectId;
223
237
  data.tenantId = this.userIdentity.tenantId;
224
238
  }
@@ -246,6 +260,34 @@ class OneDsTelemetryProvider {
246
260
  };
247
261
  this.core.track(item);
248
262
  }
263
+ async resolveTelemetryLocation(telemetryLocation) {
264
+ if (!this.anonymous && telemetryLocation === this.telemetryLocation) return;
265
+ const previousResolution = this.locationResolution ?? Promise.resolve();
266
+ const currentResolution = previousResolution.catch(() => undefined).then(() => this.applyTelemetryLocation(telemetryLocation));
267
+ this.locationResolution = currentResolution;
268
+ try {
269
+ await currentResolution;
270
+ } finally {
271
+ if (this.locationResolution === currentResolution) {
272
+ this.locationResolution = undefined;
273
+ }
274
+ }
275
+ }
276
+ async applyTelemetryLocation(telemetryLocation) {
277
+ if (!this.anonymous && telemetryLocation === this.telemetryLocation) return;
278
+ if (telemetryLocation !== this.telemetryLocation) {
279
+ await this.flush();
280
+ this.core.updateCfg({
281
+ extensionConfig: {
282
+ [this.postChannel.identifier]: {
283
+ overrideEndpointUrl: resolveCollectorUrl(this.clusterCategory, telemetryLocation)
284
+ }
285
+ }
286
+ });
287
+ this.telemetryLocation = telemetryLocation;
288
+ }
289
+ this.anonymous = false;
290
+ }
249
291
  flush() {
250
292
  return new Promise(resolve => {
251
293
  this.core.flush(true, () => {
@@ -56,6 +56,13 @@ const SAS_KEY_SIG_RE = /(?:dsas_secret|sig)=[^#)\(<>\r\n\s'",\\_&]{8}[^#)\(<>\r\
56
56
  */
57
57
  const SYMM_KEY_256_RE = /[a-z0-9/+]{43}=/gi;
58
58
 
59
+ /** Bearer credentials and compact JWTs embedded in otherwise arbitrary text. */
60
+ const BEARER_CREDENTIAL_RE = /\bbearer\s+[a-z0-9._~+\/-]+=*/gi;
61
+ const JWT_RE = /\beyJ[a-z0-9_-]+\.[a-z0-9_-]+\.[a-z0-9_-]+\b/gi;
62
+
63
+ /** Query strings and fragments on absolute URLs commonly carry user context. */
64
+ const URL_QUERY_OR_FRAGMENT_RE = /(https?:\/\/[^\s?#'"<>]+)[?#][^\s'"<>]*/gi;
65
+
59
66
  /**
60
67
  * Property key patterns that must never appear as telemetry values.
61
68
  * Checked case-insensitively against the full key name.
@@ -67,7 +74,7 @@ const SENSITIVE_KEY_RE = /password|secret|token|credential|auth(?:orization)?|be
67
74
  * Safe to use on error messages, URL paths, and arbitrary text.
68
75
  */
69
76
  function sanitizeErrorString(raw) {
70
- return raw.replace(EMAIL_RE, '<upn>').replace(PRIVATE_IPV4_RE, (_, port) => port ? `<private-ip>${port}` : '<private-ip>').replace(GOOGLE_API_KEY_RE, '<credential>').replace(SAS_KEY_SIG_RE, '<credential>').replace(SYMM_KEY_256_RE, '<credential>');
77
+ return raw.replace(URL_QUERY_OR_FRAGMENT_RE, '$1').replace(EMAIL_RE, '<upn>').replace(PRIVATE_IPV4_RE, (_, port) => port ? `<private-ip>${port}` : '<private-ip>').replace(GOOGLE_API_KEY_RE, '<credential>').replace(SAS_KEY_SIG_RE, '<credential>').replace(SYMM_KEY_256_RE, '<credential>').replace(BEARER_CREDENTIAL_RE, 'Bearer <credential>').replace(JWT_RE, '<credential>');
71
78
  }
72
79
 
73
80
  /**
@@ -86,15 +93,29 @@ function sanitizeUrl(url) {
86
93
  * - Non-string primitives passed through unchanged
87
94
  */
88
95
  function sanitizeProperties(props) {
96
+ return sanitizeRecord(props, new WeakSet(), 0);
97
+ }
98
+ const MAX_SANITIZE_DEPTH = 5;
99
+ function sanitizeRecord(props, seen, depth) {
100
+ if (seen.has(props) || depth > MAX_SANITIZE_DEPTH) return {};
101
+ seen.add(props);
89
102
  const result = {};
90
103
  for (const [key, value] of Object.entries(props)) {
91
104
  if (SENSITIVE_KEY_RE.test(key)) {
92
105
  result[key] = '<redacted>';
93
- } else if (typeof value === 'string') {
94
- result[key] = sanitizeErrorString(value);
95
106
  } else {
96
- result[key] = value;
107
+ result[key] = sanitizeValue(value, seen, depth + 1);
97
108
  }
98
109
  }
99
110
  return result;
111
+ }
112
+ function sanitizeValue(value, seen, depth) {
113
+ if (typeof value === 'string') return sanitizeErrorString(value);
114
+ if (value === null || typeof value !== 'object') return value;
115
+ if (depth > MAX_SANITIZE_DEPTH || seen.has(value)) return '<redacted>';
116
+ if (Array.isArray(value)) {
117
+ seen.add(value);
118
+ return value.map(item => sanitizeValue(item, seen, depth + 1));
119
+ }
120
+ return sanitizeRecord(value, seen, depth);
100
121
  }
@@ -4,6 +4,9 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.resolveTelemetryLocation = resolveTelemetryLocation;
7
+ exports.resolveTelemetryLocationFromDataverseRegion = resolveTelemetryLocationFromDataverseRegion;
8
+ exports.tryResolveTelemetryLocationFromDataverseRegion = tryResolveTelemetryLocationFromDataverseRegion;
9
+ exports.tryResolveTelemetryLocationFromEnvironment = tryResolveTelemetryLocationFromEnvironment;
7
10
  /*!
8
11
  * Copyright (C) Microsoft Corporation. All rights reserved.
9
12
  */
@@ -15,13 +18,23 @@ exports.resolveTelemetryLocation = resolveTelemetryLocation;
15
18
  * Source: `metadata.properties.location` in the BAP Get Environments API response.
16
19
  */
17
20
  const EU_BAP_LOCATIONS = new Set(['europe', 'france', 'germany', 'italy', 'poland', 'norway', 'sweden', 'switzerland', 'unitedkingdom']);
21
+ const NON_EU_BAP_LOCATIONS = new Set(['asia', 'australia', 'brazil', 'canada', 'india', 'japan', 'korea', 'southafrica', 'southamerica', 'unitedarabemirates', 'unitedstates']);
22
+ const EU_DATAVERSE_REGIONS = new Set(['che', 'emea', 'eur', 'fra', 'gbr', 'ger', 'ita', 'nor', 'pol', 'swe']);
23
+ const NON_EU_DATAVERSE_REGIONS = new Set(['apac', 'aus', 'can', 'ind', 'jpn', 'kor', 'latam', 'nam', 'na', 'oce', 'sam', 'sgp', 'uae', 'usa']);
24
+ function resolveKnownLocation(input, euValues, nonEuValues) {
25
+ const value = input?.trim().toLowerCase();
26
+ if (value && euValues.has(value)) return 'eu';
27
+ if (value && nonEuValues.has(value)) return 'global';
28
+ return undefined;
29
+ }
18
30
 
19
31
  /**
20
32
  * Resolve the 1DS telemetry collector location from a Power Platform BAP
21
33
  * environment `location` string (`environment.properties.location`).
22
34
  * Returns `'eu'` when the location maps to the EU data boundary, routing
23
35
  * telemetry to `eu-mobile.events.data.microsoft.com` instead of the global
24
- * US collector.
36
+ * US collector. Missing or blank locations fail closed to the EU collector so
37
+ * telemetry emitted before the environment is known does not leave the EUDB.
25
38
  *
26
39
  * Mirrors the CLI's detection in `CliLogger.ts`:
27
40
  * ```
@@ -35,8 +48,21 @@ const EU_BAP_LOCATIONS = new Set(['europe', 'france', 'germany', 'italy', 'polan
35
48
  * resolveTelemetryLocation('unitedkingdom') // → 'eu'
36
49
  * resolveTelemetryLocation('italy') // → 'eu'
37
50
  * resolveTelemetryLocation('unitedstates') // → 'global'
51
+ * resolveTelemetryLocation() // → 'eu'
38
52
  */
39
53
  function resolveTelemetryLocation(input) {
40
- if (!input) return 'global';
41
- return EU_BAP_LOCATIONS.has(input.trim().toLowerCase()) ? 'eu' : 'global';
54
+ return resolveKnownLocation(input, EU_BAP_LOCATIONS, NON_EU_BAP_LOCATIONS) ?? 'eu';
55
+ }
56
+
57
+ /** Resolve a Dataverse Global Discovery `Instance.Region` code. */
58
+ function resolveTelemetryLocationFromDataverseRegion(input) {
59
+ return tryResolveTelemetryLocationFromDataverseRegion(input) ?? 'eu';
60
+ }
61
+ function tryResolveTelemetryLocationFromDataverseRegion(input) {
62
+ return resolveKnownLocation(input, EU_DATAVERSE_REGIONS, NON_EU_DATAVERSE_REGIONS);
63
+ }
64
+
65
+ /** Resolve launch metadata, falling back from BAP location to cluster geography. */
66
+ function tryResolveTelemetryLocationFromEnvironment(location, clusterGeoShortName) {
67
+ return resolveKnownLocation(location, EU_BAP_LOCATIONS, NON_EU_BAP_LOCATIONS) ?? tryResolveTelemetryLocationFromDataverseRegion(clusterGeoShortName);
42
68
  }
@@ -4,28 +4,20 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.stringifyError = stringifyError;
7
- var _TelemetrySanitizer = require("./TelemetrySanitizer.js");
8
7
  /*!
9
8
  * Copyright (C) Microsoft Corporation. All rights reserved.
10
9
  */
11
10
 
12
11
  /**
13
- * Converts an unknown thrown value to a string suitable for telemetry.
14
- * PII (email addresses, private IPv4 addresses) is redacted before the
15
- * string is returned.
12
+ * Classifies an unknown thrown value without exporting its message, stack, or
13
+ * serialized contents. Error prose is not a safe telemetry contract because
14
+ * upstream services may include customer data or credentials in it.
16
15
  */
17
16
  function stringifyError(error) {
18
- let raw;
19
17
  if (error instanceof Error) {
20
- raw = `${error.name}: ${error.message}${error.stack ? `\n${error.stack}` : ''}`;
21
- } else if (typeof error === 'string') {
22
- raw = error;
23
- } else {
24
- try {
25
- raw = JSON.stringify(error);
26
- } catch {
27
- raw = String(error);
28
- }
18
+ return error.name || error.constructor.name || 'Error';
29
19
  }
30
- return (0, _TelemetrySanitizer.sanitizeErrorString)(raw);
20
+ if (typeof error === 'string') return 'StringError';
21
+ if (error && typeof error === 'object') return error.constructor?.name || 'ObjectError';
22
+ return `${typeof error}Error`;
31
23
  }
@@ -3,10 +3,9 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.resetDeviceId = resetDeviceId;
6
7
  exports.resolveDeviceId = resolveDeviceId;
7
- var Application = _interopRequireWildcard(require("expo-application"));
8
8
  var SecureStore = _interopRequireWildcard(require("expo-secure-store"));
9
- var _reactNative = require("react-native");
10
9
  var _GuidUtils = require("./GuidUtils.js");
11
10
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
12
11
  /*!
@@ -18,18 +17,9 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
18
17
  const DEVICE_ID_FALLBACK_KEY = 'microsoft.power-apps-native-common.device_id';
19
18
 
20
19
  /**
21
- * Returns a stable device identifier, preferring platform-native IDs that
22
- * survive app reinstalls:
23
- *
24
- * - **iOS**: `getIosIdForVendorAsync()` (IDFV) — stable while ≥1 app from the
25
- * same vendor bundle prefix is installed. Resets only when ALL vendor apps
26
- * are removed.
27
- * - **Android**: `getAndroidId()` (SSAID) — scoped to the app signing key on
28
- * Android 8+; survives reinstalls as long as the signing key is unchanged.
29
- *
30
- * If the platform API returns null (rare edge cases: first boot, some emulators)
31
- * the function falls back to a UUID persisted in `expo-secure-store`, which on
32
- * iOS survives reinstalls via the Keychain and on Android is per-install only.
20
+ * Returns a random app-scoped telemetry identifier. It does not use IDFV,
21
+ * SSAID, or another platform device identifier, and can be rotated with
22
+ * `resetDeviceId()` for deletion and opt-out workflows.
33
23
  *
34
24
  * Pass the resolved value to `OneDsTelemetryProviderConfig.deviceId` at startup.
35
25
  *
@@ -42,20 +32,6 @@ const DEVICE_ID_FALLBACK_KEY = 'microsoft.power-apps-native-common.device_id';
42
32
  * ```
43
33
  */
44
34
  async function resolveDeviceId() {
45
- if (_reactNative.Platform.OS === 'ios') {
46
- const idfv = await Application.getIosIdForVendorAsync();
47
- if (idfv) {
48
- return idfv;
49
- }
50
- } else if (_reactNative.Platform.OS === 'android') {
51
- const androidId = Application.getAndroidId();
52
- if (androidId) {
53
- return androidId;
54
- }
55
- }
56
-
57
- // Fallback: stable UUID in SecureStore (iOS: survives reinstall via Keychain;
58
- // Android: per-install only, but only reached when SSAID is unavailable).
59
35
  const existing = await SecureStore.getItemAsync(DEVICE_ID_FALLBACK_KEY);
60
36
  if (existing) {
61
37
  return existing;
@@ -63,4 +39,9 @@ async function resolveDeviceId() {
63
39
  const id = (0, _GuidUtils.generateGuid)();
64
40
  await SecureStore.setItemAsync(DEVICE_ID_FALLBACK_KEY, id);
65
41
  return id;
42
+ }
43
+
44
+ /** Deletes the app-scoped telemetry identifier so the next resolution rotates it. */
45
+ async function resetDeviceId() {
46
+ await SecureStore.deleteItemAsync(DEVICE_ID_FALLBACK_KEY);
66
47
  }
@@ -9,13 +9,13 @@ export { ConsoleAdapter } from "./telemetry/ConsoleAdapter.js";
9
9
  export { Logger } from "./telemetry/Logger.js";
10
10
  export { OneDsTelemetryProvider } from "./telemetry/OneDsTelemetryProvider.js";
11
11
  export { ApplicationInsightsTelemetryProvider } from "./telemetry/ApplicationInsightsTelemetryProvider.js";
12
- export { resolveTelemetryLocation } from "./telemetry/resolveTelemetryLocation.js";
12
+ export { resolveTelemetryLocation, resolveTelemetryLocationFromDataverseRegion, tryResolveTelemetryLocationFromDataverseRegion, tryResolveTelemetryLocationFromEnvironment } from "./telemetry/resolveTelemetryLocation.js";
13
13
  export { stringifyError } from "./telemetry/stringifyError.js";
14
14
  export { sanitizeErrorString, sanitizeUrl, sanitizeProperties } from "./telemetry/TelemetrySanitizer.js";
15
15
  export { suppressKnownWarnings } from "./telemetry/suppressKnownWarnings.js";
16
16
  export { HttpClient, getHttpCorrelationProperties } from "./http/HttpClient.js";
17
- export { resolveDeviceId } from "./utils/DeviceIdUtils.js";
17
+ export { resetDeviceId, resolveDeviceId } from "./utils/DeviceIdUtils.js";
18
18
  export { configureAppLogger, getAppLogger } from "./telemetry/AppLogger.js";
19
- export { configureCustomerTelemetryLogger, getCustomerTelemetryLogger } from "./telemetry/CustomerTelemetryLogger.js";
19
+ export { configureCustomEventsLogger, getCustomEventsLogger } from "./telemetry/CustomEventsLogger.js";
20
20
  export { ScenarioResults, TelemetryEventNames } from "./telemetry/TelemetryTypes.js";
21
21
  export { Regions, regionToClusterCategory } from "./types/ClusterCategory.js";
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+
3
+ export function buildAnonymousTelemetryData(event, appName, clusterCategory) {
4
+ const operationName = typeof event.properties.operationName === 'string' ? event.properties.operationName : undefined;
5
+ return {
6
+ app_Name: appName,
7
+ clusterCategory,
8
+ event_Name: event.eventName,
9
+ severity: event.severity,
10
+ timestamp: event.timestamp,
11
+ ...(event.duration !== undefined ? {
12
+ duration: event.duration
13
+ } : {}),
14
+ ...(operationName ? {
15
+ operation_Name: operationName
16
+ } : {}),
17
+ customDimensions: JSON.stringify(operationName ? {
18
+ operationName
19
+ } : {})
20
+ };
21
+ }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+
3
+ /*!
4
+ * Copyright (C) Microsoft Corporation. All rights reserved.
5
+ */
6
+
7
+ import { Logger } from "./Logger.js";
8
+ import { stringifyError } from "./stringifyError.js";
9
+ class CustomEventsLogger {
10
+ constructor(logger) {
11
+ this.logger = logger;
12
+ }
13
+ trackEvent(name, properties) {
14
+ this.logger.trackActivityEvent(name, this.withCustomEventsSource(properties));
15
+ }
16
+ trackError(name, error, properties) {
17
+ this.logger.trackErrorEvent(name, this.withCustomEventsSource({
18
+ ...properties,
19
+ error: error === undefined ? undefined : stringifyError(error)
20
+ }));
21
+ }
22
+ trackScenario(name, properties) {
23
+ return this.logger.trackScenario(name, this.withCustomEventsSource(properties));
24
+ }
25
+ flush() {
26
+ return this.logger.flush();
27
+ }
28
+ withCustomEventsSource(properties) {
29
+ return {
30
+ ...properties,
31
+ telemetry_source: 'custom'
32
+ };
33
+ }
34
+ }
35
+ let customEventsLogger = new CustomEventsLogger(new Logger());
36
+
37
+ /**
38
+ * Configures the custom-events singleton. Hosting infrastructure owns
39
+ * this function; application code should use getCustomEventsLogger().
40
+ */
41
+ export function configureCustomEventsLogger(logger) {
42
+ customEventsLogger = new CustomEventsLogger(logger);
43
+ }
44
+
45
+ /**
46
+ * Returns the app's custom-events logger. It is a safe no-op until the
47
+ * host configures an Application Insights provider.
48
+ */
49
+ export function getCustomEventsLogger() {
50
+ return customEventsLogger;
51
+ }