@xh/hoist 79.0.0-SNAPSHOT.1766259546947 → 79.0.0-SNAPSHOT.1766275267656

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 CHANGED
@@ -24,6 +24,9 @@
24
24
  Some apps may need to update their imports.
25
25
  * `TabContainerConfig.switcher` has been repurposed to accept a `TabSwitcherConfig`. To pass
26
26
  `TabSwitcherProps` via a parent `TabContainer`, use `TabContainerProps.switcher`.
27
+ * Tightened the typing of `LocalDate` adjustment methods with new `LocalDateUnit` type. Some less
28
+ common or ambiguous units (e.g. `date` or `d`) are no longer supported. Also typed the adjustment
29
+ `value` args to `number` where applicable.
27
30
 
28
31
  ### 🐞 Bug Fixes
29
32
 
@@ -40,6 +43,8 @@
40
43
  * `GroupingChooserProps.popoverTitle` - use `editorTitle`
41
44
  * `RelativeTimestampProps.options` - provide directly as top-level props
42
45
 
46
+ * Improved the efficiency and logging of MsalClient.
47
+ *
43
48
  ## 78.1.4 - 2025-12-05
44
49
 
45
50
  ### 🐞 Bug Fixes
@@ -50,7 +55,7 @@
50
55
 
51
56
  ### 🐞 Bug Fixes
52
57
 
53
- * Fix to Highchart timezone handling regression from version 77. Applications should note that
58
+ * Fix to Highchart timezone handling regression from version 77. Applications should note that
54
59
  Highcharts has deprecated the `time.useUTC` option and its functioning seem suspect. Apps
55
60
  should set `time.timezone` instead. See https://api.highcharts.com/highcharts/time.useUTC.
56
61
 
@@ -61,14 +66,15 @@
61
66
  ## 78.1.0 - 2025-12-02
62
67
 
63
68
  ### ⚙️ Technical
69
+
64
70
  * New property `MsalClientConfig.enableSsoSilent` to govern use of MSAL SSO api.
65
71
 
66
72
  * Existing property `MsalClientConfig.enableTelemetry` now defaults to `true`.
67
73
 
68
- * Improved use of MSAL client API, to maximize effectiveness of SSO. Improved documentation
69
- and logging. Iframe attempts will now time out by default after 3 seconds vs. 10 seconds.
70
- This can be further modified by apps via the option
71
- `MsalClientConfig.msalClientOptions.system.iFrameHashTimeout`
74
+ * Improved use of MSAL client API, to maximize effectiveness of SSO. Improved documentation
75
+ and logging. Iframe attempts will now time out by default after 3 seconds vs. 10 seconds.
76
+ This can be further modified by apps via the option
77
+ `MsalClientConfig.msalClientOptions.system.iFrameHashTimeout`
72
78
 
73
79
  ### 📚 Libraries
74
80
 
@@ -110,7 +116,8 @@
110
116
 
111
117
  ### 💥 Breaking Changes (upgrade difficulty: 🟢 LOW)
112
118
 
113
- * Apps that use and provide the `highcharts` library should be sure to update the version to v12.4.0.
119
+ * Apps that use and provide the `highcharts` library should be sure to update the version to
120
+ v12.4.0.
114
121
  Refer to `Bootstrap.js` in Toolbox for required import changes.
115
122
  * Visit https://www.highcharts.com/blog/changelog/ for specific changes.
116
123
 
@@ -197,7 +197,7 @@ export class AppContainerModel extends HoistModel {
197
197
 
198
198
  // Check auth, locking out, or showing login if possible
199
199
  this.setAppState('AUTHENTICATING');
200
- XH.authModel = createSingleton(this.appSpec.authModelClass);
200
+ XH.authModel = createSingleton(appSpec.authModelClass);
201
201
  const isAuthenticated = await XH.authModel.completeAuthAsync();
202
202
  if (!isAuthenticated) {
203
203
  throwIf(
@@ -28,8 +28,8 @@ export class AppStateModel extends HoistModel {
28
28
  * Read from timestamp set on window within index.html.
29
29
  */
30
30
  readonly loadStarted: number = window['_xhLoadTimestamp'];
31
+ readonly timings: Record<AppState, number> = {} as Record<AppState, number>;
31
32
 
32
- private timings: Record<AppState, number> = {} as Record<AppState, number>;
33
33
  private lastStateChangeTime: number = this.loadStarted;
34
34
 
35
35
  constructor() {
@@ -85,7 +85,7 @@ export class AppStateModel extends HoistModel {
85
85
  const {timings, loadStarted} = this;
86
86
  this.addReaction({
87
87
  when: () => this.state === 'RUNNING',
88
- run: () =>
88
+ run: () => {
89
89
  XH.track({
90
90
  category: 'App',
91
91
  message: `Loaded ${XH.clientAppCode}`,
@@ -98,7 +98,9 @@ export class AppStateModel extends HoistModel {
98
98
  screen: this.getScreenData()
99
99
  },
100
100
  omit: !XH.appSpec.trackAppLoad
101
- })
101
+ });
102
+ this.logDebug('Load timings', this.timings);
103
+ }
102
104
  });
103
105
  }
104
106
 
@@ -15,7 +15,7 @@ export declare class AppStateModel extends HoistModel {
15
15
  * Read from timestamp set on window within index.html.
16
16
  */
17
17
  readonly loadStarted: number;
18
- private timings;
18
+ readonly timings: Record<AppState, number>;
19
19
  private lastStateChangeTime;
20
20
  constructor();
21
21
  setAppState(nextState: AppState): void;
@@ -42,6 +42,8 @@ export type DebounceSpec = number | (DebounceSettings & {
42
42
  */
43
43
  export type Content = ReactElement | FunctionComponent | Component | ElementFactory | (() => ReactElement);
44
44
  export type DateLike = Date | LocalDate | MomentInput;
45
+ /** Valid units for the {@link LocalDate} adjustment methods. */
46
+ export type LocalDateUnit = 'year' | 'years' | 'quarter' | 'quarters' | 'month' | 'months' | 'week' | 'weeks' | 'day' | 'days';
45
47
  export type PageState =
46
48
  /**
47
49
  * Window/tab is visible and focused.
@@ -1,4 +1,5 @@
1
- import moment, { Moment, MomentInput } from 'moment';
1
+ import { LocalDateUnit } from '@xh/hoist/core';
2
+ import { Moment, MomentInput } from 'moment';
2
3
  /**
3
4
  * A Date representation that does not contain time information. Useful for business day or calendar
4
5
  * day data where time and time zone should be explicitly ignored. Client-side equivalent of the
@@ -8,15 +9,13 @@ import moment, { Moment, MomentInput } from 'moment';
8
9
  * For efficiency and to enable strict equality checks, instances of this class are memoized:
9
10
  * only a single version of the object will be created and returned for each calendar day,
10
11
  * as long as the caller uses one of the *public factory methods*, which they always should!
11
- *
12
- * Unit accepted by manipulation methods are ['year', 'quarter', 'month', 'week', 'day', 'date'].
13
12
  */
14
13
  export declare class LocalDate {
14
+ static readonly VALID_UNITS: Set<LocalDateUnit>;
15
15
  private static _instances;
16
- static VALID_UNITS: string[];
17
- private _isoString;
18
- private _moment;
19
- private _date;
16
+ private readonly _isoString;
17
+ private readonly _moment;
18
+ private readonly _date;
20
19
  /**
21
20
  * Get an instance of this class.
22
21
  * This is the standard way to get an instance of this object from serialized server-side data.
@@ -64,11 +63,11 @@ export declare class LocalDate {
64
63
  toJSON(): string;
65
64
  valueOf(): string;
66
65
  get isLocalDate(): boolean;
67
- add(value: any, unit?: string): LocalDate;
68
- subtract(value: any, unit?: string): LocalDate;
69
- addWeekdays(value: any): LocalDate;
70
- subtractWeekdays(value: any): LocalDate;
71
- startOf(unit: any): LocalDate;
66
+ add(value: number, unit?: LocalDateUnit): LocalDate;
67
+ subtract(value: number, unit?: LocalDateUnit): LocalDate;
68
+ addWeekdays(value: number): LocalDate;
69
+ subtractWeekdays(value: number): LocalDate;
70
+ startOf(unit: LocalDateUnit): LocalDate;
72
71
  startOfMonth(): LocalDate;
73
72
  startOfQuarter(): LocalDate;
74
73
  startOfYear(): LocalDate;
@@ -84,13 +83,10 @@ export declare class LocalDate {
84
83
  currentOrNextWeekday(): LocalDate;
85
84
  /** The same date if already a weekday, or the previous weekday. */
86
85
  currentOrPreviousWeekday(): LocalDate;
87
- diff(other: LocalDate, unit?: moment.unitOfTime.Diff): number;
86
+ diff(other: LocalDate, unit?: LocalDateUnit): number;
88
87
  /** @internal - use one of the static factory methods instead. */
89
88
  private constructor();
90
89
  private ensureUnitValid;
91
90
  }
92
- /**
93
- * Is the input value a local Date?
94
- * Convenience alias for LocalDate.isLocalDate()
95
- */
91
+ /** @returns true if the input value is a `LocalDate` instance. */
96
92
  export declare function isLocalDate(val: any): val is LocalDate;
@@ -63,6 +63,19 @@ export type Content =
63
63
 
64
64
  export type DateLike = Date | LocalDate | MomentInput;
65
65
 
66
+ /** Valid units for the {@link LocalDate} adjustment methods. */
67
+ export type LocalDateUnit =
68
+ | 'year'
69
+ | 'years'
70
+ | 'quarter'
71
+ | 'quarters'
72
+ | 'month'
73
+ | 'months'
74
+ | 'week'
75
+ | 'weeks'
76
+ | 'day'
77
+ | 'days';
78
+
66
79
  export type PageState =
67
80
  /**
68
81
  * Window/tab is visible and focused.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "79.0.0-SNAPSHOT.1766259546947",
3
+ "version": "79.0.0-SNAPSHOT.1766275267656",
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",
@@ -170,17 +170,19 @@ export class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTokenSpec>
170
170
  const account =
171
171
  accounts.length == 1
172
172
  ? accounts[0]
173
- : accounts.find(a => (a.username = this.getSelectedUsername()));
173
+ : accounts.find(a => a.username == this.getSelectedUsername());
174
+ let trySsoSilent = enableSsoSilent;
174
175
  if (account) {
175
176
  this.setAccount(account);
176
177
  try {
177
- this.initialTokenLoad = true;
178
178
  this.logDebug('Attempting silent token load.');
179
+ this.initialTokenLoad = true;
179
180
  const ret = await this.fetchAllTokensAsync({eagerOnly: true});
180
181
  this.noteAuthComplete('acquireSilent');
181
182
  return ret;
182
183
  } catch (e) {
183
- this.logDebug('Failed to load tokens on init, fall back to login', e.message ?? e);
184
+ if (e instanceof InteractionRequiredAuthError) trySsoSilent = false;
185
+ this.logDebug('AcquireSilent failed', e.message ?? e);
184
186
  } finally {
185
187
  this.initialTokenLoad = false;
186
188
  }
@@ -189,15 +191,18 @@ export class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTokenSpec>
189
191
  // 2) Try `ssoSilent` API to potentially reuse logged-in user on other apps
190
192
  // in same domain without interaction. This should never trigger popup/redirect, and will
191
193
  // use an iFrame (3rd party cookies required). Must fail gently.
192
- if (enableSsoSilent) {
194
+ if (trySsoSilent) {
193
195
  try {
194
196
  this.logDebug('Attempting SSO');
195
197
  await this.loginSsoAsync();
198
+ this.initialTokenLoad = true;
196
199
  const ret = await this.fetchAllTokensAsync({eagerOnly: true});
197
200
  this.noteAuthComplete('ssoSilent');
198
201
  return ret;
199
202
  } catch (e) {
200
203
  this.logDebug('SSO failed', e.message ?? e);
204
+ } finally {
205
+ this.initialTokenLoad = false;
201
206
  }
202
207
  }
203
208
 
@@ -453,7 +458,7 @@ export class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTokenSpec>
453
458
 
454
459
  private noteAuthComplete(authMethod: AuthMethod) {
455
460
  if (this.telemetry) this.telemetry.authMethod = authMethod;
456
- this.logInfo(`Authenticated user ${this.account.username} via ${authMethod}`);
461
+ this.logInfo(`Authenticated user '${this.account.username}' via ${authMethod}`);
457
462
  }
458
463
 
459
464
  private authRequestCore(): AuthRequestCore {