@xh/hoist 72.0.0-SNAPSHOT.1736809021133 → 72.0.0-SNAPSHOT.1736879763062

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
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## v72.0.0-SNAPSHOT - unreleased
4
4
 
5
+ ### ⚙️ Typescript API Adjustments
6
+
7
+ * Improved signature of `HoistBase.markPersist`.
8
+
5
9
  ## v71.0.0 - 2025-01-08
6
10
 
7
11
  ### 💥 Breaking Changes (upgrade difficulty: 🟠 MEDIUM - Hoist core update, import adjustments)
@@ -134,7 +134,7 @@ export declare abstract class HoistBase {
134
134
  * @param options - options governing the persistence of this object. These will be applied
135
135
  * on top of any default persistWith options defined on the instance itself.
136
136
  */
137
- markPersist(property: keyof this & string, options?: PersistOptions): void;
137
+ markPersist<P extends keyof this & string>(property: P, options?: PersistOptions): void;
138
138
  /** @returns true if this instance has been destroyed. */
139
139
  get isDestroyed(): boolean;
140
140
  /**
@@ -142,6 +142,8 @@ export declare abstract class BaseOAuthClient<C extends BaseOAuthClientConfig<S>
142
142
  * @param key - key for re-accessing this state, as round-tripped with redirect.
143
143
  */
144
144
  protected restoreRedirectState(key: string): void;
145
+ /** Call after requesting the provider library redirect the user away for auth. */
146
+ protected maskAfterRedirect(): Promise<void>;
145
147
  protected fetchAllTokensAsync(useCache?: boolean): Promise<TokenMap>;
146
148
  protected getLocalStorage(key: string, defaultValue?: any): any;
147
149
  protected setLocalStorage(key: string, value: any): void;
package/core/HoistBase.ts CHANGED
@@ -256,7 +256,7 @@ export abstract class HoistBase {
256
256
  * @param options - options governing the persistence of this object. These will be applied
257
257
  * on top of any default persistWith options defined on the instance itself.
258
258
  */
259
- markPersist(property: keyof this & string, options: PersistOptions = {}) {
259
+ markPersist<P extends keyof this & string>(property: P, options: PersistOptions = {}) {
260
260
  // Read from and attach to Provider, failing gently
261
261
  PersistenceProvider.create({
262
262
  persistOptions: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "72.0.0-SNAPSHOT.1736809021133",
3
+ "version": "72.0.0-SNAPSHOT.1736879763062",
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",
@@ -4,8 +4,11 @@
4
4
  *
5
5
  * Copyright © 2025 Extremely Heavy Industries Inc.
6
6
  */
7
+ import {br, fragment} from '@xh/hoist/cmp/layout';
7
8
  import {HoistBase, managed, XH} from '@xh/hoist/core';
9
+ import {Icon} from '@xh/hoist/icon';
8
10
  import {action, makeObservable} from '@xh/hoist/mobx';
11
+ import {never, wait} from '@xh/hoist/promise';
9
12
  import {Token, TokenMap} from '@xh/hoist/security/Token';
10
13
  import {Timer} from '@xh/hoist/utils/async';
11
14
  import {MINUTES, olderThan, ONE_MINUTE, SECONDS} from '@xh/hoist/utils/datetime';
@@ -285,6 +288,32 @@ export abstract class BaseOAuthClient<C extends BaseOAuthClientConfig<S>, S> ext
285
288
  window.history.replaceState(null, '', pathname + search);
286
289
  }
287
290
 
291
+ /** Call after requesting the provider library redirect the user away for auth. */
292
+ protected async maskAfterRedirect() {
293
+ // We expect the tab to unload momentarily and be redirected to the provider's login page.
294
+ // Wait here to ensure we mask the app while the browser processes the redirect...
295
+ await wait(10 * SECONDS);
296
+
297
+ // ...but also handle an observed edge case where the browser decided to open a new tab
298
+ // instead of redirecting the current one (https://github.com/xh/hoist-react/issues/3899).
299
+ // Show message below if/when user swaps back to the original tab, vs. endless spinner.
300
+ await XH.alert({
301
+ title: 'Auth / Redirect Error',
302
+ message: fragment(
303
+ 'Authentication did not complete as expected / tab was not redirected.',
304
+ br(),
305
+ br(),
306
+ 'Please click below or refresh this tab in your browser to try again.'
307
+ ),
308
+ confirmProps: {text: 'Reload', icon: Icon.refresh(), intent: 'primary', minimal: false}
309
+ });
310
+
311
+ XH.reloadApp();
312
+
313
+ // Ensure stale init does not progress - this tab should *really* be on its way out now!
314
+ await never();
315
+ }
316
+
288
317
  protected async fetchAllTokensAsync(useCache = true): Promise<TokenMap> {
289
318
  const ret: TokenMap = {},
290
319
  {accessSpecs} = this;
@@ -7,7 +7,7 @@
7
7
  import type {Auth0ClientOptions} from '@auth0/auth0-spa-js';
8
8
  import {Auth0Client} from '@auth0/auth0-spa-js';
9
9
  import {XH} from '@xh/hoist/core';
10
- import {never, wait} from '@xh/hoist/promise';
10
+ import {wait} from '@xh/hoist/promise';
11
11
  import {Token, TokenMap} from '@xh/hoist/security/Token';
12
12
  import {SECONDS} from '@xh/hoist/utils/datetime';
13
13
  import {mergeDeep, throwIf} from '@xh/hoist/utils/js';
@@ -99,11 +99,13 @@ export class AuthZeroClient extends BaseOAuthClient<AuthZeroClientConfig, AuthZe
99
99
 
100
100
  protected override async doLoginRedirectAsync(): Promise<void> {
101
101
  const appState = this.captureRedirectState();
102
+
102
103
  await this.client.loginWithRedirect({
103
104
  appState,
104
105
  authorizationParams: {scope: this.loginScope}
105
106
  });
106
- await never();
107
+
108
+ await this.maskAfterRedirect();
107
109
  }
108
110
 
109
111
  protected override async doLoginPopupAsync(): Promise<void> {
@@ -10,11 +10,9 @@ import {
10
10
  IPublicClientApplication,
11
11
  LogLevel,
12
12
  PopupRequest,
13
- RedirectRequest,
14
13
  SilentRequest
15
14
  } from '@azure/msal-browser';
16
15
  import {XH} from '@xh/hoist/core';
17
- import {never} from '@xh/hoist/promise';
18
16
  import {Token, TokenMap} from '@xh/hoist/security/Token';
19
17
  import {logDebug, logError, logInfo, logWarn, mergeDeep, throwIf} from '@xh/hoist/utils/js';
20
18
  import {flatMap, union, uniq} from 'lodash';
@@ -201,18 +199,18 @@ export class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTokenSpec>
201
199
  }
202
200
 
203
201
  protected override async doLoginRedirectAsync(): Promise<void> {
204
- const {client} = this,
205
- state = this.captureRedirectState(),
206
- opts: RedirectRequest = {
207
- state,
208
- loginHint: this.getSelectedUsername(),
209
- domainHint: this.config.domainHint,
210
- scopes: this.loginScopes,
211
- extraScopesToConsent: this.loginExtraScopesToConsent,
212
- redirectUri: this.redirectUrl
213
- };
214
- await client.acquireTokenRedirect(opts);
215
- await never();
202
+ const state = this.captureRedirectState();
203
+
204
+ await this.client.acquireTokenRedirect({
205
+ state,
206
+ loginHint: this.getSelectedUsername(),
207
+ domainHint: this.config.domainHint,
208
+ scopes: this.loginScopes,
209
+ extraScopesToConsent: this.loginExtraScopesToConsent,
210
+ redirectUri: this.redirectUrl
211
+ });
212
+
213
+ await this.maskAfterRedirect();
216
214
  }
217
215
 
218
216
  protected override async fetchIdTokenAsync(useCache: boolean = true): Promise<Token> {