@webex/internal-plugin-metrics 3.12.0 → 3.13.0-next.1
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/.repo-context.json +466 -0
- package/.sdd/manifest.json +626 -0
- package/AGENTS.md +208 -0
- package/README.md +274 -0
- package/dist/automated-user.js +19 -0
- package/dist/automated-user.js.map +1 -0
- package/dist/batcher.js +3 -0
- package/dist/batcher.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +639 -66
- package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.js +240 -103
- package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js +7 -2
- package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
- package/dist/call-diagnostic/config.js +19 -4
- package/dist/call-diagnostic/config.js.map +1 -1
- package/dist/config.js +8 -0
- package/dist/config.js.map +1 -1
- package/dist/index.js +25 -1
- package/dist/index.js.map +1 -1
- package/dist/metrics.js +118 -5
- package/dist/metrics.js.map +1 -1
- package/dist/metrics.types.js +4 -0
- package/dist/metrics.types.js.map +1 -1
- package/dist/network-telemetry.js +610 -0
- package/dist/network-telemetry.js.map +1 -0
- package/dist/new-metrics.js +63 -17
- package/dist/new-metrics.js.map +1 -1
- package/dist/privacy-and-security-permission-enricher/constants.js +18 -0
- package/dist/privacy-and-security-permission-enricher/constants.js.map +1 -0
- package/dist/privacy-and-security-permission-enricher/index.js +143 -0
- package/dist/privacy-and-security-permission-enricher/index.js.map +1 -0
- package/dist/privacy-and-security-permission-enricher/types.js +7 -0
- package/dist/privacy-and-security-permission-enricher/types.js.map +1 -0
- package/dist/privacy-and-security-permission-enricher/utils.js +71 -0
- package/dist/privacy-and-security-permission-enricher/utils.js.map +1 -0
- package/dist/types/automated-user.d.ts +2 -0
- package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +208 -5
- package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +83 -17
- package/dist/types/call-diagnostic/config.d.ts +6 -0
- package/dist/types/config.d.ts +9 -0
- package/dist/types/index.d.ts +5 -3
- package/dist/types/metrics.types.d.ts +11 -5
- package/dist/types/network-telemetry.d.ts +108 -0
- package/dist/types/new-metrics.d.ts +17 -1
- package/dist/types/privacy-and-security-permission-enricher/constants.d.ts +6 -0
- package/dist/types/privacy-and-security-permission-enricher/index.d.ts +40 -0
- package/dist/types/privacy-and-security-permission-enricher/types.d.ts +14 -0
- package/dist/types/privacy-and-security-permission-enricher/utils.d.ts +5 -0
- package/dist/types/unhandled-exception-telemetry/index.d.ts +60 -0
- package/dist/types/unhandled-exception-telemetry/utils.d.ts +6 -0
- package/dist/unhandled-exception-telemetry/index.js +331 -0
- package/dist/unhandled-exception-telemetry/index.js.map +1 -0
- package/dist/unhandled-exception-telemetry/utils.js +105 -0
- package/dist/unhandled-exception-telemetry/utils.js.map +1 -0
- package/docs/adr/index.md +27 -0
- package/docs/architecture.md +437 -0
- package/docs/getting-started.md +131 -0
- package/docs/index.md +52 -0
- package/docs/specs/README.md +88 -0
- package/package.json +12 -11
- package/schemas/repo-context.schema.json +796 -0
- package/src/automated-user.ts +16 -0
- package/src/batcher.js +4 -0
- package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +801 -74
- package/src/call-diagnostic/call-diagnostic-metrics.ts +184 -42
- package/src/call-diagnostic/call-diagnostic-metrics.util.ts +7 -1
- package/src/call-diagnostic/config.ts +16 -0
- package/src/call-diagnostic/docs/README.md +535 -0
- package/src/config.js +8 -0
- package/src/docs/README.md +703 -0
- package/src/index.ts +16 -0
- package/src/metrics.js +123 -5
- package/src/metrics.types.ts +29 -6
- package/src/network-telemetry.ts +757 -0
- package/src/new-metrics.ts +56 -6
- package/src/privacy-and-security-permission-enricher/constants.ts +33 -0
- package/src/privacy-and-security-permission-enricher/docs/README.md +356 -0
- package/src/privacy-and-security-permission-enricher/index.ts +142 -0
- package/src/privacy-and-security-permission-enricher/types.ts +21 -0
- package/src/privacy-and-security-permission-enricher/utils.ts +82 -0
- package/src/rtcMetrics/docs/README.md +447 -0
- package/src/unhandled-exception-telemetry/docs/README.md +536 -0
- package/src/unhandled-exception-telemetry/index.ts +404 -0
- package/src/unhandled-exception-telemetry/utils.ts +101 -0
- package/test/unit/spec/automated-user.ts +43 -0
- package/test/unit/spec/batcher.js +43 -0
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +14 -2
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +1435 -304
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +1265 -207
- package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +27 -0
- package/test/unit/spec/metrics.js +14 -5
- package/test/unit/spec/network-telemetry.ts +610 -0
- package/test/unit/spec/new-metrics.ts +203 -50
- package/test/unit/spec/prelogin-metrics-batcher.ts +71 -36
- package/test/unit/spec/privacy-and-security-permission-enricher.ts +318 -0
- package/test/unit/spec/unhandled-exception-telemetry/utils.ts +109 -0
- package/test/unit/spec/unhandled-exception-telemetry.ts +689 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type {ClientEventPayload, PrivacyAndSecurityPermission} from '../metrics.types';
|
|
2
|
+
import {
|
|
3
|
+
CAMERA_AND_MICROPHONE_PERMISSION_EVENTS,
|
|
4
|
+
CONTENT_SHARE_PERMISSION_EVENTS,
|
|
5
|
+
FINAL_PERMISSION_EVENTS,
|
|
6
|
+
MEDIA_TX_PERMISSION_EVENTS,
|
|
7
|
+
NO_PERMISSION_ENRICHMENT,
|
|
8
|
+
} from './constants';
|
|
9
|
+
import type {
|
|
10
|
+
PermissionEnrichmentContext,
|
|
11
|
+
PermissionEnrichmentPolicy,
|
|
12
|
+
PermissionEnrichmentRule,
|
|
13
|
+
} from './types';
|
|
14
|
+
import {
|
|
15
|
+
getChangedPermission,
|
|
16
|
+
projectPrivacyAndSecurityPermission,
|
|
17
|
+
resolveContentShareResources,
|
|
18
|
+
resolveMediaResources,
|
|
19
|
+
} from './utils';
|
|
20
|
+
|
|
21
|
+
export const PERMISSION_ENRICHMENT_RULES = [
|
|
22
|
+
{
|
|
23
|
+
events: CAMERA_AND_MICROPHONE_PERMISSION_EVENTS,
|
|
24
|
+
resolve: () => ({resources: ['camera', 'microphone'], terminal: false}),
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
events: MEDIA_TX_PERMISSION_EVENTS,
|
|
28
|
+
resolve: (payload) => ({resources: resolveMediaResources(payload), terminal: false}),
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
events: CONTENT_SHARE_PERMISSION_EVENTS,
|
|
32
|
+
resolve: (payload) => ({
|
|
33
|
+
resources: resolveContentShareResources(payload),
|
|
34
|
+
terminal: false,
|
|
35
|
+
}),
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
events: FINAL_PERMISSION_EVENTS,
|
|
39
|
+
resolve: () => ({resources: ['camera', 'microphone', 'contentShare'], terminal: true}),
|
|
40
|
+
},
|
|
41
|
+
] satisfies readonly PermissionEnrichmentRule[];
|
|
42
|
+
|
|
43
|
+
const resolvePermissionEnrichmentPolicy = (
|
|
44
|
+
name: PermissionEnrichmentContext['name'],
|
|
45
|
+
payload?: ClientEventPayload
|
|
46
|
+
): PermissionEnrichmentPolicy =>
|
|
47
|
+
PERMISSION_ENRICHMENT_RULES.find(({events}) => events.has(name))?.resolve(payload) ??
|
|
48
|
+
NO_PERMISSION_ENRICHMENT;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Enriches eligible client events with relevant browser permission changes.
|
|
52
|
+
*/
|
|
53
|
+
export default class PrivacyAndSecurityPermissionEnricher {
|
|
54
|
+
private permission?: PrivacyAndSecurityPermission;
|
|
55
|
+
|
|
56
|
+
private lastReported = new Map<string, PrivacyAndSecurityPermission>();
|
|
57
|
+
|
|
58
|
+
private readonly onEnrichmentError: (error: unknown) => void;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Creates a permission enricher.
|
|
62
|
+
* @param {Function} onEnrichmentError permission enrichment error handler
|
|
63
|
+
*/
|
|
64
|
+
constructor(onEnrichmentError: (error: unknown) => void) {
|
|
65
|
+
this.onEnrichmentError = onEnrichmentError;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Stores the latest normalized browser permission state.
|
|
70
|
+
* @param {PrivacyAndSecurityPermission} permission permission snapshot
|
|
71
|
+
* @returns {void}
|
|
72
|
+
*/
|
|
73
|
+
public setPermission(permission: PrivacyAndSecurityPermission): void {
|
|
74
|
+
this.permission = projectPrivacyAndSecurityPermission(permission, [
|
|
75
|
+
'camera',
|
|
76
|
+
'microphone',
|
|
77
|
+
'contentShare',
|
|
78
|
+
]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Returns the original payload or a copy enriched with relevant permission changes.
|
|
83
|
+
* @param {PermissionEnrichmentContext} context event context and permission history scope
|
|
84
|
+
* @returns {ClientEventPayload | undefined}
|
|
85
|
+
*/
|
|
86
|
+
public enrich({
|
|
87
|
+
name,
|
|
88
|
+
payload,
|
|
89
|
+
scope,
|
|
90
|
+
}: PermissionEnrichmentContext): ClientEventPayload | undefined {
|
|
91
|
+
const policy = resolvePermissionEnrichmentPolicy(name, payload);
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
if (payload?.privacyAndSecurityPermission !== undefined) {
|
|
95
|
+
// An explicitly supplied permission payload is authoritative for this event.
|
|
96
|
+
if (!policy.terminal) {
|
|
97
|
+
this.lastReported.set(scope, {
|
|
98
|
+
...this.lastReported.get(scope),
|
|
99
|
+
...(payload.privacyAndSecurityPermission as PrivacyAndSecurityPermission),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return payload;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (policy.resources.length === 0) {
|
|
107
|
+
return payload;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const projectedPermission = this.permission
|
|
111
|
+
? projectPrivacyAndSecurityPermission(this.permission, policy.resources)
|
|
112
|
+
: undefined;
|
|
113
|
+
|
|
114
|
+
if (!projectedPermission) {
|
|
115
|
+
return payload;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (policy.terminal) {
|
|
119
|
+
return {...payload, privacyAndSecurityPermission: projectedPermission};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const lastReported = this.lastReported.get(scope) ?? {};
|
|
123
|
+
const changedPermission = getChangedPermission(projectedPermission, lastReported);
|
|
124
|
+
|
|
125
|
+
if (!changedPermission) {
|
|
126
|
+
return payload;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
this.lastReported.set(scope, {...lastReported, ...changedPermission});
|
|
130
|
+
|
|
131
|
+
return {...payload, privacyAndSecurityPermission: changedPermission};
|
|
132
|
+
} catch (error) {
|
|
133
|
+
this.onEnrichmentError(error);
|
|
134
|
+
|
|
135
|
+
return payload;
|
|
136
|
+
} finally {
|
|
137
|
+
if (policy.terminal) {
|
|
138
|
+
this.lastReported.delete(scope);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ClientEvent,
|
|
3
|
+
ClientEventPayload,
|
|
4
|
+
PrivacyAndSecurityPermissionResource,
|
|
5
|
+
} from '../metrics.types';
|
|
6
|
+
|
|
7
|
+
export type PermissionEnrichmentPolicy = {
|
|
8
|
+
resources: readonly PrivacyAndSecurityPermissionResource[];
|
|
9
|
+
terminal: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type PermissionEnrichmentRule = {
|
|
13
|
+
events: ReadonlySet<ClientEvent['name']>;
|
|
14
|
+
resolve: (payload?: ClientEventPayload) => PermissionEnrichmentPolicy;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type PermissionEnrichmentContext = {
|
|
18
|
+
name: ClientEvent['name'];
|
|
19
|
+
payload?: ClientEventPayload;
|
|
20
|
+
scope: string;
|
|
21
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {isEqual} from 'lodash';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
ClientEventPayload,
|
|
5
|
+
PrivacyAndSecurityPermission,
|
|
6
|
+
PrivacyAndSecurityPermissionResource,
|
|
7
|
+
} from '../metrics.types';
|
|
8
|
+
|
|
9
|
+
export const resolveMediaResources = (
|
|
10
|
+
payload?: ClientEventPayload
|
|
11
|
+
): PrivacyAndSecurityPermissionResource[] => {
|
|
12
|
+
switch (payload?.mediaType) {
|
|
13
|
+
case 'audio':
|
|
14
|
+
return ['microphone'];
|
|
15
|
+
case 'video':
|
|
16
|
+
return ['camera'];
|
|
17
|
+
case 'share':
|
|
18
|
+
return ['contentShare'];
|
|
19
|
+
default:
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const resolveContentShareResources = (
|
|
25
|
+
payload?: ClientEventPayload
|
|
26
|
+
): PrivacyAndSecurityPermissionResource[] => {
|
|
27
|
+
if (payload?.mediaType !== 'share') {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return ['contentShare'];
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const copyPermissionState = (
|
|
35
|
+
source: PrivacyAndSecurityPermission,
|
|
36
|
+
target: PrivacyAndSecurityPermission,
|
|
37
|
+
resource: PrivacyAndSecurityPermissionResource
|
|
38
|
+
): void => {
|
|
39
|
+
switch (resource) {
|
|
40
|
+
case 'camera':
|
|
41
|
+
if (source.camera) {
|
|
42
|
+
target.camera = {...source.camera};
|
|
43
|
+
}
|
|
44
|
+
break;
|
|
45
|
+
case 'microphone':
|
|
46
|
+
if (source.microphone) {
|
|
47
|
+
target.microphone = {...source.microphone};
|
|
48
|
+
}
|
|
49
|
+
break;
|
|
50
|
+
case 'contentShare':
|
|
51
|
+
if (source.contentShare) {
|
|
52
|
+
target.contentShare = {...source.contentShare};
|
|
53
|
+
}
|
|
54
|
+
break;
|
|
55
|
+
default:
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const projectPrivacyAndSecurityPermission = (
|
|
61
|
+
permission: PrivacyAndSecurityPermission,
|
|
62
|
+
resources: readonly PrivacyAndSecurityPermissionResource[]
|
|
63
|
+
): PrivacyAndSecurityPermission | undefined => {
|
|
64
|
+
const projectedPermission: PrivacyAndSecurityPermission = {};
|
|
65
|
+
|
|
66
|
+
resources.forEach((resource) => {
|
|
67
|
+
copyPermissionState(permission, projectedPermission, resource);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return Object.keys(projectedPermission).length > 0 ? projectedPermission : undefined;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const getChangedPermission = (
|
|
74
|
+
current: PrivacyAndSecurityPermission,
|
|
75
|
+
previous: PrivacyAndSecurityPermission
|
|
76
|
+
): PrivacyAndSecurityPermission | undefined => {
|
|
77
|
+
const changedResources = (Object.keys(current) as PrivacyAndSecurityPermissionResource[]).filter(
|
|
78
|
+
(resource) => !isEqual(current[resource], previous[resource])
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
return projectPrivacyAndSecurityPermission(current, changedResources);
|
|
82
|
+
};
|