@x-oasis/async-call-rpc 0.1.38

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 (104) hide show
  1. package/README.md +48 -0
  2. package/dist/async-call-rpc.cjs.development.js +1275 -0
  3. package/dist/async-call-rpc.cjs.development.js.map +1 -0
  4. package/dist/async-call-rpc.cjs.production.min.js +2 -0
  5. package/dist/async-call-rpc.cjs.production.min.js.map +1 -0
  6. package/dist/async-call-rpc.esm.js +1559 -0
  7. package/dist/async-call-rpc.esm.js.map +1 -0
  8. package/dist/buffer/BufferFactory.d.ts +12 -0
  9. package/dist/buffer/DataBuffer.d.ts +2 -0
  10. package/dist/buffer/MessagePackBuffer.d.ts +10 -0
  11. package/dist/buffer/ReadBaseBuffer.d.ts +5 -0
  12. package/dist/buffer/ReadBuffer.d.ts +14 -0
  13. package/dist/buffer/SerializationFormat.d.ts +16 -0
  14. package/dist/buffer/WriteBaseBuffer.d.ts +5 -0
  15. package/dist/buffer/WriteBuffer.d.ts +5 -0
  16. package/dist/buffer/examples.d.ts +10 -0
  17. package/dist/buffer/index.d.ts +8 -0
  18. package/dist/common.d.ts +4 -0
  19. package/dist/endpoint/ProxyRPCClient.d.ts +12 -0
  20. package/dist/endpoint/RPCClientHost.d.ts +12 -0
  21. package/dist/endpoint/RPCService.d.ts +20 -0
  22. package/dist/endpoint/RPCServiceHost.d.ts +10 -0
  23. package/dist/error.d.ts +21 -0
  24. package/dist/index.d.ts +11 -0
  25. package/dist/index.js +8 -0
  26. package/dist/middlewares/buffer.d.ts +18 -0
  27. package/dist/middlewares/handleDisconnectedRequest.d.ts +7 -0
  28. package/dist/middlewares/handlePortRequest.d.ts +3 -0
  29. package/dist/middlewares/handleRequest.d.ts +3 -0
  30. package/dist/middlewares/handleRequestUtils.d.ts +10 -0
  31. package/dist/middlewares/handleResponse.d.ts +3 -0
  32. package/dist/middlewares/index.d.ts +7 -0
  33. package/dist/middlewares/logger.d.ts +4 -0
  34. package/dist/middlewares/normalize.d.ts +14 -0
  35. package/dist/middlewares/prepareRequestData.d.ts +28 -0
  36. package/dist/middlewares/sendRequest.d.ts +6 -0
  37. package/dist/middlewares/updateSeqInfo.d.ts +6 -0
  38. package/dist/middlewares/utils.d.ts +3 -0
  39. package/dist/protocol/AbstractChannelProtocol.d.ts +55 -0
  40. package/dist/protocol/MessageChannel.d.ts +18 -0
  41. package/dist/protocol/WebSocketChannel.d.ts +23 -0
  42. package/dist/protocol/WorkerChannel.d.ts +11 -0
  43. package/dist/types/buffer.d.ts +4 -0
  44. package/dist/types/channel.d.ts +27 -0
  45. package/dist/types/index.d.ts +7 -0
  46. package/dist/types/middleware.d.ts +44 -0
  47. package/dist/types/proxyChannel.d.ts +3 -0
  48. package/dist/types/proxyService.d.ts +11 -0
  49. package/dist/types/rpc.d.ts +25 -0
  50. package/dist/types/rpcProtocol.d.ts +22 -0
  51. package/dist/utils/constants.d.ts +5 -0
  52. package/dist/utils/index.d.ts +2 -0
  53. package/dist/utils/jsonrpc.d.ts +36 -0
  54. package/package.json +36 -0
  55. package/src/buffer/ARCHITECTURE.md +298 -0
  56. package/src/buffer/BufferFactory.ts +124 -0
  57. package/src/buffer/CHANGELOG.md +207 -0
  58. package/src/buffer/DataBuffer.ts +1 -0
  59. package/src/buffer/MessagePackBuffer.ts +79 -0
  60. package/src/buffer/OPTIMIZATION.md +258 -0
  61. package/src/buffer/README.md +147 -0
  62. package/src/buffer/ReadBaseBuffer.ts +20 -0
  63. package/src/buffer/ReadBuffer.ts +58 -0
  64. package/src/buffer/SerializationFormat.ts +81 -0
  65. package/src/buffer/WriteBaseBuffer.ts +20 -0
  66. package/src/buffer/WriteBuffer.ts +15 -0
  67. package/src/buffer/examples.ts +242 -0
  68. package/src/buffer/index.ts +15 -0
  69. package/src/common.ts +20 -0
  70. package/src/endpoint/ProxyRPCClient.ts +64 -0
  71. package/src/endpoint/RPCClientHost.ts +45 -0
  72. package/src/endpoint/RPCService.ts +54 -0
  73. package/src/endpoint/RPCServiceHost.ts +18 -0
  74. package/src/error.ts +98 -0
  75. package/src/index.ts +16 -0
  76. package/src/middlewares/buffer.ts +33 -0
  77. package/src/middlewares/handleDisconnectedRequest.ts +30 -0
  78. package/src/middlewares/handlePortRequest.ts +141 -0
  79. package/src/middlewares/handleRequest.ts +128 -0
  80. package/src/middlewares/handleRequestUtils.ts +43 -0
  81. package/src/middlewares/handleResponse.ts +36 -0
  82. package/src/middlewares/index.ts +11 -0
  83. package/src/middlewares/logger.ts +22 -0
  84. package/src/middlewares/normalize.ts +167 -0
  85. package/src/middlewares/prepareRequestData.ts +137 -0
  86. package/src/middlewares/sendRequest.ts +15 -0
  87. package/src/middlewares/updateSeqInfo.ts +34 -0
  88. package/src/middlewares/utils.ts +67 -0
  89. package/src/protocol/AbstractChannelProtocol.ts +343 -0
  90. package/src/protocol/MessageChannel.ts +80 -0
  91. package/src/protocol/WebSocketChannel.ts +179 -0
  92. package/src/protocol/WorkerChannel.ts +36 -0
  93. package/src/types/buffer.ts +5 -0
  94. package/src/types/channel.ts +50 -0
  95. package/src/types/index.ts +9 -0
  96. package/src/types/messageChannel.ts +133 -0
  97. package/src/types/middleware.ts +54 -0
  98. package/src/types/proxyChannel.ts +3 -0
  99. package/src/types/proxyService.ts +18 -0
  100. package/src/types/rpc.ts +61 -0
  101. package/src/types/rpcProtocol.ts +24 -0
  102. package/src/utils/constants.ts +17 -0
  103. package/src/utils/index.ts +5 -0
  104. package/src/utils/jsonrpc.ts +242 -0
@@ -0,0 +1,343 @@
1
+ import { Disposable } from '@x-oasis/disposable';
2
+ import { Event } from '@x-oasis/emitter';
3
+ import { Deferred } from '@x-oasis/deferred';
4
+ import { generateRandomKey } from '@x-oasis/id';
5
+
6
+ import {
7
+ SendingProps,
8
+ IMessageChannel,
9
+ ClientMiddleware,
10
+ SenderMiddleware,
11
+ RequestRawSequenceId,
12
+ PendingSendEntry,
13
+ AbstractChannelProtocolProps,
14
+ } from '../types';
15
+ import { runMiddlewares, sendRequest } from '../middlewares';
16
+ import ReadBaseBuffer from '../buffer/ReadBaseBuffer';
17
+ import WriteBaseBuffer from '../buffer/WriteBaseBuffer';
18
+ import { BufferFactory } from '../buffer/BufferFactory';
19
+ import { SerializationFormat } from '../buffer/SerializationFormat';
20
+ import { resumeMiddlewares } from '../middlewares/utils';
21
+
22
+ import { deserialize, serialize } from '../middlewares/buffer';
23
+ import { handleResponse } from '../middlewares/handleResponse';
24
+ import RPCService from '../endpoint/RPCService';
25
+ import { prepareNormalData } from '../middlewares/prepareRequestData';
26
+ import { updateSeqInfo } from '../middlewares/updateSeqInfo';
27
+ import { normalizeMessageChannelRawMessage } from '../middlewares/normalize';
28
+ import { handleRequest } from '../middlewares/handleRequest';
29
+
30
+ abstract class AbstractChannelProtocol
31
+ extends Disposable
32
+ implements IMessageChannel
33
+ {
34
+ private readonly _masterProcessName: string;
35
+
36
+ private _key: string;
37
+
38
+ private _service: RPCService;
39
+
40
+ private readonly _description: string;
41
+
42
+ private _seqId: RequestRawSequenceId = -1;
43
+
44
+ // decoder should comes first !!!!
45
+ protected _onMessageMiddleware: ClientMiddleware[] = [
46
+ normalizeMessageChannelRawMessage,
47
+ deserialize,
48
+ handleRequest,
49
+ handleResponse,
50
+ ];
51
+
52
+ private _senderMiddleware: SenderMiddleware[] = [
53
+ prepareNormalData,
54
+ updateSeqInfo,
55
+ serialize,
56
+ sendRequest,
57
+ ];
58
+
59
+ private _readBuffer: ReadBaseBuffer | null = null;
60
+
61
+ private _writeBuffer: WriteBaseBuffer | null = null;
62
+
63
+ private _serializationFormat: string;
64
+
65
+ private _isConnected = true;
66
+
67
+ /**
68
+ * 如果说channel存在的话,那么就是ongoing request
69
+ */
70
+ public ongoingRequests: Map<string, Deferred> = new Map();
71
+
72
+ /**
73
+ * 如果说channel不存在,那么请求就会暂时放到这个里面
74
+ */
75
+ public pendingSendEntries = new Set<PendingSendEntry>();
76
+
77
+ public requestEvents: Map<string, any> = new Map();
78
+
79
+ private onDidConnectedEvent = new Event({ name: 'on-did-connected' });
80
+
81
+ onDidConnected = this.onDidConnectedEvent.subscribe;
82
+
83
+ private onDidDisconnectedEvent = new Event({ name: 'on-did-disconnect' });
84
+
85
+ onDidDisconnected = this.onDidDisconnectedEvent.subscribe;
86
+
87
+ constructor(props?: AbstractChannelProtocolProps) {
88
+ super();
89
+ const {
90
+ description,
91
+ masterProcessName,
92
+ connected = true,
93
+ serializationFormat = SerializationFormat.JSON,
94
+ readBuffer,
95
+ writeBuffer,
96
+ } = props || {};
97
+
98
+ this._description = description;
99
+ this._isConnected = connected;
100
+ this._masterProcessName = masterProcessName;
101
+ this._serializationFormat = serializationFormat;
102
+
103
+ // 如果提供了自定义 buffer,直接使用
104
+ if (readBuffer) {
105
+ this._readBuffer = readBuffer;
106
+ }
107
+ if (writeBuffer) {
108
+ this._writeBuffer = writeBuffer;
109
+ }
110
+
111
+ // 需要创建,否则创建'message'监听时,会出现混乱
112
+ this._key = generateRandomKey();
113
+ this.registerDisposable(this.onDidConnected(this.didConnected.bind(this)));
114
+
115
+ this._onMessageMiddleware = this.decorateOnMessageMiddleware(
116
+ this._onMessageMiddleware
117
+ );
118
+ this._senderMiddleware = this.decorateSendMiddleware(
119
+ this._senderMiddleware
120
+ );
121
+
122
+ this.applyOnMessageMiddleware(this._onMessageMiddleware);
123
+ this.applySendMiddleware(this._senderMiddleware);
124
+ }
125
+
126
+ get service() {
127
+ return this._service;
128
+ }
129
+
130
+ setService(service: RPCService) {
131
+ this._service = service;
132
+ }
133
+
134
+ get senderMiddleware() {
135
+ return this._senderMiddleware;
136
+ }
137
+
138
+ /**
139
+ * Get or create read buffer instance
140
+ * Uses lazy initialization with caching for performance
141
+ *
142
+ * Priority:
143
+ * 1. Custom buffer provided in constructor
144
+ * 2. Cached instance
145
+ * 3. Create new instance using BufferFactory with configured format
146
+ *
147
+ * Subclasses can override this method to provide custom buffer logic
148
+ */
149
+ get readBuffer(): ReadBaseBuffer {
150
+ if (this._readBuffer) {
151
+ return this._readBuffer;
152
+ }
153
+
154
+ // Try to create using BufferFactory
155
+ try {
156
+ this._readBuffer = BufferFactory.createReadBuffer(
157
+ this._serializationFormat
158
+ );
159
+ } catch (error) {
160
+ // Fallback to JSON if configured format is not available
161
+ console.warn(
162
+ `[AbstractChannelProtocol] Failed to create read buffer with format "${this._serializationFormat}", falling back to JSON.`,
163
+ error
164
+ );
165
+ this._readBuffer = BufferFactory.createReadBuffer(
166
+ SerializationFormat.JSON
167
+ );
168
+ }
169
+
170
+ return this._readBuffer;
171
+ }
172
+
173
+ /**
174
+ * Get or create write buffer instance
175
+ * Uses lazy initialization with caching for performance
176
+ *
177
+ * Priority:
178
+ * 1. Custom buffer provided in constructor
179
+ * 2. Cached instance
180
+ * 3. Create new instance using BufferFactory with configured format
181
+ *
182
+ * Subclasses can override this method to provide custom buffer logic
183
+ */
184
+ get writeBuffer(): WriteBaseBuffer {
185
+ if (this._writeBuffer) {
186
+ return this._writeBuffer;
187
+ }
188
+
189
+ // Try to create using BufferFactory
190
+ try {
191
+ this._writeBuffer = BufferFactory.createWriteBuffer(
192
+ this._serializationFormat
193
+ );
194
+ } catch (error) {
195
+ // Fallback to JSON if configured format is not available
196
+ console.warn(
197
+ `[AbstractChannelProtocol] Failed to create write buffer with format "${this._serializationFormat}", falling back to JSON.`,
198
+ error
199
+ );
200
+ this._writeBuffer = BufferFactory.createWriteBuffer(
201
+ SerializationFormat.JSON
202
+ );
203
+ }
204
+
205
+ return this._writeBuffer;
206
+ }
207
+
208
+ /**
209
+ * Get the configured serialization format
210
+ */
211
+ get serializationFormat(): string {
212
+ return this._serializationFormat;
213
+ }
214
+
215
+ /**
216
+ * Set serialization format (will recreate buffers on next access)
217
+ * Note: This will clear cached buffers, new instances will be created on next access
218
+ */
219
+ setSerializationFormat(format: string): void {
220
+ if (this._serializationFormat !== format) {
221
+ this._serializationFormat = format;
222
+ // Clear cached buffers to force recreation with new format
223
+ this._readBuffer = null;
224
+ this._writeBuffer = null;
225
+ }
226
+ }
227
+
228
+ // start from 1
229
+ get seqId() {
230
+ this._seqId += 1;
231
+ return `${this._key}_${this._seqId}`;
232
+ }
233
+
234
+ get description() {
235
+ return this._description;
236
+ }
237
+
238
+ get masterProcessName() {
239
+ return this._masterProcessName;
240
+ }
241
+
242
+ addPendingSendEntry(entry: PendingSendEntry) {
243
+ this.pendingSendEntries.add(entry);
244
+ }
245
+
246
+ /**
247
+ *
248
+ * @param middlewares
249
+ * @returns
250
+ *
251
+ * 增加自定义的middleware,需要重写这个方法
252
+ */
253
+ decorateSendMiddleware(middlewares: SenderMiddleware[]) {
254
+ return middlewares;
255
+ }
256
+
257
+ decorateOnMessageMiddleware(middlewares: ClientMiddleware[]) {
258
+ return middlewares;
259
+ }
260
+
261
+ applyOnMessageMiddleware(fns: Function | Function[]) {
262
+ const copy = [].concat(fns);
263
+ this._onMessageMiddleware = [];
264
+ copy.forEach((fn) => {
265
+ if (typeof fn === 'function') {
266
+ this._onMessageMiddleware.push(fn(this));
267
+ }
268
+ });
269
+ }
270
+
271
+ applySendMiddleware(fns: Function | Function[]) {
272
+ const copy = [].concat(fns);
273
+ this._senderMiddleware = [];
274
+
275
+ copy.forEach((fn) => {
276
+ if (typeof fn === 'function') {
277
+ this._senderMiddleware.push(fn(this));
278
+ }
279
+ });
280
+ }
281
+
282
+ isConnected() {
283
+ return this._isConnected;
284
+ }
285
+
286
+ send(..._args: any[]) {
287
+ throw new Error('send method is not implemented');
288
+ }
289
+
290
+ on(..._args: any[]) {
291
+ throw new Error('onMessage method is not implemented');
292
+ }
293
+
294
+ resumePendingEntry() {
295
+ this.pendingSendEntries.forEach((entry) => {
296
+ this.pendingSendEntries.delete(entry);
297
+ resumeMiddlewares(this.senderMiddleware, entry);
298
+ });
299
+ }
300
+
301
+ didConnected() {
302
+ this.resumePendingEntry();
303
+ }
304
+
305
+ connect() {
306
+ this.onDidConnectedEvent.fire();
307
+ }
308
+
309
+ // 已经绑定了channel,这个时候需要直接触发
310
+ activate() {
311
+ this._isConnected = true;
312
+ this.onDidConnectedEvent.fire();
313
+ }
314
+
315
+ disconnect() {
316
+ this._isConnected = false;
317
+ this.onDidDisconnectedEvent.fire();
318
+ }
319
+
320
+ makeRequest(props: SendingProps, transfer?: MessagePort[]): Deferred | void;
321
+
322
+ makeRequest(
323
+ requestPath: string,
324
+ fnName: string,
325
+ ...args: any[]
326
+ ): Deferred | void;
327
+
328
+ makeRequest(...args: any[]) {
329
+ //
330
+ const { returnValue } = runMiddlewares(this.senderMiddleware, args);
331
+ if (returnValue) return returnValue;
332
+ }
333
+
334
+ sendReply(...args: any[]) {
335
+ this.send(...args);
336
+ }
337
+
338
+ onMessage(...args: any[]) {
339
+ runMiddlewares(this._onMessageMiddleware, args);
340
+ }
341
+ }
342
+
343
+ export default AbstractChannelProtocol;
@@ -0,0 +1,80 @@
1
+ // https://developer.mozilla.org/en-US/docs/Web/API/MessageChannel
2
+ import {
3
+ IMessageChannel,
4
+ AbstractChannelProtocolProps,
5
+ } from '../types/channel';
6
+ import AbstractChannelProtocol from './AbstractChannelProtocol';
7
+ import { SenderMiddleware, ClientMiddleware } from '../types';
8
+
9
+ export default class RPCMessageChannel
10
+ extends AbstractChannelProtocol
11
+ implements IMessageChannel
12
+ {
13
+ private readonly port: MessagePort;
14
+ private sender: any;
15
+ private targetOrigin: string;
16
+
17
+ constructor(
18
+ options: {
19
+ port: MessagePort;
20
+ sender?: any;
21
+ targetOrigin?: string;
22
+ } & AbstractChannelProtocolProps
23
+ ) {
24
+ // Extract channel-specific options and pass the rest to parent
25
+ const {
26
+ port,
27
+ sender = window,
28
+ targetOrigin = '*',
29
+ ...protocolOptions
30
+ } = options;
31
+ super(protocolOptions);
32
+ this.port = port;
33
+ this.targetOrigin = targetOrigin;
34
+ this.sender = sender;
35
+ // MessagePort 需要调用 start() 才能开始接收消息(当使用 addEventListener 时)
36
+ if (this.port.start) {
37
+ this.port.start();
38
+ }
39
+ }
40
+
41
+ on(listener: (event: MessageEvent) => void): void | (() => void) {
42
+ const f = (ev: MessageEvent): void => {
43
+ listener(ev);
44
+ };
45
+ this.port.addEventListener('message', f);
46
+ return () => this.port.removeEventListener('message', f);
47
+ }
48
+
49
+ send(message: any, transfer?: Transferable[]) {
50
+ if (transfer && transfer.length > 0) {
51
+ this.port.postMessage(message, transfer);
52
+ } else {
53
+ this.port.postMessage(message);
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Inherits buffer getters from AbstractChannelProtocol
59
+ * Uses BufferFactory with configured serialization format
60
+ *
61
+ * To use a different format, pass serializationFormat in constructor:
62
+ * new RPCMessageChannel({ port, serializationFormat: 'msgpack' })
63
+ *
64
+ * Or override these getters if you need custom buffer logic
65
+ */
66
+
67
+ decorateSendMiddleware(middlewares: SenderMiddleware[]) {
68
+ return middlewares;
69
+ }
70
+
71
+ decorateOnMessageMiddleware(middlewares: ClientMiddleware[]) {
72
+ return middlewares;
73
+ }
74
+
75
+ disconnect() {
76
+ if (this.port) {
77
+ this.port.close();
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,179 @@
1
+ import AbstractChannelProtocol from './AbstractChannelProtocol';
2
+ import {
3
+ SenderMiddleware,
4
+ ClientMiddleware,
5
+ AbstractChannelProtocolProps,
6
+ } from '../types';
7
+ import { normalizeWebSocketRawMessage } from '../middlewares/normalize';
8
+
9
+ export default class WebSocketChannel extends AbstractChannelProtocol {
10
+ private socket: WebSocket;
11
+ readonly name: string;
12
+ private reconnectAttempts: number;
13
+ private maxReconnectAttempts: number;
14
+ private reconnectDelay: number;
15
+
16
+ /**
17
+ * @param socket Pass the WebSocket instance (client-side) or WebSocket connection (server-side).
18
+ * @param options Configuration options
19
+ */
20
+ constructor(
21
+ socket: WebSocket,
22
+ options?: {
23
+ name?: string;
24
+ maxReconnectAttempts?: number;
25
+ reconnectDelay?: number;
26
+ connected?: boolean;
27
+ } & AbstractChannelProtocolProps
28
+ ) {
29
+ // super() must be the first statement - extract options inline using IIFE
30
+ const opts = options || {};
31
+ const {
32
+ name,
33
+ maxReconnectAttempts,
34
+ reconnectDelay,
35
+ connected,
36
+ ...protocolOptions
37
+ } = opts;
38
+
39
+ super({
40
+ connected: connected ?? false,
41
+ ...protocolOptions,
42
+ });
43
+
44
+ // Now we can assign to instance properties
45
+ this.socket = socket;
46
+ this.name = name || 'websocket';
47
+ this.reconnectAttempts = 0;
48
+ this.maxReconnectAttempts = maxReconnectAttempts ?? 5;
49
+ this.reconnectDelay = reconnectDelay ?? 1000;
50
+
51
+ // Set up WebSocket event handlers
52
+ this.setupSocketHandlers();
53
+ }
54
+
55
+ private setupSocketHandlers() {
56
+ this.socket.addEventListener('open', () => {
57
+ // WebSocket 已打开,激活连接
58
+ this.activate();
59
+ });
60
+
61
+ this.socket.addEventListener('close', () => {
62
+ // WebSocket 已关闭,断开连接
63
+ super.disconnect();
64
+ });
65
+
66
+ this.socket.addEventListener('error', (error) => {
67
+ console.error(`[WebSocketChannel ${this.name}] Error:`, error);
68
+ });
69
+ }
70
+
71
+ on(listener: (data: unknown) => void): void | (() => void) {
72
+ // Handle both browser MessageEvent and Node.js ws library format
73
+ const f = (ev: MessageEvent | Buffer | string | any): void => {
74
+ // Debug: log the event structure
75
+ if (ev === undefined || ev === null) {
76
+ console.warn(
77
+ `[WebSocketChannel ${this.name}] Received undefined/null message event`
78
+ );
79
+ return;
80
+ }
81
+
82
+ // Pass the raw event/data to listener
83
+ // The normalizeWebSocketRawMessage middleware will handle the conversion
84
+ listener(ev);
85
+ };
86
+
87
+ // Try addEventListener first (works in both browser and ws library)
88
+ if (typeof this.socket.addEventListener === 'function') {
89
+ this.socket.addEventListener('message', f);
90
+ return () => {
91
+ if (typeof this.socket.removeEventListener === 'function') {
92
+ this.socket.removeEventListener('message', f);
93
+ }
94
+ };
95
+ } else {
96
+ // Fallback to 'on' method for older ws library versions
97
+ // Type assertion for Node.js ws library
98
+ const wsSocket = this.socket as any;
99
+ if (typeof wsSocket.on === 'function') {
100
+ wsSocket.on('message', f);
101
+ return () => {
102
+ if (typeof wsSocket.off === 'function') {
103
+ wsSocket.off('message', f);
104
+ } else if (typeof wsSocket.removeListener === 'function') {
105
+ wsSocket.removeListener('message', f);
106
+ }
107
+ };
108
+ }
109
+ }
110
+
111
+ // If neither method works, return a no-op cleanup function
112
+ return () => {};
113
+ }
114
+
115
+ send(data: unknown): void {
116
+ if (this.socket.readyState === WebSocket.OPEN) {
117
+ // Data should already be serialized (string) by the middleware
118
+ // WebSocket.send() accepts string, ArrayBuffer, or Blob
119
+ if (typeof data === 'string') {
120
+ this.socket.send(data);
121
+ } else if (data instanceof ArrayBuffer || data instanceof Blob) {
122
+ this.socket.send(data);
123
+ } else {
124
+ // Fallback: serialize if not already serialized
125
+ this.socket.send(JSON.stringify(data));
126
+ }
127
+ } else {
128
+ console.warn(
129
+ `[WebSocketChannel ${this.name}] Cannot send: WebSocket is not open. State: ${this.socket.readyState}`
130
+ );
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Inherits buffer getters from AbstractChannelProtocol
136
+ * Uses BufferFactory with configured serialization format
137
+ *
138
+ * To use a different format, pass serializationFormat in constructor:
139
+ * new WebSocketChannel(socket, { serializationFormat: 'msgpack' })
140
+ *
141
+ * Or override these getters if you need custom buffer logic
142
+ */
143
+
144
+ decorateSendMiddleware(middlewares: SenderMiddleware[]) {
145
+ return middlewares;
146
+ }
147
+
148
+ decorateOnMessageMiddleware(middlewares: ClientMiddleware[]) {
149
+ // Replace the first middleware (normalizeMessageChannelRawMessage)
150
+ // with normalizeWebSocketRawMessage to handle both browser and Node.js ws library formats
151
+ // Note: We return the factory function, not the result of calling it
152
+ // because applyOnMessageMiddleware will call fn(this) for each middleware
153
+ if (middlewares.length > 0) {
154
+ return [normalizeWebSocketRawMessage, ...middlewares.slice(1)];
155
+ }
156
+ return middlewares;
157
+ }
158
+
159
+ disconnect() {
160
+ if (this.socket) {
161
+ this.socket.close();
162
+ }
163
+ super.disconnect();
164
+ }
165
+
166
+ /**
167
+ * Get the current WebSocket ready state
168
+ */
169
+ get readyState(): number {
170
+ return this.socket.readyState;
171
+ }
172
+
173
+ /**
174
+ * Check if WebSocket is open
175
+ */
176
+ isOpen(): boolean {
177
+ return this.socket.readyState === WebSocket.OPEN;
178
+ }
179
+ }
@@ -0,0 +1,36 @@
1
+ import AbstractChannelProtocol from './AbstractChannelProtocol';
2
+ import { AbstractChannelProtocolProps } from '../types/channel';
3
+
4
+ export default class WorkerChannel extends AbstractChannelProtocol {
5
+ private worker: any;
6
+ readonly name: string;
7
+
8
+ /**
9
+ * @param worker Pass the Worker in the main thread.
10
+ * @param options Configuration options including serialization format
11
+ */
12
+ constructor(
13
+ worker: any,
14
+ options?: {
15
+ name?: string;
16
+ } & AbstractChannelProtocolProps
17
+ ) {
18
+ // Extract Worker-specific options and pass the rest to parent
19
+ const { name, ...protocolOptions } = options || {};
20
+ super(protocolOptions);
21
+ this.worker = worker;
22
+ this.name = name || 'worker';
23
+ }
24
+
25
+ on(listener: (data: unknown) => void): void | (() => void) {
26
+ const f = (ev: MessageEvent): void => {
27
+ listener(ev);
28
+ };
29
+ this.worker.addEventListener('message', f);
30
+ return () => this.worker.removeEventListener('message', f);
31
+ }
32
+
33
+ send(data: unknown): void {
34
+ this.worker.postMessage(data);
35
+ }
36
+ }
@@ -0,0 +1,5 @@
1
+ export type DataBuffer = {
2
+ // refer to theia/packages/core/src/common/messaging/socket-write-buffer.ts
3
+ flush: () => void;
4
+ drain: () => void;
5
+ };
@@ -0,0 +1,50 @@
1
+ export type IMessageChannelOnClose = () => void;
2
+ export type IMessageChannelOnError = () => void;
3
+ export type IMessageChannelOnMessage = (message: any) => void;
4
+ export type IMessageChannelSend = (options: any) => void;
5
+ export type IMessageChannelDisconnect = () => void;
6
+
7
+ /**
8
+ * A channel is a bidirectional communications channel
9
+ */
10
+ export type IMessageChannel = {
11
+ onClose?: IMessageChannelOnClose;
12
+
13
+ onError?: IMessageChannelOnError;
14
+
15
+ onMessage: IMessageChannelOnMessage;
16
+
17
+ send: IMessageChannelSend;
18
+
19
+ disconnect: IMessageChannelDisconnect;
20
+ };
21
+
22
+ export type SendingProps = {
23
+ requestPath: string;
24
+ methodName: string;
25
+ args?: any[];
26
+ isOptionsRequest?: boolean;
27
+ transfer?: MessagePort[];
28
+ };
29
+
30
+ /**
31
+ * Channel protocol configuration options
32
+ */
33
+ export type AbstractChannelProtocolProps = {
34
+ description?: string;
35
+ masterProcessName?: string;
36
+ connected?: boolean;
37
+ /**
38
+ * Serialization format for buffer encoding/decoding
39
+ * @default 'json'
40
+ */
41
+ serializationFormat?: string;
42
+ /**
43
+ * Custom read buffer instance (overrides serializationFormat)
44
+ */
45
+ readBuffer?: any;
46
+ /**
47
+ * Custom write buffer instance (overrides serializationFormat)
48
+ */
49
+ writeBuffer?: any;
50
+ };
@@ -0,0 +1,9 @@
1
+ export * from './channel';
2
+ // export * from './messageChannel';
3
+ export * from './rpc';
4
+ export * from './proxyChannel';
5
+ export * from './proxyService';
6
+ export * from './middleware';
7
+
8
+ export type ClientMiddleware = any;
9
+ export type SenderMiddleware = any;