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

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,11 @@
2
2
 
3
3
  ## v72.0.0-SNAPSHOT - unreleased
4
4
 
5
+ ### ⚙️ Technical
6
+
7
+ * Added support for providing custom `PersistenceProvider` implementations to `PersistOptions`.
8
+
9
+
5
10
  ### ⚙️ Typescript API Adjustments
6
11
 
7
12
  * Improved signature of `HoistBase.markPersist`.
@@ -1,17 +1,24 @@
1
- import { DebounceSpec } from '../';
1
+ import { Class } from 'type-fest';
2
+ import { DebounceSpec, PersistenceProvider, PersistenceProviderConfig } from '../';
2
3
  import type { DashViewModel } from '@xh/hoist/desktop/cmp/dash';
3
4
  import type { ViewManagerModel } from '@xh/hoist/cmp/viewmanager';
5
+ /**
6
+ * Built-in Hoist PersistenceProviders.
7
+ */
8
+ export type PersistenceProviderType = 'pref' | 'localStorage' | 'sessionStorage' | 'dashView' | 'viewManager' | 'custom';
4
9
  export interface PersistOptions {
5
10
  /** Dot delimited path to store state. */
6
11
  path?: string;
7
12
  /** Debounce interval in ms, or a lodash debounce config. */
8
13
  debounce?: DebounceSpec;
9
14
  /**
10
- * Type of PersistenceProvider to create. If not provided, defaulted based
11
- * on the presence of `prefKey`, `localStorageKey`, `dashViewModel`, 'viewManagerModel',
12
- * `getData` and `setData`.
15
+ * Type of PersistenceProvider to create. Specify as one of the built-in string types,
16
+ * or a subclass of PersistenceProvider.
17
+ *
18
+ * If not provided, defaulted to one of the built-in string types based on the presence of
19
+ * `prefKey`, `localStorageKey`, `dashViewModel`, 'viewManagerModel', or `getData/setData`.
13
20
  */
14
- type?: 'pref' | 'localStorage' | 'sessionStorage' | 'dashView' | 'viewManager' | 'custom';
21
+ type?: PersistenceProviderType | Class<PersistenceProvider, [PersistenceProviderConfig]>;
15
22
  /** Predefined Hoist application Preference key used to store state. */
16
23
  prefKey?: string;
17
24
  /** Browser local storage key used to store state. */
@@ -1,12 +1,8 @@
1
1
  import { DebounceSpec, HoistBase, Persistable, PersistableState } from '../';
2
2
  import { PersistOptions } from './';
3
- export type PersistenceProviderConfig<S> = {
3
+ export type PersistenceProviderConfig<S = any> = {
4
4
  persistOptions: PersistOptions;
5
5
  target: Persistable<S>;
6
- owner: HoistBase;
7
- } | {
8
- persistOptions: PersistOptions;
9
- target: Persistable<S> & HoistBase;
10
6
  owner?: HoistBase;
11
7
  };
12
8
  /**
@@ -26,7 +22,7 @@ export type PersistenceProviderConfig<S> = {
26
22
  * - {@link ViewManagerProvider} - persists to saved views managed by {@link ViewManagerModel}.
27
23
  * - {@link CustomProvider} - API for app and components to provide their own storage mechanism.
28
24
  */
29
- export declare abstract class PersistenceProvider<S> {
25
+ export declare abstract class PersistenceProvider<S = any> {
30
26
  readonly path: string;
31
27
  readonly debounce: DebounceSpec;
32
28
  readonly owner: HoistBase;
@@ -59,4 +55,5 @@ export declare abstract class PersistenceProvider<S> {
59
55
  protected writeInternal(data: S): void;
60
56
  protected writeRaw(obj: Record<typeof this.path, S>): void;
61
57
  protected readRaw(): Record<typeof this.path, S>;
58
+ private static parseProviderClass;
62
59
  }
@@ -143,7 +143,7 @@ export declare abstract class BaseOAuthClient<C extends BaseOAuthClientConfig<S>
143
143
  */
144
144
  protected restoreRedirectState(key: string): void;
145
145
  /** Call after requesting the provider library redirect the user away for auth. */
146
- protected maskAfterRedirect(): Promise<void>;
146
+ protected maskAfterRedirectAsync(): Promise<void>;
147
147
  protected fetchAllTokensAsync(useCache?: boolean): Promise<TokenMap>;
148
148
  protected getLocalStorage(key: string, defaultValue?: any): any;
149
149
  protected setLocalStorage(key: string, value: any): void;
@@ -5,10 +5,22 @@
5
5
  * Copyright © 2025 Extremely Heavy Industries Inc.
6
6
  */
7
7
 
8
- import {DebounceSpec} from '../';
8
+ import {Class} from 'type-fest';
9
+ import {DebounceSpec, PersistenceProvider, PersistenceProviderConfig} from '../';
9
10
  import type {DashViewModel} from '@xh/hoist/desktop/cmp/dash'; // Import type only
10
11
  import type {ViewManagerModel} from '@xh/hoist/cmp/viewmanager'; // Import type only
11
12
 
13
+ /**
14
+ * Built-in Hoist PersistenceProviders.
15
+ */
16
+ export type PersistenceProviderType =
17
+ | 'pref'
18
+ | 'localStorage'
19
+ | 'sessionStorage'
20
+ | 'dashView'
21
+ | 'viewManager'
22
+ | 'custom';
23
+
12
24
  export interface PersistOptions {
13
25
  /** Dot delimited path to store state. */
14
26
  path?: string;
@@ -17,11 +29,13 @@ export interface PersistOptions {
17
29
  debounce?: DebounceSpec;
18
30
 
19
31
  /**
20
- * Type of PersistenceProvider to create. If not provided, defaulted based
21
- * on the presence of `prefKey`, `localStorageKey`, `dashViewModel`, 'viewManagerModel',
22
- * `getData` and `setData`.
32
+ * Type of PersistenceProvider to create. Specify as one of the built-in string types,
33
+ * or a subclass of PersistenceProvider.
34
+ *
35
+ * If not provided, defaulted to one of the built-in string types based on the presence of
36
+ * `prefKey`, `localStorageKey`, `dashViewModel`, 'viewManagerModel', or `getData/setData`.
23
37
  */
24
- type?: 'pref' | 'localStorage' | 'sessionStorage' | 'dashView' | 'viewManager' | 'custom';
38
+ type?: PersistenceProviderType | Class<PersistenceProvider, [PersistenceProviderConfig]>;
25
39
 
26
40
  /** Predefined Hoist application Preference key used to store state. */
27
41
  prefKey?: string;
@@ -12,33 +12,29 @@ import {
12
12
  get,
13
13
  isEmpty,
14
14
  isNumber,
15
+ isString,
15
16
  isUndefined,
16
17
  set,
17
18
  toPath
18
19
  } from 'lodash';
19
20
  import {IReactionDisposer, reaction} from 'mobx';
20
- import {DebounceSpec, HoistBase, Persistable, PersistableState, XH} from '../';
21
+ import {Class} from 'type-fest';
22
+ import {DebounceSpec, HoistBase, Persistable, PersistableState} from '../';
21
23
  import {
22
24
  CustomProvider,
23
25
  DashViewProvider,
24
26
  LocalStorageProvider,
25
- SessionStorageProvider,
26
27
  PersistOptions,
27
28
  PrefProvider,
29
+ SessionStorageProvider,
28
30
  ViewManagerProvider
29
31
  } from './';
30
32
 
31
- export type PersistenceProviderConfig<S> =
32
- | {
33
- persistOptions: PersistOptions;
34
- target: Persistable<S>;
35
- owner: HoistBase;
36
- }
37
- | {
38
- persistOptions: PersistOptions;
39
- target: Persistable<S> & HoistBase;
40
- owner?: HoistBase;
41
- };
33
+ export type PersistenceProviderConfig<S = any> = {
34
+ persistOptions: PersistOptions;
35
+ target: Persistable<S>;
36
+ owner?: HoistBase;
37
+ };
42
38
 
43
39
  /**
44
40
  * Abstract superclass for adaptor objects used by models and components to (re)store state to and
@@ -57,7 +53,7 @@ export type PersistenceProviderConfig<S> =
57
53
  * - {@link ViewManagerProvider} - persists to saved views managed by {@link ViewManagerModel}.
58
54
  * - {@link CustomProvider} - API for app and components to provide their own storage mechanism.
59
55
  */
60
- export abstract class PersistenceProvider<S> {
56
+ export abstract class PersistenceProvider<S = any> {
61
57
  readonly path: string;
62
58
  readonly debounce: DebounceSpec;
63
59
  readonly owner: HoistBase;
@@ -79,49 +75,14 @@ export abstract class PersistenceProvider<S> {
79
75
  * target without thrashing.
80
76
  */
81
77
  static create<S>(cfg: PersistenceProviderConfig<S>): PersistenceProvider<S> {
82
- cfg = {
83
- owner: cfg.target instanceof HoistBase ? cfg.target : cfg.owner,
84
- ...cfg
85
- };
86
- const {target, persistOptions} = cfg;
87
-
88
- let {type, ...rest} = persistOptions,
89
- ret: PersistenceProvider<S>;
90
-
78
+ let ret: PersistenceProvider<S>;
91
79
  try {
92
- if (!type) {
93
- if (rest.prefKey) type = 'pref';
94
- if (rest.localStorageKey) type = 'localStorage';
95
- if (rest.sessionStorageKey) type = 'sessionStorage';
96
- if (rest.dashViewModel) type = 'dashView';
97
- if (rest.viewManagerModel) type = 'viewManager';
98
- if (rest.getData || rest.setData) type = 'custom';
99
- }
100
-
101
- switch (type) {
102
- case 'pref':
103
- ret = new PrefProvider(cfg);
104
- break;
105
- case 'localStorage':
106
- ret = new LocalStorageProvider(cfg);
107
- break;
108
- case 'sessionStorage':
109
- ret = new SessionStorageProvider(cfg);
110
- break;
111
- case `dashView`:
112
- ret = new DashViewProvider(cfg);
113
- break;
114
- case `viewManager`:
115
- ret = new ViewManagerProvider(cfg);
116
- break;
117
- case 'custom':
118
- ret = new CustomProvider(cfg);
119
- break;
120
- default:
121
- throw XH.exception(`Unknown Persistence Provider for type: ${type}`);
122
- }
80
+ // default owner to target
81
+ cfg = {owner: cfg.target instanceof HoistBase ? cfg.target : cfg.owner, ...cfg};
123
82
 
124
- ret.bindToTarget(target);
83
+ const providerClass = this.parseProviderClass<S>(cfg.persistOptions);
84
+ ret = new providerClass(cfg);
85
+ ret.bindToTarget(cfg.target);
125
86
  return ret;
126
87
  } catch (e) {
127
88
  logError(e, cfg.owner);
@@ -211,7 +172,39 @@ export abstract class PersistenceProvider<S> {
211
172
  }
212
173
 
213
174
  protected writeRaw(obj: Record<typeof this.path, S>) {}
175
+
214
176
  protected readRaw(): Record<typeof this.path, S> {
215
177
  return null;
216
178
  }
179
+
180
+ private static parseProviderClass<S>(
181
+ opts: PersistOptions
182
+ ): Class<PersistenceProvider<S>, [PersistenceProviderConfig<S>]> {
183
+ // 1) Recognize shortcut form
184
+ const {type, ...rest} = opts;
185
+ if (!type) {
186
+ if (rest.prefKey) return PrefProvider;
187
+ if (rest.localStorageKey) return LocalStorageProvider;
188
+ if (rest.sessionStorageKey) return SessionStorageProvider;
189
+ if (rest.dashViewModel) return DashViewProvider;
190
+ if (rest.viewManagerModel) return ViewManagerProvider;
191
+ if (rest.getData || rest.setData) return CustomProvider;
192
+ }
193
+
194
+ // 2) Map any string to known Provider Class, or return raw class
195
+ const ret = isString(type)
196
+ ? {
197
+ pref: PrefProvider,
198
+ localStorage: LocalStorageProvider,
199
+ sessionStorage: SessionStorageProvider,
200
+ dashView: DashViewProvider,
201
+ viewManager: ViewManagerProvider,
202
+ custom: CustomProvider
203
+ }[type]
204
+ : type;
205
+
206
+ throwIf(!ret, `Unknown Persistence Provider: ${type}`);
207
+
208
+ return ret;
209
+ }
217
210
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "72.0.0-SNAPSHOT.1736879763062",
3
+ "version": "72.0.0-SNAPSHOT.1736893155432",
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",
@@ -289,7 +289,7 @@ export abstract class BaseOAuthClient<C extends BaseOAuthClientConfig<S>, S> ext
289
289
  }
290
290
 
291
291
  /** Call after requesting the provider library redirect the user away for auth. */
292
- protected async maskAfterRedirect() {
292
+ protected async maskAfterRedirectAsync() {
293
293
  // We expect the tab to unload momentarily and be redirected to the provider's login page.
294
294
  // Wait here to ensure we mask the app while the browser processes the redirect...
295
295
  await wait(10 * SECONDS);
@@ -105,7 +105,7 @@ export class AuthZeroClient extends BaseOAuthClient<AuthZeroClientConfig, AuthZe
105
105
  authorizationParams: {scope: this.loginScope}
106
106
  });
107
107
 
108
- await this.maskAfterRedirect();
108
+ await this.maskAfterRedirectAsync();
109
109
  }
110
110
 
111
111
  protected override async doLoginPopupAsync(): Promise<void> {
@@ -210,7 +210,7 @@ export class MsalClient extends BaseOAuthClient<MsalClientConfig, MsalTokenSpec>
210
210
  redirectUri: this.redirectUrl
211
211
  });
212
212
 
213
- await this.maskAfterRedirect();
213
+ await this.maskAfterRedirectAsync();
214
214
  }
215
215
 
216
216
  protected override async fetchIdTokenAsync(useCache: boolean = true): Promise<Token> {