@xh/hoist 73.0.0-SNAPSHOT.1744147015222 → 73.0.0-SNAPSHOT.1744228975863

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.
@@ -14,9 +14,10 @@ import {
14
14
  PopupRequest,
15
15
  SilentRequest
16
16
  } from '@azure/msal-browser';
17
- import {XH} from '@xh/hoist/core';
17
+ import {AppState, PlainObject, XH} from '@xh/hoist/core';
18
18
  import {Token} from '@xh/hoist/security/Token';
19
19
  import {logDebug, logError, logInfo, logWarn, mergeDeep, throwIf} from '@xh/hoist/utils/js';
20
+ import {withFormattedTimestamps} from '@xh/hoist/format';
20
21
  import {flatMap, union, uniq} from 'lodash';
21
22
  import {BaseOAuthClient, BaseOAuthClientConfig} from '../BaseOAuthClient';
22
23
  import {AccessTokenSpec, TelemetryResults, TokenMap} from '../Types';
@@ -266,9 +267,13 @@ export class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTokenSpec>
266
267
  //------------------------
267
268
  // Telemetry
268
269
  //------------------------
270
+ getFormattedTelemetry(): PlainObject {
271
+ return withFormattedTimestamps(this.telemetryResults);
272
+ }
273
+
269
274
  enableTelemetry(): void {
270
275
  if (this._telemetryCbHandle) {
271
- this.logInfo('Telemetry already enabled', this.telemetryResults);
276
+ this.logInfo('Telemetry already enabled', this.getFormattedTelemetry());
272
277
  return;
273
278
  }
274
279
 
@@ -279,7 +284,7 @@ export class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTokenSpec>
279
284
  try {
280
285
  const {events} = this.telemetryResults,
281
286
  {name, startTimeMs, durationMs, success, errorName, errorCode} = e,
282
- eTime = startTimeMs ? new Date(startTimeMs) : new Date();
287
+ eTime = startTimeMs ?? Date.now();
283
288
 
284
289
  const eResult = (events[name] ??= {
285
290
  firstTime: eTime,
@@ -316,10 +321,9 @@ export class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTokenSpec>
316
321
  });
317
322
  });
318
323
 
319
- // Ask TrackService to include in background health check report, if enabled on that service.
320
- // Handle TrackService not yet initialized (common, this client likely initialized before.)
324
+ // Wait for clientHealthService (this client likely initialized during earlier AUTHENTICATING.)
321
325
  this.addReaction({
322
- when: () => XH.appIsRunning,
326
+ when: () => XH.appState === AppState.INITIALIZING_APP,
323
327
  run: () => XH.clientHealthService.addSource('msalClient', () => this.telemetryResults)
324
328
  });
325
329
 
@@ -336,7 +340,7 @@ export class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTokenSpec>
336
340
  this._telemetryCbHandle = null;
337
341
 
338
342
  XH.clientHealthService.removeSource('msalClient');
339
- this.logInfo('Telemetry disabled', this.telemetryResults);
343
+ this.logInfo('Telemetry disabled', this.getFormattedTelemetry());
340
344
  }
341
345
 
342
346
  //------------------------
@@ -4,10 +4,11 @@
4
4
  *
5
5
  * Copyright © 2025 Extremely Heavy Industries Inc.
6
6
  */
7
- import {HoistService, PlainObject, XH} from '@xh/hoist/core';
7
+ import {HoistService, PageState, PlainObject, XH} from '@xh/hoist/core';
8
8
  import {Timer} from '@xh/hoist/utils/async';
9
9
  import {MINUTES} from '@xh/hoist/utils/datetime';
10
- import {find, isPlainObject, pick, round} from 'lodash';
10
+ import {withFormattedTimestamps} from '@xh/hoist/format';
11
+ import {pick, round} from 'lodash';
11
12
 
12
13
  /**
13
14
  * Service for gathering data about client health.
@@ -31,19 +32,22 @@ export class ClientHealthService extends HoistService {
31
32
  }
32
33
 
33
34
  /**
34
- * Main Entry report. Return a default report of client health.
35
+ * Main entry point. Return a default report of client health.
35
36
  */
36
37
  getReport(): ClientHealthReport {
37
38
  return {
38
- session: this.getSession(),
39
- ...this.getCustom(),
39
+ general: this.getGeneral(),
40
40
  memory: this.getMemory(),
41
41
  connection: this.getConnection(),
42
- window: this.getWindow(),
43
- screen: this.getScreen()
42
+ ...this.getCustom()
44
43
  };
45
44
  }
46
45
 
46
+ /** Get report, suitable for viewing in console. **/
47
+ getFormattedReport(): PlainObject {
48
+ return withFormattedTimestamps(this.getReport());
49
+ }
50
+
47
51
  /**
48
52
  * Register a new source for client health report data. No-op if background health report is
49
53
  * not generally enabled via `xhActivityTrackingConfig.clientHealthReport.intervalMins`.
@@ -63,11 +67,16 @@ export class ClientHealthService extends HoistService {
63
67
  // -----------------------------------
64
68
  // Generate individual report sections
65
69
  //------------------------------------
66
- getSession(): SessionData {
67
- const {loadStarted} = XH.appContainerModel.appStateModel;
70
+ getGeneral(): GeneralData {
71
+ const startTime = XH.appContainerModel.appStateModel.loadStarted,
72
+ elapsedMins = (ts: number) => round((Date.now() - ts) / 60_000, 1);
73
+
68
74
  return {
69
- startTime: loadStarted,
70
- durationMins: round((Date.now() - loadStarted) / 60_000, 1)
75
+ startTime,
76
+ durationMins: elapsedMins(startTime),
77
+ idleMins: elapsedMins(XH.lastActivityMs),
78
+ pageState: XH.pageState,
79
+ webSocket: XH.webSocketService.channelKey
71
80
  };
72
81
  }
73
82
 
@@ -89,44 +98,12 @@ export class ClientHealthService extends HoistService {
89
98
 
90
99
  const {jsHeapSizeLimit: limit, usedJSHeapSize: used} = ret;
91
100
  if (limit && used) {
92
- ret.usedPctLimit = round((used / limit) * 100, 1);
101
+ ret.usedPctLimit = round((used / limit) * 100);
93
102
  }
94
103
 
95
104
  return ret;
96
105
  }
97
106
 
98
- getScreen(): ScreenData {
99
- const screen = window.screen as any;
100
- if (!screen) return null;
101
-
102
- const ret: ScreenData = pick(screen, [
103
- 'availWidth',
104
- 'availHeight',
105
- 'width',
106
- 'height',
107
- 'colorDepth',
108
- 'pixelDepth',
109
- 'availLeft',
110
- 'availTop'
111
- ]);
112
- if (screen.orientation) {
113
- ret.orientation = pick(screen.orientation, ['angle', 'type']);
114
- }
115
- return ret;
116
- }
117
-
118
- getWindow(): WindowData {
119
- return pick(window, [
120
- 'devicePixelRatio',
121
- 'screenX',
122
- 'screenY',
123
- 'innerWidth',
124
- 'innerHeight',
125
- 'outerWidth',
126
- 'outerHeight'
127
- ]);
128
- }
129
-
130
107
  getCustom(): PlainObject {
131
108
  const ret = {};
132
109
  this.sources.forEach((cb, k) => {
@@ -144,31 +121,27 @@ export class ClientHealthService extends HoistService {
144
121
  // Implementation
145
122
  //------------------
146
123
  private sendReport() {
147
- const {
148
- intervalMins,
149
- severity: defaultSeverity,
150
- ...rest
151
- } = XH.trackService.conf.clientHealthReport ?? {};
152
-
153
- const rpt = this.getReport();
154
- let severity = defaultSeverity ?? 'INFO';
155
- if (find(rpt, (v: any) => isPlainObject(v) && v.severity === 'WARN')) {
156
- severity = 'WARN';
157
- }
124
+ const {intervalMins, ...rest} = XH.trackService.conf.clientHealthReport ?? {};
158
125
 
159
126
  XH.track({
160
127
  category: 'App',
161
128
  message: 'Submitted health report',
162
- severity,
163
129
  ...rest,
164
- data: rpt
130
+ data: {
131
+ clientId: XH.clientId,
132
+ sessionId: XH.sessionId,
133
+ ...this.getReport()
134
+ }
165
135
  });
166
136
  }
167
137
  }
168
138
 
169
- export interface SessionData {
139
+ export interface GeneralData {
170
140
  startTime: number;
171
141
  durationMins: number;
142
+ idleMins: number;
143
+ pageState: PageState;
144
+ webSocket: string;
172
145
  }
173
146
 
174
147
  export interface ConnectionData {
@@ -185,35 +158,8 @@ export interface MemoryData {
185
158
  usedJSHeapSize?: number;
186
159
  }
187
160
 
188
- export interface WindowData {
189
- devicePixelRatio: number;
190
- screenX: number;
191
- screenY: number;
192
- innerWidth: number;
193
- innerHeight: number;
194
- outerWidth: number;
195
- outerHeight: number;
196
- }
197
-
198
- export interface ScreenData {
199
- availWidth: number;
200
- availHeight: number;
201
- width: number;
202
- height: number;
203
- colorDepth: number;
204
- pixelDepth: number;
205
- availLeft: number;
206
- availTop: number;
207
- orientation?: {
208
- angle: number;
209
- type: string;
210
- };
211
- }
212
-
213
161
  export interface ClientHealthReport {
214
- session: SessionData;
162
+ general: GeneralData;
215
163
  connection: ConnectionData;
216
164
  memory: MemoryData;
217
- window: WindowData;
218
- screen: ScreenData;
219
165
  }