@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
|
@@ -18,6 +18,7 @@ import { assert, expect, spy, use } from 'chai';
|
|
|
18
18
|
import * as spies from 'chai-spies';
|
|
19
19
|
import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from './uint8-array-message-buffer';
|
|
20
20
|
import { ChannelMultiplexer, ForwardingChannel, MessageProvider } from './channel';
|
|
21
|
+
import { RpcProtocol } from './rpc-protocol';
|
|
21
22
|
|
|
22
23
|
use(spies);
|
|
23
24
|
|
|
@@ -84,5 +85,120 @@ describe('Message Channel', () => {
|
|
|
84
85
|
|
|
85
86
|
expect(openChannelSpy).to.be.called.exactly(4);
|
|
86
87
|
});
|
|
88
|
+
|
|
89
|
+
it('should reject pending open() promises when underlying channel closes', async () => {
|
|
90
|
+
const pipe = new ChannelPipe();
|
|
91
|
+
const leftMultiplexer = new ChannelMultiplexer(pipe.left);
|
|
92
|
+
// Don't create a right multiplexer, so no AckOpen will arrive
|
|
93
|
+
|
|
94
|
+
const openPromise = leftMultiplexer.open('test');
|
|
95
|
+
|
|
96
|
+
// Close the underlying channel
|
|
97
|
+
pipe.left.onCloseEmitter.fire({ reason: 'test close' });
|
|
98
|
+
|
|
99
|
+
// The open promise should reject, not hang forever
|
|
100
|
+
try {
|
|
101
|
+
await openPromise;
|
|
102
|
+
assert.fail('Expected open() promise to be rejected');
|
|
103
|
+
} catch (err) {
|
|
104
|
+
expect(err).to.be.instanceOf(Error);
|
|
105
|
+
expect((err as Error).message).to.contain('test close');
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should fire onClose on sub-channels when underlying channel closes', async () => {
|
|
110
|
+
const pipe = new ChannelPipe();
|
|
111
|
+
const leftMultiplexer = new ChannelMultiplexer(pipe.left);
|
|
112
|
+
const rightMultiplexer = new ChannelMultiplexer(pipe.right);
|
|
113
|
+
|
|
114
|
+
const leftChannel = await leftMultiplexer.open('test');
|
|
115
|
+
const rightChannel = rightMultiplexer.getOpenChannel('test');
|
|
116
|
+
assert.isDefined(rightChannel);
|
|
117
|
+
|
|
118
|
+
const leftCloseSpy = spy(() => { });
|
|
119
|
+
leftChannel.onClose(leftCloseSpy);
|
|
120
|
+
|
|
121
|
+
// Close the underlying channel from the remote side
|
|
122
|
+
pipe.left.onCloseEmitter.fire({ reason: 'underlying closed' });
|
|
123
|
+
|
|
124
|
+
expect(leftCloseSpy).to.have.been.called();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('Channel close event ordering', () => {
|
|
129
|
+
it('should not deliver onClose after close() has been called', () => {
|
|
130
|
+
const channel = new ForwardingChannel('test', () => { }, () => new Uint8ArrayWriteBuffer());
|
|
131
|
+
|
|
132
|
+
const closeSpy = spy(() => { });
|
|
133
|
+
channel.onClose(closeSpy);
|
|
134
|
+
|
|
135
|
+
// Bug pattern: close() first (disposes emitters), then fire (no-op)
|
|
136
|
+
channel.close();
|
|
137
|
+
channel.onCloseEmitter.fire({ reason: 'too late' });
|
|
138
|
+
|
|
139
|
+
// The listener should not be called because close() already disposed the emitter
|
|
140
|
+
expect(closeSpy).to.not.have.been.called();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should deliver onClose when fired before close()', () => {
|
|
144
|
+
const channel = new ForwardingChannel('test', () => { }, () => new Uint8ArrayWriteBuffer());
|
|
145
|
+
|
|
146
|
+
const closeSpy = spy(() => { });
|
|
147
|
+
channel.onClose(closeSpy);
|
|
148
|
+
|
|
149
|
+
// Correct pattern: fire first, then close
|
|
150
|
+
channel.onCloseEmitter.fire({ reason: 'proper close' });
|
|
151
|
+
channel.close();
|
|
152
|
+
|
|
153
|
+
expect(closeSpy).to.have.been.called();
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('RPC protocol with write buffer overflow', () => {
|
|
158
|
+
it('should reject the promise when commit fails due to buffer overflow', async () => {
|
|
159
|
+
// Simulate a channel whose write buffer throws on commit (e.g. SocketWriteBuffer overflow)
|
|
160
|
+
const channel = new ForwardingChannel('test', () => { }, () => {
|
|
161
|
+
const buffer = new Uint8ArrayWriteBuffer();
|
|
162
|
+
buffer.onCommit(() => {
|
|
163
|
+
throw new Error('Max disconnected buffer size exceeded');
|
|
164
|
+
});
|
|
165
|
+
return buffer;
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const protocol = new RpcProtocol(channel, undefined, { mode: 'clientOnly' });
|
|
169
|
+
|
|
170
|
+
// sendRequest should return a rejected promise, not throw synchronously
|
|
171
|
+
const promise = protocol.sendRequest('testMethod', []);
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
await promise;
|
|
175
|
+
assert.fail('Expected promise to be rejected');
|
|
176
|
+
} catch (err) {
|
|
177
|
+
expect(err).to.be.instanceOf(Error);
|
|
178
|
+
expect((err as Error).message).to.contain('buffer size exceeded');
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should not leak pending requests when commit fails', async () => {
|
|
183
|
+
const channel = new ForwardingChannel('test', () => { }, () => {
|
|
184
|
+
const buffer = new Uint8ArrayWriteBuffer();
|
|
185
|
+
buffer.onCommit(() => {
|
|
186
|
+
throw new Error('Max disconnected buffer size exceeded');
|
|
187
|
+
});
|
|
188
|
+
return buffer;
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const protocol = new RpcProtocol(channel, undefined, { mode: 'clientOnly' });
|
|
192
|
+
|
|
193
|
+
// sendRequest should return a rejected promise and clean up pendingRequests
|
|
194
|
+
try {
|
|
195
|
+
await protocol.sendRequest('testMethod', []);
|
|
196
|
+
} catch {
|
|
197
|
+
// expected: the promise is rejected due to buffer overflow
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
201
|
+
expect((protocol as any).pendingRequests.size).to.equal(0);
|
|
202
|
+
});
|
|
87
203
|
});
|
|
88
204
|
});
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
+
import { injectable } from 'inversify';
|
|
17
18
|
import { Disposable, DisposableCollection } from '../disposable';
|
|
18
19
|
import { Emitter, Event } from '../event';
|
|
19
20
|
import { ReadBuffer, WriteBuffer } from './message-buffer';
|
|
@@ -71,6 +72,7 @@ export type MessageProvider = () => ReadBuffer;
|
|
|
71
72
|
* Reusable abstract {@link Channel} implementation that sets up
|
|
72
73
|
* the basic channel event listeners and offers a generic close method.
|
|
73
74
|
*/
|
|
75
|
+
@injectable()
|
|
74
76
|
export abstract class AbstractChannel implements Channel {
|
|
75
77
|
|
|
76
78
|
onCloseEmitter: Emitter<ChannelCloseEvent> = new Emitter();
|
|
@@ -152,7 +154,7 @@ export enum MessageTypes {
|
|
|
152
154
|
* messages and always in one go.
|
|
153
155
|
*/
|
|
154
156
|
export class ChannelMultiplexer implements Disposable {
|
|
155
|
-
|
|
157
|
+
private pendingOpen: Map<string, { resolve: (channel: ForwardingChannel) => void, reject: (err: Error) => void }> = new Map();
|
|
156
158
|
protected openChannels: Map<string, ForwardingChannel> = new Map();
|
|
157
159
|
|
|
158
160
|
protected readonly onOpenChannelEmitter = new Emitter<{ id: string, channel: Channel }>();
|
|
@@ -180,9 +182,11 @@ export class ChannelMultiplexer implements Disposable {
|
|
|
180
182
|
onUnderlyingChannelClose(event?: ChannelCloseEvent): void {
|
|
181
183
|
if (!this.toDispose.disposed) {
|
|
182
184
|
this.toDispose.push(Disposable.create(() => {
|
|
185
|
+
const reason = event?.reason ?? 'Multiplexer main channel has been closed from the remote side!';
|
|
186
|
+
this.pendingOpen.forEach(pending => pending.reject(new Error(reason)));
|
|
183
187
|
this.pendingOpen.clear();
|
|
184
188
|
this.openChannels.forEach(channel => {
|
|
185
|
-
channel.onCloseEmitter.fire(event ?? { reason
|
|
189
|
+
channel.onCloseEmitter.fire(event ?? { reason });
|
|
186
190
|
});
|
|
187
191
|
|
|
188
192
|
this.openChannels.clear();
|
|
@@ -212,13 +216,13 @@ export class ChannelMultiplexer implements Disposable {
|
|
|
212
216
|
}
|
|
213
217
|
|
|
214
218
|
protected handleAckOpen(id: string): void {
|
|
215
|
-
// edge case: both
|
|
216
|
-
const
|
|
217
|
-
if (
|
|
219
|
+
// edge case: both sides try to open a channel at the same time.
|
|
220
|
+
const pending = this.pendingOpen.get(id);
|
|
221
|
+
if (pending) {
|
|
218
222
|
const channel = this.createChannel(id);
|
|
219
223
|
this.pendingOpen.delete(id);
|
|
220
224
|
this.openChannels.set(id, channel);
|
|
221
|
-
resolve(channel);
|
|
225
|
+
pending.resolve(channel);
|
|
222
226
|
this.onOpenChannelEmitter.fire({ id, channel });
|
|
223
227
|
} else {
|
|
224
228
|
console.error(`not expecting ack-open on for ${id}`);
|
|
@@ -229,10 +233,10 @@ export class ChannelMultiplexer implements Disposable {
|
|
|
229
233
|
if (!this.openChannels.has(id)) {
|
|
230
234
|
const channel = this.createChannel(id);
|
|
231
235
|
this.openChannels.set(id, channel);
|
|
232
|
-
const
|
|
233
|
-
if (
|
|
234
|
-
// edge case: both
|
|
235
|
-
resolve(channel);
|
|
236
|
+
const pending = this.pendingOpen.get(id);
|
|
237
|
+
if (pending) {
|
|
238
|
+
// edge case: both sides try to open a channel at the same time.
|
|
239
|
+
pending.resolve(channel);
|
|
236
240
|
}
|
|
237
241
|
this.underlyingChannel.getWriteBuffer().writeUint8(MessageTypes.AckOpen).writeString(id).commit();
|
|
238
242
|
this.onOpenChannelEmitter.fire({ id, channel });
|
|
@@ -283,7 +287,7 @@ export class ChannelMultiplexer implements Disposable {
|
|
|
283
287
|
throw new Error(`Another channel with the id '${id}' is already open.`);
|
|
284
288
|
}
|
|
285
289
|
const result = new Promise<Channel>((resolve, reject) => {
|
|
286
|
-
this.pendingOpen.set(id, resolve);
|
|
290
|
+
this.pendingOpen.set(id, { resolve, reject });
|
|
287
291
|
});
|
|
288
292
|
this.underlyingChannel.getWriteBuffer().writeUint8(MessageTypes.Open).writeString(id).commit();
|
|
289
293
|
return result;
|
|
@@ -178,9 +178,18 @@ export class RpcProtocol {
|
|
|
178
178
|
const disposableWrapper = new DisposableWrapper();
|
|
179
179
|
this.pendingRequestCancellationEventListeners.set(id, disposableWrapper);
|
|
180
180
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
181
|
+
try {
|
|
182
|
+
const output = this.channel.getWriteBuffer();
|
|
183
|
+
this.encoder.request(output, id, method, args);
|
|
184
|
+
output.commit();
|
|
185
|
+
} catch (err) {
|
|
186
|
+
// The message could not be sent (e.g. write buffer overflow).
|
|
187
|
+
// Clean up the pending request and reject the promise.
|
|
188
|
+
this.pendingRequests.delete(id);
|
|
189
|
+
this.disposeCancellationEventListener(id);
|
|
190
|
+
reply.reject(err);
|
|
191
|
+
return reply.promise;
|
|
192
|
+
}
|
|
184
193
|
|
|
185
194
|
if (cancellationToken?.isCancellationRequested) {
|
|
186
195
|
this.sendCancel(id);
|
|
@@ -113,7 +113,7 @@ export class Uint8ArrayWriteBuffer implements WriteBuffer, Disposable {
|
|
|
113
113
|
return this;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
private onCommitEmitter = new Emitter<Uint8Array>();
|
|
116
|
+
private onCommitEmitter = new Emitter<Uint8Array>({ errorHandling: 'propagate' });
|
|
117
117
|
get onCommit(): Event<Uint8Array> {
|
|
118
118
|
return this.onCommitEmitter.event;
|
|
119
119
|
}
|
|
@@ -14,13 +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 { injectable } from 'inversify';
|
|
17
18
|
import { WebSocket } from './web-socket-channel';
|
|
18
19
|
|
|
20
|
+
@injectable()
|
|
19
21
|
export class SocketWriteBuffer {
|
|
20
|
-
private static
|
|
22
|
+
private static readonly DEFAULT_DISCONNECTED_BUFFER_SIZE = 100 * 1024;
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
protected disconnectedBuffer: Uint8Array | undefined;
|
|
25
|
+
protected bufferWritePosition = 0;
|
|
26
|
+
|
|
27
|
+
protected get maxBufferSize(): number {
|
|
28
|
+
return SocketWriteBuffer.DEFAULT_DISCONNECTED_BUFFER_SIZE;
|
|
29
|
+
}
|
|
24
30
|
|
|
25
31
|
buffer(data: Uint8Array): void {
|
|
26
32
|
this.ensureWriteBuffer(data.byteLength);
|
|
@@ -30,7 +36,7 @@ export class SocketWriteBuffer {
|
|
|
30
36
|
|
|
31
37
|
protected ensureWriteBuffer(byteLength: number): void {
|
|
32
38
|
if (!this.disconnectedBuffer) {
|
|
33
|
-
this.disconnectedBuffer = new Uint8Array(
|
|
39
|
+
this.disconnectedBuffer = new Uint8Array(this.maxBufferSize);
|
|
34
40
|
this.bufferWritePosition = 0;
|
|
35
41
|
}
|
|
36
42
|
|
|
@@ -0,0 +1,91 @@
|
|
|
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
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
18
|
+
|
|
19
|
+
import { LogLevel } from '../logger';
|
|
20
|
+
import { Measurement, MeasurementOptions } from './measurement';
|
|
21
|
+
import { Stopwatch } from './stopwatch';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A simple {@link Stopwatch} that uses a caller-supplied time function and logs
|
|
25
|
+
* via `console`. Usable without Inversify DI: this class does not assign nor
|
|
26
|
+
* use the inherited `logger` field.
|
|
27
|
+
*/
|
|
28
|
+
export class SimpleStopwatch extends Stopwatch {
|
|
29
|
+
|
|
30
|
+
constructor(owner: string, now: () => number) {
|
|
31
|
+
super({ owner, now });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
start(name: string, options?: MeasurementOptions): Measurement {
|
|
35
|
+
const now = this.defaultLogOptions.now;
|
|
36
|
+
const startTime = now();
|
|
37
|
+
|
|
38
|
+
return this.createMeasurement(name, () => ({
|
|
39
|
+
startTime,
|
|
40
|
+
duration: now() - startTime
|
|
41
|
+
}), options);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
protected override log(measurement: Measurement, activity: string, options: {
|
|
45
|
+
now: () => number;
|
|
46
|
+
owner?: string;
|
|
47
|
+
context?: string;
|
|
48
|
+
arguments?: any[];
|
|
49
|
+
} & MeasurementOptions): void {
|
|
50
|
+
const elapsed = measurement.stop();
|
|
51
|
+
const level = this.logLevel(elapsed, options);
|
|
52
|
+
|
|
53
|
+
if (Number.isNaN(elapsed)) {
|
|
54
|
+
switch (level) {
|
|
55
|
+
case LogLevel.ERROR:
|
|
56
|
+
case LogLevel.FATAL:
|
|
57
|
+
break;
|
|
58
|
+
default:
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const origin = options.owner ?? 'application';
|
|
64
|
+
const timeFromStart = `${(options.now() / 1000).toFixed(3)} s since ${origin} start`;
|
|
65
|
+
const whatWasMeasured = options.context ? `[${options.context}] ${activity}` : activity;
|
|
66
|
+
const message = `${whatWasMeasured}: ${elapsed.toFixed(1)} ms [${timeFromStart}]`;
|
|
67
|
+
const args = options.arguments ?? [];
|
|
68
|
+
|
|
69
|
+
switch (level) {
|
|
70
|
+
case LogLevel.FATAL:
|
|
71
|
+
case LogLevel.ERROR:
|
|
72
|
+
console.error(message, ...args);
|
|
73
|
+
break;
|
|
74
|
+
case LogLevel.WARN:
|
|
75
|
+
console.warn(message, ...args);
|
|
76
|
+
break;
|
|
77
|
+
case LogLevel.INFO:
|
|
78
|
+
console.info(message, ...args);
|
|
79
|
+
break;
|
|
80
|
+
case LogLevel.DEBUG:
|
|
81
|
+
console.debug(message, ...args);
|
|
82
|
+
break;
|
|
83
|
+
case LogLevel.TRACE:
|
|
84
|
+
console.trace(message, ...args);
|
|
85
|
+
break;
|
|
86
|
+
default:
|
|
87
|
+
console.log(message, ...args);
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|