@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.
Files changed (257) hide show
  1. package/README.md +19 -19
  2. package/i18n/nls.cs.json +97 -51
  3. package/i18n/nls.de.json +97 -51
  4. package/i18n/nls.es.json +97 -51
  5. package/i18n/nls.fr.json +97 -51
  6. package/i18n/nls.hu.json +97 -51
  7. package/i18n/nls.it.json +97 -51
  8. package/i18n/nls.ja.json +97 -51
  9. package/i18n/nls.json +101 -55
  10. package/i18n/nls.ko.json +97 -51
  11. package/i18n/nls.pl.json +97 -51
  12. package/i18n/nls.pt-br.json +97 -51
  13. package/i18n/nls.ru.json +97 -51
  14. package/i18n/nls.tr.json +97 -51
  15. package/i18n/nls.zh-cn.json +97 -51
  16. package/i18n/nls.zh-tw.json +97 -51
  17. package/lib/browser/catalog.json +169 -10
  18. package/lib/browser/common-frontend-contribution.js +2 -2
  19. package/lib/browser/common-frontend-contribution.js.map +1 -1
  20. package/lib/browser/components/card.d.ts.map +1 -1
  21. package/lib/browser/components/card.js +11 -3
  22. package/lib/browser/components/card.js.map +1 -1
  23. package/lib/browser/connection-status-service.js +1 -1
  24. package/lib/browser/connection-status-service.js.map +1 -1
  25. package/lib/browser/frontend-application-contribution.d.ts +2 -2
  26. package/lib/browser/frontend-application-contribution.d.ts.map +1 -1
  27. package/lib/browser/frontend-application.d.ts +2 -0
  28. package/lib/browser/frontend-application.d.ts.map +1 -1
  29. package/lib/browser/frontend-application.js +15 -6
  30. package/lib/browser/frontend-application.js.map +1 -1
  31. package/lib/browser/keyboard/index.d.ts +1 -0
  32. package/lib/browser/keyboard/index.d.ts.map +1 -1
  33. package/lib/browser/keyboard/index.js +1 -0
  34. package/lib/browser/keyboard/index.js.map +1 -1
  35. package/lib/browser/keyboard/keyboard-utils.d.ts +17 -0
  36. package/lib/browser/keyboard/keyboard-utils.d.ts.map +1 -0
  37. package/lib/browser/keyboard/keyboard-utils.js +40 -0
  38. package/lib/browser/keyboard/keyboard-utils.js.map +1 -0
  39. package/lib/browser/menu/browser-menu-plugin.d.ts.map +1 -1
  40. package/lib/browser/menu/browser-menu-plugin.js +8 -1
  41. package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
  42. package/lib/browser/messaging/connection-source.d.ts +2 -1
  43. package/lib/browser/messaging/connection-source.d.ts.map +1 -1
  44. package/lib/browser/messaging/connection-source.js.map +1 -1
  45. package/lib/browser/messaging/messaging-frontend-module.d.ts.map +1 -1
  46. package/lib/browser/messaging/messaging-frontend-module.js +3 -0
  47. package/lib/browser/messaging/messaging-frontend-module.js.map +1 -1
  48. package/lib/browser/messaging/service-connection-provider.d.ts +2 -2
  49. package/lib/browser/messaging/service-connection-provider.d.ts.map +1 -1
  50. package/lib/browser/messaging/service-connection-provider.js +2 -2
  51. package/lib/browser/messaging/service-connection-provider.js.map +1 -1
  52. package/lib/browser/messaging/ws-connection-provider.d.ts +1 -1
  53. package/lib/browser/messaging/ws-connection-provider.d.ts.map +1 -1
  54. package/lib/browser/messaging/ws-connection-provider.js +3 -3
  55. package/lib/browser/messaging/ws-connection-provider.js.map +1 -1
  56. package/lib/browser/messaging/ws-connection-source.d.ts +4 -2
  57. package/lib/browser/messaging/ws-connection-source.d.ts.map +1 -1
  58. package/lib/browser/messaging/ws-connection-source.js +15 -10
  59. package/lib/browser/messaging/ws-connection-source.js.map +1 -1
  60. package/lib/browser/performance/frontend-stopwatch.js +1 -1
  61. package/lib/browser/performance/frontend-stopwatch.js.map +1 -1
  62. package/lib/browser/preload/i18n-preload-contribution.d.ts +1 -1
  63. package/lib/browser/preload/i18n-preload-contribution.d.ts.map +1 -1
  64. package/lib/browser/preload/i18n-preload-contribution.js +2 -2
  65. package/lib/browser/preload/i18n-preload-contribution.js.map +1 -1
  66. package/lib/browser/preload/os-preload-contribution.d.ts +1 -1
  67. package/lib/browser/preload/os-preload-contribution.d.ts.map +1 -1
  68. package/lib/browser/preload/os-preload-contribution.js +6 -6
  69. package/lib/browser/preload/os-preload-contribution.js.map +1 -1
  70. package/lib/browser/saveable-service.d.ts +9 -2
  71. package/lib/browser/saveable-service.d.ts.map +1 -1
  72. package/lib/browser/saveable-service.js +34 -25
  73. package/lib/browser/saveable-service.js.map +1 -1
  74. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts +1 -0
  75. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts.map +1 -1
  76. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js +10 -1
  77. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js.map +1 -1
  78. package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.d.ts +2 -0
  79. package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.d.ts.map +1 -1
  80. package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.js +11 -2
  81. package/lib/browser/shell/tab-bar-toolbar/tab-toolbar-item.js.map +1 -1
  82. package/lib/browser/window/browser-window-module.d.ts.map +1 -1
  83. package/lib/browser/window/browser-window-module.js +2 -0
  84. package/lib/browser/window/browser-window-module.js.map +1 -1
  85. package/lib/browser/window/default-secondary-window-service.d.ts +2 -0
  86. package/lib/browser/window/default-secondary-window-service.d.ts.map +1 -1
  87. package/lib/browser/window/default-secondary-window-service.js +7 -0
  88. package/lib/browser/window/default-secondary-window-service.js.map +1 -1
  89. package/lib/browser/window/window-focus-service.d.ts +71 -0
  90. package/lib/browser/window/window-focus-service.d.ts.map +1 -0
  91. package/lib/browser/window/window-focus-service.js +173 -0
  92. package/lib/browser/window/window-focus-service.js.map +1 -0
  93. package/lib/common/event.d.ts +16 -0
  94. package/lib/common/event.d.ts.map +1 -1
  95. package/lib/common/event.js +20 -2
  96. package/lib/common/event.js.map +1 -1
  97. package/lib/common/event.spec.js +63 -0
  98. package/lib/common/event.spec.js.map +1 -1
  99. package/lib/common/glob.d.ts +2 -0
  100. package/lib/common/glob.d.ts.map +1 -1
  101. package/lib/common/glob.js +8 -7
  102. package/lib/common/glob.js.map +1 -1
  103. package/lib/common/message-rpc/channel.d.ts +1 -1
  104. package/lib/common/message-rpc/channel.d.ts.map +1 -1
  105. package/lib/common/message-rpc/channel.js +20 -12
  106. package/lib/common/message-rpc/channel.js.map +1 -1
  107. package/lib/common/message-rpc/channel.spec.d.ts.map +1 -1
  108. package/lib/common/message-rpc/channel.spec.js +94 -0
  109. package/lib/common/message-rpc/channel.spec.js.map +1 -1
  110. package/lib/common/message-rpc/rpc-protocol.d.ts.map +1 -1
  111. package/lib/common/message-rpc/rpc-protocol.js +13 -3
  112. package/lib/common/message-rpc/rpc-protocol.js.map +1 -1
  113. package/lib/common/message-rpc/uint8-array-message-buffer.d.ts.map +1 -1
  114. package/lib/common/message-rpc/uint8-array-message-buffer.js +1 -1
  115. package/lib/common/message-rpc/uint8-array-message-buffer.js.map +1 -1
  116. package/lib/common/messaging/index.d.ts +1 -0
  117. package/lib/common/messaging/index.d.ts.map +1 -1
  118. package/lib/common/messaging/index.js +1 -0
  119. package/lib/common/messaging/index.js.map +1 -1
  120. package/lib/common/messaging/socket-write-buffer.d.ts +4 -3
  121. package/lib/common/messaging/socket-write-buffer.d.ts.map +1 -1
  122. package/lib/common/messaging/socket-write-buffer.js +14 -4
  123. package/lib/common/messaging/socket-write-buffer.js.map +1 -1
  124. package/lib/common/performance/index.d.ts +1 -0
  125. package/lib/common/performance/index.d.ts.map +1 -1
  126. package/lib/common/performance/index.js +1 -0
  127. package/lib/common/performance/index.js.map +1 -1
  128. package/lib/common/performance/simple-stopwatch.d.ts +18 -0
  129. package/lib/common/performance/simple-stopwatch.d.ts.map +1 -0
  130. package/lib/common/performance/simple-stopwatch.js +80 -0
  131. package/lib/common/performance/simple-stopwatch.js.map +1 -0
  132. package/lib/common/performance/stopwatch.d.ts +41 -0
  133. package/lib/common/performance/stopwatch.d.ts.map +1 -1
  134. package/lib/common/performance/stopwatch.js +89 -3
  135. package/lib/common/performance/stopwatch.js.map +1 -1
  136. package/lib/common/performance/stopwatch.spec.d.ts +2 -0
  137. package/lib/common/performance/stopwatch.spec.d.ts.map +1 -0
  138. package/lib/common/performance/stopwatch.spec.js +256 -0
  139. package/lib/common/performance/stopwatch.spec.js.map +1 -0
  140. package/lib/common/preferences/index.d.ts +1 -0
  141. package/lib/common/preferences/index.d.ts.map +1 -1
  142. package/lib/common/preferences/index.js +1 -0
  143. package/lib/common/preferences/index.js.map +1 -1
  144. package/lib/common/preferences/preference-utils.d.ts +6 -0
  145. package/lib/common/preferences/preference-utils.d.ts.map +1 -0
  146. package/lib/common/preferences/preference-utils.js +29 -0
  147. package/lib/common/preferences/preference-utils.js.map +1 -0
  148. package/lib/common/resource.d.ts +2 -0
  149. package/lib/common/resource.d.ts.map +1 -1
  150. package/lib/common/resource.js +7 -3
  151. package/lib/common/resource.js.map +1 -1
  152. package/lib/electron-browser/menu/electron-main-menu-factory.d.ts.map +1 -1
  153. package/lib/electron-browser/menu/electron-main-menu-factory.js +5 -1
  154. package/lib/electron-browser/menu/electron-main-menu-factory.js.map +1 -1
  155. package/lib/electron-browser/messaging/electron-messaging-frontend-module.d.ts.map +1 -1
  156. package/lib/electron-browser/messaging/electron-messaging-frontend-module.js +3 -0
  157. package/lib/electron-browser/messaging/electron-messaging-frontend-module.js.map +1 -1
  158. package/lib/electron-browser/window/electron-window-module.d.ts.map +1 -1
  159. package/lib/electron-browser/window/electron-window-module.js +2 -0
  160. package/lib/electron-browser/window/electron-window-module.js.map +1 -1
  161. package/lib/electron-main/electron-api-main.d.ts.map +1 -1
  162. package/lib/electron-main/electron-api-main.js +4 -2
  163. package/lib/electron-main/electron-api-main.js.map +1 -1
  164. package/lib/electron-main/electron-main-application-module.d.ts.map +1 -1
  165. package/lib/electron-main/electron-main-application-module.js +3 -0
  166. package/lib/electron-main/electron-main-application-module.js.map +1 -1
  167. package/lib/electron-main/electron-main-application.d.ts +2 -0
  168. package/lib/electron-main/electron-main-application.d.ts.map +1 -1
  169. package/lib/electron-main/electron-main-application.js +14 -5
  170. package/lib/electron-main/electron-main-application.js.map +1 -1
  171. package/lib/electron-main/theia-electron-window.d.ts.map +1 -1
  172. package/lib/electron-main/theia-electron-window.js +3 -0
  173. package/lib/electron-main/theia-electron-window.js.map +1 -1
  174. package/lib/node/backend-application.d.ts +2 -0
  175. package/lib/node/backend-application.d.ts.map +1 -1
  176. package/lib/node/backend-application.js +17 -5
  177. package/lib/node/backend-application.js.map +1 -1
  178. package/lib/node/messaging/default-messaging-service.d.ts.map +1 -1
  179. package/lib/node/messaging/default-messaging-service.js +1 -0
  180. package/lib/node/messaging/default-messaging-service.js.map +1 -1
  181. package/lib/node/messaging/index.d.ts +1 -0
  182. package/lib/node/messaging/index.d.ts.map +1 -1
  183. package/lib/node/messaging/index.js +1 -0
  184. package/lib/node/messaging/index.js.map +1 -1
  185. package/lib/node/messaging/messaging-backend-module.d.ts.map +1 -1
  186. package/lib/node/messaging/messaging-backend-module.js +4 -0
  187. package/lib/node/messaging/messaging-backend-module.js.map +1 -1
  188. package/lib/node/messaging/test/default-messaging-service.spec.d.ts +2 -0
  189. package/lib/node/messaging/test/default-messaging-service.spec.d.ts.map +1 -0
  190. package/lib/node/messaging/test/default-messaging-service.spec.js +81 -0
  191. package/lib/node/messaging/test/default-messaging-service.spec.js.map +1 -0
  192. package/lib/node/messaging/websocket-frontend-connection-service.d.ts +9 -5
  193. package/lib/node/messaging/websocket-frontend-connection-service.d.ts.map +1 -1
  194. package/lib/node/messaging/websocket-frontend-connection-service.js +21 -5
  195. package/lib/node/messaging/websocket-frontend-connection-service.js.map +1 -1
  196. package/lib/node/performance/node-stopwatch.js +1 -1
  197. package/lib/node/performance/node-stopwatch.js.map +1 -1
  198. package/lib/node/process-utils.d.ts.map +1 -1
  199. package/lib/node/process-utils.js +9 -1
  200. package/lib/node/process-utils.js.map +1 -1
  201. package/package.json +34 -34
  202. package/src/browser/common-frontend-contribution.ts +2 -2
  203. package/src/browser/components/card.tsx +13 -2
  204. package/src/browser/connection-status-service.ts +1 -1
  205. package/src/browser/frontend-application-contribution.ts +2 -2
  206. package/src/browser/frontend-application.ts +26 -17
  207. package/src/browser/keyboard/index.ts +1 -0
  208. package/src/browser/keyboard/keyboard-utils.ts +37 -0
  209. package/src/browser/menu/browser-menu-plugin.ts +8 -1
  210. package/src/browser/messaging/connection-source.ts +2 -1
  211. package/src/browser/messaging/messaging-frontend-module.ts +3 -0
  212. package/src/browser/messaging/service-connection-provider.ts +2 -2
  213. package/src/browser/messaging/ws-connection-provider.ts +1 -1
  214. package/src/browser/messaging/ws-connection-source.ts +7 -4
  215. package/src/browser/performance/frontend-stopwatch.ts +1 -1
  216. package/src/browser/preload/i18n-preload-contribution.ts +1 -1
  217. package/src/browser/preload/os-preload-contribution.ts +1 -1
  218. package/src/browser/saveable-service.ts +34 -27
  219. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx +14 -1
  220. package/src/browser/shell/tab-bar-toolbar/tab-toolbar-item.tsx +13 -2
  221. package/src/browser/style/card.css +4 -2
  222. package/src/browser/style/hover-service.css +7 -0
  223. package/src/browser/window/browser-window-module.ts +2 -0
  224. package/src/browser/window/default-secondary-window-service.ts +6 -0
  225. package/src/browser/window/window-focus-service.ts +187 -0
  226. package/src/common/event.spec.ts +80 -0
  227. package/src/common/event.ts +31 -2
  228. package/src/common/glob.ts +2 -2
  229. package/src/common/i18n/nls.metadata.json +4254 -1058
  230. package/src/common/message-rpc/channel.spec.ts +116 -0
  231. package/src/common/message-rpc/channel.ts +15 -11
  232. package/src/common/message-rpc/rpc-protocol.ts +12 -3
  233. package/src/common/message-rpc/uint8-array-message-buffer.ts +1 -1
  234. package/src/common/messaging/index.ts +1 -0
  235. package/src/common/messaging/socket-write-buffer.ts +10 -4
  236. package/src/common/performance/index.ts +1 -0
  237. package/src/common/performance/simple-stopwatch.ts +91 -0
  238. package/src/common/performance/stopwatch.spec.ts +321 -0
  239. package/src/common/performance/stopwatch.ts +103 -2
  240. package/src/common/preferences/index.ts +1 -0
  241. package/src/common/preferences/preference-utils.ts +28 -0
  242. package/src/common/resource.ts +8 -2
  243. package/src/electron-browser/menu/electron-main-menu-factory.ts +5 -1
  244. package/src/electron-browser/messaging/electron-messaging-frontend-module.ts +3 -0
  245. package/src/electron-browser/window/electron-window-module.ts +2 -0
  246. package/src/electron-main/electron-api-main.ts +4 -2
  247. package/src/electron-main/electron-main-application-module.ts +3 -0
  248. package/src/electron-main/electron-main-application.ts +21 -5
  249. package/src/electron-main/theia-electron-window.ts +3 -0
  250. package/src/node/backend-application.ts +27 -10
  251. package/src/node/messaging/default-messaging-service.ts +1 -0
  252. package/src/node/messaging/index.ts +1 -0
  253. package/src/node/messaging/messaging-backend-module.ts +5 -1
  254. package/src/node/messaging/test/default-messaging-service.spec.ts +85 -0
  255. package/src/node/messaging/websocket-frontend-connection-service.ts +20 -7
  256. package/src/node/performance/node-stopwatch.ts +1 -1
  257. 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
- protected pendingOpen: Map<string, (channel: ForwardingChannel) => void> = new Map();
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: 'Multiplexer main channel has been closed from the remote side!' });
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 side try to open a channel at the same time.
216
- const resolve = this.pendingOpen.get(id);
217
- if (resolve) {
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 resolve = this.pendingOpen.get(id);
233
- if (resolve) {
234
- // edge case: both side try to open a channel at the same time.
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
- const output = this.channel.getWriteBuffer();
182
- this.encoder.request(output, id, method, args);
183
- output.commit();
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
  }
@@ -17,3 +17,4 @@
17
17
  export * from './handler';
18
18
  export * from './proxy-factory';
19
19
  export * from './connection-error-handler';
20
+ export * from './socket-write-buffer';
@@ -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 DISCONNECTED_BUFFER_SIZE = 100 * 1024;
22
+ private static readonly DEFAULT_DISCONNECTED_BUFFER_SIZE = 100 * 1024;
21
23
 
22
- private disconnectedBuffer: Uint8Array | undefined;
23
- private bufferWritePosition = 0;
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(SocketWriteBuffer.DISCONNECTED_BUFFER_SIZE);
39
+ this.disconnectedBuffer = new Uint8Array(this.maxBufferSize);
34
40
  this.bufferWritePosition = 0;
35
41
  }
36
42
 
@@ -16,4 +16,5 @@
16
16
 
17
17
  export * from './measurement';
18
18
  export * from './stopwatch';
19
+ export * from './simple-stopwatch';
19
20
  export * from './measurement-protocol';
@@ -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
+ }