@xh/hoist 75.0.0-SNAPSHOT.1753720955374 → 75.0.0-SNAPSHOT.1753726213836

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
@@ -37,6 +37,10 @@
37
37
 
38
38
  ### ⚙️ Technical
39
39
 
40
+ * WebSockets are now enabled by default for client apps, as they have been on the server since Hoist
41
+ Core v20.2. Maintaining a WebSocket connection back to the Hoist server enables useful Admin
42
+ Console functionality and is recommended, but clients that must disable WebSockets can do so via
43
+ `AppSpec.disableWebSockets`. Note `AppSpec.enableWebSockets` is deprecated and can be removed.
40
44
  * Hoist now sets a reference to an app's singleton `AuthModel` on a static `instance` property of
41
45
  the app-specified class. App developers can declare a typed static `instance` property on their
42
46
  model class and use it to access the singleton with its proper type, vs. `XH.authModel`.
@@ -1,85 +1,103 @@
1
- import { HoistAppModel, HoistAuthModel, ElementFactory, HoistProps } from '@xh/hoist/core';
1
+ import { ElementFactory, HoistAppModel, HoistAuthModel, HoistProps } from '@xh/hoist/core';
2
2
  import { Component, ComponentClass, FunctionComponent } from 'react';
3
3
  /**
4
- * Specification for a client-side Hoist application.
5
- * Passed to XH.renderApp() to kick-off app rendering and available thereafter as `XH.appSpec`.
4
+ * Spec for a client-side Hoist application. A config matching this class's shape is provided
5
+ * to {@link XHApi.renderApp} to kick-off app rendering and is available thereafter as `XH.appSpec`.
6
6
  */
7
7
  export declare class AppSpec<T extends HoistAppModel = HoistAppModel> {
8
+ /**
9
+ * AuthModel class for the application. Note this is a reference to the class itself, not an
10
+ * instance, and must extend {@link HoistAuthModel}. If not provided, Hoist will fallback to
11
+ * the superclass implementation, but note that class would only be suitable for fully
12
+ * transparent SSO based solutions such as NTLM. Common patterns such as OAuth will require an
13
+ * extended implementation.
14
+ */
15
+ authModelClass?: new () => HoistAuthModel;
16
+ /**
17
+ * Method for determining if user may access the app. If a string, will be interpreted as the
18
+ * role required for basic UI access. Return false to show a generated lockout message, or use
19
+ * the object form to provide a custom message.
20
+ */
21
+ checkAccess: string | ((user: object) => boolean | {
22
+ hasAccess: boolean;
23
+ message: string;
24
+ });
8
25
  /**
9
26
  * Short code for this particular JS client application.
27
+ *
10
28
  * Will default to the `appCode` specified within the project's Webpack config, but can be
11
29
  * set to a more specific value (e.g. 'myAppMobile') to identify the client app in common
12
- * code or configs.
30
+ * code or configs that support distinct settings for different client apps.
13
31
  */
14
32
  clientAppCode?: string;
15
33
  /**
16
34
  * Display name for this particular JS client application.
35
+ *
17
36
  * As with `clientAppCode` above, this will default to the global `appName` specified by
18
37
  * the project's Webpack config, but can be set here to a more specific value (e.g.
19
38
  * 'MyApp Mobile').
20
39
  */
21
40
  clientAppName?: string;
22
41
  /**
23
- * Root Model class for the application. Note this is a reference to the class itself, not an
24
- * instance, and must extend {@link HoistAppModel}.
25
- */
26
- modelClass: new () => T;
27
- /**
28
- * AuthModel class for the application. Note this is a reference to the class itself, not an
29
- * instance, and must extend {@link HoistAuthModel}.
30
- */
31
- authModelClass?: new () => HoistAuthModel;
32
- /**
33
- * Root HoistComponent for the application. Despite the name, functional components are fully
34
- * supported and expected.
42
+ * Root HoistComponent for the application.
43
+ * Despite the name, functional components are fully supported and expected.
35
44
  */
36
45
  componentClass: ComponentClass<HoistProps> | FunctionComponent<HoistProps>;
37
46
  /**
38
47
  * Container component to be used to host this application.
39
- * This class determines the platform used by Hoist. The value should be imported from
48
+ *
49
+ * This class determines the platform used by Hoist - almost all apps will import and specify
40
50
  * either `@xh/hoist/desktop/AppContainer` or `@xh/hoist/mobile/AppContainer`.
41
51
  */
42
52
  containerClass: ComponentClass<HoistProps> | FunctionComponent<HoistProps>;
43
- /** True if the app should use the Hoist mobile toolkit.*/
44
- isMobileApp: boolean;
45
- /** True to show a login form on initialization when not authenticated. (default false) */
46
- enableLoginForm?: boolean;
47
53
  /**
48
- * True to show logout options in the app, for apps with auth schemes that can support this
49
- * operation (e.g. OAuth). (default false)
54
+ * True to disable Hoist's built-in WebSocket support for this client app. Even if the app
55
+ * itself is not using WebSockets for business data, the Hoist Admin Console's "Clients" tab and
56
+ * related functionality benefit from having them enabled, so disable only if there is a good
57
+ * reason to do so.
50
58
  */
51
- enableLogout?: boolean;
59
+ disableWebSockets?: boolean;
52
60
  /**
53
- * Method for determining if user may access the app.
54
- * If a string, will be interpreted as the role required for basic UI access.
61
+ * True to disable Field-level XSS protection by default across all Stores/Fields in the app.
62
+ * For use with secure, internal apps that do not display arbitrary/external user input and
63
+ * have tight performance tolerances and/or load very large record sets.
64
+ * @see FieldSpec.disableXssProtection
55
65
  */
56
- checkAccess: string | ((user: object) => boolean | {
57
- hasAccess: boolean;
58
- message: string;
59
- });
66
+ disableXssProtection?: boolean;
60
67
  /**
61
- * Write a track log statement after the app has loaded and fully initialized?
62
- * Message will include elapsed time of asset loading and init.
68
+ * True to show a login form on initialization when not authenticated. Default is `false` as
69
+ * most Hoist applications are expected to use OAuth or SSO for authn.
63
70
  */
64
- trackAppLoad?: boolean;
65
- /** True to enable Hoist websocket connectivity. (default false) */
66
- webSocketsEnabled?: boolean;
71
+ enableLoginForm?: boolean;
67
72
  /**
68
- * Optional custom Component to display when App has been suspended. The component will
69
- * receive a single prop -- onReactivate -- a callback called when the user has acknowledged
73
+ * True to show logout options in the app, for apps with auth schemes that can support this
74
+ * operation (e.g. OAuth). Default is `false` as most Hoist applications are expected to use
75
+ * SSO or to run in internal environments where logout is not required / typical.
76
+ */
77
+ enableLogout?: boolean;
78
+ /**
79
+ * Optional custom Component to display when the app has been suspended. The component will
80
+ * receive a single `onReactivate` prop, a no-arg callback fired when the user has acknowledged
70
81
  * the suspension and wishes to reload the app.
71
82
  */
72
83
  idlePanel?: ElementFactory | FunctionComponent | Component;
84
+ /** True if the app should use the Hoist mobile toolkit.*/
85
+ isMobileApp: boolean;
73
86
  /**
74
87
  * Optional custom Component to display when the user is denied access to app. Intended for
75
88
  * apps that implement custom auth flows. See also `lockoutMessage` for a more lightweight
76
89
  * customization option.
77
90
  */
78
91
  lockoutPanel?: ElementFactory | FunctionComponent | Component;
79
- /** Optional message to show on login form (see showLoginForm). */
80
- loginMessage?: string;
81
92
  /** Optional message to show users when denied access to app. */
82
93
  lockoutMessage?: string;
94
+ /** Optional message to show on login form, if `showLoginForm: true`. */
95
+ loginMessage?: string;
96
+ /**
97
+ * Root Model class for the application. Note this is a reference to the class itself, not an
98
+ * instance, and must extend {@link HoistAppModel}.
99
+ */
100
+ modelClass: new () => T;
83
101
  /**
84
102
  * True to show the built-in browser context menu when no app-specific menu would be shown
85
103
  * (e.g. from a Grid). False (the default) prevents the browser menu from being shown anywhere
@@ -87,30 +105,31 @@ export declare class AppSpec<T extends HoistAppModel = HoistAppModel> {
87
105
  */
88
106
  showBrowserContextMenu?: boolean;
89
107
  /**
90
- * True to disable Field-level XSS protection by default across all Stores/Fields in the app.
91
- * For use with secure, internal apps that do not display arbitrary/external user input and
92
- * have tight performance tolerances and/or load very large record sets.
93
- * @see FieldConfig.disableXssProtection
108
+ * True (default) to write a track log statement after the app has loaded and fully
109
+ * initialized, including a breakdown of elapsed time throughout the init process.
94
110
  */
95
- disableXssProtection?: boolean;
96
- constructor({ clientAppCode, clientAppName, modelClass, componentClass, containerClass, authModelClass, isMobileApp, checkAccess, enableLoginForm, enableLogout, trackAppLoad, webSocketsEnabled, idlePanel, lockoutPanel, loginMessage, lockoutMessage, showBrowserContextMenu, disableXssProtection }: {
111
+ trackAppLoad?: boolean;
112
+ /** @deprecated - use {@link AppSpec.disableWebSockets} instead. */
113
+ webSocketsEnabled?: boolean;
114
+ constructor({ authModelClass, checkAccess, clientAppCode, clientAppName, componentClass, containerClass, disableWebSockets, disableXssProtection, enableLoginForm, enableLogout, idlePanel, isMobileApp, lockoutMessage, lockoutPanel, loginMessage, modelClass, showBrowserContextMenu, trackAppLoad, webSocketsEnabled }: {
115
+ authModelClass?: typeof HoistAuthModel;
116
+ checkAccess: any;
97
117
  clientAppCode?: string;
98
118
  clientAppName?: string;
99
- modelClass: any;
100
119
  componentClass: any;
101
120
  containerClass: any;
102
- authModelClass?: typeof HoistAuthModel;
103
- isMobileApp: any;
104
- checkAccess: any;
121
+ disableWebSockets?: boolean;
122
+ disableXssProtection?: boolean;
105
123
  enableLoginForm?: boolean;
106
124
  enableLogout?: boolean;
107
- trackAppLoad?: boolean;
108
- webSocketsEnabled?: boolean;
109
125
  idlePanel?: any;
126
+ isMobileApp: any;
127
+ lockoutMessage?: any;
110
128
  lockoutPanel?: any;
111
129
  loginMessage?: any;
112
- lockoutMessage?: any;
130
+ modelClass: any;
113
131
  showBrowserContextMenu?: boolean;
114
- disableXssProtection?: boolean;
132
+ trackAppLoad?: boolean;
133
+ webSocketsEnabled: any;
115
134
  });
116
135
  }
@@ -2,13 +2,13 @@ import { HoistModel, PlainObject } from './';
2
2
  /**
3
3
  * Base class for managing authentication lifecycle.
4
4
  *
5
- * Hoist will consult this model early in the initialization sequence, prior to full Hoist
5
+ * Hoist will consult this model early in the initialization sequence, prior to full Hoist
6
6
  * initialization. This means that several core services (identity, configs, prefs) will *not* be
7
7
  * available, but it provides the app a hook to do early service initialization or other work to
8
8
  * support flows such as OAuth.
9
9
  *
10
10
  * The base implementation of this class is minimal and would be adequate only for fully
11
- * transparent SSO based solutions such as NTLM. Applications wishing to provide custom
11
+ * transparent SSO based solutions such as NTLM. Applications wishing to provide custom
12
12
  * authentication should spec an extended version of this class in the {@link AppSpec} passed to
13
13
  * {@link XHApi#renderApp}.
14
14
  */
@@ -24,14 +24,14 @@ import { HoistService, PlainObject } from '@xh/hoist/core';
24
24
  */
25
25
  export declare class WebSocketService extends HoistService {
26
26
  static instance: WebSocketService;
27
- readonly HEARTBEAT_TOPIC = "xhHeartbeat";
28
27
  /** Check connection and send a new heartbeat (which should be promptly ack'd) every 10s. */
28
+ readonly HEARTBEAT_TOPIC = "xhHeartbeat";
29
29
  readonly HEARTBEAT_INTERVAL: number;
30
30
  readonly REG_SUCCESS_TOPIC = "xhRegistrationSuccess";
31
31
  readonly FORCE_APP_SUSPEND_TOPIC = "xhForceAppSuspend";
32
32
  readonly REQ_CLIENT_HEALTH_RPT_TOPIC = "xhRequestClientHealthReport";
33
33
  readonly METADATA_FOR_HANDSHAKE: string[];
34
- /** True if WebSockets generally enabled - set statically in code via {@link AppSpec}. */
34
+ /** True if WebSockets not explicitly disabled via {@link AppSpec.disableWebSockets}. */
35
35
  enabled: boolean;
36
36
  /** Unique channel assigned by server upon successful connection. */
37
37
  channelKey: string;
package/core/AppSpec.ts CHANGED
@@ -4,26 +4,44 @@
4
4
  *
5
5
  * Copyright © 2025 Extremely Heavy Industries Inc.
6
6
  */
7
- import {XH, HoistAppModel, HoistAuthModel, ElementFactory, HoistProps} from '@xh/hoist/core';
8
- import {throwIf} from '@xh/hoist/utils/js';
9
- import {isFunction, isNil, isString} from 'lodash';
7
+ import {ElementFactory, HoistAppModel, HoistAuthModel, HoistProps, XH} from '@xh/hoist/core';
8
+ import {apiDeprecated, throwIf} from '@xh/hoist/utils/js';
9
+ import {isFunction, isNil, isString, isUndefined} from 'lodash';
10
10
  import {Component, ComponentClass, FunctionComponent} from 'react';
11
11
 
12
12
  /**
13
- * Specification for a client-side Hoist application.
14
- * Passed to XH.renderApp() to kick-off app rendering and available thereafter as `XH.appSpec`.
13
+ * Spec for a client-side Hoist application. A config matching this class's shape is provided
14
+ * to {@link XHApi.renderApp} to kick-off app rendering and is available thereafter as `XH.appSpec`.
15
15
  */
16
16
  export class AppSpec<T extends HoistAppModel = HoistAppModel> {
17
+ /**
18
+ * AuthModel class for the application. Note this is a reference to the class itself, not an
19
+ * instance, and must extend {@link HoistAuthModel}. If not provided, Hoist will fallback to
20
+ * the superclass implementation, but note that class would only be suitable for fully
21
+ * transparent SSO based solutions such as NTLM. Common patterns such as OAuth will require an
22
+ * extended implementation.
23
+ */
24
+ authModelClass?: new () => HoistAuthModel;
25
+
26
+ /**
27
+ * Method for determining if user may access the app. If a string, will be interpreted as the
28
+ * role required for basic UI access. Return false to show a generated lockout message, or use
29
+ * the object form to provide a custom message.
30
+ */
31
+ checkAccess: string | ((user: object) => boolean | {hasAccess: boolean; message: string});
32
+
17
33
  /**
18
34
  * Short code for this particular JS client application.
35
+ *
19
36
  * Will default to the `appCode` specified within the project's Webpack config, but can be
20
37
  * set to a more specific value (e.g. 'myAppMobile') to identify the client app in common
21
- * code or configs.
38
+ * code or configs that support distinct settings for different client apps.
22
39
  */
23
40
  clientAppCode?: string;
24
41
 
25
42
  /**
26
43
  * Display name for this particular JS client application.
44
+ *
27
45
  * As with `clientAppCode` above, this will default to the global `appName` specified by
28
46
  * the project's Webpack config, but can be set here to a more specific value (e.g.
29
47
  * 'MyApp Mobile').
@@ -31,64 +49,58 @@ export class AppSpec<T extends HoistAppModel = HoistAppModel> {
31
49
  clientAppName?: string;
32
50
 
33
51
  /**
34
- * Root Model class for the application. Note this is a reference to the class itself, not an
35
- * instance, and must extend {@link HoistAppModel}.
36
- */
37
- modelClass: new () => T;
38
-
39
- /**
40
- * AuthModel class for the application. Note this is a reference to the class itself, not an
41
- * instance, and must extend {@link HoistAuthModel}.
42
- */
43
- authModelClass?: new () => HoistAuthModel;
44
-
45
- /**
46
- * Root HoistComponent for the application. Despite the name, functional components are fully
47
- * supported and expected.
52
+ * Root HoistComponent for the application.
53
+ * Despite the name, functional components are fully supported and expected.
48
54
  */
49
55
  componentClass: ComponentClass<HoistProps> | FunctionComponent<HoistProps>;
50
56
 
51
57
  /**
52
58
  * Container component to be used to host this application.
53
- * This class determines the platform used by Hoist. The value should be imported from
59
+ *
60
+ * This class determines the platform used by Hoist - almost all apps will import and specify
54
61
  * either `@xh/hoist/desktop/AppContainer` or `@xh/hoist/mobile/AppContainer`.
55
62
  */
56
63
  containerClass: ComponentClass<HoistProps> | FunctionComponent<HoistProps>;
57
64
 
58
- /** True if the app should use the Hoist mobile toolkit.*/
59
- isMobileApp: boolean;
60
-
61
- /** True to show a login form on initialization when not authenticated. (default false) */
62
- enableLoginForm?: boolean;
63
-
64
65
  /**
65
- * True to show logout options in the app, for apps with auth schemes that can support this
66
- * operation (e.g. OAuth). (default false)
66
+ * True to disable Hoist's built-in WebSocket support for this client app. Even if the app
67
+ * itself is not using WebSockets for business data, the Hoist Admin Console's "Clients" tab and
68
+ * related functionality benefit from having them enabled, so disable only if there is a good
69
+ * reason to do so.
67
70
  */
68
- enableLogout?: boolean;
71
+ disableWebSockets?: boolean;
69
72
 
70
73
  /**
71
- * Method for determining if user may access the app.
72
- * If a string, will be interpreted as the role required for basic UI access.
74
+ * True to disable Field-level XSS protection by default across all Stores/Fields in the app.
75
+ * For use with secure, internal apps that do not display arbitrary/external user input and
76
+ * have tight performance tolerances and/or load very large record sets.
77
+ * @see FieldSpec.disableXssProtection
73
78
  */
74
- checkAccess: string | ((user: object) => boolean | {hasAccess: boolean; message: string});
79
+ disableXssProtection?: boolean;
75
80
 
76
81
  /**
77
- * Write a track log statement after the app has loaded and fully initialized?
78
- * Message will include elapsed time of asset loading and init.
82
+ * True to show a login form on initialization when not authenticated. Default is `false` as
83
+ * most Hoist applications are expected to use OAuth or SSO for authn.
79
84
  */
80
- trackAppLoad?: boolean;
85
+ enableLoginForm?: boolean;
81
86
 
82
- /** True to enable Hoist websocket connectivity. (default false) */
83
- webSocketsEnabled?: boolean;
87
+ /**
88
+ * True to show logout options in the app, for apps with auth schemes that can support this
89
+ * operation (e.g. OAuth). Default is `false` as most Hoist applications are expected to use
90
+ * SSO or to run in internal environments where logout is not required / typical.
91
+ */
92
+ enableLogout?: boolean;
84
93
 
85
94
  /**
86
- * Optional custom Component to display when App has been suspended. The component will
87
- * receive a single prop -- onReactivate -- a callback called when the user has acknowledged
95
+ * Optional custom Component to display when the app has been suspended. The component will
96
+ * receive a single `onReactivate` prop, a no-arg callback fired when the user has acknowledged
88
97
  * the suspension and wishes to reload the app.
89
98
  */
90
99
  idlePanel?: ElementFactory | FunctionComponent | Component;
91
100
 
101
+ /** True if the app should use the Hoist mobile toolkit.*/
102
+ isMobileApp: boolean;
103
+
92
104
  /**
93
105
  * Optional custom Component to display when the user is denied access to app. Intended for
94
106
  * apps that implement custom auth flows. See also `lockoutMessage` for a more lightweight
@@ -96,12 +108,18 @@ export class AppSpec<T extends HoistAppModel = HoistAppModel> {
96
108
  */
97
109
  lockoutPanel?: ElementFactory | FunctionComponent | Component;
98
110
 
99
- /** Optional message to show on login form (see showLoginForm). */
100
- loginMessage?: string;
101
-
102
111
  /** Optional message to show users when denied access to app. */
103
112
  lockoutMessage?: string;
104
113
 
114
+ /** Optional message to show on login form, if `showLoginForm: true`. */
115
+ loginMessage?: string;
116
+
117
+ /**
118
+ * Root Model class for the application. Note this is a reference to the class itself, not an
119
+ * instance, and must extend {@link HoistAppModel}.
120
+ */
121
+ modelClass: new () => T;
122
+
105
123
  /**
106
124
  * True to show the built-in browser context menu when no app-specific menu would be shown
107
125
  * (e.g. from a Grid). False (the default) prevents the browser menu from being shown anywhere
@@ -110,67 +128,80 @@ export class AppSpec<T extends HoistAppModel = HoistAppModel> {
110
128
  showBrowserContextMenu?: boolean;
111
129
 
112
130
  /**
113
- * True to disable Field-level XSS protection by default across all Stores/Fields in the app.
114
- * For use with secure, internal apps that do not display arbitrary/external user input and
115
- * have tight performance tolerances and/or load very large record sets.
116
- * @see FieldConfig.disableXssProtection
131
+ * True (default) to write a track log statement after the app has loaded and fully
132
+ * initialized, including a breakdown of elapsed time throughout the init process.
117
133
  */
118
- disableXssProtection?: boolean;
134
+ trackAppLoad?: boolean;
135
+
136
+ /** @deprecated - use {@link AppSpec.disableWebSockets} instead. */
137
+ webSocketsEnabled?: boolean;
119
138
 
120
139
  constructor({
140
+ authModelClass = HoistAuthModel,
141
+ checkAccess,
121
142
  clientAppCode = XH.appCode,
122
143
  clientAppName = XH.appName,
123
- modelClass,
124
144
  componentClass,
125
145
  containerClass,
126
- authModelClass = HoistAuthModel,
127
- isMobileApp,
128
- checkAccess,
146
+ disableWebSockets = false,
147
+ disableXssProtection = false,
129
148
  enableLoginForm = false,
130
149
  enableLogout = false,
131
- trackAppLoad = true,
132
- webSocketsEnabled = false,
133
150
  idlePanel = null,
151
+ isMobileApp,
152
+ lockoutMessage = null,
134
153
  lockoutPanel = null,
135
154
  loginMessage = null,
136
- lockoutMessage = null,
155
+ modelClass,
137
156
  showBrowserContextMenu = false,
138
- disableXssProtection = false
157
+ trackAppLoad = true,
158
+ webSocketsEnabled
139
159
  }) {
140
- throwIf(!modelClass, 'A Hoist App must define a modelClass.');
141
160
  throwIf(!componentClass, 'A Hoist App must define a componentClass');
142
- throwIf(isNil(isMobileApp), 'A Hoist App must define isMobileApp');
161
+
143
162
  throwIf(
144
163
  !containerClass,
145
- `Please import and provide containerClass from "@xh/hoist/${
146
- isMobileApp ? 'mobile' : 'desktop'
147
- }/AppContainer".`
164
+ `Please import and provide containerClass from "@xh/hoist/${isMobileApp ? 'mobile' : 'desktop'}/AppContainer".`
148
165
  );
149
166
 
167
+ throwIf(!modelClass, 'A Hoist App must define a modelClass.');
168
+
169
+ throwIf(isNil(isMobileApp), 'A Hoist App must define isMobileApp');
170
+
150
171
  throwIf(
151
172
  !isString(checkAccess) && !isFunction(checkAccess),
152
173
  'A Hoist App must specify a required role string or a function for checkAccess.'
153
174
  );
154
175
 
176
+ if (!isUndefined(webSocketsEnabled)) {
177
+ let msg: string;
178
+ if (webSocketsEnabled === false) {
179
+ disableWebSockets = true;
180
+ msg = `Specify disableWebSockets: true to continue actively disabling WebSockets if required.`;
181
+ } else {
182
+ msg = `WebSockets are now enabled by default - this property can be safely removed from your appSpec.`;
183
+ }
184
+ apiDeprecated('webSocketsEnabled', {msg, v: 'v78'});
185
+ }
186
+
187
+ this.authModelClass = authModelClass;
188
+ this.checkAccess = checkAccess;
155
189
  this.clientAppCode = clientAppCode;
156
190
  this.clientAppName = clientAppName;
157
-
158
- this.modelClass = modelClass;
159
191
  this.componentClass = componentClass;
160
192
  this.containerClass = containerClass;
161
- this.authModelClass = authModelClass;
162
- this.isMobileApp = isMobileApp;
163
- this.checkAccess = checkAccess;
164
-
165
- this.trackAppLoad = trackAppLoad;
166
- this.webSocketsEnabled = webSocketsEnabled;
193
+ this.disableWebSockets = disableWebSockets;
194
+ this.disableXssProtection = disableXssProtection;
195
+ this.enableLoginForm = enableLoginForm;
196
+ this.enableLogout = enableLogout;
167
197
  this.idlePanel = idlePanel;
198
+ this.isMobileApp = isMobileApp;
199
+ this.lockoutMessage = lockoutMessage;
168
200
  this.lockoutPanel = lockoutPanel;
169
- this.enableLogout = enableLogout;
170
- this.enableLoginForm = enableLoginForm;
171
201
  this.loginMessage = loginMessage;
172
- this.lockoutMessage = lockoutMessage;
202
+ this.modelClass = modelClass;
173
203
  this.showBrowserContextMenu = showBrowserContextMenu;
174
- this.disableXssProtection = disableXssProtection;
204
+ this.trackAppLoad = trackAppLoad;
205
+ this.webSocketsEnabled = !disableWebSockets;
175
206
  }
176
207
  }
@@ -9,13 +9,13 @@ import {HoistModel, PlainObject, XH} from './';
9
9
  /**
10
10
  * Base class for managing authentication lifecycle.
11
11
  *
12
- * Hoist will consult this model early in the initialization sequence, prior to full Hoist
12
+ * Hoist will consult this model early in the initialization sequence, prior to full Hoist
13
13
  * initialization. This means that several core services (identity, configs, prefs) will *not* be
14
14
  * available, but it provides the app a hook to do early service initialization or other work to
15
15
  * support flows such as OAuth.
16
16
  *
17
17
  * The base implementation of this class is minimal and would be adequate only for fully
18
- * transparent SSO based solutions such as NTLM. Applications wishing to provide custom
18
+ * transparent SSO based solutions such as NTLM. Applications wishing to provide custom
19
19
  * authentication should spec an extended version of this class in the {@link AppSpec} passed to
20
20
  * {@link XHApi#renderApp}.
21
21
  */
package/core/XH.ts CHANGED
@@ -386,7 +386,6 @@ export class XHApi {
386
386
  clientAppCode: 'admin',
387
387
  clientAppName: `${this.appName} Admin`,
388
388
  isMobileApp: false,
389
- webSocketsEnabled: true,
390
389
  checkAccess: 'HOIST_ADMIN_READER',
391
390
  ...appSpec
392
391
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xh/hoist",
3
- "version": "75.0.0-SNAPSHOT.1753720955374",
3
+ "version": "75.0.0-SNAPSHOT.1753726213836",
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",
@@ -38,8 +38,8 @@ import {find, pull} from 'lodash';
38
38
  export class WebSocketService extends HoistService {
39
39
  static instance: WebSocketService;
40
40
 
41
- readonly HEARTBEAT_TOPIC = 'xhHeartbeat';
42
41
  /** Check connection and send a new heartbeat (which should be promptly ack'd) every 10s. */
42
+ readonly HEARTBEAT_TOPIC = 'xhHeartbeat';
43
43
  readonly HEARTBEAT_INTERVAL = 10 * SECONDS;
44
44
 
45
45
  readonly REG_SUCCESS_TOPIC = 'xhRegistrationSuccess';
@@ -47,8 +47,8 @@ export class WebSocketService extends HoistService {
47
47
  readonly REQ_CLIENT_HEALTH_RPT_TOPIC = 'xhRequestClientHealthReport';
48
48
  readonly METADATA_FOR_HANDSHAKE = ['appVersion', 'appBuild', 'loadId', 'tabId'];
49
49
 
50
- /** True if WebSockets generally enabled - set statically in code via {@link AppSpec}. */
51
- enabled: boolean = XH.appSpec.webSocketsEnabled;
50
+ /** True if WebSockets not explicitly disabled via {@link AppSpec.disableWebSockets}. */
51
+ enabled: boolean = !XH.appSpec.disableWebSockets;
52
52
 
53
53
  /** Unique channel assigned by server upon successful connection. */
54
54
  @observable channelKey: string = null;
@@ -84,7 +84,7 @@ export class WebSocketService extends HoistService {
84
84
  const {environmentService} = XH;
85
85
  if (environmentService.get('webSocketsEnabled') === false) {
86
86
  this.logError(
87
- `WebSockets enabled on this client app but disabled on server. Adjust your server-side config.`
87
+ `WebSockets enabled on this client app but disabled on server - unexpected! WebSockets will not be available, review and reconcile your server configuration.`
88
88
  );
89
89
  this.enabled = false;
90
90
  return;