@xh/hoist 73.0.0-SNAPSHOT.1744325910318 → 73.0.0-SNAPSHOT.1744386127895
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/CHANGELOG.md +1 -0
- package/admin/tabs/activity/tracking/ActivityTrackingModel.ts +22 -14
- package/build/types/admin/tabs/activity/tracking/ActivityTrackingModel.d.ts +1 -1
- package/build/types/security/Types.d.ts +0 -32
- package/build/types/security/msal/MsalClient.d.ts +40 -4
- package/build/types/svc/ClientHealthService.d.ts +2 -1
- package/build/types/svc/WebSocketService.d.ts +35 -14
- package/data/filter/BaseFilterFieldSpec.ts +6 -2
- package/package.json +1 -1
- package/security/Types.ts +0 -34
- package/security/msal/MsalClient.ts +46 -9
- package/svc/ClientHealthService.ts +4 -3
- package/svc/WebSocketService.ts +67 -31
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
* Added option from the Admin Console > Websockets tab to request a client health report from any
|
|
8
8
|
connected clients.
|
|
9
|
+
* Enabled telemetry reporting from `WebSocketService`.
|
|
9
10
|
* Updated `MenuItem.actionFn()` to receive the click event as an additional argument.
|
|
10
11
|
|
|
11
12
|
## v72.4.0 - 2025-04-09
|
|
@@ -5,17 +5,17 @@
|
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import {exportFilename} from '@xh/hoist/admin/AdminUtils';
|
|
8
|
-
import
|
|
8
|
+
import * as Col from '@xh/hoist/admin/columns';
|
|
9
9
|
import {FilterChooserModel} from '@xh/hoist/cmp/filter';
|
|
10
10
|
import {FormModel} from '@xh/hoist/cmp/form';
|
|
11
11
|
import {GridModel, TreeStyle} from '@xh/hoist/cmp/grid';
|
|
12
|
+
import {GroupingChooserModel} from '@xh/hoist/cmp/grouping';
|
|
12
13
|
import {HoistModel, LoadSpec, managed, XH} from '@xh/hoist/core';
|
|
13
14
|
import {Cube, CubeFieldSpec, FieldSpec} from '@xh/hoist/data';
|
|
14
15
|
import {fmtNumber} from '@xh/hoist/format';
|
|
15
16
|
import {action, computed, makeObservable} from '@xh/hoist/mobx';
|
|
16
17
|
import {LocalDate} from '@xh/hoist/utils/datetime';
|
|
17
|
-
import
|
|
18
|
-
import {isEmpty, round} from 'lodash';
|
|
18
|
+
import {compact, isEmpty, round} from 'lodash';
|
|
19
19
|
import moment from 'moment';
|
|
20
20
|
|
|
21
21
|
export const PERSIST_ACTIVITY = {localStorageKey: 'xhAdminActivityState'};
|
|
@@ -109,14 +109,13 @@ export class ActivityTrackingModel extends HoistModel {
|
|
|
109
109
|
] as CubeFieldSpec[]
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
-
const enableValues = true;
|
|
113
112
|
this.filterChooserModel = new FilterChooserModel({
|
|
114
113
|
fieldSpecs: [
|
|
115
|
-
{field: 'category'
|
|
114
|
+
{field: 'category'},
|
|
116
115
|
{field: 'correlationId'},
|
|
117
|
-
{field: 'username', displayName: 'User'
|
|
118
|
-
{field: 'device'
|
|
119
|
-
{field: 'browser'
|
|
116
|
+
{field: 'username', displayName: 'User'},
|
|
117
|
+
{field: 'device'},
|
|
118
|
+
{field: 'browser'},
|
|
120
119
|
{
|
|
121
120
|
field: 'elapsed',
|
|
122
121
|
valueRenderer: v => {
|
|
@@ -330,12 +329,21 @@ export class ActivityTrackingModel extends HoistModel {
|
|
|
330
329
|
}
|
|
331
330
|
|
|
332
331
|
private async loadLookupsAsync() {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
332
|
+
try {
|
|
333
|
+
const lookups = await XH.fetchJson({url: 'trackLogAdmin/lookups'});
|
|
334
|
+
this.filterChooserModel.fieldSpecs.forEach(spec => {
|
|
335
|
+
const {field} = spec,
|
|
336
|
+
lookup = lookups[field] ? compact(lookups[field]) : null;
|
|
337
|
+
|
|
338
|
+
if (!isEmpty(lookup)) {
|
|
339
|
+
spec.values = lookup;
|
|
340
|
+
spec.enableValues = true;
|
|
341
|
+
spec.hasExplicitValues = true;
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
} catch (e) {
|
|
345
|
+
XH.handleException(e, {title: 'Error loading lookups for filtering'});
|
|
346
|
+
}
|
|
339
347
|
}
|
|
340
348
|
|
|
341
349
|
@computed
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { GroupingChooserModel } from '@xh/hoist/cmp/grouping';
|
|
2
1
|
import { FilterChooserModel } from '@xh/hoist/cmp/filter';
|
|
3
2
|
import { FormModel } from '@xh/hoist/cmp/form';
|
|
4
3
|
import { GridModel } from '@xh/hoist/cmp/grid';
|
|
4
|
+
import { GroupingChooserModel } from '@xh/hoist/cmp/grouping';
|
|
5
5
|
import { HoistModel, LoadSpec } from '@xh/hoist/core';
|
|
6
6
|
import { Cube } from '@xh/hoist/data';
|
|
7
7
|
import { LocalDate } from '@xh/hoist/utils/datetime';
|
|
@@ -12,35 +12,3 @@ export interface AccessTokenSpec {
|
|
|
12
12
|
scopes: string[];
|
|
13
13
|
}
|
|
14
14
|
export type TokenMap = Record<string, Token>;
|
|
15
|
-
/** Aggregated telemetry results, produced by {@link MsalClient} when enabled via config. */
|
|
16
|
-
export interface TelemetryResults {
|
|
17
|
-
/** Stats across all events */
|
|
18
|
-
summary: {
|
|
19
|
-
successCount: number;
|
|
20
|
-
failureCount: number;
|
|
21
|
-
maxDuration: number;
|
|
22
|
-
lastFailureTime: number;
|
|
23
|
-
};
|
|
24
|
-
/** Stats by event type */
|
|
25
|
-
events: Record<string, TelemetryEventResults>;
|
|
26
|
-
}
|
|
27
|
-
/** Aggregated telemetry results for a single type of event. */
|
|
28
|
-
export interface TelemetryEventResults {
|
|
29
|
-
firstTime: number;
|
|
30
|
-
lastTime: number;
|
|
31
|
-
successCount: number;
|
|
32
|
-
failureCount: number;
|
|
33
|
-
/** Timing info (in ms) for event instances reported with duration. */
|
|
34
|
-
duration?: {
|
|
35
|
-
count: number;
|
|
36
|
-
total: number;
|
|
37
|
-
average: number;
|
|
38
|
-
max: number;
|
|
39
|
-
};
|
|
40
|
-
lastFailure?: {
|
|
41
|
-
time: number;
|
|
42
|
-
duration: number;
|
|
43
|
-
code: string;
|
|
44
|
-
name: string;
|
|
45
|
-
};
|
|
46
|
-
}
|
|
@@ -3,7 +3,7 @@ import { LogLevel } from '@azure/msal-browser';
|
|
|
3
3
|
import { PlainObject } from '@xh/hoist/core';
|
|
4
4
|
import { Token } from '@xh/hoist/security/Token';
|
|
5
5
|
import { BaseOAuthClient, BaseOAuthClientConfig } from '../BaseOAuthClient';
|
|
6
|
-
import { AccessTokenSpec,
|
|
6
|
+
import { AccessTokenSpec, TokenMap } from '../Types';
|
|
7
7
|
export interface MsalClientConfig extends BaseOAuthClientConfig<MsalTokenSpec> {
|
|
8
8
|
/**
|
|
9
9
|
* Authority for your organization's tenant: `https://login.microsoftonline.com/[tenantId]`.
|
|
@@ -19,8 +19,7 @@ export interface MsalClientConfig extends BaseOAuthClientConfig<MsalTokenSpec> {
|
|
|
19
19
|
domainHint?: string;
|
|
20
20
|
/**
|
|
21
21
|
* True to enable support for built-in telemetry provided by this class's internal MSAL client.
|
|
22
|
-
* Captured performance events will be summarized
|
|
23
|
-
* See https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/performance.md
|
|
22
|
+
* Captured performance events will be summarized as {@link MsalClientTelemetry}.
|
|
24
23
|
*/
|
|
25
24
|
enableTelemetry?: boolean;
|
|
26
25
|
/**
|
|
@@ -88,7 +87,7 @@ export declare class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTo
|
|
|
88
87
|
private account;
|
|
89
88
|
private initialTokenLoad;
|
|
90
89
|
/** Enable telemetry via `enableTelemetry` ctor config, or via {@link enableTelemetry}. */
|
|
91
|
-
|
|
90
|
+
telemetry: MsalClientTelemetry;
|
|
92
91
|
private _telemetryCbHandle;
|
|
93
92
|
constructor(config: MsalClientConfig);
|
|
94
93
|
protected doInitAsync(): Promise<TokenMap>;
|
|
@@ -108,3 +107,40 @@ export declare class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTo
|
|
|
108
107
|
private get refreshOffsetArgs();
|
|
109
108
|
private noteUserAuthenticated;
|
|
110
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Telemetry produced by this client (if enabled) + included in {@link ClientHealthService}
|
|
112
|
+
* reporting. Leverages MSAL's opt-in support for emitting performance events.
|
|
113
|
+
* See https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/performance.md
|
|
114
|
+
*/
|
|
115
|
+
interface MsalClientTelemetry {
|
|
116
|
+
/** Stats across all events */
|
|
117
|
+
summary: {
|
|
118
|
+
successCount: number;
|
|
119
|
+
failureCount: number;
|
|
120
|
+
maxDuration: number;
|
|
121
|
+
lastFailureTime: number;
|
|
122
|
+
};
|
|
123
|
+
/** Stats by event type */
|
|
124
|
+
events: Record<string, MsalEventTelemetry>;
|
|
125
|
+
}
|
|
126
|
+
/** Aggregated telemetry results for a single type of event. */
|
|
127
|
+
interface MsalEventTelemetry {
|
|
128
|
+
firstTime: number;
|
|
129
|
+
lastTime: number;
|
|
130
|
+
successCount: number;
|
|
131
|
+
failureCount: number;
|
|
132
|
+
/** Timing info (in ms) for event instances reported with duration. */
|
|
133
|
+
duration?: {
|
|
134
|
+
count: number;
|
|
135
|
+
total: number;
|
|
136
|
+
average: number;
|
|
137
|
+
max: number;
|
|
138
|
+
};
|
|
139
|
+
lastFailure?: {
|
|
140
|
+
time: number;
|
|
141
|
+
duration: number;
|
|
142
|
+
code: string;
|
|
143
|
+
name: string;
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HoistService, PageState, PlainObject } from '@xh/hoist/core';
|
|
2
|
+
import { WebSocketTelemetry } from '@xh/hoist/svc/WebSocketService';
|
|
2
3
|
/**
|
|
3
4
|
* Service for gathering data about the current state and health of the client app, for submission
|
|
4
5
|
* to the server or review on the console during interactive troubleshooting.
|
|
@@ -42,7 +43,6 @@ export interface GeneralData {
|
|
|
42
43
|
durationMins: number;
|
|
43
44
|
idleMins: number;
|
|
44
45
|
pageState: PageState;
|
|
45
|
-
webSocket: string;
|
|
46
46
|
}
|
|
47
47
|
export interface ConnectionData {
|
|
48
48
|
downlink: number;
|
|
@@ -60,4 +60,5 @@ export interface ClientHealthReport {
|
|
|
60
60
|
general: GeneralData;
|
|
61
61
|
connection: ConnectionData;
|
|
62
62
|
memory: MemoryData;
|
|
63
|
+
webSockets: WebSocketTelemetry;
|
|
63
64
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HoistService } from '@xh/hoist/core';
|
|
1
|
+
import { HoistService, PlainObject } from '@xh/hoist/core';
|
|
2
2
|
/**
|
|
3
3
|
* Establishes and maintains a websocket connection to the Hoist server, if enabled via `AppSpec`.
|
|
4
4
|
*
|
|
@@ -28,6 +28,8 @@ export declare class WebSocketService extends HoistService {
|
|
|
28
28
|
readonly REG_SUCCESS_TOPIC = "xhRegistrationSuccess";
|
|
29
29
|
readonly FORCE_APP_SUSPEND_TOPIC = "xhForceAppSuspend";
|
|
30
30
|
readonly REQ_CLIENT_HEALTH_RPT_TOPIC = "xhRequestClientHealthReport";
|
|
31
|
+
/** True if WebSockets generally enabled - set statically in code via {@link AppSpec}. */
|
|
32
|
+
enabled: boolean;
|
|
31
33
|
/** Unique channel assigned by server upon successful connection. */
|
|
32
34
|
channelKey: string;
|
|
33
35
|
/** Last time a message was received, including heartbeat messages. */
|
|
@@ -36,10 +38,10 @@ export declare class WebSocketService extends HoistService {
|
|
|
36
38
|
get connected(): boolean;
|
|
37
39
|
/** Set to true to log all sent/received messages - very chatty. */
|
|
38
40
|
logMessages: boolean;
|
|
41
|
+
telemetry: WebSocketTelemetry;
|
|
39
42
|
private _timer;
|
|
40
43
|
private _socket;
|
|
41
44
|
private _subsByTopic;
|
|
42
|
-
enabled: boolean;
|
|
43
45
|
constructor();
|
|
44
46
|
initAsync(): Promise<void>;
|
|
45
47
|
/**
|
|
@@ -62,23 +64,24 @@ export declare class WebSocketService extends HoistService {
|
|
|
62
64
|
* Send a message back to the server via the connected websocket.
|
|
63
65
|
*/
|
|
64
66
|
sendMessage(message: WebSocketMessage): void;
|
|
65
|
-
connect(): void;
|
|
66
|
-
disconnect(): void;
|
|
67
|
-
heartbeatOrReconnect(): void;
|
|
68
|
-
private onServerInstanceChange;
|
|
69
67
|
shutdown(): void;
|
|
68
|
+
getFormattedTelemetry(): PlainObject;
|
|
69
|
+
private connect;
|
|
70
|
+
private disconnect;
|
|
71
|
+
private heartbeatOrReconnect;
|
|
72
|
+
private onServerInstanceChange;
|
|
70
73
|
onOpen(ev: any): void;
|
|
71
74
|
onClose(ev: any): void;
|
|
72
75
|
onError(ev: any): void;
|
|
73
76
|
onMessage(rawMsg: MessageEvent): void;
|
|
74
|
-
notifySubscribers
|
|
75
|
-
getSubsForTopic
|
|
76
|
-
updateConnectedStatus
|
|
77
|
-
installChannelKey
|
|
78
|
-
updateLastMessageTime
|
|
79
|
-
buildWebSocketUrl
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
private notifySubscribers;
|
|
78
|
+
private getSubsForTopic;
|
|
79
|
+
private updateConnectedStatus;
|
|
80
|
+
private installChannelKey;
|
|
81
|
+
private updateLastMessageTime;
|
|
82
|
+
private buildWebSocketUrl;
|
|
83
|
+
private maybeLogMessage;
|
|
84
|
+
private noteTelemetryEvent;
|
|
82
85
|
}
|
|
83
86
|
/**
|
|
84
87
|
* Wrapper class to encapsulate and manage a subscription to messages for a given topic + handler.
|
|
@@ -94,3 +97,21 @@ export interface WebSocketMessage {
|
|
|
94
97
|
topic: string;
|
|
95
98
|
data?: any;
|
|
96
99
|
}
|
|
100
|
+
/** Telemetry collected by this service + included in {@link ClientHealthService} reporting. */
|
|
101
|
+
export interface WebSocketTelemetry {
|
|
102
|
+
channelKey: string;
|
|
103
|
+
subscriptionCount: number;
|
|
104
|
+
events: {
|
|
105
|
+
connOpened?: WebSocketEventTelemetry;
|
|
106
|
+
connClosed?: WebSocketEventTelemetry;
|
|
107
|
+
connError?: WebSocketEventTelemetry;
|
|
108
|
+
msgReceived?: WebSocketEventTelemetry;
|
|
109
|
+
msgSent?: WebSocketEventTelemetry;
|
|
110
|
+
heartbeatReconnectAttempt?: WebSocketEventTelemetry;
|
|
111
|
+
instanceChangeReconnectAttempt?: WebSocketEventTelemetry;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export interface WebSocketEventTelemetry {
|
|
115
|
+
count: number;
|
|
116
|
+
lastTime: number;
|
|
117
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import {HoistBase} from '@xh/hoist/core';
|
|
8
8
|
import {Field, Store, FieldFilter, FieldType, genDisplayName, View} from '@xh/hoist/data';
|
|
9
|
-
import {isEmpty} from 'lodash';
|
|
9
|
+
import {compact, isArray, isEmpty} from 'lodash';
|
|
10
10
|
import {FieldFilterOperator} from './Types';
|
|
11
11
|
|
|
12
12
|
export interface BaseFilterFieldSpecConfig {
|
|
@@ -72,7 +72,11 @@ export abstract class BaseFilterFieldSpec extends HoistBase {
|
|
|
72
72
|
this.displayName = displayName ?? sourceField?.displayName ?? genDisplayName(field);
|
|
73
73
|
this.ops = this.parseOperators(ops);
|
|
74
74
|
this.forceSelection = forceSelection ?? false;
|
|
75
|
-
this.values = values
|
|
75
|
+
this.values = isArray(values)
|
|
76
|
+
? compact(values)
|
|
77
|
+
: this.isBoolFieldType
|
|
78
|
+
? [true, false]
|
|
79
|
+
: null;
|
|
76
80
|
this.hasExplicitValues = !isEmpty(this.values);
|
|
77
81
|
this.enableValues = this.hasExplicitValues || (enableValues ?? this.isEnumerableByDefault);
|
|
78
82
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xh/hoist",
|
|
3
|
-
"version": "73.0.0-SNAPSHOT.
|
|
3
|
+
"version": "73.0.0-SNAPSHOT.1744386127895",
|
|
4
4
|
"description": "Hoist add-on for building and deploying React Applications.",
|
|
5
5
|
"repository": "github:xh/hoist-react",
|
|
6
6
|
"homepage": "https://xh.io",
|
package/security/Types.ts
CHANGED
|
@@ -22,37 +22,3 @@ export interface AccessTokenSpec {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export type TokenMap = Record<string, Token>;
|
|
25
|
-
|
|
26
|
-
/** Aggregated telemetry results, produced by {@link MsalClient} when enabled via config. */
|
|
27
|
-
export interface TelemetryResults {
|
|
28
|
-
/** Stats across all events */
|
|
29
|
-
summary: {
|
|
30
|
-
successCount: number;
|
|
31
|
-
failureCount: number;
|
|
32
|
-
maxDuration: number;
|
|
33
|
-
lastFailureTime: number;
|
|
34
|
-
};
|
|
35
|
-
/** Stats by event type */
|
|
36
|
-
events: Record<string, TelemetryEventResults>;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** Aggregated telemetry results for a single type of event. */
|
|
40
|
-
export interface TelemetryEventResults {
|
|
41
|
-
firstTime: number;
|
|
42
|
-
lastTime: number;
|
|
43
|
-
successCount: number;
|
|
44
|
-
failureCount: number;
|
|
45
|
-
/** Timing info (in ms) for event instances reported with duration. */
|
|
46
|
-
duration?: {
|
|
47
|
-
count: number;
|
|
48
|
-
total: number;
|
|
49
|
-
average: number;
|
|
50
|
-
max: number;
|
|
51
|
-
};
|
|
52
|
-
lastFailure?: {
|
|
53
|
-
time: number;
|
|
54
|
-
duration: number;
|
|
55
|
-
code: string;
|
|
56
|
-
name: string;
|
|
57
|
-
};
|
|
58
|
-
}
|
|
@@ -20,7 +20,7 @@ import {logDebug, logError, logInfo, logWarn, mergeDeep, throwIf} from '@xh/hois
|
|
|
20
20
|
import {withFormattedTimestamps} from '@xh/hoist/format';
|
|
21
21
|
import {flatMap, union, uniq} from 'lodash';
|
|
22
22
|
import {BaseOAuthClient, BaseOAuthClientConfig} from '../BaseOAuthClient';
|
|
23
|
-
import {AccessTokenSpec,
|
|
23
|
+
import {AccessTokenSpec, TokenMap} from '../Types';
|
|
24
24
|
|
|
25
25
|
export interface MsalClientConfig extends BaseOAuthClientConfig<MsalTokenSpec> {
|
|
26
26
|
/**
|
|
@@ -39,8 +39,7 @@ export interface MsalClientConfig extends BaseOAuthClientConfig<MsalTokenSpec> {
|
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* True to enable support for built-in telemetry provided by this class's internal MSAL client.
|
|
42
|
-
* Captured performance events will be summarized
|
|
43
|
-
* See https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/performance.md
|
|
42
|
+
* Captured performance events will be summarized as {@link MsalClientTelemetry}.
|
|
44
43
|
*/
|
|
45
44
|
enableTelemetry?: boolean;
|
|
46
45
|
|
|
@@ -115,7 +114,7 @@ export class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTokenSpec>
|
|
|
115
114
|
private initialTokenLoad: boolean;
|
|
116
115
|
|
|
117
116
|
/** Enable telemetry via `enableTelemetry` ctor config, or via {@link enableTelemetry}. */
|
|
118
|
-
|
|
117
|
+
telemetry: MsalClientTelemetry = null;
|
|
119
118
|
private _telemetryCbHandle: string = null;
|
|
120
119
|
|
|
121
120
|
constructor(config: MsalClientConfig) {
|
|
@@ -268,7 +267,7 @@ export class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTokenSpec>
|
|
|
268
267
|
// Telemetry
|
|
269
268
|
//------------------------
|
|
270
269
|
getFormattedTelemetry(): PlainObject {
|
|
271
|
-
return withFormattedTimestamps(this.
|
|
270
|
+
return withFormattedTimestamps(this.telemetry);
|
|
272
271
|
}
|
|
273
272
|
|
|
274
273
|
enableTelemetry(): void {
|
|
@@ -277,7 +276,7 @@ export class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTokenSpec>
|
|
|
277
276
|
return;
|
|
278
277
|
}
|
|
279
278
|
|
|
280
|
-
this.
|
|
279
|
+
this.telemetry = {
|
|
281
280
|
summary: {
|
|
282
281
|
successCount: 0,
|
|
283
282
|
failureCount: 0,
|
|
@@ -290,7 +289,7 @@ export class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTokenSpec>
|
|
|
290
289
|
this._telemetryCbHandle = this.client.addPerformanceCallback(events => {
|
|
291
290
|
events.forEach(e => {
|
|
292
291
|
try {
|
|
293
|
-
const {summary, events} = this.
|
|
292
|
+
const {summary, events} = this.telemetry,
|
|
294
293
|
{name, startTimeMs, durationMs, success, errorName, errorCode} = e,
|
|
295
294
|
eTime = startTimeMs ?? Date.now();
|
|
296
295
|
|
|
@@ -339,10 +338,10 @@ export class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTokenSpec>
|
|
|
339
338
|
// Wait for clientHealthService (this client likely initialized during earlier AUTHENTICATING.)
|
|
340
339
|
this.addReaction({
|
|
341
340
|
when: () => XH.appState === AppState.INITIALIZING_APP,
|
|
342
|
-
run: () => XH.clientHealthService.addSource('msalClient', () => this.
|
|
341
|
+
run: () => XH.clientHealthService.addSource('msalClient', () => this.telemetry)
|
|
343
342
|
});
|
|
344
343
|
|
|
345
|
-
this.
|
|
344
|
+
this.logDebug('Telemetry enabled');
|
|
346
345
|
}
|
|
347
346
|
|
|
348
347
|
disableTelemetry(): void {
|
|
@@ -449,3 +448,41 @@ export class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTokenSpec>
|
|
|
449
448
|
this.logDebug('User Authenticated', account.username);
|
|
450
449
|
}
|
|
451
450
|
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Telemetry produced by this client (if enabled) + included in {@link ClientHealthService}
|
|
454
|
+
* reporting. Leverages MSAL's opt-in support for emitting performance events.
|
|
455
|
+
* See https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/performance.md
|
|
456
|
+
*/
|
|
457
|
+
interface MsalClientTelemetry {
|
|
458
|
+
/** Stats across all events */
|
|
459
|
+
summary: {
|
|
460
|
+
successCount: number;
|
|
461
|
+
failureCount: number;
|
|
462
|
+
maxDuration: number;
|
|
463
|
+
lastFailureTime: number;
|
|
464
|
+
};
|
|
465
|
+
/** Stats by event type */
|
|
466
|
+
events: Record<string, MsalEventTelemetry>;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/** Aggregated telemetry results for a single type of event. */
|
|
470
|
+
interface MsalEventTelemetry {
|
|
471
|
+
firstTime: number;
|
|
472
|
+
lastTime: number;
|
|
473
|
+
successCount: number;
|
|
474
|
+
failureCount: number;
|
|
475
|
+
/** Timing info (in ms) for event instances reported with duration. */
|
|
476
|
+
duration?: {
|
|
477
|
+
count: number;
|
|
478
|
+
total: number;
|
|
479
|
+
average: number;
|
|
480
|
+
max: number;
|
|
481
|
+
};
|
|
482
|
+
lastFailure?: {
|
|
483
|
+
time: number;
|
|
484
|
+
duration: number;
|
|
485
|
+
code: string;
|
|
486
|
+
name: string;
|
|
487
|
+
};
|
|
488
|
+
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
7
|
import {HoistService, PageState, PlainObject, TrackOptions, XH} from '@xh/hoist/core';
|
|
8
|
+
import {WebSocketTelemetry} from '@xh/hoist/svc/WebSocketService';
|
|
8
9
|
import {Timer} from '@xh/hoist/utils/async';
|
|
9
10
|
import {MINUTES} from '@xh/hoist/utils/datetime';
|
|
10
11
|
import {withFormattedTimestamps} from '@xh/hoist/format';
|
|
@@ -42,6 +43,7 @@ export class ClientHealthService extends HoistService {
|
|
|
42
43
|
general: this.getGeneral(),
|
|
43
44
|
memory: this.getMemory(),
|
|
44
45
|
connection: this.getConnection(),
|
|
46
|
+
webSockets: XH.webSocketService.telemetry,
|
|
45
47
|
...this.getCustom()
|
|
46
48
|
};
|
|
47
49
|
}
|
|
@@ -87,8 +89,7 @@ export class ClientHealthService extends HoistService {
|
|
|
87
89
|
startTime,
|
|
88
90
|
durationMins: elapsedMins(startTime),
|
|
89
91
|
idleMins: elapsedMins(XH.lastActivityMs),
|
|
90
|
-
pageState: XH.pageState
|
|
91
|
-
webSocket: XH.webSocketService.channelKey
|
|
92
|
+
pageState: XH.pageState
|
|
92
93
|
};
|
|
93
94
|
}
|
|
94
95
|
|
|
@@ -154,7 +155,6 @@ export interface GeneralData {
|
|
|
154
155
|
durationMins: number;
|
|
155
156
|
idleMins: number;
|
|
156
157
|
pageState: PageState;
|
|
157
|
-
webSocket: string;
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
export interface ConnectionData {
|
|
@@ -175,4 +175,5 @@ export interface ClientHealthReport {
|
|
|
175
175
|
general: GeneralData;
|
|
176
176
|
connection: ConnectionData;
|
|
177
177
|
memory: MemoryData;
|
|
178
|
+
webSockets: WebSocketTelemetry;
|
|
178
179
|
}
|