@theia/core 1.73.0-next.7 → 1.73.0
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/README.md +26 -16
- package/i18n/nls.cs.json +268 -36
- package/i18n/nls.de.json +268 -36
- package/i18n/nls.es.json +268 -36
- package/i18n/nls.fr.json +268 -36
- package/i18n/nls.hu.json +268 -36
- package/i18n/nls.it.json +268 -36
- package/i18n/nls.ja.json +268 -36
- package/i18n/nls.json +268 -36
- package/i18n/nls.ko.json +268 -36
- package/i18n/nls.pl.json +268 -36
- package/i18n/nls.pt-br.json +268 -36
- package/i18n/nls.ru.json +268 -36
- package/i18n/nls.tr.json +268 -36
- package/i18n/nls.zh-cn.json +268 -36
- package/i18n/nls.zh-tw.json +268 -36
- package/lib/browser/authentication-service.d.ts +2 -0
- package/lib/browser/authentication-service.d.ts.map +1 -1
- package/lib/browser/authentication-service.js +8 -2
- package/lib/browser/authentication-service.js.map +1 -1
- package/lib/browser/catalog.json +251 -72
- package/lib/browser/frontend-application.d.ts +2 -0
- package/lib/browser/frontend-application.d.ts.map +1 -1
- package/lib/browser/frontend-application.js +15 -9
- package/lib/browser/frontend-application.js.map +1 -1
- package/lib/browser/json-schema-store.d.ts +2 -0
- package/lib/browser/json-schema-store.d.ts.map +1 -1
- package/lib/browser/json-schema-store.js +8 -2
- package/lib/browser/json-schema-store.js.map +1 -1
- package/lib/browser/keybinding.js +2 -1
- package/lib/browser/keybinding.js.map +1 -1
- package/lib/browser/markdown-rendering/markdown.d.ts +1 -1
- package/lib/browser/markdown-rendering/markdown.d.ts.map +1 -1
- package/lib/browser/markdown-rendering/markdown.js +1 -1
- package/lib/browser/markdown-rendering/markdown.js.map +1 -1
- package/lib/browser/menu/composite-menu-node.js +4 -4
- package/lib/browser/menu/composite-menu-node.js.map +1 -1
- package/lib/browser/messaging/ws-connection-source.d.ts.map +1 -1
- package/lib/browser/messaging/ws-connection-source.js +1 -6
- package/lib/browser/messaging/ws-connection-source.js.map +1 -1
- package/lib/browser/preferences/frontend-config-preference-contributions.d.ts +2 -0
- package/lib/browser/preferences/frontend-config-preference-contributions.d.ts.map +1 -1
- package/lib/browser/preferences/frontend-config-preference-contributions.js +7 -1
- package/lib/browser/preferences/frontend-config-preference-contributions.js.map +1 -1
- package/lib/browser/preferences/preference-proxy.spec.js +7 -0
- package/lib/browser/preferences/preference-proxy.spec.js.map +1 -1
- package/lib/browser/preferences/preference-schema-provider.spec.js +3 -0
- package/lib/browser/preferences/preference-schema-provider.spec.js.map +1 -1
- package/lib/browser/preferences/preference-validation-service.d.ts +2 -0
- package/lib/browser/preferences/preference-validation-service.d.ts.map +1 -1
- package/lib/browser/preferences/preference-validation-service.js +11 -5
- package/lib/browser/preferences/preference-validation-service.js.map +1 -1
- package/lib/browser/preferences/preference-validation-service.spec.js +2 -0
- package/lib/browser/preferences/preference-validation-service.spec.js.map +1 -1
- package/lib/browser/secondary-window-handler.d.ts +2 -0
- package/lib/browser/secondary-window-handler.d.ts.map +1 -1
- package/lib/browser/secondary-window-handler.js +12 -6
- package/lib/browser/secondary-window-handler.js.map +1 -1
- package/lib/browser/shell/application-shell.d.ts +2 -0
- package/lib/browser/shell/application-shell.d.ts.map +1 -1
- package/lib/browser/shell/application-shell.js +9 -3
- package/lib/browser/shell/application-shell.js.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.js +6 -6
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.js.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.js +2 -2
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.js.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.js +131 -0
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.js.map +1 -1
- package/lib/browser/status-bar/status-bar.d.ts +2 -2
- package/lib/browser/status-bar/status-bar.d.ts.map +1 -1
- package/lib/browser/status-bar/status-bar.js.map +1 -1
- package/lib/browser/test/jsdom.d.ts +4 -0
- package/lib/browser/test/jsdom.d.ts.map +1 -1
- package/lib/browser/test/jsdom.js +18 -0
- package/lib/browser/test/jsdom.js.map +1 -1
- package/lib/browser/tree/tree-container.spec.js +4 -0
- package/lib/browser/tree/tree-container.spec.js.map +1 -1
- package/lib/browser/tree/tree-model.d.ts +1 -0
- package/lib/browser/tree/tree-model.d.ts.map +1 -1
- package/lib/browser/tree/tree-model.js +3 -0
- package/lib/browser/tree/tree-model.js.map +1 -1
- package/lib/browser/tree/tree-view-welcome-widget.d.ts +1 -0
- package/lib/browser/tree/tree-view-welcome-widget.d.ts.map +1 -1
- package/lib/browser/tree/tree-view-welcome-widget.js +8 -6
- package/lib/browser/tree/tree-view-welcome-widget.js.map +1 -1
- package/lib/browser/tree/tree-view-welcome-widget.spec.d.ts +2 -0
- package/lib/browser/tree/tree-view-welcome-widget.spec.d.ts.map +1 -0
- package/lib/browser/tree/tree-view-welcome-widget.spec.js +68 -0
- package/lib/browser/tree/tree-view-welcome-widget.spec.js.map +1 -0
- package/lib/browser/tree/tree-widget.js +2 -2
- package/lib/browser/tree/tree-widget.js.map +1 -1
- package/lib/browser/tree/tree.d.ts +24 -2
- package/lib/browser/tree/tree.d.ts.map +1 -1
- package/lib/browser/tree/tree.js +20 -2
- package/lib/browser/tree/tree.js.map +1 -1
- package/lib/browser/tree/tree.spec.js +27 -0
- package/lib/browser/tree/tree.spec.js.map +1 -1
- package/lib/browser/window/default-window-service.d.ts +2 -1
- package/lib/browser/window/default-window-service.d.ts.map +1 -1
- package/lib/browser/window/default-window-service.js +10 -5
- package/lib/browser/window/default-window-service.js.map +1 -1
- package/lib/browser/window/default-window-service.spec.js +2 -0
- package/lib/browser/window/default-window-service.spec.js.map +1 -1
- package/lib/browser-only/messaging/frontend-only-service-connection-provider.d.ts +2 -0
- package/lib/browser-only/messaging/frontend-only-service-connection-provider.d.ts.map +1 -1
- package/lib/browser-only/messaging/frontend-only-service-connection-provider.js +8 -2
- package/lib/browser-only/messaging/frontend-only-service-connection-provider.js.map +1 -1
- package/lib/common/command.d.ts +2 -0
- package/lib/common/command.d.ts.map +1 -1
- package/lib/common/command.js +10 -4
- package/lib/common/command.js.map +1 -1
- package/lib/common/preferences/injectable-preference-proxy.d.ts +3 -3
- package/lib/common/preferences/injectable-preference-proxy.d.ts.map +1 -1
- package/lib/common/preferences/injectable-preference-proxy.js +6 -1
- package/lib/common/preferences/injectable-preference-proxy.js.map +1 -1
- package/lib/common/preferences/preference-proxy.d.ts.map +1 -1
- package/lib/common/preferences/preference-proxy.js +6 -1
- package/lib/common/preferences/preference-proxy.js.map +1 -1
- package/lib/electron-main/electron-main-application.d.ts.map +1 -1
- package/lib/electron-main/electron-main-application.js +4 -2
- package/lib/electron-main/electron-main-application.js.map +1 -1
- package/lib/electron-node/hosting/electron-ws-origin-validator.d.ts.map +1 -1
- package/lib/electron-node/hosting/electron-ws-origin-validator.js +5 -2
- package/lib/electron-node/hosting/electron-ws-origin-validator.js.map +1 -1
- package/lib/electron-node/hosting/electron-ws-origin-validator.spec.d.ts +2 -0
- package/lib/electron-node/hosting/electron-ws-origin-validator.spec.d.ts.map +1 -0
- package/lib/electron-node/hosting/electron-ws-origin-validator.spec.js +59 -0
- package/lib/electron-node/hosting/electron-ws-origin-validator.spec.js.map +1 -0
- package/lib/electron-node/request/electron-backend-request-service.d.ts +2 -0
- package/lib/electron-node/request/electron-backend-request-service.d.ts.map +1 -1
- package/lib/electron-node/request/electron-backend-request-service.js +7 -1
- package/lib/electron-node/request/electron-backend-request-service.js.map +1 -1
- package/lib/electron-node/token/electron-token-backend-contribution.d.ts +2 -0
- package/lib/electron-node/token/electron-token-backend-contribution.d.ts.map +1 -1
- package/lib/electron-node/token/electron-token-backend-contribution.js +7 -1
- package/lib/electron-node/token/electron-token-backend-contribution.js.map +1 -1
- package/lib/electron-node/token/electron-token-validator.d.ts +2 -0
- package/lib/electron-node/token/electron-token-validator.d.ts.map +1 -1
- package/lib/electron-node/token/electron-token-validator.js +7 -1
- package/lib/electron-node/token/electron-token-validator.js.map +1 -1
- package/lib/node/backend-application-module.d.ts.map +1 -1
- package/lib/node/backend-application-module.js +2 -0
- package/lib/node/backend-application-module.js.map +1 -1
- package/lib/node/backend-application.d.ts +11 -0
- package/lib/node/backend-application.d.ts.map +1 -1
- package/lib/node/backend-application.js +40 -12
- package/lib/node/backend-application.js.map +1 -1
- package/lib/node/backend-application.spec.js +4 -2
- package/lib/node/backend-application.spec.js.map +1 -1
- package/lib/node/hosting/backend-hosting-module.d.ts.map +1 -1
- package/lib/node/hosting/backend-hosting-module.js +7 -0
- package/lib/node/hosting/backend-hosting-module.js.map +1 -1
- package/lib/node/hosting/browser-connection-token.d.ts +46 -0
- package/lib/node/hosting/browser-connection-token.d.ts.map +1 -0
- package/lib/node/hosting/browser-connection-token.js +120 -0
- package/lib/node/hosting/browser-connection-token.js.map +1 -0
- package/lib/node/hosting/browser-connection-token.spec.d.ts +2 -0
- package/lib/node/hosting/browser-connection-token.spec.d.ts.map +1 -0
- package/lib/node/hosting/browser-connection-token.spec.js +128 -0
- package/lib/node/hosting/browser-connection-token.spec.js.map +1 -0
- package/lib/node/hosting/ws-origin-validator.d.ts.map +1 -1
- package/lib/node/hosting/ws-origin-validator.js +21 -4
- package/lib/node/hosting/ws-origin-validator.js.map +1 -1
- package/lib/node/hosting/ws-origin-validator.spec.d.ts +2 -0
- package/lib/node/hosting/ws-origin-validator.spec.d.ts.map +1 -0
- package/lib/node/hosting/ws-origin-validator.spec.js +131 -0
- package/lib/node/hosting/ws-origin-validator.spec.js.map +1 -0
- package/lib/node/index.d.ts +1 -0
- package/lib/node/index.d.ts.map +1 -1
- package/lib/node/index.js +3 -1
- package/lib/node/index.js.map +1 -1
- package/lib/node/logger-cli-contribution.d.ts +6 -0
- package/lib/node/logger-cli-contribution.d.ts.map +1 -1
- package/lib/node/logger-cli-contribution.js +28 -5
- package/lib/node/logger-cli-contribution.js.map +1 -1
- package/lib/node/logger-cli-contribution.spec.js +54 -0
- package/lib/node/logger-cli-contribution.spec.js.map +1 -1
- package/lib/node/messaging/default-messaging-service.d.ts +2 -0
- package/lib/node/messaging/default-messaging-service.d.ts.map +1 -1
- package/lib/node/messaging/default-messaging-service.js +8 -2
- package/lib/node/messaging/default-messaging-service.js.map +1 -1
- package/lib/node/messaging/test/default-messaging-service.spec.js +2 -0
- package/lib/node/messaging/test/default-messaging-service.spec.js.map +1 -1
- package/lib/node/messaging/websocket-endpoint.d.ts +7 -0
- package/lib/node/messaging/websocket-endpoint.d.ts.map +1 -1
- package/lib/node/messaging/websocket-endpoint.js +21 -6
- package/lib/node/messaging/websocket-endpoint.js.map +1 -1
- package/lib/node/process-utils.d.ts +2 -0
- package/lib/node/process-utils.d.ts.map +1 -1
- package/lib/node/process-utils.js +8 -2
- package/lib/node/process-utils.js.map +1 -1
- package/lib/node/process-utils.spec.js +11 -9
- package/lib/node/process-utils.spec.js.map +1 -1
- package/lib/node/request/backend-request-facade.d.ts +19 -0
- package/lib/node/request/backend-request-facade.d.ts.map +1 -1
- package/lib/node/request/backend-request-facade.js +93 -3
- package/lib/node/request/backend-request-facade.js.map +1 -1
- package/lib/node/request/backend-request-facade.spec.d.ts +2 -0
- package/lib/node/request/backend-request-facade.spec.d.ts.map +1 -0
- package/lib/node/request/backend-request-facade.spec.js +171 -0
- package/lib/node/request/backend-request-facade.spec.js.map +1 -0
- package/package.json +29 -20
- package/src/browser/authentication-service.ts +6 -3
- package/src/browser/frontend-application.ts +13 -9
- package/src/browser/json-schema-store.ts +6 -2
- package/src/browser/keybinding.ts +2 -2
- package/src/browser/markdown-rendering/markdown.tsx +2 -2
- package/src/browser/menu/composite-menu-node.ts +4 -4
- package/src/browser/messaging/ws-connection-source.ts +1 -6
- package/src/browser/preferences/frontend-config-preference-contributions.ts +7 -2
- package/src/browser/preferences/preference-proxy.spec.ts +8 -0
- package/src/browser/preferences/preference-schema-provider.spec.ts +5 -0
- package/src/browser/preferences/preference-validation-service.spec.ts +3 -1
- package/src/browser/preferences/preference-validation-service.ts +9 -6
- package/src/browser/secondary-window-handler.ts +11 -7
- package/src/browser/shell/application-shell.ts +8 -4
- package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.tsx +6 -6
- package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts +2 -2
- package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.ts +159 -1
- package/src/browser/status-bar/status-bar.tsx +3 -3
- package/src/browser/test/jsdom.ts +18 -0
- package/src/browser/tree/tree-container.spec.ts +6 -0
- package/src/browser/tree/tree-model.ts +4 -0
- package/src/browser/tree/tree-view-welcome-widget.spec.ts +82 -0
- package/src/browser/tree/tree-view-welcome-widget.tsx +10 -11
- package/src/browser/tree/tree-widget.tsx +2 -2
- package/src/browser/tree/tree.spec.ts +30 -0
- package/src/browser/tree/tree.ts +31 -4
- package/src/browser/window/default-window-service.spec.ts +3 -1
- package/src/browser/window/default-window-service.ts +9 -6
- package/src/browser-only/messaging/frontend-only-service-connection-provider.ts +8 -3
- package/src/common/command.ts +8 -4
- package/src/common/i18n/nls.metadata.json +2659 -374
- package/src/common/preferences/injectable-preference-proxy.ts +10 -4
- package/src/common/preferences/preference-proxy.ts +10 -4
- package/src/electron-main/electron-main-application.ts +4 -2
- package/src/electron-node/hosting/electron-ws-origin-validator.spec.ts +69 -0
- package/src/electron-node/hosting/electron-ws-origin-validator.ts +5 -2
- package/src/electron-node/request/electron-backend-request-service.ts +6 -2
- package/src/electron-node/token/electron-token-backend-contribution.ts +6 -2
- package/src/electron-node/token/electron-token-validator.ts +6 -2
- package/src/node/backend-application-module.ts +4 -2
- package/src/node/backend-application.spec.ts +5 -3
- package/src/node/backend-application.ts +33 -11
- package/src/node/hosting/backend-hosting-module.ts +10 -0
- package/src/node/hosting/browser-connection-token.spec.ts +167 -0
- package/src/node/hosting/browser-connection-token.ts +126 -0
- package/src/node/hosting/ws-origin-validator.spec.ts +193 -0
- package/src/node/hosting/ws-origin-validator.ts +23 -4
- package/src/node/index.ts +1 -0
- package/src/node/logger-cli-contribution.spec.ts +65 -0
- package/src/node/logger-cli-contribution.ts +32 -5
- package/src/node/messaging/default-messaging-service.ts +6 -2
- package/src/node/messaging/test/default-messaging-service.spec.ts +3 -1
- package/src/node/messaging/websocket-endpoint.ts +23 -7
- package/src/node/process-utils.spec.ts +12 -9
- package/src/node/process-utils.ts +7 -3
- package/src/node/request/backend-request-facade.spec.ts +203 -0
- package/src/node/request/backend-request-facade.ts +108 -3
|
@@ -106,7 +106,13 @@ export class InjectablePreferenceProxy<T extends Record<string, JSONValue>> impl
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
get(target: unknown, property: string, receiver: unknown): unknown {
|
|
109
|
+
get(target: unknown, property: string | symbol, receiver: unknown): unknown {
|
|
110
|
+
// React 19 dev mode calls Object.prototype.toString when handling prop diffs, which
|
|
111
|
+
// probes Symbol.toStringTag. Throwing should be avoided in this case, it crashes the React DOM.
|
|
112
|
+
if (property === Symbol.toStringTag) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
110
116
|
if (typeof property !== 'string') {
|
|
111
117
|
throw new Error(`Unexpected property: ${String(property)}`);
|
|
112
118
|
}
|
|
@@ -156,7 +162,7 @@ export class InjectablePreferenceProxy<T extends Record<string, JSONValue>> impl
|
|
|
156
162
|
}
|
|
157
163
|
}
|
|
158
164
|
|
|
159
|
-
set(target: unknown, property: string, value: unknown, receiver: unknown): boolean {
|
|
165
|
+
set(target: unknown, property: string | symbol, value: unknown, receiver: unknown): boolean {
|
|
160
166
|
if (typeof property !== 'string') {
|
|
161
167
|
throw new Error(`Unexpected property: ${String(property)}`);
|
|
162
168
|
}
|
|
@@ -212,8 +218,8 @@ export class InjectablePreferenceProxy<T extends Record<string, JSONValue>> impl
|
|
|
212
218
|
return properties;
|
|
213
219
|
}
|
|
214
220
|
|
|
215
|
-
getOwnPropertyDescriptor(target: unknown, property: string): PropertyDescriptor {
|
|
216
|
-
if (this.ownKeys().includes(property)) {
|
|
221
|
+
getOwnPropertyDescriptor(target: unknown, property: string | symbol): PropertyDescriptor {
|
|
222
|
+
if (typeof property === 'string' && this.ownKeys().includes(property)) {
|
|
217
223
|
return {
|
|
218
224
|
enumerable: true,
|
|
219
225
|
configurable: true
|
|
@@ -239,7 +239,7 @@ export function createPreferenceProxy<T>(preferences: PreferenceService, promise
|
|
|
239
239
|
return properties;
|
|
240
240
|
};
|
|
241
241
|
|
|
242
|
-
const set: (target: any, prop: string, value: any, receiver: any) => boolean = (_, property: string | symbol | number, value: any) => {
|
|
242
|
+
const set: (target: any, prop: string | symbol, value: any, receiver: any) => boolean = (_, property: string | symbol | number, value: any) => {
|
|
243
243
|
if (typeof property !== 'string') {
|
|
244
244
|
throw new Error(`unexpected property: ${String(property)}`);
|
|
245
245
|
}
|
|
@@ -270,7 +270,13 @@ export function createPreferenceProxy<T>(preferences: PreferenceService, promise
|
|
|
270
270
|
return false;
|
|
271
271
|
};
|
|
272
272
|
|
|
273
|
-
const get: (target: any, prop: string) => any = (_, property: string | symbol | number) => {
|
|
273
|
+
const get: (target: any, prop: string | symbol) => any = (_, property: string | symbol | number) => {
|
|
274
|
+
// React 19 dev mode calls Object.prototype.toString when handling prop diffs, which
|
|
275
|
+
// probes Symbol.toStringTag. Throwing should be avoided in this case, it crashes the React DOM.
|
|
276
|
+
if (property === Symbol.toStringTag) {
|
|
277
|
+
return undefined;
|
|
278
|
+
}
|
|
279
|
+
|
|
274
280
|
if (typeof property !== 'string') {
|
|
275
281
|
throw new Error(`unexpected property: ${String(property)}`);
|
|
276
282
|
}
|
|
@@ -347,8 +353,8 @@ export function createPreferenceProxy<T>(preferences: PreferenceService, promise
|
|
|
347
353
|
return new Proxy({}, {
|
|
348
354
|
get,
|
|
349
355
|
ownKeys,
|
|
350
|
-
getOwnPropertyDescriptor: (_, property: string) => {
|
|
351
|
-
if (ownKeys().indexOf(property) !== -1) {
|
|
356
|
+
getOwnPropertyDescriptor: (_, property: string | symbol) => {
|
|
357
|
+
if (typeof property === 'string' && ownKeys().indexOf(property) !== -1) {
|
|
352
358
|
return {
|
|
353
359
|
enumerable: true,
|
|
354
360
|
configurable: true
|
|
@@ -394,6 +394,8 @@ export class ElectronMainApplication {
|
|
|
394
394
|
const cancelTokenSource = new CancellationTokenSource();
|
|
395
395
|
const minTime = timeout(splashScreenOptions.minDuration ?? 0, cancelTokenSource.token);
|
|
396
396
|
const maxTime = timeout(splashScreenOptions.maxDuration ?? 30000, cancelTokenSource.token);
|
|
397
|
+
// Swallow rejections that occur when the cancellation token is cancelled after one of the timers wins.
|
|
398
|
+
const ignoreCancellation = () => { /* timer was cancelled, intentionally ignored */ };
|
|
397
399
|
|
|
398
400
|
const showWindowAndCloseSplashScreen = () => {
|
|
399
401
|
cancelTokenSource.cancel();
|
|
@@ -404,10 +406,10 @@ export class ElectronMainApplication {
|
|
|
404
406
|
};
|
|
405
407
|
TheiaRendererAPI.onApplicationStateChanged(mainWindow.webContents, state => {
|
|
406
408
|
if (state === 'ready') {
|
|
407
|
-
minTime.then(() => showWindowAndCloseSplashScreen());
|
|
409
|
+
minTime.then(() => showWindowAndCloseSplashScreen(), ignoreCancellation);
|
|
408
410
|
}
|
|
409
411
|
});
|
|
410
|
-
maxTime.then(() => showWindowAndCloseSplashScreen());
|
|
412
|
+
maxTime.then(() => showWindowAndCloseSplashScreen(), ignoreCancellation);
|
|
411
413
|
return splashScreenWindow;
|
|
412
414
|
}
|
|
413
415
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 STMicroelectronics and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { expect } from 'chai';
|
|
18
|
+
import * as http from 'http';
|
|
19
|
+
import { ElectronWsOriginValidator } from './electron-ws-origin-validator';
|
|
20
|
+
import { BackendRemoteService } from '../../node/remote/backend-remote-service';
|
|
21
|
+
|
|
22
|
+
describe('ElectronWsOriginValidator', () => {
|
|
23
|
+
|
|
24
|
+
function createValidator(isRemote = false): ElectronWsOriginValidator {
|
|
25
|
+
const remoteService = { isRemoteServer: () => isRemote } as BackendRemoteService;
|
|
26
|
+
const validator = new ElectronWsOriginValidator();
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
+
(validator as any)['backendRemoteService'] = remoteService;
|
|
29
|
+
return validator;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function createRequest(origin?: string): http.IncomingMessage {
|
|
33
|
+
const request = {
|
|
34
|
+
headers: {} as http.IncomingHttpHeaders
|
|
35
|
+
} as http.IncomingMessage;
|
|
36
|
+
if (origin !== undefined) {
|
|
37
|
+
request.headers.origin = origin;
|
|
38
|
+
}
|
|
39
|
+
return request;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
it('should allow file:// origin (standard Electron case)', () => {
|
|
43
|
+
expect(createValidator().allowWsUpgrade(createRequest('file://'))).to.be.true;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should allow null origin (opaque origin from file:// pages)', () => {
|
|
47
|
+
expect(createValidator().allowWsUpgrade(createRequest('null'))).to.be.true;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should allow absent origin (same-origin polling requests)', () => {
|
|
51
|
+
expect(createValidator().allowWsUpgrade(createRequest(undefined))).to.be.true;
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should reject cross-origin requests', () => {
|
|
55
|
+
expect(createValidator().allowWsUpgrade(createRequest('http://evil.com'))).to.be.false;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should reject http://localhost origin (not expected in Electron)', () => {
|
|
59
|
+
expect(createValidator().allowWsUpgrade(createRequest('http://localhost:3000'))).to.be.false;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should allow empty string origin (treated as absent)', () => {
|
|
63
|
+
expect(createValidator().allowWsUpgrade(createRequest(''))).to.be.true;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should allow any origin when running as remote server', () => {
|
|
67
|
+
expect(createValidator(true).allowWsUpgrade(createRequest('http://remote-host.com'))).to.be.true;
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -30,8 +30,11 @@ export class ElectronWsOriginValidator implements WsRequestValidatorContribution
|
|
|
30
30
|
if (this.backendRemoteService.isRemoteServer()) {
|
|
31
31
|
return true;
|
|
32
32
|
}
|
|
33
|
+
const origin = request.headers.origin;
|
|
33
34
|
// On Electron the main page is served from the `file` protocol.
|
|
34
|
-
//
|
|
35
|
-
|
|
35
|
+
// Chromium may send "file://" or "null" (opaque origin) or omit the
|
|
36
|
+
// header entirely depending on the request type (WebSocket upgrade vs
|
|
37
|
+
// HTTP polling). All three are expected for same-origin Electron requests.
|
|
38
|
+
return !origin || origin === 'file://' || origin === 'null';
|
|
36
39
|
}
|
|
37
40
|
}
|
|
@@ -14,15 +14,19 @@
|
|
|
14
14
|
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
********************************************************************************/
|
|
16
16
|
|
|
17
|
-
import { decorate, injectable } from 'inversify';
|
|
17
|
+
import { decorate, injectable, inject, named } from 'inversify';
|
|
18
18
|
import { NodeRequestOptions, NodeRequestService } from '@theia/request/lib/node-request-service';
|
|
19
19
|
import { ElectronSecurityToken } from '../../electron-common/electron-token';
|
|
20
|
+
import { ILogger } from '../../common';
|
|
20
21
|
|
|
21
22
|
decorate(injectable(), NodeRequestService);
|
|
22
23
|
|
|
23
24
|
@injectable()
|
|
24
25
|
export class ElectronBackendRequestService extends NodeRequestService {
|
|
25
26
|
|
|
27
|
+
@inject(ILogger) @named('core:ElectronBackendRequestService')
|
|
28
|
+
protected readonly logger: ILogger;
|
|
29
|
+
|
|
26
30
|
override async getProxyUrl(url: string): Promise<string | undefined> {
|
|
27
31
|
if (this.proxyUrl) {
|
|
28
32
|
return this.proxyUrl;
|
|
@@ -34,7 +38,7 @@ export class ElectronBackendRequestService extends NodeRequestService {
|
|
|
34
38
|
return this.buildProxyUrl(url, proxyHost);
|
|
35
39
|
}
|
|
36
40
|
} catch (e) {
|
|
37
|
-
|
|
41
|
+
this.logger.error('Could not resolve electron proxy.', e);
|
|
38
42
|
}
|
|
39
43
|
return super.getProxyUrl(url);
|
|
40
44
|
}
|
|
@@ -15,9 +15,10 @@
|
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
17
|
import express = require('express');
|
|
18
|
-
import { injectable, inject } from 'inversify';
|
|
18
|
+
import { injectable, inject, named } from 'inversify';
|
|
19
19
|
import { BackendApplicationContribution } from '../../node';
|
|
20
20
|
import { ElectronTokenValidator } from './electron-token-validator';
|
|
21
|
+
import { ILogger } from '../../common/logger';
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* This component contributes an Express middleware that will refuse all
|
|
@@ -26,6 +27,9 @@ import { ElectronTokenValidator } from './electron-token-validator';
|
|
|
26
27
|
@injectable()
|
|
27
28
|
export class ElectronTokenBackendContribution implements BackendApplicationContribution {
|
|
28
29
|
|
|
30
|
+
@inject(ILogger) @named('core:ElectronTokenBackendContribution')
|
|
31
|
+
protected readonly logger: ILogger;
|
|
32
|
+
|
|
29
33
|
@inject(ElectronTokenValidator)
|
|
30
34
|
protected readonly tokenValidator: ElectronTokenValidator;
|
|
31
35
|
|
|
@@ -40,7 +44,7 @@ export class ElectronTokenBackendContribution implements BackendApplicationContr
|
|
|
40
44
|
if (this.tokenValidator.allowRequest(req)) {
|
|
41
45
|
next();
|
|
42
46
|
} else {
|
|
43
|
-
|
|
47
|
+
this.logger.error(`refused an http request: ${req.connection.remoteAddress}`);
|
|
44
48
|
res.sendStatus(403);
|
|
45
49
|
}
|
|
46
50
|
}
|
|
@@ -17,10 +17,11 @@
|
|
|
17
17
|
import * as http from 'http';
|
|
18
18
|
import * as cookie from 'cookie';
|
|
19
19
|
import * as crypto from 'crypto';
|
|
20
|
-
import { injectable, postConstruct } from 'inversify';
|
|
20
|
+
import { injectable, postConstruct, inject, named } from 'inversify';
|
|
21
21
|
import { isObject, isString, MaybePromise } from '../../common';
|
|
22
22
|
import { ElectronSecurityToken } from '../../electron-common/electron-token';
|
|
23
23
|
import { WsRequestValidatorContribution } from '../../node/ws-request-validators';
|
|
24
|
+
import { ILogger } from '../../common/logger';
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* On Electron, we want to make sure that only Electron's browser-windows access the backend services.
|
|
@@ -28,6 +29,9 @@ import { WsRequestValidatorContribution } from '../../node/ws-request-validators
|
|
|
28
29
|
@injectable()
|
|
29
30
|
export class ElectronTokenValidator implements WsRequestValidatorContribution {
|
|
30
31
|
|
|
32
|
+
@inject(ILogger) @named('core:ElectronTokenValidator')
|
|
33
|
+
protected readonly logger: ILogger;
|
|
34
|
+
|
|
31
35
|
protected electronSecurityToken?: ElectronSecurityToken;
|
|
32
36
|
|
|
33
37
|
@postConstruct()
|
|
@@ -70,7 +74,7 @@ export class ElectronTokenValidator implements WsRequestValidatorContribution {
|
|
|
70
74
|
const expected = Buffer.from(this.electronSecurityToken!.value, 'utf8');
|
|
71
75
|
return received.byteLength === expected.byteLength && crypto.timingSafeEqual(received, expected);
|
|
72
76
|
} catch (error) {
|
|
73
|
-
|
|
77
|
+
this.logger.error(error);
|
|
74
78
|
}
|
|
75
79
|
}
|
|
76
80
|
return false;
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
} from '../common';
|
|
37
37
|
import {
|
|
38
38
|
BackendApplication, BackendApplicationContribution, BackendApplicationCliContribution,
|
|
39
|
-
BackendApplicationServer, BackendApplicationPath, RootContainer
|
|
39
|
+
BackendApplicationServer, BackendApplicationPath, EarlyExpressMiddleware, RootContainer
|
|
40
40
|
} from './backend-application';
|
|
41
41
|
import { CliManager, CliContribution } from './cli';
|
|
42
42
|
import { IPCConnectionProvider } from './messaging';
|
|
@@ -55,7 +55,7 @@ import { ProcessUtils } from './process-utils';
|
|
|
55
55
|
import { ProxyCliContribution } from './request/proxy-cli-contribution';
|
|
56
56
|
import { bindNodeStopwatch, bindBackendStopwatchServer } from './performance';
|
|
57
57
|
import { OSBackendProviderImpl } from './os-backend-provider';
|
|
58
|
-
import { BackendRequestFacade } from './request/backend-request-facade';
|
|
58
|
+
import { BackendRequestFacade, BackendRequestAllowedContribution } from './request/backend-request-facade';
|
|
59
59
|
import { FileSystemLocking, FileSystemLockingImpl } from './filesystem-locking';
|
|
60
60
|
import { BackendRemoteService } from './remote/backend-remote-service';
|
|
61
61
|
import { RemoteCliContribution } from './remote/remote-cli-contribution';
|
|
@@ -89,6 +89,7 @@ export const backendApplicationModule = new ContainerModule(bind => {
|
|
|
89
89
|
bind(BackendApplicationCliContribution).toSelf().inSingletonScope();
|
|
90
90
|
bind(CliContribution).toService(BackendApplicationCliContribution);
|
|
91
91
|
|
|
92
|
+
bind(EarlyExpressMiddleware).toSelf().inSingletonScope();
|
|
92
93
|
bind(BackendApplication).toSelf().inSingletonScope();
|
|
93
94
|
bind(RootContainer).toDynamicValue(({ container }) => {
|
|
94
95
|
let root = container;
|
|
@@ -152,6 +153,7 @@ export const backendApplicationModule = new ContainerModule(bind => {
|
|
|
152
153
|
|
|
153
154
|
bindRootContributionProvider(bind, RemoteCliContribution);
|
|
154
155
|
bind(BackendRemoteService).toSelf().inSingletonScope();
|
|
156
|
+
bindRootContributionProvider(bind, BackendRequestAllowedContribution);
|
|
155
157
|
bind(BackendRequestFacade).toSelf().inSingletonScope();
|
|
156
158
|
bind(ConnectionHandler).toDynamicValue(
|
|
157
159
|
ctx => new RpcConnectionHandler(REQUEST_SERVICE_PATH, () => ctx.container.get(BackendRequestFacade))
|
|
@@ -19,16 +19,17 @@ import * as sinon from 'sinon';
|
|
|
19
19
|
import { Container, ContainerModule, injectable, preDestroy } from 'inversify';
|
|
20
20
|
import { bindContributionProvider, ILogger, Stopwatch } from '../common';
|
|
21
21
|
import { Deferred } from '../common/promise-util';
|
|
22
|
-
import { MockLogger } from '../common/test/mock-logger';
|
|
23
22
|
import { NodeStopwatch } from './performance/node-stopwatch';
|
|
24
23
|
import { ProcessUtils } from './process-utils';
|
|
25
24
|
import {
|
|
26
25
|
BackendApplication,
|
|
27
26
|
BackendApplicationCliContribution,
|
|
28
27
|
BackendApplicationContribution,
|
|
28
|
+
EarlyExpressMiddleware,
|
|
29
29
|
RootContainer
|
|
30
30
|
} from './backend-application';
|
|
31
31
|
import { CliContribution } from './cli';
|
|
32
|
+
import { MockLogger } from '../common/test/mock-logger';
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* Test subclass that exposes the protected `gracefulShutdown` for direct testing.
|
|
@@ -82,12 +83,12 @@ describe('BackendApplication', () => {
|
|
|
82
83
|
const container = new Container();
|
|
83
84
|
|
|
84
85
|
container.bind(RootContainer).toConstantValue(container);
|
|
85
|
-
|
|
86
86
|
container.bind(ILogger).to(MockLogger).inSingletonScope();
|
|
87
87
|
container.bind(Stopwatch).to(NodeStopwatch).inSingletonScope();
|
|
88
88
|
container.bind(ProcessUtils).toSelf().inSingletonScope();
|
|
89
89
|
container.bind(BackendApplicationCliContribution).toSelf().inSingletonScope();
|
|
90
90
|
container.bind(CliContribution).toService(BackendApplicationCliContribution);
|
|
91
|
+
container.bind(EarlyExpressMiddleware).toSelf().inSingletonScope();
|
|
91
92
|
bindContributionProvider(container, BackendApplicationContribution);
|
|
92
93
|
|
|
93
94
|
container.bind(TestBackendApplication).toSelf().inSingletonScope();
|
|
@@ -242,7 +243,8 @@ describe('BackendApplication', () => {
|
|
|
242
243
|
container.bind(BackendApplicationContribution).toConstantValue({
|
|
243
244
|
onStop: () => { secondRan = true; }
|
|
244
245
|
});
|
|
245
|
-
const
|
|
246
|
+
const mockLogger = container.get(ILogger) as ILogger;
|
|
247
|
+
const errorStub = sandbox.stub(mockLogger, 'error');
|
|
246
248
|
|
|
247
249
|
const app = container.get(TestBackendApplication);
|
|
248
250
|
await app.invokeGracefulShutdown();
|
|
@@ -28,6 +28,7 @@ import { Deferred, timeoutReject } from '../common/promise-util';
|
|
|
28
28
|
import { environment } from '../common/index';
|
|
29
29
|
import { AddressInfo } from 'net';
|
|
30
30
|
import { ProcessUtils } from './process-utils';
|
|
31
|
+
import { ILogger } from '../common/logger';
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* The path to the application project directory. This is the directory where the application code is located.
|
|
@@ -53,6 +54,16 @@ const DEFAULT_HOST = 'localhost';
|
|
|
53
54
|
const DEFAULT_SSL = false;
|
|
54
55
|
const DEFAULT_DNS_DEFAULT_RESULT_ORDER: DnsResultOrder = 'ipv4first';
|
|
55
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Shared registry for Express middleware that must run before static file handlers.
|
|
59
|
+
* Contributions push handlers during `initialize()`, and `BackendApplication`
|
|
60
|
+
* applies them at the start of `configure()`, before gzipped handlers and `express.static()`.
|
|
61
|
+
*/
|
|
62
|
+
@injectable()
|
|
63
|
+
export class EarlyExpressMiddleware {
|
|
64
|
+
readonly handlers: express.Handler[] = [];
|
|
65
|
+
}
|
|
66
|
+
|
|
56
67
|
export const BackendApplicationServer = Symbol('BackendApplicationServer');
|
|
57
68
|
/**
|
|
58
69
|
* This service is responsible for serving the frontend files.
|
|
@@ -174,12 +185,18 @@ export class BackendApplication {
|
|
|
174
185
|
|
|
175
186
|
protected readonly app: express.Application = express();
|
|
176
187
|
|
|
188
|
+
@inject(EarlyExpressMiddleware)
|
|
189
|
+
protected readonly earlyMiddleware: EarlyExpressMiddleware;
|
|
190
|
+
|
|
177
191
|
@inject(ProcessUtils)
|
|
178
192
|
protected readonly processUtils: ProcessUtils;
|
|
179
193
|
|
|
180
194
|
@inject(Stopwatch)
|
|
181
195
|
protected readonly stopwatch: Stopwatch;
|
|
182
196
|
|
|
197
|
+
@inject(ILogger) @named('core:BackendApplication')
|
|
198
|
+
protected readonly logger: ILogger;
|
|
199
|
+
|
|
183
200
|
@inject(RootContainer)
|
|
184
201
|
protected readonly rootContainer: interfaces.Container;
|
|
185
202
|
|
|
@@ -201,7 +218,7 @@ export class BackendApplication {
|
|
|
201
218
|
// Workaround for Electron not installing a handler to ignore SIGPIPE error
|
|
202
219
|
// (https://github.com/electron/electron/issues/13254)
|
|
203
220
|
process.on('SIGPIPE', () => {
|
|
204
|
-
|
|
221
|
+
this.logger.error(new Error('Unexpected SIGPIPE'));
|
|
205
222
|
});
|
|
206
223
|
|
|
207
224
|
// Handles normal process termination.
|
|
@@ -221,7 +238,7 @@ export class BackendApplication {
|
|
|
221
238
|
await this.measureContribution(contribution, 'initialize',
|
|
222
239
|
() => contribution.initialize!());
|
|
223
240
|
} catch (error) {
|
|
224
|
-
|
|
241
|
+
this.logger.error('Could not initialize contribution', error);
|
|
225
242
|
}
|
|
226
243
|
}
|
|
227
244
|
}));
|
|
@@ -240,6 +257,11 @@ export class BackendApplication {
|
|
|
240
257
|
protected async configure(): Promise<void> {
|
|
241
258
|
await this.initialize();
|
|
242
259
|
|
|
260
|
+
// Apply early middleware before static file handlers.
|
|
261
|
+
for (const handler of this.earlyMiddleware.handlers) {
|
|
262
|
+
this.app.use(handler);
|
|
263
|
+
}
|
|
264
|
+
|
|
243
265
|
this.app.get('*.js', this.serveGzipped.bind(this, 'text/javascript'));
|
|
244
266
|
this.app.get('*.js.map', this.serveGzipped.bind(this, 'application/json'));
|
|
245
267
|
this.app.get('*.css', this.serveGzipped.bind(this, 'text/css'));
|
|
@@ -258,11 +280,11 @@ export class BackendApplication {
|
|
|
258
280
|
await this.measureContribution(contribution, 'configure',
|
|
259
281
|
() => contribution.configure!(this.app));
|
|
260
282
|
} catch (error) {
|
|
261
|
-
|
|
283
|
+
this.logger.error('Could not configure contribution', error);
|
|
262
284
|
}
|
|
263
285
|
}
|
|
264
286
|
}));
|
|
265
|
-
|
|
287
|
+
this.logger.info('configured all backend app contributions');
|
|
266
288
|
}
|
|
267
289
|
|
|
268
290
|
use(...handlers: express.Handler[]): void {
|
|
@@ -297,14 +319,14 @@ export class BackendApplication {
|
|
|
297
319
|
try {
|
|
298
320
|
key = await fs.readFile(this.cliParams.certkey as string);
|
|
299
321
|
} catch (err) {
|
|
300
|
-
|
|
322
|
+
this.logger.error("Can't read certificate key");
|
|
301
323
|
throw err;
|
|
302
324
|
}
|
|
303
325
|
|
|
304
326
|
try {
|
|
305
327
|
cert = await fs.readFile(this.cliParams.cert as string);
|
|
306
328
|
} catch (err) {
|
|
307
|
-
|
|
329
|
+
this.logger.error("Can't read certificate");
|
|
308
330
|
throw err;
|
|
309
331
|
}
|
|
310
332
|
server = https.createServer({ key, cert }, this.app);
|
|
@@ -323,7 +345,7 @@ export class BackendApplication {
|
|
|
323
345
|
// address should be defined at this point
|
|
324
346
|
const address = server.address()!;
|
|
325
347
|
const url = typeof address === 'string' ? address : this.getHttpUrl(address, this.cliParams.ssl);
|
|
326
|
-
|
|
348
|
+
this.logger.info(`Theia app listening on ${url}.`);
|
|
327
349
|
deferred.resolve(server);
|
|
328
350
|
});
|
|
329
351
|
|
|
@@ -336,7 +358,7 @@ export class BackendApplication {
|
|
|
336
358
|
await this.measureContribution(contribution, 'onStart',
|
|
337
359
|
() => contribution.onStart!(server));
|
|
338
360
|
} catch (error) {
|
|
339
|
-
|
|
361
|
+
this.logger.error('Could not start contribution', error);
|
|
340
362
|
}
|
|
341
363
|
}
|
|
342
364
|
}
|
|
@@ -415,7 +437,7 @@ export class BackendApplication {
|
|
|
415
437
|
try {
|
|
416
438
|
await contrib.onStop(this.app);
|
|
417
439
|
} catch (error) {
|
|
418
|
-
|
|
440
|
+
this.logger.error('Could not stop contribution', error);
|
|
419
441
|
}
|
|
420
442
|
}
|
|
421
443
|
}));
|
|
@@ -466,9 +488,9 @@ export class BackendApplication {
|
|
|
466
488
|
|
|
467
489
|
protected handleUncaughtError(error: Error): void {
|
|
468
490
|
if (error) {
|
|
469
|
-
|
|
491
|
+
this.logger.error('Uncaught Exception: ', error.toString());
|
|
470
492
|
if (error.stack) {
|
|
471
|
-
|
|
493
|
+
this.logger.error(error.stack);
|
|
472
494
|
}
|
|
473
495
|
}
|
|
474
496
|
}
|
|
@@ -15,12 +15,22 @@
|
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
17
|
import { ContainerModule } from 'inversify';
|
|
18
|
+
import { BackendApplicationContribution } from '../backend-application';
|
|
18
19
|
import { WsRequestValidatorContribution } from '../ws-request-validators';
|
|
19
20
|
import { BackendApplicationHosts } from './backend-application-hosts';
|
|
21
|
+
import {
|
|
22
|
+
BrowserConnectionToken, BrowserConnectionTokenBackendContribution, createBrowserConnectionToken
|
|
23
|
+
} from './browser-connection-token';
|
|
20
24
|
import { WsOriginValidator } from './ws-origin-validator';
|
|
21
25
|
|
|
22
26
|
export default new ContainerModule(bind => {
|
|
23
27
|
bind(BackendApplicationHosts).toSelf().inSingletonScope();
|
|
24
28
|
bind(WsOriginValidator).toSelf().inSingletonScope();
|
|
25
29
|
bind(WsRequestValidatorContribution).toService(WsOriginValidator);
|
|
30
|
+
|
|
31
|
+
// Cookie-based connection token protects both HTTP and WebSocket endpoints.
|
|
32
|
+
bind(BrowserConnectionToken).toConstantValue(createBrowserConnectionToken());
|
|
33
|
+
bind(BrowserConnectionTokenBackendContribution).toSelf().inSingletonScope();
|
|
34
|
+
bind(BackendApplicationContribution).toService(BrowserConnectionTokenBackendContribution);
|
|
35
|
+
bind(WsRequestValidatorContribution).toService(BrowserConnectionTokenBackendContribution);
|
|
26
36
|
});
|