@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.
Files changed (72) hide show
  1. package/LICENSE +112 -0
  2. package/README.md +15 -0
  3. package/lib/NOTICE.txt +179 -0
  4. package/lib/commonjs/TelemetryTypes.js +29 -0
  5. package/lib/commonjs/index.js +112 -0
  6. package/lib/commonjs/package.json +1 -0
  7. package/lib/commonjs/telemetry/AppLogger.js +49 -0
  8. package/lib/commonjs/telemetry/ConsoleAdapter.js +64 -0
  9. package/lib/commonjs/telemetry/ILogger.js +5 -0
  10. package/lib/commonjs/telemetry/Logger.js +151 -0
  11. package/lib/commonjs/telemetry/OneDsTelemetryProvider.js +276 -0
  12. package/lib/commonjs/telemetry/TelemetrySanitizer.js +100 -0
  13. package/lib/commonjs/telemetry/TelemetryTypes.js +29 -0
  14. package/lib/commonjs/telemetry/resolveTelemetryLocation.js +42 -0
  15. package/lib/commonjs/telemetry/stringifyError.js +31 -0
  16. package/lib/commonjs/telemetry/suppressKnownWarnings.js +28 -0
  17. package/lib/commonjs/types/ClusterCategory.js +54 -0
  18. package/lib/commonjs/utils/DeviceIdUtils.js +66 -0
  19. package/lib/commonjs/utils/GuidUtils.js +23 -0
  20. package/lib/commonjs/utils/PlatformInfoUtils.js +60 -0
  21. package/lib/module/TelemetryTypes.js +25 -0
  22. package/lib/module/index.js +18 -0
  23. package/lib/module/package.json +1 -0
  24. package/lib/module/telemetry/AppLogger.js +44 -0
  25. package/lib/module/telemetry/ConsoleAdapter.js +59 -0
  26. package/lib/module/telemetry/ILogger.js +3 -0
  27. package/lib/module/telemetry/Logger.js +146 -0
  28. package/lib/module/telemetry/OneDsTelemetryProvider.js +271 -0
  29. package/lib/module/telemetry/TelemetrySanitizer.js +94 -0
  30. package/lib/module/telemetry/TelemetryTypes.js +25 -0
  31. package/lib/module/telemetry/resolveTelemetryLocation.js +38 -0
  32. package/lib/module/telemetry/stringifyError.js +28 -0
  33. package/lib/module/telemetry/suppressKnownWarnings.js +25 -0
  34. package/lib/module/types/ClusterCategory.js +49 -0
  35. package/lib/module/utils/DeviceIdUtils.js +62 -0
  36. package/lib/module/utils/GuidUtils.js +19 -0
  37. package/lib/module/utils/PlatformInfoUtils.js +55 -0
  38. package/lib/typescript/commonjs/TelemetryTypes.d.ts +88 -0
  39. package/lib/typescript/commonjs/index.d.ts +19 -0
  40. package/lib/typescript/commonjs/package.json +1 -0
  41. package/lib/typescript/commonjs/telemetry/AppLogger.d.ts +35 -0
  42. package/lib/typescript/commonjs/telemetry/ConsoleAdapter.d.ts +24 -0
  43. package/lib/typescript/commonjs/telemetry/ILogger.d.ts +45 -0
  44. package/lib/typescript/commonjs/telemetry/Logger.d.ts +28 -0
  45. package/lib/typescript/commonjs/telemetry/OneDsTelemetryProvider.d.ts +39 -0
  46. package/lib/typescript/commonjs/telemetry/TelemetrySanitizer.d.ts +21 -0
  47. package/lib/typescript/commonjs/telemetry/TelemetryTypes.d.ts +88 -0
  48. package/lib/typescript/commonjs/telemetry/resolveTelemetryLocation.d.ts +26 -0
  49. package/lib/typescript/commonjs/telemetry/stringifyError.d.ts +10 -0
  50. package/lib/typescript/commonjs/telemetry/suppressKnownWarnings.d.ts +19 -0
  51. package/lib/typescript/commonjs/types/ClusterCategory.d.ts +37 -0
  52. package/lib/typescript/commonjs/utils/DeviceIdUtils.d.ts +29 -0
  53. package/lib/typescript/commonjs/utils/GuidUtils.d.ts +11 -0
  54. package/lib/typescript/commonjs/utils/PlatformInfoUtils.d.ts +33 -0
  55. package/lib/typescript/module/TelemetryTypes.d.ts +88 -0
  56. package/lib/typescript/module/index.d.ts +19 -0
  57. package/lib/typescript/module/package.json +1 -0
  58. package/lib/typescript/module/telemetry/AppLogger.d.ts +35 -0
  59. package/lib/typescript/module/telemetry/ConsoleAdapter.d.ts +24 -0
  60. package/lib/typescript/module/telemetry/ILogger.d.ts +45 -0
  61. package/lib/typescript/module/telemetry/Logger.d.ts +28 -0
  62. package/lib/typescript/module/telemetry/OneDsTelemetryProvider.d.ts +39 -0
  63. package/lib/typescript/module/telemetry/TelemetrySanitizer.d.ts +21 -0
  64. package/lib/typescript/module/telemetry/TelemetryTypes.d.ts +88 -0
  65. package/lib/typescript/module/telemetry/resolveTelemetryLocation.d.ts +26 -0
  66. package/lib/typescript/module/telemetry/stringifyError.d.ts +10 -0
  67. package/lib/typescript/module/telemetry/suppressKnownWarnings.d.ts +19 -0
  68. package/lib/typescript/module/types/ClusterCategory.d.ts +37 -0
  69. package/lib/typescript/module/utils/DeviceIdUtils.d.ts +29 -0
  70. package/lib/typescript/module/utils/GuidUtils.d.ts +11 -0
  71. package/lib/typescript/module/utils/PlatformInfoUtils.d.ts +33 -0
  72. package/package.json +83 -0
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Regions = void 0;
7
+ exports.regionToClusterCategory = regionToClusterCategory;
8
+ /*!
9
+ * Copyright (C) Microsoft Corporation. All rights reserved.
10
+ */
11
+
12
+ /**
13
+ * Identifies the Power Platform cluster environment category.
14
+ * Used to resolve telemetry endpoints, instrumentation keys, and collector URLs.
15
+ *
16
+ * This is the **normalised** internal form used by telemetry providers.
17
+ * The raw `region` string from power.config.json may differ — use
18
+ * `regionToClusterCategory()` to convert before passing here.
19
+ *
20
+ * Canonical power.config.json `region` values → ClusterCategory mapping:
21
+ * preview → prod
22
+ * gccmoderate → gov
23
+ * gcchigh → high
24
+ * (all others are identity mappings)
25
+ */
26
+
27
+ /**
28
+ * All valid `region` strings accepted in power.config.json.
29
+ * Mirrors the `Region` type from `@microsoft/power-apps-common/services`.
30
+ */
31
+ const Regions = exports.Regions = ['local', 'dev', 'test', 'preprod', 'preview', 'prod', 'firstrelease', 'gccmoderate', 'gcchigh', 'dod', 'mooncake', 'ex', 'rx'];
32
+ /**
33
+ * Normalise a power.config.json `region` string to the internal
34
+ * `ClusterCategory` used by telemetry providers.
35
+ *
36
+ * Mirrors the CLI's `regionToClusterCategory` in OneDSWriter.ts:
37
+ * preview → prod (preview ring uses production telemetry)
38
+ * gccmoderate → gov (GCC Moderate sovereign cloud)
39
+ * gcchigh → high (GCC High sovereign cloud)
40
+ * firstrelease → firstrelease (first-release prod ring)
41
+ * all others → identity
42
+ */
43
+ function regionToClusterCategory(region) {
44
+ switch (region) {
45
+ case 'preview':
46
+ return 'prod';
47
+ case 'gccmoderate':
48
+ return 'gov';
49
+ case 'gcchigh':
50
+ return 'high';
51
+ default:
52
+ return region;
53
+ }
54
+ }
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.resolveDeviceId = resolveDeviceId;
7
+ var Application = _interopRequireWildcard(require("expo-application"));
8
+ var SecureStore = _interopRequireWildcard(require("expo-secure-store"));
9
+ var _reactNative = require("react-native");
10
+ var _GuidUtils = require("./GuidUtils.js");
11
+ 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
+ /*!
13
+ * Copyright (C) Microsoft Corporation. All rights reserved.
14
+ */
15
+
16
+ // Fallback key used when the platform native ID is unavailable.
17
+ // Only alphanumeric, dots, hyphens, and underscores are valid for SecureStore.
18
+ const DEVICE_ID_FALLBACK_KEY = 'microsoft.power-apps-native-common.device_id';
19
+
20
+ /**
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.
33
+ *
34
+ * Pass the resolved value to `OneDsTelemetryProviderConfig.deviceId` at startup.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * import { resolveDeviceId, OneDsTelemetryProvider } from '@microsoft/power-apps-native-common';
39
+ *
40
+ * const deviceId = await resolveDeviceId();
41
+ * const telemetry = new OneDsTelemetryProvider({ deviceId, ... });
42
+ * ```
43
+ */
44
+ 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
+ const existing = await SecureStore.getItemAsync(DEVICE_ID_FALLBACK_KEY);
60
+ if (existing) {
61
+ return existing;
62
+ }
63
+ const id = (0, _GuidUtils.generateGuid)();
64
+ await SecureStore.setItemAsync(DEVICE_ID_FALLBACK_KEY, id);
65
+ return id;
66
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.generateGuid = generateGuid;
7
+ require("react-native-get-random-values");
8
+ var _uuid = require("uuid");
9
+ /*!
10
+ * Copyright (C) Microsoft Corporation. All rights reserved.
11
+ */
12
+
13
+ // Must be imported before 'uuid' so that the crypto.getRandomValues polyfill is
14
+ // in place when uuid initialises on Hermes/JSC (React Native).
15
+
16
+ /**
17
+ * Generates a RFC-4122 v4 GUID/UUID string.
18
+ * Uses the `uuid` package (backed by `react-native-get-random-values` on RN)
19
+ * instead of `crypto.randomUUID()` which is unavailable in Hermes/JSC.
20
+ */
21
+ function generateGuid() {
22
+ return (0, _uuid.v4)();
23
+ }
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.resolvePlatformInfo = resolvePlatformInfo;
7
+ var Device = _interopRequireWildcard(require("expo-device"));
8
+ var _reactNative = require("react-native");
9
+ 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); }
10
+ /*!
11
+ * Copyright (C) Microsoft Corporation. All rights reserved.
12
+ */
13
+
14
+ // Maps expo-device DeviceType numeric enum to a readable string.
15
+ const DEVICE_TYPE_LABEL = {
16
+ 0: 'Unknown',
17
+ 1: 'Phone',
18
+ 2: 'Tablet',
19
+ 3: 'Desktop',
20
+ 4: 'TV'
21
+ };
22
+
23
+ /**
24
+ * Resolves 1DS Part A Common Schema device/OS/locale values using `expo-device`
25
+ * and the Intl API. Provides more accurate data than raw Platform.constants:
26
+ *
27
+ * - Correctly returns `'iPadOS'` for iPad (not `'iOS'`).
28
+ * - Returns human-friendly `modelName` (e.g. `'iPhone 16 Pro'`, `'Pixel 9'`).
29
+ * - Returns proper `manufacturer` (e.g. `'Apple'`, `'Google'`, `'Samsung'`).
30
+ * - Includes form-factor `deviceType` (Phone / Tablet / Desktop / TV).
31
+ *
32
+ * Safe to call once at provider construction time — all values are stable for
33
+ * the lifetime of the process.
34
+ */
35
+ function resolvePlatformInfo() {
36
+ // expo-device returns 'iOS' or 'iPadOS' on Apple platforms.
37
+ // On Android, osName maps to Build.VERSION.BASE_OS which can be a build
38
+ // fingerprint on some OEMs, so we always use the literal 'Android' there.
39
+ const osName = _reactNative.Platform.OS === 'ios' ? Device.osName ?? 'iOS' : 'Android';
40
+ const osVersion = Device.osVersion ?? String(_reactNative.Platform.Version);
41
+ const deviceMake = Device.manufacturer ?? (_reactNative.Platform.OS === 'ios' ? 'Apple' : '');
42
+ const deviceModel = Device.modelName ?? '';
43
+ const deviceType = DEVICE_TYPE_LABEL[Device.deviceType ?? 0] ?? 'Unknown';
44
+ let userLocale = '';
45
+ let userTimeZone = '';
46
+ try {
47
+ const dtf = Intl.DateTimeFormat().resolvedOptions();
48
+ userLocale = dtf.locale;
49
+ userTimeZone = dtf.timeZone;
50
+ } catch {/* Intl unavailable — leave empty */}
51
+ return {
52
+ osName,
53
+ osVersion,
54
+ deviceMake,
55
+ deviceModel,
56
+ deviceType,
57
+ userLocale,
58
+ userTimeZone
59
+ };
60
+ }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+
3
+ /*!
4
+ * Copyright (C) Microsoft Corporation. All rights reserved.
5
+ */
6
+
7
+ /** Canonical 1DS event names matching webplayer conventions. */
8
+ export const TelemetryEventNames = {
9
+ Activity: 'PowerAppsClient.Activity',
10
+ Error: 'PowerAppsClient.Error',
11
+ StartScenario: 'PowerAppsClient.StartScenario',
12
+ EndScenario: 'PowerAppsClient.EndScenario'
13
+ };
14
+ export const ScenarioResults = {
15
+ Success: 'Success',
16
+ Failed: 'Failed',
17
+ SuccessWithErrors: 'SuccessWithErrors'
18
+ };
19
+
20
+ /** Telemetry data residency location. EU routes to eu-mobile collector. */
21
+
22
+ /**
23
+ * Structural type for the 1DS telemetry envelope passed to telemetry initializers.
24
+ * Decouples consumers from the internal `ITelemetryItem` type.
25
+ */
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+
3
+ /*!
4
+ * power-apps-native-common
5
+ * Shared utilities and types for Power Apps native packages
6
+ */
7
+
8
+ export { ConsoleAdapter } from "./telemetry/ConsoleAdapter.js";
9
+ export { Logger } from "./telemetry/Logger.js";
10
+ export { OneDsTelemetryProvider } from "./telemetry/OneDsTelemetryProvider.js";
11
+ export { resolveTelemetryLocation } from "./telemetry/resolveTelemetryLocation.js";
12
+ export { stringifyError } from "./telemetry/stringifyError.js";
13
+ export { sanitizeErrorString, sanitizeUrl, sanitizeProperties } from "./telemetry/TelemetrySanitizer.js";
14
+ export { suppressKnownWarnings } from "./telemetry/suppressKnownWarnings.js";
15
+ export { resolveDeviceId } from "./utils/DeviceIdUtils.js";
16
+ export { configureAppLogger, getAppLogger } from "./telemetry/AppLogger.js";
17
+ export { ScenarioResults, TelemetryEventNames } from "./telemetry/TelemetryTypes.js";
18
+ export { Regions, regionToClusterCategory } from "./types/ClusterCategory.js";
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+
3
+ /*!
4
+ * Copyright (C) Microsoft Corporation. All rights reserved.
5
+ *
6
+ * power-apps-native-common — app-level logger singleton
7
+ *
8
+ * Provides a single shared `ILogger` instance for the entire app process.
9
+ * The logger starts as a no-op (no providers) and is configured once at
10
+ * startup by the hosting infrastructure (e.g. `PowerAppsProvider`).
11
+ *
12
+ * App code and library packages call `getAppLogger()` to emit telemetry
13
+ * without coupling to a specific provider or needing to pass a logger around.
14
+ *
15
+ * Usage in a library:
16
+ *
17
+ * import { getAppLogger } from '@microsoft/power-apps-native-common';
18
+ * getAppLogger().trackActivityEvent('MyLib.SomeOperation', { … });
19
+ *
20
+ * Usage in the host / root layout (once, at startup):
21
+ *
22
+ * import { configureAppLogger } from '@microsoft/power-apps-native-common';
23
+ * configureAppLogger(new Logger({ providers: [myProvider] }));
24
+ */
25
+
26
+ import { Logger } from "./Logger.js";
27
+ let _logger = new Logger(); // no-op until configured
28
+
29
+ /**
30
+ * Replace the shared app logger with a configured instance.
31
+ * All subsequent `getAppLogger()` calls return the new instance.
32
+ * Idempotent — calling again replaces the previous logger.
33
+ */
34
+ export function configureAppLogger(logger) {
35
+ _logger = logger;
36
+ }
37
+
38
+ /**
39
+ * Returns the shared app-level logger.
40
+ * No-op (no providers) until `configureAppLogger` is called.
41
+ */
42
+ export function getAppLogger() {
43
+ return _logger;
44
+ }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+
3
+ /*!
4
+ * Copyright (C) Microsoft Corporation. All rights reserved.
5
+ */
6
+
7
+ import { generateGuid } from "../utils/GuidUtils.js";
8
+ /**
9
+ * A telemetry adapter that writes events to the JavaScript console.
10
+ * Useful during development and for unit-testing telemetry flows.
11
+ * Can be replaced in production by the native telemetry adapter.
12
+ */
13
+ export class ConsoleAdapter {
14
+ defaultProperties = {};
15
+ constructor(minSeverity = 'verbose') {
16
+ this.minSeverity = minSeverity;
17
+ this._sessionId = generateGuid();
18
+ }
19
+ track(event) {
20
+ const payload = {
21
+ ...event.properties,
22
+ sessionId: this._sessionId,
23
+ ...this.defaultProperties,
24
+ ...(this.userIdentity ?? {})
25
+ };
26
+ const label = `[${event.severity.toUpperCase()}] ${event.eventName} | ${event.properties['operationName'] ?? ''} (#${event.sequenceNumber})`;
27
+ switch (event.severity) {
28
+ case 'error':
29
+ console.error(label, payload);
30
+ break;
31
+ case 'warning':
32
+ console.warn(label, payload);
33
+ break;
34
+ case 'verbose':
35
+ console.debug(label, payload);
36
+ break;
37
+ default:
38
+ console.log(label, payload);
39
+ }
40
+ }
41
+ flush() {
42
+ return Promise.resolve();
43
+ }
44
+ teardown() {
45
+ return Promise.resolve();
46
+ }
47
+ get sessionId() {
48
+ return this._sessionId;
49
+ }
50
+ set sessionId(value) {
51
+ this._sessionId = value;
52
+ }
53
+ identifyUser(identity) {
54
+ this.userIdentity = identity;
55
+ }
56
+ setDefaultProperty(name, value) {
57
+ this.defaultProperties[name] = value;
58
+ }
59
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+
3
+ export {};
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+
3
+ /*!
4
+ * Copyright (C) Microsoft Corporation. All rights reserved.
5
+ */
6
+
7
+ import { generateGuid } from "../utils/GuidUtils.js";
8
+ import { stringifyError } from "./stringifyError.js";
9
+ import { sanitizeProperties } from "./TelemetrySanitizer.js";
10
+ import { ScenarioResults, TelemetryEventNames } from "./TelemetryTypes.js";
11
+ const severityOrder = {
12
+ verbose: 0,
13
+ info: 1,
14
+ warning: 2,
15
+ error: 3
16
+ };
17
+
18
+ /**
19
+ * Structured logger with canonical event names, severity filtering,
20
+ * sequence numbering, and scenario tracking.
21
+ * Supports multiple telemetry adapters; no-op safe when none are given.
22
+ */
23
+ export class Logger {
24
+ sequence = 0;
25
+ constructor(config) {
26
+ this.providers = config?.providers ?? [];
27
+ // Use the least-restrictive minSeverity among all providers, falling back to 'verbose'.
28
+ if (this.providers.length === 0) {
29
+ this.minSeverity = 'verbose';
30
+ } else {
31
+ const order = severityOrder;
32
+ this.minSeverity = this.providers.reduce((min, p) => {
33
+ const pSev = p.minSeverity ?? 'verbose';
34
+ return order[pSev] < order[min] ? pSev : min;
35
+ }, 'error');
36
+ }
37
+ }
38
+ trackActivityEvent(name, properties, eventName) {
39
+ this.emit('info', eventName ?? TelemetryEventNames.Activity, name, properties);
40
+ }
41
+ trackVerboseEvent(name, properties, eventName) {
42
+ this.emit('verbose', eventName ?? TelemetryEventNames.Activity, name, properties);
43
+ }
44
+ trackErrorEvent(name, properties, eventName) {
45
+ this.emit('error', eventName ?? TelemetryEventNames.Error, name, properties);
46
+ }
47
+ trackScenario(name, properties) {
48
+ const startTime = typeof performance !== 'undefined' ? performance.now() : Date.now();
49
+ const scenarioGuid = generateGuid();
50
+ const scenarioProps = {
51
+ ...properties,
52
+ scenarioName: name,
53
+ scenarioGuid
54
+ };
55
+ this.emit('info', TelemetryEventNames.StartScenario, name, scenarioProps);
56
+ return {
57
+ complete: extraProps => {
58
+ const duration = (typeof performance !== 'undefined' ? performance.now() : Date.now()) - startTime;
59
+ this.emit('info', TelemetryEventNames.EndScenario, name, {
60
+ ...scenarioProps,
61
+ ...extraProps,
62
+ scenarioResult: ScenarioResults.Success,
63
+ duration
64
+ });
65
+ },
66
+ completeWithError: extraProps => {
67
+ const duration = (typeof performance !== 'undefined' ? performance.now() : Date.now()) - startTime;
68
+ this.emit('warning', TelemetryEventNames.EndScenario, name, {
69
+ ...scenarioProps,
70
+ ...extraProps,
71
+ scenarioResult: ScenarioResults.SuccessWithErrors,
72
+ duration
73
+ });
74
+ },
75
+ fail: (error, extraProps) => {
76
+ const duration = (typeof performance !== 'undefined' ? performance.now() : Date.now()) - startTime;
77
+ this.emit('error', TelemetryEventNames.EndScenario, name, {
78
+ ...scenarioProps,
79
+ ...extraProps,
80
+ scenarioResult: ScenarioResults.Failed,
81
+ error: error !== undefined ? stringifyError(error) : undefined,
82
+ duration
83
+ });
84
+ }
85
+ };
86
+ }
87
+ setDefaultProperty(name, value) {
88
+ for (const p of this.providers) p.setDefaultProperty(name, value);
89
+ }
90
+ identifyUser(identity) {
91
+ for (const p of this.providers) p.identifyUser(identity);
92
+ }
93
+ get sessionId() {
94
+ return this.providers[0]?.sessionId;
95
+ }
96
+ set sessionId(value) {
97
+ for (const p of this.providers) p.sessionId = value;
98
+ }
99
+ async flush() {
100
+ await Promise.all(this.providers.map(p => p.flush()));
101
+ }
102
+ shouldLog(severity) {
103
+ return severityOrder[severity] >= severityOrder[this.minSeverity];
104
+ }
105
+ emit(severity, eventName, operationName, properties) {
106
+ if (this.providers.length === 0) {
107
+ return;
108
+ }
109
+ if (!this.shouldLog(severity)) {
110
+ return;
111
+ }
112
+ const flatProps = flattenProperties(properties);
113
+ flatProps.operationName = operationName;
114
+ const event = {
115
+ eventName,
116
+ timestamp: new Date().toISOString(),
117
+ severity,
118
+ sequenceNumber: ++this.sequence,
119
+ duration: typeof flatProps.duration === 'number' ? flatProps.duration : undefined,
120
+ properties: flatProps
121
+ };
122
+ for (const p of this.providers) {
123
+ // Per-adapter severity filtering
124
+ const adapterMin = p.minSeverity ?? 'verbose';
125
+ if (severityOrder[severity] >= severityOrder[adapterMin]) {
126
+ p.track(event);
127
+ }
128
+ }
129
+ }
130
+ }
131
+
132
+ /** Convert Record<string, unknown> to Record<string, string | number | boolean> by JSON-stringifying non-primitives. Applies PII sanitization before flattening. */
133
+ function flattenProperties(properties) {
134
+ const sanitized = properties ? sanitizeProperties(properties) : undefined;
135
+ const result = {};
136
+ if (!sanitized) return result;
137
+ for (const [key, value] of Object.entries(sanitized)) {
138
+ if (value === undefined) continue;
139
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
140
+ result[key] = value;
141
+ } else {
142
+ result[key] = JSON.stringify(value);
143
+ }
144
+ }
145
+ return result;
146
+ }