@xh/hoist 75.0.0-SNAPSHOT.1753722797428 → 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 +4 -0
- package/build/types/core/AppSpec.d.ts +12 -4
- package/build/types/svc/WebSocketService.d.ts +2 -2
- package/core/AppSpec.ts +27 -6
- package/core/XH.ts +0 -1
- package/package.json +1 -1
- package/svc/WebSocketService.ts +4 -4
- package/tsconfig.tsbuildinfo +1 -1
- package/utils/js/LangUtils.ts +1 -1
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,4 +1,4 @@
|
|
|
1
|
-
import { HoistAppModel, HoistAuthModel,
|
|
1
|
+
import { ElementFactory, HoistAppModel, HoistAuthModel, HoistProps } from '@xh/hoist/core';
|
|
2
2
|
import { Component, ComponentClass, FunctionComponent } from 'react';
|
|
3
3
|
/**
|
|
4
4
|
* Spec for a client-side Hoist application. A config matching this class's shape is provided
|
|
@@ -50,6 +50,13 @@ export declare class AppSpec<T extends HoistAppModel = HoistAppModel> {
|
|
|
50
50
|
* either `@xh/hoist/desktop/AppContainer` or `@xh/hoist/mobile/AppContainer`.
|
|
51
51
|
*/
|
|
52
52
|
containerClass: ComponentClass<HoistProps> | FunctionComponent<HoistProps>;
|
|
53
|
+
/**
|
|
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.
|
|
58
|
+
*/
|
|
59
|
+
disableWebSockets?: boolean;
|
|
53
60
|
/**
|
|
54
61
|
* True to disable Field-level XSS protection by default across all Stores/Fields in the app.
|
|
55
62
|
* For use with secure, internal apps that do not display arbitrary/external user input and
|
|
@@ -102,15 +109,16 @@ export declare class AppSpec<T extends HoistAppModel = HoistAppModel> {
|
|
|
102
109
|
* initialized, including a breakdown of elapsed time throughout the init process.
|
|
103
110
|
*/
|
|
104
111
|
trackAppLoad?: boolean;
|
|
105
|
-
/**
|
|
112
|
+
/** @deprecated - use {@link AppSpec.disableWebSockets} instead. */
|
|
106
113
|
webSocketsEnabled?: boolean;
|
|
107
|
-
constructor({ authModelClass, checkAccess, clientAppCode, clientAppName, componentClass, containerClass, disableXssProtection, enableLoginForm, enableLogout, idlePanel, isMobileApp, lockoutMessage, lockoutPanel, loginMessage, modelClass, showBrowserContextMenu, trackAppLoad, webSocketsEnabled }: {
|
|
114
|
+
constructor({ authModelClass, checkAccess, clientAppCode, clientAppName, componentClass, containerClass, disableWebSockets, disableXssProtection, enableLoginForm, enableLogout, idlePanel, isMobileApp, lockoutMessage, lockoutPanel, loginMessage, modelClass, showBrowserContextMenu, trackAppLoad, webSocketsEnabled }: {
|
|
108
115
|
authModelClass?: typeof HoistAuthModel;
|
|
109
116
|
checkAccess: any;
|
|
110
117
|
clientAppCode?: string;
|
|
111
118
|
clientAppName?: string;
|
|
112
119
|
componentClass: any;
|
|
113
120
|
containerClass: any;
|
|
121
|
+
disableWebSockets?: boolean;
|
|
114
122
|
disableXssProtection?: boolean;
|
|
115
123
|
enableLoginForm?: boolean;
|
|
116
124
|
enableLogout?: boolean;
|
|
@@ -122,6 +130,6 @@ export declare class AppSpec<T extends HoistAppModel = HoistAppModel> {
|
|
|
122
130
|
modelClass: any;
|
|
123
131
|
showBrowserContextMenu?: boolean;
|
|
124
132
|
trackAppLoad?: boolean;
|
|
125
|
-
webSocketsEnabled
|
|
133
|
+
webSocketsEnabled: any;
|
|
126
134
|
});
|
|
127
135
|
}
|
|
@@ -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
|
|
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,9 +4,9 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Copyright © 2025 Extremely Heavy Industries Inc.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
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
|
/**
|
|
@@ -62,6 +62,14 @@ export class AppSpec<T extends HoistAppModel = HoistAppModel> {
|
|
|
62
62
|
*/
|
|
63
63
|
containerClass: ComponentClass<HoistProps> | FunctionComponent<HoistProps>;
|
|
64
64
|
|
|
65
|
+
/**
|
|
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.
|
|
70
|
+
*/
|
|
71
|
+
disableWebSockets?: boolean;
|
|
72
|
+
|
|
65
73
|
/**
|
|
66
74
|
* True to disable Field-level XSS protection by default across all Stores/Fields in the app.
|
|
67
75
|
* For use with secure, internal apps that do not display arbitrary/external user input and
|
|
@@ -125,7 +133,7 @@ export class AppSpec<T extends HoistAppModel = HoistAppModel> {
|
|
|
125
133
|
*/
|
|
126
134
|
trackAppLoad?: boolean;
|
|
127
135
|
|
|
128
|
-
/**
|
|
136
|
+
/** @deprecated - use {@link AppSpec.disableWebSockets} instead. */
|
|
129
137
|
webSocketsEnabled?: boolean;
|
|
130
138
|
|
|
131
139
|
constructor({
|
|
@@ -135,6 +143,7 @@ export class AppSpec<T extends HoistAppModel = HoistAppModel> {
|
|
|
135
143
|
clientAppName = XH.appName,
|
|
136
144
|
componentClass,
|
|
137
145
|
containerClass,
|
|
146
|
+
disableWebSockets = false,
|
|
138
147
|
disableXssProtection = false,
|
|
139
148
|
enableLoginForm = false,
|
|
140
149
|
enableLogout = false,
|
|
@@ -146,7 +155,7 @@ export class AppSpec<T extends HoistAppModel = HoistAppModel> {
|
|
|
146
155
|
modelClass,
|
|
147
156
|
showBrowserContextMenu = false,
|
|
148
157
|
trackAppLoad = true,
|
|
149
|
-
webSocketsEnabled
|
|
158
|
+
webSocketsEnabled
|
|
150
159
|
}) {
|
|
151
160
|
throwIf(!componentClass, 'A Hoist App must define a componentClass');
|
|
152
161
|
|
|
@@ -164,12 +173,24 @@ export class AppSpec<T extends HoistAppModel = HoistAppModel> {
|
|
|
164
173
|
'A Hoist App must specify a required role string or a function for checkAccess.'
|
|
165
174
|
);
|
|
166
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
|
+
|
|
167
187
|
this.authModelClass = authModelClass;
|
|
168
188
|
this.checkAccess = checkAccess;
|
|
169
189
|
this.clientAppCode = clientAppCode;
|
|
170
190
|
this.clientAppName = clientAppName;
|
|
171
191
|
this.componentClass = componentClass;
|
|
172
192
|
this.containerClass = containerClass;
|
|
193
|
+
this.disableWebSockets = disableWebSockets;
|
|
173
194
|
this.disableXssProtection = disableXssProtection;
|
|
174
195
|
this.enableLoginForm = enableLoginForm;
|
|
175
196
|
this.enableLogout = enableLogout;
|
|
@@ -181,6 +202,6 @@ export class AppSpec<T extends HoistAppModel = HoistAppModel> {
|
|
|
181
202
|
this.modelClass = modelClass;
|
|
182
203
|
this.showBrowserContextMenu = showBrowserContextMenu;
|
|
183
204
|
this.trackAppLoad = trackAppLoad;
|
|
184
|
-
this.webSocketsEnabled =
|
|
205
|
+
this.webSocketsEnabled = !disableWebSockets;
|
|
185
206
|
}
|
|
186
207
|
}
|
package/core/XH.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xh/hoist",
|
|
3
|
-
"version": "75.0.0-SNAPSHOT.
|
|
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",
|
package/svc/WebSocketService.ts
CHANGED
|
@@ -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
|
|
51
|
-
enabled: boolean = XH.appSpec.
|
|
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
|
|
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;
|