@theia/core 1.26.0 → 1.27.0-next.10

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 (164) hide show
  1. package/README.md +6 -7
  2. package/lib/browser/messaging/ws-connection-provider.d.ts +5 -4
  3. package/lib/browser/messaging/ws-connection-provider.d.ts.map +1 -1
  4. package/lib/browser/messaging/ws-connection-provider.js +30 -23
  5. package/lib/browser/messaging/ws-connection-provider.js.map +1 -1
  6. package/lib/browser/progress-status-bar-item.d.ts +1 -1
  7. package/lib/browser/progress-status-bar-item.d.ts.map +1 -1
  8. package/lib/browser/tree/tree-compression/compressed-tree-widget.js +2 -2
  9. package/lib/browser/tree/tree-compression/compressed-tree-widget.js.map +1 -1
  10. package/lib/browser/widgets/select-component.d.ts +4 -1
  11. package/lib/browser/widgets/select-component.d.ts.map +1 -1
  12. package/lib/browser/widgets/select-component.js +30 -16
  13. package/lib/browser/widgets/select-component.js.map +1 -1
  14. package/lib/common/cancellation.d.ts +1 -0
  15. package/lib/common/cancellation.d.ts.map +1 -1
  16. package/lib/common/cancellation.js +8 -0
  17. package/lib/common/cancellation.js.map +1 -1
  18. package/lib/common/index.d.ts +2 -0
  19. package/lib/common/index.d.ts.map +1 -1
  20. package/lib/common/index.js +2 -0
  21. package/lib/common/index.js.map +1 -1
  22. package/lib/common/message-rpc/channel.d.ts +106 -0
  23. package/lib/common/message-rpc/channel.d.ts.map +1 -0
  24. package/lib/common/message-rpc/channel.js +195 -0
  25. package/lib/common/message-rpc/channel.js.map +1 -0
  26. package/lib/common/message-rpc/channel.spec.d.ts +9 -0
  27. package/lib/common/message-rpc/channel.spec.d.ts.map +1 -0
  28. package/lib/common/message-rpc/channel.spec.js +80 -0
  29. package/lib/common/message-rpc/channel.spec.js.map +1 -0
  30. package/lib/common/message-rpc/index.d.ts +4 -0
  31. package/lib/common/message-rpc/index.d.ts.map +1 -0
  32. package/lib/{node/messaging/logger.js → common/message-rpc/index.js} +6 -19
  33. package/lib/common/message-rpc/index.js.map +1 -0
  34. package/lib/common/message-rpc/message-buffer.d.ts +50 -0
  35. package/lib/common/message-rpc/message-buffer.d.ts.map +1 -0
  36. package/lib/common/message-rpc/message-buffer.js +56 -0
  37. package/lib/common/message-rpc/message-buffer.js.map +1 -0
  38. package/lib/common/message-rpc/rpc-message-encoder.d.ts +159 -0
  39. package/lib/common/message-rpc/rpc-message-encoder.d.ts.map +1 -0
  40. package/lib/common/message-rpc/rpc-message-encoder.js +362 -0
  41. package/lib/common/message-rpc/rpc-message-encoder.js.map +1 -0
  42. package/lib/common/message-rpc/rpc-message-encoder.spec.d.ts +2 -0
  43. package/lib/common/message-rpc/rpc-message-encoder.spec.d.ts.map +1 -0
  44. package/lib/common/message-rpc/rpc-message-encoder.spec.js +37 -0
  45. package/lib/common/message-rpc/rpc-message-encoder.spec.js.map +1 -0
  46. package/lib/common/message-rpc/rpc-protocol.d.ts +61 -0
  47. package/lib/common/message-rpc/rpc-protocol.d.ts.map +1 -0
  48. package/lib/common/message-rpc/rpc-protocol.js +183 -0
  49. package/lib/common/message-rpc/rpc-protocol.js.map +1 -0
  50. package/lib/common/message-rpc/uint8-array-message-buffer.d.ts +52 -0
  51. package/lib/common/message-rpc/uint8-array-message-buffer.d.ts.map +1 -0
  52. package/lib/common/message-rpc/uint8-array-message-buffer.js +169 -0
  53. package/lib/common/message-rpc/uint8-array-message-buffer.js.map +1 -0
  54. package/lib/common/message-rpc/uint8-array-message-buffer.spec.d.ts +2 -0
  55. package/lib/common/message-rpc/uint8-array-message-buffer.spec.d.ts.map +1 -0
  56. package/lib/common/message-rpc/uint8-array-message-buffer.spec.js +39 -0
  57. package/lib/common/message-rpc/uint8-array-message-buffer.spec.js.map +1 -0
  58. package/lib/common/messaging/abstract-connection-provider.d.ts +9 -8
  59. package/lib/common/messaging/abstract-connection-provider.d.ts.map +1 -1
  60. package/lib/common/messaging/abstract-connection-provider.js +20 -35
  61. package/lib/common/messaging/abstract-connection-provider.js.map +1 -1
  62. package/lib/common/messaging/connection-error-handler.d.ts +1 -2
  63. package/lib/common/messaging/connection-error-handler.d.ts.map +1 -1
  64. package/lib/common/messaging/connection-error-handler.js +1 -1
  65. package/lib/common/messaging/connection-error-handler.js.map +1 -1
  66. package/lib/common/messaging/handler.d.ts +2 -2
  67. package/lib/common/messaging/handler.d.ts.map +1 -1
  68. package/lib/common/messaging/proxy-factory.d.ts +13 -7
  69. package/lib/common/messaging/proxy-factory.d.ts.map +1 -1
  70. package/lib/common/messaging/proxy-factory.js +18 -13
  71. package/lib/common/messaging/proxy-factory.js.map +1 -1
  72. package/lib/common/messaging/proxy-factory.spec.js +4 -15
  73. package/lib/common/messaging/proxy-factory.spec.js.map +1 -1
  74. package/lib/common/messaging/web-socket-channel.d.ts +54 -48
  75. package/lib/common/messaging/web-socket-channel.d.ts.map +1 -1
  76. package/lib/common/messaging/web-socket-channel.js +41 -105
  77. package/lib/common/messaging/web-socket-channel.js.map +1 -1
  78. package/lib/electron-browser/messaging/electron-ipc-connection-provider.d.ts +2 -2
  79. package/lib/electron-browser/messaging/electron-ipc-connection-provider.d.ts.map +1 -1
  80. package/lib/electron-browser/messaging/electron-ipc-connection-provider.js +18 -7
  81. package/lib/electron-browser/messaging/electron-ipc-connection-provider.js.map +1 -1
  82. package/lib/electron-browser/messaging/electron-ws-connection-provider.d.ts +2 -2
  83. package/lib/electron-browser/messaging/electron-ws-connection-provider.d.ts.map +1 -1
  84. package/lib/electron-browser/messaging/electron-ws-connection-provider.js +5 -7
  85. package/lib/electron-browser/messaging/electron-ws-connection-provider.js.map +1 -1
  86. package/lib/electron-main/messaging/electron-messaging-contribution.d.ts +37 -9
  87. package/lib/electron-main/messaging/electron-messaging-contribution.d.ts.map +1 -1
  88. package/lib/electron-main/messaging/electron-messaging-contribution.js +83 -68
  89. package/lib/electron-main/messaging/electron-messaging-contribution.js.map +1 -1
  90. package/lib/electron-main/messaging/electron-messaging-service.d.ts +2 -8
  91. package/lib/electron-main/messaging/electron-messaging-service.d.ts.map +1 -1
  92. package/lib/electron-main/messaging/electron-messaging-service.js.map +1 -1
  93. package/lib/electron-main/theia-electron-window.d.ts.map +1 -1
  94. package/lib/electron-main/theia-electron-window.js +11 -8
  95. package/lib/electron-main/theia-electron-window.js.map +1 -1
  96. package/lib/node/messaging/binary-message-pipe.d.ts +45 -0
  97. package/lib/node/messaging/binary-message-pipe.d.ts.map +1 -0
  98. package/lib/node/messaging/binary-message-pipe.js +152 -0
  99. package/lib/node/messaging/binary-message-pipe.js.map +1 -0
  100. package/lib/node/messaging/ipc-bootstrap.js +2 -11
  101. package/lib/node/messaging/ipc-bootstrap.js.map +1 -1
  102. package/lib/node/messaging/ipc-channel.d.ts +26 -0
  103. package/lib/node/messaging/ipc-channel.d.ts.map +1 -0
  104. package/lib/node/messaging/ipc-channel.js +86 -0
  105. package/lib/node/messaging/ipc-channel.js.map +1 -0
  106. package/lib/node/messaging/ipc-connection-provider.d.ts +3 -5
  107. package/lib/node/messaging/ipc-connection-provider.d.ts.map +1 -1
  108. package/lib/node/messaging/ipc-connection-provider.js +14 -31
  109. package/lib/node/messaging/ipc-connection-provider.js.map +1 -1
  110. package/lib/node/messaging/ipc-protocol.d.ts +2 -2
  111. package/lib/node/messaging/ipc-protocol.d.ts.map +1 -1
  112. package/lib/node/messaging/messaging-contribution.d.ts +6 -9
  113. package/lib/node/messaging/messaging-contribution.d.ts.map +1 -1
  114. package/lib/node/messaging/messaging-contribution.js +23 -68
  115. package/lib/node/messaging/messaging-contribution.js.map +1 -1
  116. package/lib/node/messaging/messaging-service.d.ts +4 -23
  117. package/lib/node/messaging/messaging-service.d.ts.map +1 -1
  118. package/lib/node/messaging/messaging-service.js +1 -15
  119. package/lib/node/messaging/messaging-service.js.map +1 -1
  120. package/lib/node/messaging/test/test-web-socket-channel.d.ts +4 -2
  121. package/lib/node/messaging/test/test-web-socket-channel.d.ts.map +1 -1
  122. package/lib/node/messaging/test/test-web-socket-channel.js +25 -12
  123. package/lib/node/messaging/test/test-web-socket-channel.js.map +1 -1
  124. package/package.json +5 -8
  125. package/src/browser/messaging/ws-connection-provider.ts +34 -25
  126. package/src/browser/progress-status-bar-item.ts +1 -1
  127. package/src/browser/style/menus.css +1 -0
  128. package/src/browser/tree/tree-compression/compressed-tree-widget.tsx +2 -2
  129. package/src/browser/widgets/select-component.tsx +37 -17
  130. package/src/common/cancellation.ts +8 -0
  131. package/src/common/index.ts +2 -0
  132. package/src/common/message-rpc/channel.spec.ts +88 -0
  133. package/src/common/message-rpc/channel.ts +260 -0
  134. package/src/{node/messaging/logger.ts → common/message-rpc/index.ts} +4 -23
  135. package/src/common/message-rpc/message-buffer.ts +99 -0
  136. package/src/common/message-rpc/rpc-message-encoder.spec.ts +42 -0
  137. package/src/common/message-rpc/rpc-message-encoder.ts +497 -0
  138. package/src/common/message-rpc/rpc-protocol.ts +217 -0
  139. package/src/common/message-rpc/uint8-array-message-buffer.spec.ts +41 -0
  140. package/src/common/message-rpc/uint8-array-message-buffer.ts +206 -0
  141. package/src/common/messaging/abstract-connection-provider.ts +28 -37
  142. package/src/common/messaging/connection-error-handler.ts +1 -2
  143. package/src/common/messaging/handler.ts +2 -2
  144. package/src/common/messaging/proxy-factory.spec.ts +4 -17
  145. package/src/common/messaging/proxy-factory.ts +27 -16
  146. package/src/common/messaging/web-socket-channel.ts +79 -135
  147. package/src/electron-browser/messaging/electron-ipc-connection-provider.ts +21 -7
  148. package/src/electron-browser/messaging/electron-ws-connection-provider.ts +5 -8
  149. package/src/electron-main/messaging/electron-messaging-contribution.ts +87 -65
  150. package/src/electron-main/messaging/electron-messaging-service.ts +2 -8
  151. package/src/electron-main/theia-electron-window.ts +12 -9
  152. package/src/node/messaging/binary-message-pipe.ts +168 -0
  153. package/src/node/messaging/ipc-bootstrap.ts +3 -11
  154. package/src/node/messaging/ipc-channel.ts +97 -0
  155. package/src/node/messaging/ipc-connection-provider.ts +18 -35
  156. package/src/node/messaging/ipc-protocol.ts +2 -2
  157. package/src/node/messaging/messaging-contribution.ts +29 -74
  158. package/src/node/messaging/messaging-service.ts +4 -31
  159. package/src/node/messaging/test/test-web-socket-channel.ts +26 -17
  160. package/lib/node/messaging/logger.d.ts +0 -8
  161. package/lib/node/messaging/logger.d.ts.map +0 -1
  162. package/lib/node/messaging/logger.js.map +0 -1
  163. package/shared/vscode-ws-jsonrpc/index.d.ts +0 -1
  164. package/shared/vscode-ws-jsonrpc/index.js +0 -1
@@ -0,0 +1,497 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2022 Red Hat, Inc. 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 WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+ /* eslint-disable @typescript-eslint/no-explicit-any */
17
+
18
+ import { ReadBuffer, WriteBuffer } from './message-buffer';
19
+
20
+ /**
21
+ * This code lets you encode rpc protocol messages (request/reply/notification/error/cancel)
22
+ * into a channel write buffer and decode the same messages from a read buffer.
23
+ * Custom encoders/decoders can be registered to specially handling certain types of values
24
+ * to be encoded. Clients are responsible for ensuring that the set of tags for encoders
25
+ * is distinct and the same at both ends of a channel.
26
+ */
27
+
28
+ export type RpcMessage = RequestMessage | ReplyMessage | ReplyErrMessage | CancelMessage | NotificationMessage;
29
+
30
+ export const enum RpcMessageType {
31
+ Request = 1,
32
+ Notification = 2,
33
+ Reply = 3,
34
+ ReplyErr = 4,
35
+ Cancel = 5,
36
+ }
37
+
38
+ export interface CancelMessage {
39
+ type: RpcMessageType.Cancel;
40
+ id: number;
41
+ }
42
+
43
+ export interface RequestMessage {
44
+ type: RpcMessageType.Request;
45
+ id: number;
46
+ method: string;
47
+ args: any[];
48
+ }
49
+
50
+ export interface NotificationMessage {
51
+ type: RpcMessageType.Notification;
52
+ id: number;
53
+ method: string;
54
+ args: any[];
55
+ }
56
+
57
+ export interface ReplyMessage {
58
+ type: RpcMessageType.Reply;
59
+ id: number;
60
+ res: any;
61
+ }
62
+
63
+ export interface ReplyErrMessage {
64
+ type: RpcMessageType.ReplyErr;
65
+ id: number;
66
+ err: any;
67
+ }
68
+
69
+ export interface SerializedError {
70
+ readonly $isError: true;
71
+ readonly name: string;
72
+ readonly message: string;
73
+ readonly stack: string;
74
+ }
75
+
76
+ /**
77
+ * A special error that can be returned in case a request
78
+ * has failed. Provides additional information i.e. an error code
79
+ * and additional error data.
80
+ */
81
+ export class ResponseError extends Error {
82
+ constructor(readonly code: number, message: string, readonly data: any) {
83
+ super(message);
84
+ }
85
+ }
86
+
87
+ /**
88
+ * The tag values for the default {@link ValueEncoder}s & {@link ValueDecoder}s
89
+ */
90
+
91
+ export enum ObjectType {
92
+ JSON = 0,
93
+ ByteArray = 1,
94
+ ObjectArray = 2,
95
+ Undefined = 3,
96
+ Object = 4,
97
+ String = 5,
98
+ Boolean = 6,
99
+ Number = 7,
100
+ // eslint-disable-next-line @typescript-eslint/no-shadow
101
+ ResponseError = 8,
102
+ Error = 9
103
+
104
+ }
105
+
106
+ /**
107
+ * A value encoder writes javascript values to a write buffer. Encoders will be asked
108
+ * in turn (ordered by their tag value, descending) whether they can encode a given value
109
+ * This means encoders with higher tag values have priority. Since the default encoders
110
+ * have tag values from 1-7, they can be easily overridden.
111
+ */
112
+ export interface ValueEncoder {
113
+ /**
114
+ * Returns true if this encoder wants to encode this value.
115
+ * @param value the value to be encoded
116
+ */
117
+ is(value: any): boolean;
118
+ /**
119
+ * Write the given value to the buffer. Will only be called if {@link is(value)} returns true.
120
+ * @param buf The buffer to write to
121
+ * @param value The value to be written
122
+ * @param recursiveEncode A function that will use the encoders registered on the {@link MessageEncoder}
123
+ * to write a value to the underlying buffer. This is used mostly to write structures like an array
124
+ * without having to know how to encode the values in the array
125
+ */
126
+ write(buf: WriteBuffer, value: any, recursiveEncode?: (buf: WriteBuffer, value: any) => void): void;
127
+ }
128
+
129
+ /**
130
+ * Reads javascript values from a read buffer
131
+ */
132
+ export interface ValueDecoder {
133
+ /**
134
+ * Reads a value from a read buffer. This method will be called for the decoder that is
135
+ * registered for the tag associated with the value encoder that encoded this value.
136
+ * @param buf The read buffer to read from
137
+ * @param recursiveDecode A function that will use the decoders registered on the {@link RpcMessageDecoder}
138
+ * to read values from the underlying read buffer. This is used mostly to decode structures like an array
139
+ * without having to know how to decode the values in the array.
140
+ */
141
+ read(buf: ReadBuffer, recursiveDecode: (buf: ReadBuffer) => unknown): unknown;
142
+ }
143
+
144
+ /**
145
+ * A `RpcMessageDecoder` parses a a binary message received via {@link ReadBuffer} into a {@link RpcMessage}
146
+ */
147
+ export class RpcMessageDecoder {
148
+
149
+ protected decoders: Map<number, ValueDecoder> = new Map();
150
+
151
+ constructor() {
152
+ this.registerDecoder(ObjectType.JSON, {
153
+ read: buf => {
154
+ const json = buf.readString();
155
+ return JSON.parse(json);
156
+ }
157
+ });
158
+ this.registerDecoder(ObjectType.Error, {
159
+ read: buf => {
160
+ const serializedError: SerializedError = JSON.parse(buf.readString());
161
+ const error = new Error(serializedError.message);
162
+ Object.assign(error, serializedError);
163
+ return error;
164
+ }
165
+ });
166
+
167
+ this.registerDecoder(ObjectType.ResponseError, {
168
+ read: buf => {
169
+ const error = JSON.parse(buf.readString());
170
+ return new ResponseError(error.code, error.message, error.data);
171
+ }
172
+ });
173
+ this.registerDecoder(ObjectType.ByteArray, {
174
+ read: buf => buf.readBytes()
175
+ });
176
+
177
+ this.registerDecoder(ObjectType.ObjectArray, {
178
+ read: buf => this.readArray(buf)
179
+ });
180
+
181
+ this.registerDecoder(ObjectType.Undefined, {
182
+ read: () => undefined
183
+ });
184
+
185
+ this.registerDecoder(ObjectType.Object, {
186
+ read: (buf, recursiveRead) => {
187
+ const propertyCount = buf.readLength();
188
+ const result = Object.create({});
189
+ for (let i = 0; i < propertyCount; i++) {
190
+ const key = buf.readString();
191
+ const value = recursiveRead(buf);
192
+ result[key] = value;
193
+ }
194
+ return result;
195
+ }
196
+ });
197
+
198
+ this.registerDecoder(ObjectType.String, {
199
+ read: (buf, recursiveRead) => buf.readString()
200
+ });
201
+
202
+ this.registerDecoder(ObjectType.Boolean, {
203
+ read: buf => buf.readUint8() === 1
204
+ });
205
+
206
+ this.registerDecoder(ObjectType.Number, {
207
+ read: buf => buf.readNumber()
208
+ });
209
+
210
+ }
211
+
212
+ /**
213
+ * Registers a new {@link ValueDecoder} for the given tag.
214
+ * After the successful registration the {@link tagIntType} is recomputed
215
+ * by retrieving the highest tag value and calculating the required Uint size to store it.
216
+ * @param tag the tag for which the decoder should be registered.
217
+ * @param decoder the decoder that should be registered.
218
+ */
219
+ registerDecoder(tag: number, decoder: ValueDecoder): void {
220
+ if (this.decoders.has(tag)) {
221
+ throw new Error(`Decoder already registered: ${tag}`);
222
+ }
223
+ this.decoders.set(tag, decoder);
224
+ }
225
+ parse(buf: ReadBuffer): RpcMessage {
226
+ try {
227
+ const msgType = buf.readUint8();
228
+
229
+ switch (msgType) {
230
+ case RpcMessageType.Request:
231
+ return this.parseRequest(buf);
232
+ case RpcMessageType.Notification:
233
+ return this.parseNotification(buf);
234
+ case RpcMessageType.Reply:
235
+ return this.parseReply(buf);
236
+ case RpcMessageType.ReplyErr:
237
+ return this.parseReplyErr(buf);
238
+ case RpcMessageType.Cancel:
239
+ return this.parseCancel(buf);
240
+ }
241
+ throw new Error(`Unknown message type: ${msgType}`);
242
+ } catch (e) {
243
+ // exception does not show problematic content: log it!
244
+ console.log('failed to parse message: ' + buf);
245
+ throw e;
246
+ }
247
+ }
248
+
249
+ protected parseCancel(msg: ReadBuffer): CancelMessage {
250
+ const callId = msg.readUint32();
251
+ return {
252
+ type: RpcMessageType.Cancel,
253
+ id: callId
254
+ };
255
+ }
256
+
257
+ protected parseRequest(msg: ReadBuffer): RequestMessage {
258
+ const callId = msg.readUint32();
259
+ const method = msg.readString();
260
+ let args = this.readArray(msg);
261
+ // convert `null` to `undefined`, since we don't use `null` in internal plugin APIs
262
+ args = args.map(arg => arg === null ? undefined : arg); // eslint-disable-line no-null/no-null
263
+
264
+ return {
265
+ type: RpcMessageType.Request,
266
+ id: callId,
267
+ method: method,
268
+ args: args
269
+ };
270
+ }
271
+
272
+ protected parseNotification(msg: ReadBuffer): NotificationMessage {
273
+ const callId = msg.readUint32();
274
+ const method = msg.readString();
275
+ let args = this.readArray(msg);
276
+ // convert `null` to `undefined`, since we don't use `null` in internal plugin APIs
277
+ args = args.map(arg => arg === null ? undefined : arg); // eslint-disable-line no-null/no-null
278
+
279
+ return {
280
+ type: RpcMessageType.Notification,
281
+ id: callId,
282
+ method: method,
283
+ args: args
284
+ };
285
+ }
286
+
287
+ parseReply(msg: ReadBuffer): ReplyMessage {
288
+ const callId = msg.readUint32();
289
+ const value = this.readTypedValue(msg);
290
+ return {
291
+ type: RpcMessageType.Reply,
292
+ id: callId,
293
+ res: value
294
+ };
295
+ }
296
+
297
+ parseReplyErr(msg: ReadBuffer): ReplyErrMessage {
298
+ const callId = msg.readUint32();
299
+
300
+ const err = this.readTypedValue(msg);
301
+
302
+ return {
303
+ type: RpcMessageType.ReplyErr,
304
+ id: callId,
305
+ err
306
+ };
307
+ }
308
+
309
+ readArray(buf: ReadBuffer): any[] {
310
+ const length = buf.readLength();
311
+ const result = new Array(length);
312
+ for (let i = 0; i < length; i++) {
313
+ result[i] = this.readTypedValue(buf);
314
+ }
315
+ return result;
316
+ }
317
+
318
+ readTypedValue(buf: ReadBuffer): any {
319
+ const type = buf.readUint8();
320
+ const decoder = this.decoders.get(type);
321
+ if (!decoder) {
322
+ throw new Error(`No decoder for tag ${type}`);
323
+ }
324
+ return decoder.read(buf, innerBuffer => this.readTypedValue(innerBuffer));
325
+ }
326
+ }
327
+
328
+ /**
329
+ * A `RpcMessageEncoder` writes {@link RpcMessage} objects to a {@link WriteBuffer}. Note that it is
330
+ * up to clients to commit the message. This allows for multiple messages being
331
+ * encoded before sending.
332
+ */
333
+ export class RpcMessageEncoder {
334
+
335
+ protected readonly encoders: [number, ValueEncoder][] = [];
336
+ protected readonly registeredTags: Set<number> = new Set();
337
+
338
+ constructor() {
339
+ this.registerEncoders();
340
+ }
341
+
342
+ protected registerEncoders(): void {
343
+ // encoders will be consulted in reverse order of registration, so the JSON fallback needs to be last
344
+ this.registerEncoder(ObjectType.JSON, {
345
+ is: () => true,
346
+ write: (buf, value) => {
347
+ buf.writeString(JSON.stringify(value));
348
+ }
349
+ });
350
+
351
+ this.registerEncoder(ObjectType.Object, {
352
+ is: value => typeof value === 'object',
353
+ write: (buf, object, recursiveEncode) => {
354
+ const properties = Object.keys(object);
355
+ const relevant = [];
356
+ for (const property of properties) {
357
+ const value = object[property];
358
+ if (typeof value !== 'function') {
359
+ relevant.push([property, value]);
360
+ }
361
+ }
362
+
363
+ buf.writeLength(relevant.length);
364
+ for (const [property, value] of relevant) {
365
+ buf.writeString(property);
366
+ recursiveEncode?.(buf, value);
367
+ }
368
+ }
369
+ });
370
+
371
+ this.registerEncoder(ObjectType.Error, {
372
+ is: value => value instanceof Error,
373
+ write: (buf, error: Error) => {
374
+ const { name, message } = error;
375
+ const stack: string = (<any>error).stacktrace || error.stack;
376
+ const serializedError = {
377
+ $isError: true,
378
+ name,
379
+ message,
380
+ stack
381
+ };
382
+ buf.writeString(JSON.stringify(serializedError));
383
+ }
384
+ });
385
+
386
+ this.registerEncoder(ObjectType.ResponseError, {
387
+ is: value => value instanceof ResponseError,
388
+ write: (buf, value) => buf.writeString(JSON.stringify(value))
389
+ });
390
+
391
+ this.registerEncoder(ObjectType.Undefined, {
392
+ // eslint-disable-next-line no-null/no-null
393
+ is: value => value == null,
394
+ write: () => { }
395
+ });
396
+
397
+ this.registerEncoder(ObjectType.ObjectArray, {
398
+ is: value => Array.isArray(value),
399
+ write: (buf, value) => {
400
+ this.writeArray(buf, value);
401
+ }
402
+ });
403
+
404
+ this.registerEncoder(ObjectType.ByteArray, {
405
+ is: value => value instanceof Uint8Array,
406
+ write: (buf, value) => {
407
+ buf.writeBytes(value);
408
+ }
409
+ });
410
+
411
+ this.registerEncoder(ObjectType.String, {
412
+ is: value => typeof value === 'string',
413
+ write: (buf, value) => {
414
+ buf.writeString(value);
415
+ }
416
+ });
417
+
418
+ this.registerEncoder(ObjectType.Boolean, {
419
+ is: value => typeof value === 'boolean',
420
+ write: (buf, value) => {
421
+ buf.writeUint8(value === true ? 1 : 0);
422
+ }
423
+ });
424
+
425
+ this.registerEncoder(ObjectType.Number, {
426
+ is: value => typeof value === 'number',
427
+ write: (buf, value) => {
428
+ buf.writeNumber(value);
429
+ }
430
+ });
431
+ }
432
+
433
+ /**
434
+ * Registers a new {@link ValueEncoder} for the given tag.
435
+ * After the successful registration the {@link tagIntType} is recomputed
436
+ * by retrieving the highest tag value and calculating the required Uint size to store it.
437
+ * @param tag the tag for which the encoder should be registered.
438
+ * @param decoder the encoder that should be registered.
439
+ */
440
+ registerEncoder<T>(tag: number, encoder: ValueEncoder): void {
441
+ if (this.registeredTags.has(tag)) {
442
+ throw new Error(`Tag already registered: ${tag}`);
443
+ }
444
+ this.registeredTags.add(tag);
445
+ this.encoders.push([tag, encoder]);
446
+ }
447
+
448
+ cancel(buf: WriteBuffer, requestId: number): void {
449
+ buf.writeUint8(RpcMessageType.Cancel);
450
+ buf.writeUint32(requestId);
451
+ }
452
+
453
+ notification(buf: WriteBuffer, requestId: number, method: string, args: any[]): void {
454
+ buf.writeUint8(RpcMessageType.Notification);
455
+ buf.writeUint32(requestId);
456
+ buf.writeString(method);
457
+ this.writeArray(buf, args);
458
+ }
459
+
460
+ request(buf: WriteBuffer, requestId: number, method: string, args: any[]): void {
461
+ buf.writeUint8(RpcMessageType.Request);
462
+ buf.writeUint32(requestId);
463
+ buf.writeString(method);
464
+ this.writeArray(buf, args);
465
+ }
466
+
467
+ replyOK(buf: WriteBuffer, requestId: number, res: any): void {
468
+ buf.writeUint8(RpcMessageType.Reply);
469
+ buf.writeUint32(requestId);
470
+ this.writeTypedValue(buf, res);
471
+ }
472
+
473
+ replyErr(buf: WriteBuffer, requestId: number, err: any): void {
474
+ buf.writeUint8(RpcMessageType.ReplyErr);
475
+ buf.writeUint32(requestId);
476
+ this.writeTypedValue(buf, err);
477
+ }
478
+
479
+ writeTypedValue(buf: WriteBuffer, value: any): void {
480
+ for (let i: number = this.encoders.length - 1; i >= 0; i--) {
481
+ if (this.encoders[i][1].is(value)) {
482
+ buf.writeUint8(this.encoders[i][0]);
483
+ this.encoders[i][1].write(buf, value, (innerBuffer, innerValue) => {
484
+ this.writeTypedValue(innerBuffer, innerValue);
485
+ });
486
+ return;
487
+ }
488
+ }
489
+ }
490
+
491
+ writeArray(buf: WriteBuffer, value: any[]): void {
492
+ buf.writeLength(value.length);
493
+ for (let i = 0; i < value.length; i++) {
494
+ this.writeTypedValue(buf, value[i]);
495
+ }
496
+ }
497
+ }