@theia/core 1.71.0-next.8 → 1.71.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 +19 -19
- package/i18n/nls.cs.json +97 -51
- package/i18n/nls.de.json +97 -51
- package/i18n/nls.es.json +97 -51
- package/i18n/nls.fr.json +97 -51
- package/i18n/nls.hu.json +97 -51
- package/i18n/nls.it.json +97 -51
- package/i18n/nls.ja.json +97 -51
- package/i18n/nls.json +101 -55
- package/i18n/nls.ko.json +97 -51
- package/i18n/nls.pl.json +97 -51
- package/i18n/nls.pt-br.json +97 -51
- package/i18n/nls.ru.json +97 -51
- package/i18n/nls.tr.json +97 -51
- package/i18n/nls.zh-cn.json +97 -51
- package/i18n/nls.zh-tw.json +97 -51
- package/lib/browser/catalog.json +169 -10
- package/lib/browser/common-frontend-contribution.js +2 -2
- package/lib/browser/common-frontend-contribution.js.map +1 -1
- package/lib/browser/components/card.d.ts.map +1 -1
- package/lib/browser/components/card.js +11 -3
- package/lib/browser/components/card.js.map +1 -1
- package/lib/browser/connection-status-service.js +1 -1
- package/lib/browser/connection-status-service.js.map +1 -1
- package/lib/browser/frontend-application-contribution.d.ts +2 -2
- package/lib/browser/frontend-application-contribution.d.ts.map +1 -1
- 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 -6
- package/lib/browser/frontend-application.js.map +1 -1
- package/lib/browser/keyboard/index.d.ts +1 -0
- package/lib/browser/keyboard/index.d.ts.map +1 -1
- package/lib/browser/keyboard/index.js +1 -0
- package/lib/browser/keyboard/index.js.map +1 -1
- package/lib/browser/keyboard/keyboard-utils.d.ts +17 -0
- package/lib/browser/keyboard/keyboard-utils.d.ts.map +1 -0
- package/lib/browser/keyboard/keyboard-utils.js +40 -0
- package/lib/browser/keyboard/keyboard-utils.js.map +1 -0
- package/lib/browser/menu/browser-menu-plugin.d.ts.map +1 -1
- package/lib/browser/menu/browser-menu-plugin.js +8 -1
- package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
- package/lib/browser/messaging/connection-source.d.ts +2 -1
- package/lib/browser/messaging/connection-source.d.ts.map +1 -1
- package/lib/browser/messaging/connection-source.js.map +1 -1
- package/lib/browser/messaging/messaging-frontend-module.d.ts.map +1 -1
- package/lib/browser/messaging/messaging-frontend-module.js +3 -0
- package/lib/browser/messaging/messaging-frontend-module.js.map +1 -1
- package/lib/browser/messaging/service-connection-provider.d.ts +2 -2
- package/lib/browser/messaging/service-connection-provider.d.ts.map +1 -1
- package/lib/browser/messaging/service-connection-provider.js +2 -2
- package/lib/browser/messaging/service-connection-provider.js.map +1 -1
- package/lib/browser/messaging/ws-connection-provider.d.ts +1 -1
- package/lib/browser/messaging/ws-connection-provider.d.ts.map +1 -1
- package/lib/browser/messaging/ws-connection-provider.js +3 -3
- package/lib/browser/messaging/ws-connection-provider.js.map +1 -1
- package/lib/browser/messaging/ws-connection-source.d.ts +4 -2
- package/lib/browser/messaging/ws-connection-source.d.ts.map +1 -1
- package/lib/browser/messaging/ws-connection-source.js +15 -10
- package/lib/browser/messaging/ws-connection-source.js.map +1 -1
- package/lib/browser/performance/frontend-stopwatch.js +1 -1
- package/lib/browser/performance/frontend-stopwatch.js.map +1 -1
- package/lib/browser/preload/i18n-preload-contribution.d.ts +1 -1
- package/lib/browser/preload/i18n-preload-contribution.d.ts.map +1 -1
- package/lib/browser/preload/i18n-preload-contribution.js +2 -2
- package/lib/browser/preload/i18n-preload-contribution.js.map +1 -1
- package/lib/browser/preload/os-preload-contribution.d.ts +1 -1
- package/lib/browser/preload/os-preload-contribution.d.ts.map +1 -1
- package/lib/browser/preload/os-preload-contribution.js +6 -6
- package/lib/browser/preload/os-preload-contribution.js.map +1 -1
- package/lib/browser/saveable-service.d.ts +9 -2
- package/lib/browser/saveable-service.d.ts.map +1 -1
- package/lib/browser/saveable-service.js +34 -25
- package/lib/browser/saveable-service.js.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts +1 -0
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js +10 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.d.ts +2 -0
- package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.d.ts.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.js +11 -2
- package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.js.map +1 -1
- package/lib/browser/window/browser-window-module.d.ts.map +1 -1
- package/lib/browser/window/browser-window-module.js +2 -0
- package/lib/browser/window/browser-window-module.js.map +1 -1
- package/lib/browser/window/default-secondary-window-service.d.ts +2 -0
- package/lib/browser/window/default-secondary-window-service.d.ts.map +1 -1
- package/lib/browser/window/default-secondary-window-service.js +7 -0
- package/lib/browser/window/default-secondary-window-service.js.map +1 -1
- package/lib/browser/window/window-focus-service.d.ts +71 -0
- package/lib/browser/window/window-focus-service.d.ts.map +1 -0
- package/lib/browser/window/window-focus-service.js +173 -0
- package/lib/browser/window/window-focus-service.js.map +1 -0
- package/lib/common/event.d.ts +16 -0
- package/lib/common/event.d.ts.map +1 -1
- package/lib/common/event.js +20 -2
- package/lib/common/event.js.map +1 -1
- package/lib/common/event.spec.js +63 -0
- package/lib/common/event.spec.js.map +1 -1
- package/lib/common/glob.d.ts +2 -0
- package/lib/common/glob.d.ts.map +1 -1
- package/lib/common/glob.js +8 -7
- package/lib/common/glob.js.map +1 -1
- package/lib/common/message-rpc/channel.d.ts +1 -1
- package/lib/common/message-rpc/channel.d.ts.map +1 -1
- package/lib/common/message-rpc/channel.js +20 -12
- package/lib/common/message-rpc/channel.js.map +1 -1
- package/lib/common/message-rpc/channel.spec.d.ts.map +1 -1
- package/lib/common/message-rpc/channel.spec.js +94 -0
- package/lib/common/message-rpc/channel.spec.js.map +1 -1
- package/lib/common/message-rpc/rpc-protocol.d.ts.map +1 -1
- package/lib/common/message-rpc/rpc-protocol.js +13 -3
- package/lib/common/message-rpc/rpc-protocol.js.map +1 -1
- package/lib/common/message-rpc/uint8-array-message-buffer.d.ts.map +1 -1
- package/lib/common/message-rpc/uint8-array-message-buffer.js +1 -1
- package/lib/common/message-rpc/uint8-array-message-buffer.js.map +1 -1
- package/lib/common/messaging/index.d.ts +1 -0
- package/lib/common/messaging/index.d.ts.map +1 -1
- package/lib/common/messaging/index.js +1 -0
- package/lib/common/messaging/index.js.map +1 -1
- package/lib/common/messaging/socket-write-buffer.d.ts +4 -3
- package/lib/common/messaging/socket-write-buffer.d.ts.map +1 -1
- package/lib/common/messaging/socket-write-buffer.js +14 -4
- package/lib/common/messaging/socket-write-buffer.js.map +1 -1
- package/lib/common/performance/index.d.ts +1 -0
- package/lib/common/performance/index.d.ts.map +1 -1
- package/lib/common/performance/index.js +1 -0
- package/lib/common/performance/index.js.map +1 -1
- package/lib/common/performance/simple-stopwatch.d.ts +18 -0
- package/lib/common/performance/simple-stopwatch.d.ts.map +1 -0
- package/lib/common/performance/simple-stopwatch.js +80 -0
- package/lib/common/performance/simple-stopwatch.js.map +1 -0
- package/lib/common/performance/stopwatch.d.ts +41 -0
- package/lib/common/performance/stopwatch.d.ts.map +1 -1
- package/lib/common/performance/stopwatch.js +89 -3
- package/lib/common/performance/stopwatch.js.map +1 -1
- package/lib/common/performance/stopwatch.spec.d.ts +2 -0
- package/lib/common/performance/stopwatch.spec.d.ts.map +1 -0
- package/lib/common/performance/stopwatch.spec.js +256 -0
- package/lib/common/performance/stopwatch.spec.js.map +1 -0
- package/lib/common/preferences/index.d.ts +1 -0
- package/lib/common/preferences/index.d.ts.map +1 -1
- package/lib/common/preferences/index.js +1 -0
- package/lib/common/preferences/index.js.map +1 -1
- package/lib/common/preferences/preference-utils.d.ts +6 -0
- package/lib/common/preferences/preference-utils.d.ts.map +1 -0
- package/lib/common/preferences/preference-utils.js +29 -0
- package/lib/common/preferences/preference-utils.js.map +1 -0
- package/lib/common/resource.d.ts +2 -0
- package/lib/common/resource.d.ts.map +1 -1
- package/lib/common/resource.js +7 -3
- package/lib/common/resource.js.map +1 -1
- package/lib/electron-browser/menu/electron-main-menu-factory.d.ts.map +1 -1
- package/lib/electron-browser/menu/electron-main-menu-factory.js +5 -1
- package/lib/electron-browser/menu/electron-main-menu-factory.js.map +1 -1
- package/lib/electron-browser/messaging/electron-messaging-frontend-module.d.ts.map +1 -1
- package/lib/electron-browser/messaging/electron-messaging-frontend-module.js +3 -0
- package/lib/electron-browser/messaging/electron-messaging-frontend-module.js.map +1 -1
- package/lib/electron-browser/window/electron-window-module.d.ts.map +1 -1
- package/lib/electron-browser/window/electron-window-module.js +2 -0
- package/lib/electron-browser/window/electron-window-module.js.map +1 -1
- package/lib/electron-main/electron-api-main.d.ts.map +1 -1
- package/lib/electron-main/electron-api-main.js +4 -2
- package/lib/electron-main/electron-api-main.js.map +1 -1
- package/lib/electron-main/electron-main-application-module.d.ts.map +1 -1
- package/lib/electron-main/electron-main-application-module.js +3 -0
- package/lib/electron-main/electron-main-application-module.js.map +1 -1
- package/lib/electron-main/electron-main-application.d.ts +2 -0
- package/lib/electron-main/electron-main-application.d.ts.map +1 -1
- package/lib/electron-main/electron-main-application.js +14 -5
- package/lib/electron-main/electron-main-application.js.map +1 -1
- package/lib/electron-main/theia-electron-window.d.ts.map +1 -1
- package/lib/electron-main/theia-electron-window.js +3 -0
- package/lib/electron-main/theia-electron-window.js.map +1 -1
- package/lib/node/backend-application.d.ts +2 -0
- package/lib/node/backend-application.d.ts.map +1 -1
- package/lib/node/backend-application.js +17 -5
- package/lib/node/backend-application.js.map +1 -1
- package/lib/node/messaging/default-messaging-service.d.ts.map +1 -1
- package/lib/node/messaging/default-messaging-service.js +1 -0
- package/lib/node/messaging/default-messaging-service.js.map +1 -1
- package/lib/node/messaging/index.d.ts +1 -0
- package/lib/node/messaging/index.d.ts.map +1 -1
- package/lib/node/messaging/index.js +1 -0
- package/lib/node/messaging/index.js.map +1 -1
- package/lib/node/messaging/messaging-backend-module.d.ts.map +1 -1
- package/lib/node/messaging/messaging-backend-module.js +4 -0
- package/lib/node/messaging/messaging-backend-module.js.map +1 -1
- package/lib/node/messaging/test/default-messaging-service.spec.d.ts +2 -0
- package/lib/node/messaging/test/default-messaging-service.spec.d.ts.map +1 -0
- package/lib/node/messaging/test/default-messaging-service.spec.js +81 -0
- package/lib/node/messaging/test/default-messaging-service.spec.js.map +1 -0
- package/lib/node/messaging/websocket-frontend-connection-service.d.ts +9 -5
- package/lib/node/messaging/websocket-frontend-connection-service.d.ts.map +1 -1
- package/lib/node/messaging/websocket-frontend-connection-service.js +21 -5
- package/lib/node/messaging/websocket-frontend-connection-service.js.map +1 -1
- package/lib/node/performance/node-stopwatch.js +1 -1
- package/lib/node/performance/node-stopwatch.js.map +1 -1
- package/lib/node/process-utils.d.ts.map +1 -1
- package/lib/node/process-utils.js +9 -1
- package/lib/node/process-utils.js.map +1 -1
- package/package.json +34 -34
- package/src/browser/common-frontend-contribution.ts +2 -2
- package/src/browser/components/card.tsx +13 -2
- package/src/browser/connection-status-service.ts +1 -1
- package/src/browser/frontend-application-contribution.ts +2 -2
- package/src/browser/frontend-application.ts +26 -17
- package/src/browser/keyboard/index.ts +1 -0
- package/src/browser/keyboard/keyboard-utils.ts +37 -0
- package/src/browser/menu/browser-menu-plugin.ts +8 -1
- package/src/browser/messaging/connection-source.ts +2 -1
- package/src/browser/messaging/messaging-frontend-module.ts +3 -0
- package/src/browser/messaging/service-connection-provider.ts +2 -2
- package/src/browser/messaging/ws-connection-provider.ts +1 -1
- package/src/browser/messaging/ws-connection-source.ts +7 -4
- package/src/browser/performance/frontend-stopwatch.ts +1 -1
- package/src/browser/preload/i18n-preload-contribution.ts +1 -1
- package/src/browser/preload/os-preload-contribution.ts +1 -1
- package/src/browser/saveable-service.ts +34 -27
- package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx +14 -1
- package/src/browser/shell/tab-bar-toolbar/tab-toolbar-item.tsx +13 -2
- package/src/browser/style/card.css +4 -2
- package/src/browser/style/hover-service.css +7 -0
- package/src/browser/window/browser-window-module.ts +2 -0
- package/src/browser/window/default-secondary-window-service.ts +6 -0
- package/src/browser/window/window-focus-service.ts +187 -0
- package/src/common/event.spec.ts +80 -0
- package/src/common/event.ts +31 -2
- package/src/common/glob.ts +2 -2
- package/src/common/i18n/nls.metadata.json +4254 -1058
- package/src/common/message-rpc/channel.spec.ts +116 -0
- package/src/common/message-rpc/channel.ts +15 -11
- package/src/common/message-rpc/rpc-protocol.ts +12 -3
- package/src/common/message-rpc/uint8-array-message-buffer.ts +1 -1
- package/src/common/messaging/index.ts +1 -0
- package/src/common/messaging/socket-write-buffer.ts +10 -4
- package/src/common/performance/index.ts +1 -0
- package/src/common/performance/simple-stopwatch.ts +91 -0
- package/src/common/performance/stopwatch.spec.ts +321 -0
- package/src/common/performance/stopwatch.ts +103 -2
- package/src/common/preferences/index.ts +1 -0
- package/src/common/preferences/preference-utils.ts +28 -0
- package/src/common/resource.ts +8 -2
- package/src/electron-browser/menu/electron-main-menu-factory.ts +5 -1
- package/src/electron-browser/messaging/electron-messaging-frontend-module.ts +3 -0
- package/src/electron-browser/window/electron-window-module.ts +2 -0
- package/src/electron-main/electron-api-main.ts +4 -2
- package/src/electron-main/electron-main-application-module.ts +3 -0
- package/src/electron-main/electron-main-application.ts +21 -5
- package/src/electron-main/theia-electron-window.ts +3 -0
- package/src/node/backend-application.ts +27 -10
- package/src/node/messaging/default-messaging-service.ts +1 -0
- package/src/node/messaging/index.ts +1 -0
- package/src/node/messaging/messaging-backend-module.ts +5 -1
- package/src/node/messaging/test/default-messaging-service.spec.ts +85 -0
- package/src/node/messaging/websocket-frontend-connection-service.ts +20 -7
- package/src/node/performance/node-stopwatch.ts +1 -1
- package/src/node/process-utils.ts +9 -1
|
@@ -30,6 +30,7 @@ import URI from '../common/uri';
|
|
|
30
30
|
import { FileUri } from '../common/file-uri';
|
|
31
31
|
import { Deferred, timeout } from '../common/promise-util';
|
|
32
32
|
import { MaybePromise } from '../common/types';
|
|
33
|
+
import { Stopwatch } from '../common/performance';
|
|
33
34
|
import { ContributionProvider } from '../common/contribution-provider';
|
|
34
35
|
import { ElectronSecurityTokenService } from './electron-security-token-service';
|
|
35
36
|
import { ElectronSecurityToken } from '../electron-common/electron-token';
|
|
@@ -49,6 +50,8 @@ export { ElectronMainApplicationGlobals };
|
|
|
49
50
|
|
|
50
51
|
const createYargs: (argv?: string[], cwd?: string) => Argv = require('yargs/yargs');
|
|
51
52
|
|
|
53
|
+
const ELECTRON_TIMER_WARNING_THRESHOLD = 50;
|
|
54
|
+
|
|
52
55
|
/**
|
|
53
56
|
* Options passed to the main/default command handler.
|
|
54
57
|
*/
|
|
@@ -171,6 +174,9 @@ export class ElectronMainApplication {
|
|
|
171
174
|
@inject(TheiaElectronWindowFactory)
|
|
172
175
|
protected readonly windowFactory: TheiaElectronWindowFactory;
|
|
173
176
|
|
|
177
|
+
@inject(Stopwatch)
|
|
178
|
+
protected readonly stopwatch: Stopwatch;
|
|
179
|
+
|
|
174
180
|
protected isPortable = this.makePortable();
|
|
175
181
|
|
|
176
182
|
protected readonly electronStore = new Storage<{
|
|
@@ -229,15 +235,19 @@ export class ElectronMainApplication {
|
|
|
229
235
|
await fs.mkdir(args.electronUserData, { recursive: true });
|
|
230
236
|
app.setPath('userData', args.electronUserData);
|
|
231
237
|
}
|
|
238
|
+
const startupMeasurement = this.stopwatch.start('electron-main-startup');
|
|
232
239
|
this.useNativeWindowFrame = this.getTitleBarStyle(config) === 'native';
|
|
233
240
|
this._config = config;
|
|
234
241
|
this.hookApplicationEvents();
|
|
235
242
|
this.showInitialWindow(argv.includes('--open-url') ? argv[argv.length - 1] : undefined);
|
|
236
|
-
const port = await this.startBackend();
|
|
243
|
+
const port = await this.stopwatch.startAsync('electron-main-start-backend', 'Starting backend', () => this.startBackend());
|
|
237
244
|
this._backendPort.resolve(port);
|
|
238
245
|
await app.whenReady();
|
|
239
|
-
await this.
|
|
240
|
-
|
|
246
|
+
await this.stopwatch.startAsync('electron-main-security-token', 'Attaching security token',
|
|
247
|
+
() => this.attachElectronSecurityToken(port));
|
|
248
|
+
await this.stopwatch.startAsync('electron-main-start-contributions', 'Starting contributions',
|
|
249
|
+
() => this.startContributions());
|
|
250
|
+
startupMeasurement.info('Startup sequence completed');
|
|
241
251
|
|
|
242
252
|
this.handleMainCommand({
|
|
243
253
|
file: args.file,
|
|
@@ -920,8 +930,14 @@ export class ElectronMainApplication {
|
|
|
920
930
|
protected async startContributions(): Promise<void> {
|
|
921
931
|
const promises = [];
|
|
922
932
|
for (const contribution of this.contributions.getContributions()) {
|
|
923
|
-
|
|
924
|
-
|
|
933
|
+
const onStart = contribution.onStart;
|
|
934
|
+
if (onStart) {
|
|
935
|
+
promises.push(this.stopwatch.startAsync(
|
|
936
|
+
`${contribution.constructor.name}.onStart`,
|
|
937
|
+
`${contribution.constructor.name}.onStart`,
|
|
938
|
+
() => onStart.call(contribution, this),
|
|
939
|
+
{ thresholdMillis: ELECTRON_TIMER_WARNING_THRESHOLD }
|
|
940
|
+
));
|
|
925
941
|
}
|
|
926
942
|
}
|
|
927
943
|
await Promise.all(promises);
|
|
@@ -140,6 +140,9 @@ export class TheiaElectronWindow {
|
|
|
140
140
|
|
|
141
141
|
protected async doCloseWindow(): Promise<void> {
|
|
142
142
|
this.closeIsConfirmed = true;
|
|
143
|
+
// Hide the window immediately so the user perceives an instant close.
|
|
144
|
+
// This is done after veto checks have passed to ensure save dialogs remain visible.
|
|
145
|
+
this._window.hide();
|
|
143
146
|
await TheiaRendererAPI.sendAboutToClose(this._window.webContents);
|
|
144
147
|
this._window.close();
|
|
145
148
|
}
|
|
@@ -22,7 +22,7 @@ import * as express from 'express';
|
|
|
22
22
|
import * as yargs from 'yargs';
|
|
23
23
|
import * as fs from 'fs-extra';
|
|
24
24
|
import { inject, named, injectable, postConstruct } from 'inversify';
|
|
25
|
-
import { ContributionProvider, MaybePromise, Stopwatch } from '../common';
|
|
25
|
+
import { ContributionProvider, LogLevel, MaybePromise, MeasurementContext, Stopwatch } from '../common';
|
|
26
26
|
import { CliContribution } from './cli';
|
|
27
27
|
import { Deferred } from '../common/promise-util';
|
|
28
28
|
import { environment } from '../common/index';
|
|
@@ -164,6 +164,8 @@ export class BackendApplication {
|
|
|
164
164
|
|
|
165
165
|
private _configured: Promise<void>;
|
|
166
166
|
|
|
167
|
+
private settlementContext?: MeasurementContext<BackendApplicationContribution>;
|
|
168
|
+
|
|
167
169
|
constructor(
|
|
168
170
|
@inject(ContributionProvider) @named(BackendApplicationContribution)
|
|
169
171
|
protected readonly contributionsProvider: ContributionProvider<BackendApplicationContribution>,
|
|
@@ -195,9 +197,8 @@ export class BackendApplication {
|
|
|
195
197
|
await Promise.all(this.contributionsProvider.getContributions().map(async contribution => {
|
|
196
198
|
if (contribution.initialize) {
|
|
197
199
|
try {
|
|
198
|
-
await this.
|
|
199
|
-
() => contribution.initialize!()
|
|
200
|
-
);
|
|
200
|
+
await this.measureContribution(contribution, 'initialize',
|
|
201
|
+
() => contribution.initialize!());
|
|
201
202
|
} catch (error) {
|
|
202
203
|
console.error('Could not initialize contribution', error);
|
|
203
204
|
}
|
|
@@ -211,6 +212,7 @@ export class BackendApplication {
|
|
|
211
212
|
|
|
212
213
|
@postConstruct()
|
|
213
214
|
protected init(): void {
|
|
215
|
+
this.settlementContext = new MeasurementContext(this.stopwatch, 'Backend', TIMER_WARNING_THRESHOLD);
|
|
214
216
|
this._configured = this.configure();
|
|
215
217
|
}
|
|
216
218
|
|
|
@@ -232,7 +234,8 @@ export class BackendApplication {
|
|
|
232
234
|
await Promise.all(this.contributionsProvider.getContributions().map(async contribution => {
|
|
233
235
|
if (contribution.configure) {
|
|
234
236
|
try {
|
|
235
|
-
await contribution
|
|
237
|
+
await this.measureContribution(contribution, 'configure',
|
|
238
|
+
() => contribution.configure!(this.app));
|
|
236
239
|
} catch (error) {
|
|
237
240
|
console.error('Could not configure contribution', error);
|
|
238
241
|
}
|
|
@@ -246,6 +249,8 @@ export class BackendApplication {
|
|
|
246
249
|
}
|
|
247
250
|
|
|
248
251
|
async start(port?: number, hostname?: string): Promise<http.Server | https.Server> {
|
|
252
|
+
const startupMeasurement = this.stopwatch.start('backend-startup');
|
|
253
|
+
|
|
249
254
|
hostname ??= this.cliParams.hostname;
|
|
250
255
|
port ??= this.cliParams.port;
|
|
251
256
|
|
|
@@ -307,15 +312,17 @@ export class BackendApplication {
|
|
|
307
312
|
for (const contribution of this.contributionsProvider.getContributions()) {
|
|
308
313
|
if (contribution.onStart) {
|
|
309
314
|
try {
|
|
310
|
-
await this.
|
|
311
|
-
() => contribution.onStart!(server)
|
|
312
|
-
);
|
|
315
|
+
await this.measureContribution(contribution, 'onStart',
|
|
316
|
+
() => contribution.onStart!(server));
|
|
313
317
|
} catch (error) {
|
|
314
318
|
console.error('Could not start contribution', error);
|
|
315
319
|
}
|
|
316
320
|
}
|
|
317
321
|
}
|
|
318
|
-
|
|
322
|
+
await deferred.promise;
|
|
323
|
+
startupMeasurement.info('Backend application startup sequence completed (async work may still be pending)');
|
|
324
|
+
this.settlementContext?.armAllSettled();
|
|
325
|
+
return server;
|
|
319
326
|
}
|
|
320
327
|
|
|
321
328
|
protected getHttpUrl({ address, port, family }: AddressInfo, ssl?: boolean): string {
|
|
@@ -358,8 +365,18 @@ export class BackendApplication {
|
|
|
358
365
|
next();
|
|
359
366
|
}
|
|
360
367
|
|
|
368
|
+
protected async measureContribution<T>(contribution: BackendApplicationContribution, hook: string, fn: () => MaybePromise<T>): Promise<T> {
|
|
369
|
+
let innerResult: MaybePromise<T>;
|
|
370
|
+
this.settlementContext?.ensureEntry(contribution);
|
|
371
|
+
const result = await this.measure(contribution.constructor.name + '.' + hook,
|
|
372
|
+
() => (innerResult = fn())
|
|
373
|
+
);
|
|
374
|
+
this.settlementContext?.trackSettlement(contribution, innerResult!);
|
|
375
|
+
return result;
|
|
376
|
+
}
|
|
377
|
+
|
|
361
378
|
protected async measure<T>(name: string, fn: () => MaybePromise<T>): Promise<T> {
|
|
362
|
-
return this.stopwatch.startAsync(name, `Backend ${name}`, fn, { thresholdMillis: TIMER_WARNING_THRESHOLD });
|
|
379
|
+
return this.stopwatch.startAsync(name, `Backend ${name}`, fn, { thresholdMillis: TIMER_WARNING_THRESHOLD, defaultLogLevel: LogLevel.DEBUG });
|
|
363
380
|
}
|
|
364
381
|
|
|
365
382
|
protected handleUncaughtError(error: Error): void {
|
|
@@ -71,6 +71,7 @@ export class DefaultMessagingService implements MessagingService, BackendApplica
|
|
|
71
71
|
protected createMainChannelContainer(socket: Channel): Container {
|
|
72
72
|
const connectionContainer: Container = this.container.createChild() as Container;
|
|
73
73
|
connectionContainer.bind(MainChannel).toConstantValue(socket);
|
|
74
|
+
socket.onClose(() => connectionContainer.unbindAllAsync().catch(e => console.error(e)));
|
|
74
75
|
return connectionContainer;
|
|
75
76
|
}
|
|
76
77
|
|
|
@@ -24,8 +24,9 @@ import { MessagingListener, MessagingListenerContribution } from './messaging-li
|
|
|
24
24
|
import { FrontendConnectionService } from './frontend-connection-service';
|
|
25
25
|
import { BackendApplicationContribution } from '../backend-application';
|
|
26
26
|
import { connectionCloseServicePath } from '../../common/messaging/connection-management';
|
|
27
|
-
import { WebsocketFrontendConnectionService } from './websocket-frontend-connection-service';
|
|
27
|
+
import { ReconnectableSocketChannel, WebsocketFrontendConnectionService } from './websocket-frontend-connection-service';
|
|
28
28
|
import { WebsocketEndpoint } from './websocket-endpoint';
|
|
29
|
+
import { SocketWriteBuffer } from '../../common/messaging/socket-write-buffer';
|
|
29
30
|
|
|
30
31
|
export const messagingBackendModule = new ContainerModule(bind => {
|
|
31
32
|
bindRootContributionProvider(bind, ConnectionContainerModule);
|
|
@@ -36,6 +37,9 @@ export const messagingBackendModule = new ContainerModule(bind => {
|
|
|
36
37
|
bind(MessagingContainer).toDynamicValue(({ container }) => container).inSingletonScope();
|
|
37
38
|
bind(WebsocketEndpoint).toSelf().inSingletonScope();
|
|
38
39
|
bind(BackendApplicationContribution).toService(WebsocketEndpoint);
|
|
40
|
+
// Transient: each connection gets its own private buffer and channel instances.
|
|
41
|
+
bind(SocketWriteBuffer).toSelf();
|
|
42
|
+
bind(ReconnectableSocketChannel).toSelf();
|
|
39
43
|
bind(WebsocketFrontendConnectionService).toSelf().inSingletonScope();
|
|
40
44
|
bind(FrontendConnectionService).toService(WebsocketFrontendConnectionService);
|
|
41
45
|
bind(MessagingListener).toSelf().inSingletonScope();
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource 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 { Container, injectable, preDestroy } from 'inversify';
|
|
19
|
+
import { ConnectionHandler, bindContributionProvider, servicesPath } from '../../../common';
|
|
20
|
+
import { BasicChannel, Channel } from '../../../common/message-rpc/channel';
|
|
21
|
+
import { Uint8ArrayWriteBuffer } from '../../../common/message-rpc/uint8-array-message-buffer';
|
|
22
|
+
import { ConnectionContainerModule } from '../connection-container-module';
|
|
23
|
+
import { DefaultMessagingService, MessagingContainer } from '../default-messaging-service';
|
|
24
|
+
import { FrontendConnectionService } from '../frontend-connection-service';
|
|
25
|
+
import { MessagingService } from '../messaging-service';
|
|
26
|
+
|
|
27
|
+
describe('DefaultMessagingService', () => {
|
|
28
|
+
|
|
29
|
+
describe('when a frontend connection closes', () => {
|
|
30
|
+
|
|
31
|
+
it('disposes the connection-scoped child container, invoking @preDestroy on bound singleton services', async () => {
|
|
32
|
+
let canaryDisposed = false;
|
|
33
|
+
|
|
34
|
+
@injectable()
|
|
35
|
+
class CanaryConnectionHandler implements ConnectionHandler {
|
|
36
|
+
readonly path = 'canary';
|
|
37
|
+
onConnection(_channel: Channel): void { /* not relevant for this test */ }
|
|
38
|
+
|
|
39
|
+
@preDestroy()
|
|
40
|
+
protected onPreDestroy(): void {
|
|
41
|
+
canaryDisposed = true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const canaryModule = ConnectionContainerModule.create(({ bind }) => {
|
|
46
|
+
bind(CanaryConnectionHandler).toSelf().inSingletonScope();
|
|
47
|
+
bind(ConnectionHandler).toService(CanaryConnectionHandler);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const container = new Container();
|
|
51
|
+
container.bind(MessagingContainer).toConstantValue(container);
|
|
52
|
+
container.bind(DefaultMessagingService).toSelf().inSingletonScope();
|
|
53
|
+
container.bind(ConnectionContainerModule).toConstantValue(canaryModule);
|
|
54
|
+
bindContributionProvider(container, ConnectionContainerModule);
|
|
55
|
+
bindContributionProvider(container, MessagingService.Contribution);
|
|
56
|
+
|
|
57
|
+
let serviceHandler: ((params: MessagingService.PathParams, mainChannel: Channel) => void) | undefined;
|
|
58
|
+
const frontendConnectionService: FrontendConnectionService = {
|
|
59
|
+
registerConnectionHandler(path, callback): void {
|
|
60
|
+
if (path === servicesPath) {
|
|
61
|
+
serviceHandler = callback;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
container.bind(FrontendConnectionService).toConstantValue(frontendConnectionService);
|
|
66
|
+
|
|
67
|
+
const messagingService = container.get(DefaultMessagingService);
|
|
68
|
+
messagingService.initialize();
|
|
69
|
+
|
|
70
|
+
expect(serviceHandler, 'connection handler not registered on the services path').to.not.be.undefined;
|
|
71
|
+
|
|
72
|
+
const mainChannel = new BasicChannel(() => new Uint8ArrayWriteBuffer());
|
|
73
|
+
serviceHandler!({}, mainChannel);
|
|
74
|
+
|
|
75
|
+
expect(canaryDisposed, 'canary should not be disposed before the channel is closed').to.be.false;
|
|
76
|
+
|
|
77
|
+
mainChannel.onCloseEmitter.fire({ reason: 'frontend connection closed' });
|
|
78
|
+
await new Promise<void>(resolve => setImmediate(resolve));
|
|
79
|
+
|
|
80
|
+
expect(canaryDisposed, 'canary @preDestroy was not invoked').to.be.true;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
});
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
|
|
16
16
|
import { Channel, WriteBuffer } from '../../common/message-rpc';
|
|
17
17
|
import { MessagingService } from './messaging-service';
|
|
18
|
-
import { inject, injectable } from 'inversify';
|
|
18
|
+
import { inject, injectable, interfaces } from 'inversify';
|
|
19
19
|
import { Socket } from 'socket.io';
|
|
20
|
-
import { ConnectionHandlers } from './default-messaging-service';
|
|
20
|
+
import { ConnectionHandlers, MessagingContainer } from './default-messaging-service';
|
|
21
21
|
import { SocketWriteBuffer } from '../../common/messaging/socket-write-buffer';
|
|
22
22
|
import { FrontendConnectionService } from './frontend-connection-service';
|
|
23
23
|
import { AbstractChannel } from '../../common/message-rpc/channel';
|
|
@@ -33,6 +33,9 @@ export class WebsocketFrontendConnectionService implements FrontendConnectionSer
|
|
|
33
33
|
@inject(WebsocketEndpoint)
|
|
34
34
|
protected readonly websocketServer: WebsocketEndpoint;
|
|
35
35
|
|
|
36
|
+
@inject(MessagingContainer)
|
|
37
|
+
protected readonly container: interfaces.Container;
|
|
38
|
+
|
|
36
39
|
protected readonly wsHandlers = new ConnectionHandlers();
|
|
37
40
|
protected readonly connectionsByFrontend = new Map<string, ReconnectableSocketChannel>();
|
|
38
41
|
protected readonly closeTimeouts = new Map<string, NodeJS.Timeout>();
|
|
@@ -89,12 +92,13 @@ export class WebsocketFrontendConnectionService implements FrontendConnectionSer
|
|
|
89
92
|
this.closeTimeouts.delete(frontEndId);
|
|
90
93
|
|
|
91
94
|
connection.onCloseEmitter.fire({ reason });
|
|
95
|
+
connection.drainBuffer();
|
|
92
96
|
connection.close();
|
|
93
97
|
}
|
|
94
98
|
|
|
95
99
|
protected createConnection(socket: Socket, frontEndId: string): ReconnectableSocketChannel {
|
|
96
100
|
console.info(`creating connection for ${frontEndId}`);
|
|
97
|
-
const channel =
|
|
101
|
+
const channel = this.container.get(ReconnectableSocketChannel);
|
|
98
102
|
channel.connect(socket);
|
|
99
103
|
|
|
100
104
|
this.connectionsByFrontend.set(frontEndId, channel);
|
|
@@ -136,12 +140,17 @@ export class WebsocketFrontendConnectionService implements FrontendConnectionSer
|
|
|
136
140
|
}
|
|
137
141
|
}
|
|
138
142
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
+
@injectable()
|
|
144
|
+
export class ReconnectableSocketChannel extends AbstractChannel {
|
|
145
|
+
protected socket: Socket | undefined;
|
|
146
|
+
|
|
147
|
+
@inject(SocketWriteBuffer)
|
|
148
|
+
protected socketBuffer: SocketWriteBuffer;
|
|
149
|
+
|
|
150
|
+
protected disposables = new DisposableCollection();
|
|
143
151
|
|
|
144
152
|
connect(socket: Socket): void {
|
|
153
|
+
this.disposables.dispose();
|
|
145
154
|
this.disposables = new DisposableCollection();
|
|
146
155
|
this.socket = socket;
|
|
147
156
|
const errorHandler = (err: Error) => {
|
|
@@ -171,6 +180,10 @@ class ReconnectableSocketChannel extends AbstractChannel {
|
|
|
171
180
|
this.socket = undefined;
|
|
172
181
|
}
|
|
173
182
|
|
|
183
|
+
drainBuffer(): void {
|
|
184
|
+
this.socketBuffer.drain();
|
|
185
|
+
}
|
|
186
|
+
|
|
174
187
|
override getWriteBuffer(): WriteBuffer {
|
|
175
188
|
const writeBuffer = new Uint8ArrayWriteBuffer();
|
|
176
189
|
writeBuffer.onCommit(data => {
|
|
@@ -32,7 +32,15 @@ export class ProcessUtils {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
protected winTerminateProcessTree(ppid: number): void {
|
|
35
|
-
|
|
35
|
+
const result = cp.spawnSync('taskkill.exe', ['/f', '/t', '/pid', ppid.toString(10)], { encoding: 'utf8' });
|
|
36
|
+
if (result.error) {
|
|
37
|
+
throw result.error;
|
|
38
|
+
}
|
|
39
|
+
// taskkill may exit with a non-zero code when some child processes have already exited.
|
|
40
|
+
// This is expected during shutdown — log but don't throw.
|
|
41
|
+
if (result.status !== 0) {
|
|
42
|
+
console.warn(`taskkill.exe exited with ${result.status} for PID ${ppid}. Output:\n${JSON.stringify(result.output)}`);
|
|
43
|
+
}
|
|
36
44
|
}
|
|
37
45
|
|
|
38
46
|
protected unixTerminateProcessTree(ppid: number): void {
|