@microsoft/power-apps-native-common 0.1.8
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/LICENSE +112 -0
- package/README.md +15 -0
- package/lib/NOTICE.txt +179 -0
- package/lib/commonjs/TelemetryTypes.js +29 -0
- package/lib/commonjs/index.js +112 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/telemetry/AppLogger.js +49 -0
- package/lib/commonjs/telemetry/ConsoleAdapter.js +64 -0
- package/lib/commonjs/telemetry/ILogger.js +5 -0
- package/lib/commonjs/telemetry/Logger.js +151 -0
- package/lib/commonjs/telemetry/OneDsTelemetryProvider.js +276 -0
- package/lib/commonjs/telemetry/TelemetrySanitizer.js +100 -0
- package/lib/commonjs/telemetry/TelemetryTypes.js +29 -0
- package/lib/commonjs/telemetry/resolveTelemetryLocation.js +42 -0
- package/lib/commonjs/telemetry/stringifyError.js +31 -0
- package/lib/commonjs/telemetry/suppressKnownWarnings.js +28 -0
- package/lib/commonjs/types/ClusterCategory.js +54 -0
- package/lib/commonjs/utils/DeviceIdUtils.js +66 -0
- package/lib/commonjs/utils/GuidUtils.js +23 -0
- package/lib/commonjs/utils/PlatformInfoUtils.js +60 -0
- package/lib/module/TelemetryTypes.js +25 -0
- package/lib/module/index.js +18 -0
- package/lib/module/package.json +1 -0
- package/lib/module/telemetry/AppLogger.js +44 -0
- package/lib/module/telemetry/ConsoleAdapter.js +59 -0
- package/lib/module/telemetry/ILogger.js +3 -0
- package/lib/module/telemetry/Logger.js +146 -0
- package/lib/module/telemetry/OneDsTelemetryProvider.js +271 -0
- package/lib/module/telemetry/TelemetrySanitizer.js +94 -0
- package/lib/module/telemetry/TelemetryTypes.js +25 -0
- package/lib/module/telemetry/resolveTelemetryLocation.js +38 -0
- package/lib/module/telemetry/stringifyError.js +28 -0
- package/lib/module/telemetry/suppressKnownWarnings.js +25 -0
- package/lib/module/types/ClusterCategory.js +49 -0
- package/lib/module/utils/DeviceIdUtils.js +62 -0
- package/lib/module/utils/GuidUtils.js +19 -0
- package/lib/module/utils/PlatformInfoUtils.js +55 -0
- package/lib/typescript/commonjs/TelemetryTypes.d.ts +88 -0
- package/lib/typescript/commonjs/index.d.ts +19 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/commonjs/telemetry/AppLogger.d.ts +35 -0
- package/lib/typescript/commonjs/telemetry/ConsoleAdapter.d.ts +24 -0
- package/lib/typescript/commonjs/telemetry/ILogger.d.ts +45 -0
- package/lib/typescript/commonjs/telemetry/Logger.d.ts +28 -0
- package/lib/typescript/commonjs/telemetry/OneDsTelemetryProvider.d.ts +39 -0
- package/lib/typescript/commonjs/telemetry/TelemetrySanitizer.d.ts +21 -0
- package/lib/typescript/commonjs/telemetry/TelemetryTypes.d.ts +88 -0
- package/lib/typescript/commonjs/telemetry/resolveTelemetryLocation.d.ts +26 -0
- package/lib/typescript/commonjs/telemetry/stringifyError.d.ts +10 -0
- package/lib/typescript/commonjs/telemetry/suppressKnownWarnings.d.ts +19 -0
- package/lib/typescript/commonjs/types/ClusterCategory.d.ts +37 -0
- package/lib/typescript/commonjs/utils/DeviceIdUtils.d.ts +29 -0
- package/lib/typescript/commonjs/utils/GuidUtils.d.ts +11 -0
- package/lib/typescript/commonjs/utils/PlatformInfoUtils.d.ts +33 -0
- package/lib/typescript/module/TelemetryTypes.d.ts +88 -0
- package/lib/typescript/module/index.d.ts +19 -0
- package/lib/typescript/module/package.json +1 -0
- package/lib/typescript/module/telemetry/AppLogger.d.ts +35 -0
- package/lib/typescript/module/telemetry/ConsoleAdapter.d.ts +24 -0
- package/lib/typescript/module/telemetry/ILogger.d.ts +45 -0
- package/lib/typescript/module/telemetry/Logger.d.ts +28 -0
- package/lib/typescript/module/telemetry/OneDsTelemetryProvider.d.ts +39 -0
- package/lib/typescript/module/telemetry/TelemetrySanitizer.d.ts +21 -0
- package/lib/typescript/module/telemetry/TelemetryTypes.d.ts +88 -0
- package/lib/typescript/module/telemetry/resolveTelemetryLocation.d.ts +26 -0
- package/lib/typescript/module/telemetry/stringifyError.d.ts +10 -0
- package/lib/typescript/module/telemetry/suppressKnownWarnings.d.ts +19 -0
- package/lib/typescript/module/types/ClusterCategory.d.ts +37 -0
- package/lib/typescript/module/utils/DeviceIdUtils.d.ts +29 -0
- package/lib/typescript/module/utils/GuidUtils.d.ts +11 -0
- package/lib/typescript/module/utils/PlatformInfoUtils.d.ts +33 -0
- package/package.json +83 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.Logger = void 0;
|
|
7
|
+
var _GuidUtils = require("../utils/GuidUtils.js");
|
|
8
|
+
var _stringifyError = require("./stringifyError.js");
|
|
9
|
+
var _TelemetrySanitizer = require("./TelemetrySanitizer.js");
|
|
10
|
+
var _TelemetryTypes = require("./TelemetryTypes.js");
|
|
11
|
+
/*!
|
|
12
|
+
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const severityOrder = {
|
|
16
|
+
verbose: 0,
|
|
17
|
+
info: 1,
|
|
18
|
+
warning: 2,
|
|
19
|
+
error: 3
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Structured logger with canonical event names, severity filtering,
|
|
24
|
+
* sequence numbering, and scenario tracking.
|
|
25
|
+
* Supports multiple telemetry adapters; no-op safe when none are given.
|
|
26
|
+
*/
|
|
27
|
+
class Logger {
|
|
28
|
+
sequence = 0;
|
|
29
|
+
constructor(config) {
|
|
30
|
+
this.providers = config?.providers ?? [];
|
|
31
|
+
// Use the least-restrictive minSeverity among all providers, falling back to 'verbose'.
|
|
32
|
+
if (this.providers.length === 0) {
|
|
33
|
+
this.minSeverity = 'verbose';
|
|
34
|
+
} else {
|
|
35
|
+
const order = severityOrder;
|
|
36
|
+
this.minSeverity = this.providers.reduce((min, p) => {
|
|
37
|
+
const pSev = p.minSeverity ?? 'verbose';
|
|
38
|
+
return order[pSev] < order[min] ? pSev : min;
|
|
39
|
+
}, 'error');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
trackActivityEvent(name, properties, eventName) {
|
|
43
|
+
this.emit('info', eventName ?? _TelemetryTypes.TelemetryEventNames.Activity, name, properties);
|
|
44
|
+
}
|
|
45
|
+
trackVerboseEvent(name, properties, eventName) {
|
|
46
|
+
this.emit('verbose', eventName ?? _TelemetryTypes.TelemetryEventNames.Activity, name, properties);
|
|
47
|
+
}
|
|
48
|
+
trackErrorEvent(name, properties, eventName) {
|
|
49
|
+
this.emit('error', eventName ?? _TelemetryTypes.TelemetryEventNames.Error, name, properties);
|
|
50
|
+
}
|
|
51
|
+
trackScenario(name, properties) {
|
|
52
|
+
const startTime = typeof performance !== 'undefined' ? performance.now() : Date.now();
|
|
53
|
+
const scenarioGuid = (0, _GuidUtils.generateGuid)();
|
|
54
|
+
const scenarioProps = {
|
|
55
|
+
...properties,
|
|
56
|
+
scenarioName: name,
|
|
57
|
+
scenarioGuid
|
|
58
|
+
};
|
|
59
|
+
this.emit('info', _TelemetryTypes.TelemetryEventNames.StartScenario, name, scenarioProps);
|
|
60
|
+
return {
|
|
61
|
+
complete: extraProps => {
|
|
62
|
+
const duration = (typeof performance !== 'undefined' ? performance.now() : Date.now()) - startTime;
|
|
63
|
+
this.emit('info', _TelemetryTypes.TelemetryEventNames.EndScenario, name, {
|
|
64
|
+
...scenarioProps,
|
|
65
|
+
...extraProps,
|
|
66
|
+
scenarioResult: _TelemetryTypes.ScenarioResults.Success,
|
|
67
|
+
duration
|
|
68
|
+
});
|
|
69
|
+
},
|
|
70
|
+
completeWithError: extraProps => {
|
|
71
|
+
const duration = (typeof performance !== 'undefined' ? performance.now() : Date.now()) - startTime;
|
|
72
|
+
this.emit('warning', _TelemetryTypes.TelemetryEventNames.EndScenario, name, {
|
|
73
|
+
...scenarioProps,
|
|
74
|
+
...extraProps,
|
|
75
|
+
scenarioResult: _TelemetryTypes.ScenarioResults.SuccessWithErrors,
|
|
76
|
+
duration
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
fail: (error, extraProps) => {
|
|
80
|
+
const duration = (typeof performance !== 'undefined' ? performance.now() : Date.now()) - startTime;
|
|
81
|
+
this.emit('error', _TelemetryTypes.TelemetryEventNames.EndScenario, name, {
|
|
82
|
+
...scenarioProps,
|
|
83
|
+
...extraProps,
|
|
84
|
+
scenarioResult: _TelemetryTypes.ScenarioResults.Failed,
|
|
85
|
+
error: error !== undefined ? (0, _stringifyError.stringifyError)(error) : undefined,
|
|
86
|
+
duration
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
setDefaultProperty(name, value) {
|
|
92
|
+
for (const p of this.providers) p.setDefaultProperty(name, value);
|
|
93
|
+
}
|
|
94
|
+
identifyUser(identity) {
|
|
95
|
+
for (const p of this.providers) p.identifyUser(identity);
|
|
96
|
+
}
|
|
97
|
+
get sessionId() {
|
|
98
|
+
return this.providers[0]?.sessionId;
|
|
99
|
+
}
|
|
100
|
+
set sessionId(value) {
|
|
101
|
+
for (const p of this.providers) p.sessionId = value;
|
|
102
|
+
}
|
|
103
|
+
async flush() {
|
|
104
|
+
await Promise.all(this.providers.map(p => p.flush()));
|
|
105
|
+
}
|
|
106
|
+
shouldLog(severity) {
|
|
107
|
+
return severityOrder[severity] >= severityOrder[this.minSeverity];
|
|
108
|
+
}
|
|
109
|
+
emit(severity, eventName, operationName, properties) {
|
|
110
|
+
if (this.providers.length === 0) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (!this.shouldLog(severity)) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const flatProps = flattenProperties(properties);
|
|
117
|
+
flatProps.operationName = operationName;
|
|
118
|
+
const event = {
|
|
119
|
+
eventName,
|
|
120
|
+
timestamp: new Date().toISOString(),
|
|
121
|
+
severity,
|
|
122
|
+
sequenceNumber: ++this.sequence,
|
|
123
|
+
duration: typeof flatProps.duration === 'number' ? flatProps.duration : undefined,
|
|
124
|
+
properties: flatProps
|
|
125
|
+
};
|
|
126
|
+
for (const p of this.providers) {
|
|
127
|
+
// Per-adapter severity filtering
|
|
128
|
+
const adapterMin = p.minSeverity ?? 'verbose';
|
|
129
|
+
if (severityOrder[severity] >= severityOrder[adapterMin]) {
|
|
130
|
+
p.track(event);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Convert Record<string, unknown> to Record<string, string | number | boolean> by JSON-stringifying non-primitives. Applies PII sanitization before flattening. */
|
|
137
|
+
exports.Logger = Logger;
|
|
138
|
+
function flattenProperties(properties) {
|
|
139
|
+
const sanitized = properties ? (0, _TelemetrySanitizer.sanitizeProperties)(properties) : undefined;
|
|
140
|
+
const result = {};
|
|
141
|
+
if (!sanitized) return result;
|
|
142
|
+
for (const [key, value] of Object.entries(sanitized)) {
|
|
143
|
+
if (value === undefined) continue;
|
|
144
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
145
|
+
result[key] = value;
|
|
146
|
+
} else {
|
|
147
|
+
result[key] = JSON.stringify(value);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.OneDsTelemetryProvider = void 0;
|
|
7
|
+
var _applicationinsightsCoreJs = require("@microsoft/applicationinsights-core-js");
|
|
8
|
+
var _dsPostJs = require("@microsoft/1ds-post-js");
|
|
9
|
+
var _GuidUtils = require("../utils/GuidUtils.js");
|
|
10
|
+
var _PlatformInfoUtils = require("../utils/PlatformInfoUtils.js");
|
|
11
|
+
/*!
|
|
12
|
+
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** 1DS Aria project IDs shared by the webplayer and CLI. */
|
|
16
|
+
const AriaProjectIds = {
|
|
17
|
+
Production: '5c3a61f48d7c4522912765ae7465ab5c-fb3d6606-fe59-4d6b-9bb1-5e993d889672-7138',
|
|
18
|
+
NonProduction: '3fb21f3df5c44380bf452fd710f4d50c-0e3f82aa-6d27-41d8-b727-c96b28f99992-7504',
|
|
19
|
+
Mooncake: '1c1d139788f74386bd95e752dd781371-04b4424b-e5c1-46ae-8312-28e9fa5832c0-6923',
|
|
20
|
+
GccModerate: '0d7685a948a140789ac92933d3dc2d87-2dd0c1ce-8f10-41f0-a70a-2f9e51a851c8-6823',
|
|
21
|
+
GccHigh: 'd31229fb23b0406790b9332e5f465dfc-a6b19a0e-170d-4570-bda5-f35e3668e162-7067',
|
|
22
|
+
DoD: '41cc10dad35043adbcdace2896484ab4-5ebbc1d6-04c9-486d-a6b6-e5963cd778da-7542'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Default property-to-column promotion map matching the webplayer convention.
|
|
27
|
+
* Properties matching these keys are promoted to top-level data columns;
|
|
28
|
+
* the rest go into the customDimensions JSON blob.
|
|
29
|
+
*/
|
|
30
|
+
const DefaultPropertyToColumn = {
|
|
31
|
+
apiId: 'apiId',
|
|
32
|
+
appSequenceNumber: 'appSequenceNumber',
|
|
33
|
+
duration: 'duration',
|
|
34
|
+
environmentId: 'environmentId',
|
|
35
|
+
httpMethod: 'httpMethod',
|
|
36
|
+
httpStatus: 'httpStatus',
|
|
37
|
+
operationName: 'operation_Name',
|
|
38
|
+
playerAppId: 'playerAppId',
|
|
39
|
+
scenarioGuid: 'scenarioGuid',
|
|
40
|
+
scenarioName: 'scenarioName',
|
|
41
|
+
scenarioResult: 'scenarioResult',
|
|
42
|
+
serverLocation: 'serverLocation',
|
|
43
|
+
url: 'url',
|
|
44
|
+
webPlayerUrl: 'webPlayerUrl'
|
|
45
|
+
};
|
|
46
|
+
function resolveInstrumentationKey(clusterCategory) {
|
|
47
|
+
switch (clusterCategory) {
|
|
48
|
+
case 'prod':
|
|
49
|
+
case 'firstrelease':
|
|
50
|
+
case 'ex':
|
|
51
|
+
case 'rx':
|
|
52
|
+
return AriaProjectIds.Production;
|
|
53
|
+
case 'mooncake':
|
|
54
|
+
return AriaProjectIds.Mooncake;
|
|
55
|
+
case 'gov':
|
|
56
|
+
return AriaProjectIds.GccModerate;
|
|
57
|
+
case 'high':
|
|
58
|
+
return AriaProjectIds.GccHigh;
|
|
59
|
+
case 'dod':
|
|
60
|
+
return AriaProjectIds.DoD;
|
|
61
|
+
default:
|
|
62
|
+
return AriaProjectIds.NonProduction;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function resolveCollectorUrl(clusterCategory, telemetryLocation = 'global') {
|
|
66
|
+
switch (clusterCategory) {
|
|
67
|
+
case 'mooncake':
|
|
68
|
+
return 'https://collector.azure.cn/OneCollector/1.0/';
|
|
69
|
+
case 'gov':
|
|
70
|
+
case 'high':
|
|
71
|
+
return 'https://tb.events.data.microsoft.com/OneCollector/1.0/';
|
|
72
|
+
case 'dod':
|
|
73
|
+
return 'https://pf.events.data.microsoft.com/OneCollector/1.0/';
|
|
74
|
+
case 'ex':
|
|
75
|
+
return 'https://collector.azure.eaglex.ic.gov/OneCollector/1.0/';
|
|
76
|
+
case 'rx':
|
|
77
|
+
return 'https://collector.azure.microsoft.scloud/OneCollector/1.0/';
|
|
78
|
+
default:
|
|
79
|
+
if (telemetryLocation === 'eu') {
|
|
80
|
+
return 'https://eu-mobile.events.data.microsoft.com/OneCollector/1.0/';
|
|
81
|
+
}
|
|
82
|
+
return 'https://us-mobile.events.data.microsoft.com/OneCollector/1.0/';
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function resolveAppName(clusterCategory) {
|
|
86
|
+
switch (clusterCategory) {
|
|
87
|
+
case 'local':
|
|
88
|
+
case 'dev':
|
|
89
|
+
case 'test':
|
|
90
|
+
case 'preprod':
|
|
91
|
+
return 'powerappsclientnonprod';
|
|
92
|
+
default:
|
|
93
|
+
return 'powerappsclient';
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
class OneDsTelemetryProvider {
|
|
97
|
+
defaultProperties = {};
|
|
98
|
+
constructor(config) {
|
|
99
|
+
this._sessionId = (0, _GuidUtils.generateGuid)();
|
|
100
|
+
this.appName = resolveAppName(config.clusterCategory);
|
|
101
|
+
this.appVersion = config.appVersion ?? '';
|
|
102
|
+
this.clientType = config.clientType;
|
|
103
|
+
this.clusterCategory = config.clusterCategory;
|
|
104
|
+
this.deviceId = config.deviceId ?? 'react-native';
|
|
105
|
+
this.minSeverity = config.minSeverity ?? 'info';
|
|
106
|
+
const {
|
|
107
|
+
osName,
|
|
108
|
+
osVersion,
|
|
109
|
+
deviceMake,
|
|
110
|
+
deviceModel,
|
|
111
|
+
deviceType,
|
|
112
|
+
userLocale,
|
|
113
|
+
userTimeZone
|
|
114
|
+
} = (0, _PlatformInfoUtils.resolvePlatformInfo)();
|
|
115
|
+
this.osName = osName;
|
|
116
|
+
this.osVersion = osVersion;
|
|
117
|
+
this.deviceMake = deviceMake;
|
|
118
|
+
this.deviceModel = deviceModel;
|
|
119
|
+
this.deviceType = deviceType;
|
|
120
|
+
this.userLocale = userLocale;
|
|
121
|
+
this.userTimeZone = userTimeZone;
|
|
122
|
+
|
|
123
|
+
// Merge default promotion map with consumer overrides (overrides win)
|
|
124
|
+
this.propertyColumnMap = {
|
|
125
|
+
...DefaultPropertyToColumn,
|
|
126
|
+
...config.propertyColumnMap
|
|
127
|
+
};
|
|
128
|
+
if (config.commonProperties) {
|
|
129
|
+
Object.assign(this.defaultProperties, config.commonProperties);
|
|
130
|
+
}
|
|
131
|
+
const postChannel = new _dsPostJs.PostChannel();
|
|
132
|
+
const instrumentationKey = config.instrumentationKey ?? resolveInstrumentationKey(config.clusterCategory);
|
|
133
|
+
const collectorUrl = resolveCollectorUrl(config.clusterCategory, config.telemetryLocation);
|
|
134
|
+
const coreConfig = {
|
|
135
|
+
instrumentationKey,
|
|
136
|
+
channels: [[postChannel]]
|
|
137
|
+
};
|
|
138
|
+
coreConfig.extensionConfig = {
|
|
139
|
+
[postChannel.identifier]: {
|
|
140
|
+
// Override endpoint explicitly — PostChannel reads overrideEndpointUrl
|
|
141
|
+
// from its own extensionConfig, not from coreConfig.endpointUrl.
|
|
142
|
+
overrideEndpointUrl: collectorUrl,
|
|
143
|
+
// Use React Native's fetch instead of PostChannel's XHR path, which
|
|
144
|
+
// uses browser-specific CORS and Beacon APIs not available in RN.
|
|
145
|
+
httpXHROverride: {
|
|
146
|
+
sendPOST: (payload, oncomplete) => {
|
|
147
|
+
const headers = {
|
|
148
|
+
...(payload.headers ?? {})
|
|
149
|
+
};
|
|
150
|
+
fetch(payload.urlString, {
|
|
151
|
+
method: 'POST',
|
|
152
|
+
headers,
|
|
153
|
+
body: typeof payload.data === 'string' ? payload.data : payload.data
|
|
154
|
+
}).then(async r => {
|
|
155
|
+
oncomplete(r.status, {});
|
|
156
|
+
}).catch(err => {
|
|
157
|
+
console.warn('[OneDsTelemetryProvider] sendPOST FAILED', err);
|
|
158
|
+
oncomplete(0, {});
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
...config.channelConfig
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
this.core = new _applicationinsightsCoreJs.AppInsightsCore();
|
|
166
|
+
this.core.initialize(coreConfig, []);
|
|
167
|
+
this.core.addTelemetryInitializer(envelope => {
|
|
168
|
+
const ext = envelope.ext = envelope.ext ?? {};
|
|
169
|
+
const utc = ext['utc'] = ext['utc'] ?? {};
|
|
170
|
+
utc['eventFlags'] = (utc['eventFlags'] || 0) | 0x100000;
|
|
171
|
+
const app = ext['app'] = ext['app'] ?? {};
|
|
172
|
+
app['sesId'] = this._sessionId;
|
|
173
|
+
if (this.appVersion) app['ver'] = this.appVersion; // → AppInfo_Version
|
|
174
|
+
const os = ext['os'] = ext['os'] ?? {};
|
|
175
|
+
os['name'] = this.osName; // → DeviceInfo_OsName
|
|
176
|
+
os['ver'] = this.osVersion; // → DeviceInfo_OsVersion
|
|
177
|
+
// ext.device.make/model → DeviceInfo_Make / DeviceInfo_Model
|
|
178
|
+
// Note: localId is intentionally omitted — it requires a strict UUID format
|
|
179
|
+
// and our device_Id column in data carries the same info once resolved.
|
|
180
|
+
const device = ext['device'] = ext['device'] ?? {};
|
|
181
|
+
if (this.deviceMake) device['make'] = this.deviceMake;
|
|
182
|
+
if (this.deviceModel) device['model'] = this.deviceModel;
|
|
183
|
+
if (this.deviceType) device['deviceClass'] = this.deviceType;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Register a telemetry initializer that runs on every envelope before send.
|
|
189
|
+
* Use this to add Part A Common Schema extensions (ext.app, ext.web, ext.os, etc.)
|
|
190
|
+
* that require platform-specific APIs (e.g. browser info, OS info).
|
|
191
|
+
*/
|
|
192
|
+
addTelemetryInitializer(initializer) {
|
|
193
|
+
this.core.addTelemetryInitializer(initializer);
|
|
194
|
+
}
|
|
195
|
+
track(event) {
|
|
196
|
+
const allProperties = {
|
|
197
|
+
...this.defaultProperties,
|
|
198
|
+
...event.properties
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// Build data object with fixed columns matching webplayer convention
|
|
202
|
+
const data = {
|
|
203
|
+
app_Name: this.appName,
|
|
204
|
+
clientType: this.clientType,
|
|
205
|
+
clusterCategory: this.clusterCategory,
|
|
206
|
+
device_Id: this.deviceId,
|
|
207
|
+
DeviceInfo_Make: this.deviceMake,
|
|
208
|
+
...(this.deviceModel ? {
|
|
209
|
+
DeviceInfo_Model: this.deviceModel
|
|
210
|
+
} : {}),
|
|
211
|
+
userLocale: this.userLocale || undefined,
|
|
212
|
+
userTimeZone: this.userTimeZone || undefined,
|
|
213
|
+
event_Name: event.eventName,
|
|
214
|
+
sequenceNumber: event.sequenceNumber,
|
|
215
|
+
session_Id: this._sessionId,
|
|
216
|
+
severity: event.severity,
|
|
217
|
+
timestamp: event.timestamp
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// User identity promoted to top-level columns directly
|
|
221
|
+
if (this.userIdentity) {
|
|
222
|
+
data.objectId = this.userIdentity.objectId;
|
|
223
|
+
data.tenantId = this.userIdentity.tenantId;
|
|
224
|
+
}
|
|
225
|
+
if (event.duration !== undefined) {
|
|
226
|
+
data.duration = event.duration;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Promote properties matching the column map to top-level data fields
|
|
230
|
+
// Properties are kept in allProperties so they also appear in customDimensions
|
|
231
|
+
for (const [propKey, columnName] of Object.entries(this.propertyColumnMap)) {
|
|
232
|
+
if (propKey in allProperties) {
|
|
233
|
+
data[columnName] = allProperties[propKey];
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Serialize remaining properties as customDimensions
|
|
238
|
+
data.customDimensions = JSON.stringify(allProperties);
|
|
239
|
+
const item = {
|
|
240
|
+
// 'event' routes all events to the Kusto `event` table.
|
|
241
|
+
// The actual event name is stored in data.event_Name.
|
|
242
|
+
name: 'event',
|
|
243
|
+
time: event.timestamp,
|
|
244
|
+
baseType: 'custom',
|
|
245
|
+
data
|
|
246
|
+
};
|
|
247
|
+
this.core.track(item);
|
|
248
|
+
}
|
|
249
|
+
flush() {
|
|
250
|
+
return new Promise(resolve => {
|
|
251
|
+
this.core.flush(true, () => {
|
|
252
|
+
resolve();
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
teardown() {
|
|
257
|
+
return new Promise(resolve => {
|
|
258
|
+
this.core.unload(true, () => {
|
|
259
|
+
resolve();
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
get sessionId() {
|
|
264
|
+
return this._sessionId;
|
|
265
|
+
}
|
|
266
|
+
set sessionId(value) {
|
|
267
|
+
this._sessionId = value;
|
|
268
|
+
}
|
|
269
|
+
identifyUser(identity) {
|
|
270
|
+
this.userIdentity = identity;
|
|
271
|
+
}
|
|
272
|
+
setDefaultProperty(name, value) {
|
|
273
|
+
this.defaultProperties[name] = value;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
exports.OneDsTelemetryProvider = OneDsTelemetryProvider;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.sanitizeErrorString = sanitizeErrorString;
|
|
7
|
+
exports.sanitizeProperties = sanitizeProperties;
|
|
8
|
+
exports.sanitizeUrl = sanitizeUrl;
|
|
9
|
+
/*!
|
|
10
|
+
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Utilities for stripping PII and credentials from strings and property bags
|
|
15
|
+
* before they enter telemetry. All functions are pure and synchronous — safe
|
|
16
|
+
* to call on the hot path.
|
|
17
|
+
*
|
|
18
|
+
* Patterns detected (aligned with the CLI's ScrubUtils / CredScan KB):
|
|
19
|
+
* - Email / UPN addresses → '<upn>'
|
|
20
|
+
* - Private IPv4 addresses → '<private-ip>'
|
|
21
|
+
* - URL query strings / fragments → stripped (may carry tokens/user context)
|
|
22
|
+
* - Google API keys (AIza…) → '<credential>'
|
|
23
|
+
* - SAS key sig= parameter → '<credential>'
|
|
24
|
+
* - 256-bit symmetric keys (b64) → '<credential>'
|
|
25
|
+
*
|
|
26
|
+
* Property-bag rules:
|
|
27
|
+
* - Keys matching a PII/credential denylist → value replaced with '<redacted>'
|
|
28
|
+
* - String values run through sanitizeErrorString
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/** Matches email addresses / UPNs (simplified RFC 5322). */
|
|
32
|
+
const EMAIL_RE = /[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}/g;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Matches private / loopback IPv4 addresses (RFC 1918 + 127.0.0.0/8 +
|
|
36
|
+
* link-local 169.254.0.0/16).
|
|
37
|
+
*/
|
|
38
|
+
const PRIVATE_IPV4_RE = /\b(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3}|172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|127\.\d{1,3}\.\d{1,3}\.\d{1,3}|169\.254\.\d{1,3}\.\d{1,3})(:\d+)?\b/g;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Google API key pattern. Aligned with CredScan KB:
|
|
42
|
+
* https://msazure.visualstudio.com/One/_git/SecEng-CredScan-Core
|
|
43
|
+
*/
|
|
44
|
+
const GOOGLE_API_KEY_RE = /AIza[a-z0-9_\-]{35}/gi;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* SAS key sig= parameter. Aligned with CredScan KB (modified for JS):
|
|
48
|
+
* matches `sig=` or `dsas_secret=` followed by ≥8 non-whitespace chars
|
|
49
|
+
* (excluding delimiters that would indicate a false positive).
|
|
50
|
+
*/
|
|
51
|
+
const SAS_KEY_SIG_RE = /(?:dsas_secret|sig)=[^#)\(<>\r\n\s'",\\_&]{8}[^#)\(<>\r\n\s'",\\_&]*/gi;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 256-bit symmetric key in base64 (43 base64url chars + `=` pad).
|
|
55
|
+
* Aligned with CredScan KB SymmKey256 pattern.
|
|
56
|
+
*/
|
|
57
|
+
const SYMM_KEY_256_RE = /[a-z0-9/+]{43}=/gi;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Property key patterns that must never appear as telemetry values.
|
|
61
|
+
* Checked case-insensitively against the full key name.
|
|
62
|
+
*/
|
|
63
|
+
const SENSITIVE_KEY_RE = /password|secret|token|credential|auth(?:orization)?|bearer|apikey|api[_-]key|upn|email|username|accesstoken|refreshtoken|idtoken/i;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Redacts PII and credentials from a string.
|
|
67
|
+
* Safe to use on error messages, URL paths, and arbitrary text.
|
|
68
|
+
*/
|
|
69
|
+
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>');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Strips the query string and URL fragment from a URL string, then redacts any
|
|
75
|
+
* PII/credentials in the remaining path.
|
|
76
|
+
*/
|
|
77
|
+
function sanitizeUrl(url) {
|
|
78
|
+
const withoutQuery = url.replace(/[?#].*/, '');
|
|
79
|
+
return sanitizeErrorString(withoutQuery);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Returns a copy of `props` with PII and credentials scrubbed:
|
|
84
|
+
* - Keys matching SENSITIVE_KEY_RE → value replaced with '<redacted>'
|
|
85
|
+
* - String values run through sanitizeErrorString
|
|
86
|
+
* - Non-string primitives passed through unchanged
|
|
87
|
+
*/
|
|
88
|
+
function sanitizeProperties(props) {
|
|
89
|
+
const result = {};
|
|
90
|
+
for (const [key, value] of Object.entries(props)) {
|
|
91
|
+
if (SENSITIVE_KEY_RE.test(key)) {
|
|
92
|
+
result[key] = '<redacted>';
|
|
93
|
+
} else if (typeof value === 'string') {
|
|
94
|
+
result[key] = sanitizeErrorString(value);
|
|
95
|
+
} else {
|
|
96
|
+
result[key] = value;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.TelemetryEventNames = exports.ScenarioResults = void 0;
|
|
7
|
+
/*!
|
|
8
|
+
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** Canonical 1DS event names matching webplayer conventions. */
|
|
12
|
+
const TelemetryEventNames = exports.TelemetryEventNames = {
|
|
13
|
+
Activity: 'PowerAppsClient.Activity',
|
|
14
|
+
Error: 'PowerAppsClient.Error',
|
|
15
|
+
StartScenario: 'PowerAppsClient.StartScenario',
|
|
16
|
+
EndScenario: 'PowerAppsClient.EndScenario'
|
|
17
|
+
};
|
|
18
|
+
const ScenarioResults = exports.ScenarioResults = {
|
|
19
|
+
Success: 'Success',
|
|
20
|
+
Failed: 'Failed',
|
|
21
|
+
SuccessWithErrors: 'SuccessWithErrors'
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** Telemetry data residency location. EU routes to eu-mobile collector. */
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Structural type for the 1DS telemetry envelope passed to telemetry initializers.
|
|
28
|
+
* Decouples consumers from the internal `ITelemetryItem` type.
|
|
29
|
+
*/
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.resolveTelemetryLocation = resolveTelemetryLocation;
|
|
7
|
+
/*!
|
|
8
|
+
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Power Platform BAP environment `location` field values that fall under the
|
|
13
|
+
* EU data boundary. Mirrors `EUGeoRegions` from the CLI's `Logger/Constants.ts`.
|
|
14
|
+
*
|
|
15
|
+
* Source: `metadata.properties.location` in the BAP Get Environments API response.
|
|
16
|
+
*/
|
|
17
|
+
const EU_BAP_LOCATIONS = new Set(['europe', 'france', 'germany', 'italy', 'poland', 'norway', 'sweden', 'switzerland', 'unitedkingdom']);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the 1DS telemetry collector location from a Power Platform BAP
|
|
21
|
+
* environment `location` string (`environment.properties.location`).
|
|
22
|
+
* Returns `'eu'` when the location maps to the EU data boundary, routing
|
|
23
|
+
* telemetry to `eu-mobile.events.data.microsoft.com` instead of the global
|
|
24
|
+
* US collector.
|
|
25
|
+
*
|
|
26
|
+
* Mirrors the CLI's detection in `CliLogger.ts`:
|
|
27
|
+
* ```
|
|
28
|
+
* if (metadata?.properties?.location && EUGeoRegions.has(metadata.properties.location)) {
|
|
29
|
+
* telemetryLocation = 'eu';
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* resolveTelemetryLocation('europe') // → 'eu'
|
|
35
|
+
* resolveTelemetryLocation('unitedkingdom') // → 'eu'
|
|
36
|
+
* resolveTelemetryLocation('italy') // → 'eu'
|
|
37
|
+
* resolveTelemetryLocation('unitedstates') // → 'global'
|
|
38
|
+
*/
|
|
39
|
+
function resolveTelemetryLocation(input) {
|
|
40
|
+
if (!input) return 'global';
|
|
41
|
+
return EU_BAP_LOCATIONS.has(input.trim().toLowerCase()) ? 'eu' : 'global';
|
|
42
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.stringifyError = stringifyError;
|
|
7
|
+
var _TelemetrySanitizer = require("./TelemetrySanitizer.js");
|
|
8
|
+
/*!
|
|
9
|
+
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
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.
|
|
16
|
+
*/
|
|
17
|
+
function stringifyError(error) {
|
|
18
|
+
let raw;
|
|
19
|
+
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
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return (0, _TelemetrySanitizer.sanitizeErrorString)(raw);
|
|
31
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.suppressKnownWarnings = suppressKnownWarnings;
|
|
7
|
+
var _reactNative = require("react-native");
|
|
8
|
+
/*!
|
|
9
|
+
* Copyright (C) Microsoft Corporation. All rights reserved.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Suppresses known noisy LogBox warnings that originate from third-party
|
|
14
|
+
* packages and are not actionable in a React Native context.
|
|
15
|
+
*
|
|
16
|
+
* Call once at app startup (e.g. in the root `_layout.tsx` or `App.tsx`)
|
|
17
|
+
* before rendering any UI.
|
|
18
|
+
*
|
|
19
|
+
* **Why these warnings appear:**
|
|
20
|
+
* - `"Deprecated API for given entry type."` — emitted by React Native's
|
|
21
|
+
* Performance API when a caller requests an entry type (e.g. `"navigation"`,
|
|
22
|
+
* `"resource"`) that only exists in the browser W3C spec. The
|
|
23
|
+
* `@microsoft/power-apps` package calls `getEntriesByType('navigation')` for
|
|
24
|
+
* app-load telemetry; on React Native the call returns `[]` harmlessly.
|
|
25
|
+
*/
|
|
26
|
+
function suppressKnownWarnings() {
|
|
27
|
+
_reactNative.LogBox.ignoreLogs(['Deprecated API for given entry type.']);
|
|
28
|
+
}
|