websocket-text-relay 1.0.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 (50) hide show
  1. package/.eslintrc +29 -0
  2. package/LICENSE +21 -0
  3. package/README.md +40 -0
  4. package/docs/code-structure.md +77 -0
  5. package/docs/creating-text-editor-plugin.md +10 -0
  6. package/docs/dev-getting-started.md +8 -0
  7. package/index.js +87 -0
  8. package/package.json +45 -0
  9. package/src/language-server/JsonRpcInterface.js +126 -0
  10. package/src/language-server/JsonRpcInterface.test.js +496 -0
  11. package/src/language-server/LspReader.js +119 -0
  12. package/src/language-server/LspReader.test.js +508 -0
  13. package/src/language-server/LspWriter.js +44 -0
  14. package/src/language-server/LspWriter.test.js +179 -0
  15. package/src/language-server/constants.js +23 -0
  16. package/src/ui/css/fonts/Montserrat-Black.ttf +0 -0
  17. package/src/ui/css/fonts/Montserrat-Bold.ttf +0 -0
  18. package/src/ui/css/fonts/Montserrat-ExtraBold.ttf +0 -0
  19. package/src/ui/css/fonts/Montserrat-ExtraLight.ttf +0 -0
  20. package/src/ui/css/fonts/Montserrat-Light.ttf +0 -0
  21. package/src/ui/css/fonts/Montserrat-Medium.ttf +0 -0
  22. package/src/ui/css/fonts/Montserrat-Regular.ttf +0 -0
  23. package/src/ui/css/fonts/Montserrat-SemiBold.ttf +0 -0
  24. package/src/ui/css/fonts/Montserrat-Thin.ttf +0 -0
  25. package/src/ui/css/fonts.css +54 -0
  26. package/src/ui/css/main.css +198 -0
  27. package/src/ui/index.html +36 -0
  28. package/src/ui/js/components/ActivityTimeseriesGraph.js +149 -0
  29. package/src/ui/js/components/HeaderSummary.js +23 -0
  30. package/src/ui/js/components/ServerStatus.js +31 -0
  31. package/src/ui/js/components/SessionLabels.js +171 -0
  32. package/src/ui/js/components/SessionWedges.js +107 -0
  33. package/src/ui/js/components/StatusRing.js +42 -0
  34. package/src/ui/js/components/grids.js +29 -0
  35. package/src/ui/js/index.js +117 -0
  36. package/src/ui/js/main.js +97 -0
  37. package/src/ui/js/util/DependencyManager.js +31 -0
  38. package/src/ui/js/util/EventEmitter.js +40 -0
  39. package/src/ui/js/util/WebsocketClient.js +76 -0
  40. package/src/ui/js/util/constants.js +9 -0
  41. package/src/ui/js/util/drawing.js +128 -0
  42. package/src/websocket-interface/WebsocketClient.js +56 -0
  43. package/src/websocket-interface/WebsocketInterface.js +71 -0
  44. package/src/websocket-interface/WtrSession.js +122 -0
  45. package/src/websocket-interface/httpServer.js +58 -0
  46. package/src/websocket-interface/sessionManager.js +107 -0
  47. package/src/websocket-interface/util.js +12 -0
  48. package/src/websocket-interface/websocketApi.js +116 -0
  49. package/src/websocket-interface/websocketServer.js +18 -0
  50. package/start.js +4 -0
@@ -0,0 +1,508 @@
1
+ import { PassThrough } from 'node:stream'
2
+ import { describe, it, expect, beforeAll } from "vitest"
3
+ import { LspReader } from './LspReader.js'
4
+ import { parseErrorCode, parseHeaderErrorMessage, parseJsonErrorMessage } from './constants.js'
5
+
6
+ describe("LspReader", () => {
7
+ describe("simple case", () => {
8
+ describe("when a message comes through the input stream", () => {
9
+ const inputData = `Content-Length: 73\r\n\r\n{"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}`
10
+ const expectedMessageObj = {"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}
11
+ let actualMessageObj
12
+
13
+ beforeAll(() => {
14
+ const inputStream = new PassThrough()
15
+ const lspReader = new LspReader()
16
+ inputStream.pipe(lspReader)
17
+ lspReader.on('data', (message) => {
18
+ actualMessageObj = message
19
+ })
20
+ inputStream.write(inputData)
21
+ })
22
+
23
+ it("should parse the incoming data and emit the json rpc message object", () => {
24
+ expect(actualMessageObj).to.deep.equal(expectedMessageObj)
25
+ })
26
+ })
27
+
28
+ describe("when content-length header isn't capitalized", () => {
29
+ const inputData = `content-length: 73\r\n\r\n{"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}`
30
+ const expectedMessageObj = {"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}
31
+ let actualMessageObj
32
+
33
+ beforeAll(() => {
34
+ const inputStream = new PassThrough()
35
+ const lspReader = new LspReader()
36
+ inputStream.pipe(lspReader)
37
+ lspReader.on('data', (message) => {
38
+ actualMessageObj = message
39
+ })
40
+ inputStream.write(inputData)
41
+ })
42
+
43
+ it("should parse the incoming data and emit the json rpc message object", () => {
44
+ expect(actualMessageObj).to.deep.equal(expectedMessageObj)
45
+ })
46
+ })
47
+ })
48
+
49
+ describe("error cases", () => {
50
+ describe("when Content-Length header is missing", () => {
51
+ const inputData = `Bad-Header: 73\r\n\r\n{"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}`
52
+ const expectedError = {code: parseErrorCode, message: parseHeaderErrorMessage}
53
+ let actualMessageObj = null
54
+ let actualError
55
+
56
+ beforeAll(() => {
57
+ const inputStream = new PassThrough()
58
+ const lspReader = new LspReader()
59
+ inputStream.pipe(lspReader)
60
+ lspReader.on('data', (message) => {
61
+ actualMessageObj = message
62
+ })
63
+ lspReader.on('parse-error', (error) => {
64
+ actualError = error
65
+ })
66
+ inputStream.write(inputData)
67
+ })
68
+
69
+ it("should emit an 'parse-error' event with the parse header error message", () => {
70
+ expect(actualError).to.deep.equal(expectedError)
71
+ })
72
+
73
+ it("should not emit any objects", () => {
74
+ expect(actualMessageObj).toBeNull()
75
+ })
76
+ })
77
+
78
+ describe("when recovering from a bad header error", () => {
79
+ const inputData = [
80
+ `Bad-Header: 73\r\n\r\n{"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}`,
81
+ `Content-Length: 73\r\n\r\n{"jsonrpc":"2.0","id":"1","method":"test/test-method","params":{"b":"2"}}`
82
+ ]
83
+ const expectedError = {code: parseErrorCode, message: parseHeaderErrorMessage}
84
+ const expectedObject = {"jsonrpc":"2.0","id":"1","method":"test/test-method","params":{"b":"2"}}
85
+ let actualMessageObjs = []
86
+ let actualErrors = []
87
+
88
+ beforeAll(() => {
89
+ const inputStream = new PassThrough()
90
+ const lspReader = new LspReader()
91
+ inputStream.pipe(lspReader)
92
+ lspReader.on('data', (message) => {
93
+ actualMessageObjs.push(message)
94
+ })
95
+ lspReader.on('parse-error', (error) => {
96
+ actualErrors.push(error)
97
+ })
98
+ inputData.forEach((data) => {
99
+ inputStream.write(data)
100
+ })
101
+ })
102
+
103
+ it("should emit only 1 'parse-error' event with the parse header error message", () => {
104
+ expect(actualErrors.length).toEqual(1)
105
+ expect(actualErrors[0]).to.deep.equal(expectedError)
106
+ })
107
+
108
+ it("should not emit one object from the message with the valid header", () => {
109
+ expect(actualMessageObjs.length).toEqual(1)
110
+ expect(actualMessageObjs[0]).to.deep.equal(expectedObject)
111
+ })
112
+ })
113
+
114
+ describe("when recovering from an invalid JSON error", () => {
115
+ const inputData = [
116
+ `Content-Length: 73\r\n\r\n{"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}>`,
117
+ `Content-Length: 73\r\n\r\n{"jsonrpc":"2.0","id":"1","method":"test/test-method","params":{"b":"2"}}`
118
+ ]
119
+ const expectedError = {code: parseErrorCode, message: parseJsonErrorMessage}
120
+ const expectedObject = {"jsonrpc":"2.0","id":"1","method":"test/test-method","params":{"b":"2"}}
121
+ let actualMessageObjs = []
122
+ let actualErrors = []
123
+
124
+ beforeAll(() => {
125
+ const inputStream = new PassThrough()
126
+ const lspReader = new LspReader()
127
+ inputStream.pipe(lspReader)
128
+ lspReader.on('data', (message) => {
129
+ actualMessageObjs.push(message)
130
+ })
131
+ lspReader.on('parse-error', (error) => {
132
+ actualErrors.push(error)
133
+ })
134
+ inputData.forEach((data) => {
135
+ inputStream.write(data)
136
+ })
137
+ })
138
+
139
+ it("should emit only 1 'parse-error' event with the parse header error message", () => {
140
+ expect(actualErrors.length).toEqual(1)
141
+ expect(actualErrors[0]).to.deep.equal(expectedError)
142
+ })
143
+
144
+ it("should not emit one object from the message with the valid header", () => {
145
+ expect(actualMessageObjs.length).toEqual(1)
146
+ expect(actualMessageObjs[0]).to.deep.equal(expectedObject)
147
+ })
148
+ })
149
+
150
+ describe("when JSON is invalid", () => {
151
+ const inputData = `Content-Length: 73\r\n\r\n{"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}>`
152
+ const expectedError = {code: parseErrorCode, message: parseJsonErrorMessage}
153
+ let actualMessageObj = null
154
+ let actualError
155
+
156
+ beforeAll(() => {
157
+ const inputStream = new PassThrough()
158
+ const lspReader = new LspReader()
159
+ inputStream.pipe(lspReader)
160
+ lspReader.on('data', (message) => {
161
+ actualMessageObj = message
162
+ })
163
+ lspReader.on('parse-error', (error) => {
164
+ actualError = error
165
+ })
166
+ inputStream.write(inputData)
167
+ })
168
+
169
+ it("should emit an 'parse-error' event with the parse JSON error message", () => {
170
+ expect(actualError).to.deep.equal(expectedError)
171
+ })
172
+
173
+ it("should not emit any objects", () => {
174
+ expect(actualMessageObj).toBeNull()
175
+ })
176
+ })
177
+ })
178
+
179
+ describe("single message buffering cases", () => {
180
+ describe("when the message is split in the middle of the header", () => {
181
+ const inputData = [`Content-Len`, `gth: 73\r\n\r\n{"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}`]
182
+ const expectedMessageObj = {"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}
183
+ let actualMessageObj
184
+ let actualError = null
185
+
186
+ beforeAll(() => {
187
+ const inputStream = new PassThrough()
188
+ const lspReader = new LspReader()
189
+ inputStream.pipe(lspReader)
190
+ lspReader.on('data', (message) => {
191
+ actualMessageObj = message
192
+ })
193
+ lspReader.on('parse-error', (error) => {
194
+ actualError = error
195
+ })
196
+
197
+ inputData.forEach((dataChunk) => {
198
+ inputStream.write(dataChunk)
199
+ })
200
+ })
201
+
202
+ it("should not emit an error", () => {
203
+ expect(actualError).toBeNull()
204
+ })
205
+
206
+ it("should buffer the data until it can parse the incoming data and emit the json rpc message object", () => {
207
+ expect(actualMessageObj).to.deep.equal(expectedMessageObj)
208
+ })
209
+ })
210
+
211
+ describe("when the message is split in the middle of the header many times", () => {
212
+ const inputData = [`Cont`, `ent-L`, `en`, `gth`, `: 7`, `3\r`, `\n`, `\r\n`, `{"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}`]
213
+ const expectedMessageObj = {"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}
214
+ let actualMessageObj
215
+ let actualError = null
216
+
217
+ beforeAll(() => {
218
+ const inputStream = new PassThrough()
219
+ const lspReader = new LspReader()
220
+ inputStream.pipe(lspReader)
221
+ lspReader.on('data', (message) => {
222
+ actualMessageObj = message
223
+ })
224
+ lspReader.on('parse-error', (error) => {
225
+ actualError = error
226
+ })
227
+
228
+ inputData.forEach((dataChunk) => {
229
+ inputStream.write(dataChunk)
230
+ })
231
+ })
232
+
233
+ it("should not emit an error", () => {
234
+ expect(actualError).toBeNull()
235
+ })
236
+
237
+ it("should buffer the data until it can parse the incoming data and emit the json rpc message object", () => {
238
+ expect(actualMessageObj).to.deep.equal(expectedMessageObj)
239
+ })
240
+ })
241
+
242
+ describe("when the message is split in the middle of the JSON", () => {
243
+ const inputData = [`Content-Length: 73\r\n\r\n{"jsonrpc":"2.0",`, `"id":"0","method":"test/test-method","params":{"a":"1"}}`]
244
+ const expectedMessageObj = {"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}
245
+ let actualMessageObj
246
+ let actualError = null
247
+
248
+ beforeAll(() => {
249
+ const inputStream = new PassThrough()
250
+ const lspReader = new LspReader()
251
+ inputStream.pipe(lspReader)
252
+ lspReader.on('data', (message) => {
253
+ actualMessageObj = message
254
+ })
255
+ lspReader.on('parse-error', (error) => {
256
+ actualError = error
257
+ })
258
+
259
+ inputData.forEach((dataChunk) => {
260
+ inputStream.write(dataChunk)
261
+ })
262
+ })
263
+
264
+ it("should not emit an error", () => {
265
+ expect(actualError).toBeNull()
266
+ })
267
+
268
+ it("should buffer the data until it can parse the incoming data and emit the json rpc message object", () => {
269
+ expect(actualMessageObj).to.deep.equal(expectedMessageObj)
270
+ })
271
+ })
272
+
273
+ describe("when the message is split in the middle of the header and JSON many times", () => {
274
+ const inputData = [`Con`, `tent-Len`, `gth: 73\r`, `\n`, `\r\n`, `{`, `"jso`, `nrpc":"2.0"`, `,"id":"0","method":"te`, `st/test-method","params":{"a":`, `"1"}`, `}`]
275
+ const expectedMessageObj = {"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}
276
+ let actualMessageObj
277
+ let actualError = null
278
+
279
+ beforeAll(() => {
280
+ const inputStream = new PassThrough()
281
+ const lspReader = new LspReader()
282
+ inputStream.pipe(lspReader)
283
+ lspReader.on('data', (message) => {
284
+ actualMessageObj = message
285
+ })
286
+ lspReader.on('parse-error', (error) => {
287
+ actualError = error
288
+ })
289
+
290
+ inputData.forEach((dataChunk) => {
291
+ inputStream.write(dataChunk)
292
+ })
293
+ })
294
+
295
+ it("should not emit an error", () => {
296
+ expect(actualError).toBeNull()
297
+ })
298
+
299
+ it("should buffer the data until it can parse the incoming data and emit the json rpc message object", () => {
300
+ expect(actualMessageObj).to.deep.equal(expectedMessageObj)
301
+ })
302
+ })
303
+ })
304
+
305
+ describe("when the buffer needs resizing", () => {
306
+ describe("when a message larger than the buffer size comes through the input stream", () => {
307
+ const inputData = `Content-Length: 73\r\n\r\n{"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}`
308
+ const expectedMessageObj = {"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}
309
+ let lspReader
310
+ let actualMessageObj
311
+ let actualInitialBufferSize
312
+
313
+ beforeAll(() => {
314
+ const inputStream = new PassThrough()
315
+ lspReader = new LspReader({initialBufferSize: 10})
316
+ actualInitialBufferSize = lspReader.buffer.length
317
+ inputStream.pipe(lspReader)
318
+ lspReader.on('data', (message) => {
319
+ actualMessageObj = message
320
+ })
321
+ inputStream.write(inputData)
322
+ })
323
+
324
+ it("should construct the lspReader with the passed in initialBufferSize", () => {
325
+ expect(actualInitialBufferSize).toEqual(10)
326
+ })
327
+
328
+ it("should need to trigger a resize", () => {
329
+ expect(inputData.length).toBeGreaterThan(actualInitialBufferSize)
330
+ })
331
+
332
+ it("should increase the buffer to a size larger than the message chunk", () => {
333
+ expect(lspReader.buffer.length).toBeGreaterThan(inputData.length)
334
+ })
335
+
336
+ it("should parse the incoming data and emit the json rpc message object", () => {
337
+ expect(actualMessageObj).to.deep.equal(expectedMessageObj)
338
+ })
339
+ })
340
+
341
+ describe("when a buffer that already has data needs resizing", () => {
342
+ const inputData = [`Content-Len`, `gth: 73\r\n\r\n{"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}`]
343
+ const expectedMessageObj = {"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}
344
+ let actualMessageObj
345
+ let lspReader
346
+ let actualInitialBufferSize
347
+ let actualError = null
348
+
349
+ beforeAll(() => {
350
+ const inputStream = new PassThrough()
351
+ lspReader = new LspReader({initialBufferSize: 70})
352
+ actualInitialBufferSize = lspReader.buffer.length
353
+ inputStream.pipe(lspReader)
354
+ lspReader.on('data', (message) => {
355
+ actualMessageObj = message
356
+ })
357
+ lspReader.on('parse-error', (error) => {
358
+ actualError = error
359
+ })
360
+
361
+ inputData.forEach((dataChunk) => {
362
+ inputStream.write(dataChunk)
363
+ })
364
+ })
365
+
366
+ it("should not emit an error", () => {
367
+ expect(actualError).toBeNull()
368
+ })
369
+
370
+ it("should construct the lspReader with the passed in initialBufferSize", () => {
371
+ expect(actualInitialBufferSize).toEqual(70)
372
+ })
373
+
374
+ it("should need to trigger a resize after the first buffered message", () => {
375
+ expect(inputData[0].length).toBeLessThan(actualInitialBufferSize)
376
+ expect(inputData[1].length).toBeGreaterThan(actualInitialBufferSize)
377
+ })
378
+
379
+ it("should increase the buffer to a size larger than the combined message chunks", () => {
380
+ expect(lspReader.buffer.length).toBeGreaterThan(inputData[0].length + inputData[1].length)
381
+ })
382
+
383
+ it("should buffer the data until it can parse the incoming data and emit the json rpc message object", () => {
384
+ expect(actualMessageObj).to.deep.equal(expectedMessageObj)
385
+ })
386
+ })
387
+ })
388
+
389
+ describe("when multiple message come in separate chunks", () => {
390
+ const inputData = [
391
+ `Content-Length: 73\r\n\r\n{"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}`,
392
+ `Content-Length: 73\r\n\r\n{"jsonrpc":"2.0","id":"1","method":"test/test-method","params":{"b":"2"}}`,
393
+ `Content-Length: 73\r\n\r\n{"jsonrpc":"2.0","id":"2","method":"test/test-method","params":{"c":"3"}}`
394
+ ]
395
+
396
+ const expectedMessageObjs = [
397
+ {"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}},
398
+ {"jsonrpc":"2.0","id":"1","method":"test/test-method","params":{"b":"2"}},
399
+ {"jsonrpc":"2.0","id":"2","method":"test/test-method","params":{"c":"3"}}
400
+ ]
401
+ let actualMessageObjs = []
402
+ let actualError = null
403
+
404
+ beforeAll(() => {
405
+ const inputStream = new PassThrough()
406
+ const lspReader = new LspReader()
407
+ inputStream.pipe(lspReader)
408
+ lspReader.on('data', (message) => {
409
+ actualMessageObjs.push(message)
410
+ })
411
+ lspReader.on('parse-error', (error) => {
412
+ actualError = error
413
+ })
414
+
415
+ inputData.forEach((dataChunk) => {
416
+ inputStream.write(dataChunk)
417
+ })
418
+ })
419
+
420
+ it("should not emit an error", () => {
421
+ expect(actualError).toBeNull()
422
+ })
423
+
424
+ it("should emit each message in the order it was received", () => {
425
+ expect(actualMessageObjs).to.deep.equal(expectedMessageObjs)
426
+ })
427
+ })
428
+
429
+ describe("when multiple message come in the same chunk", () => {
430
+ const inputDataMessages = [
431
+ `Content-Length: 73\r\n\r\n{"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}`,
432
+ `Content-Length: 73\r\n\r\n{"jsonrpc":"2.0","id":"1","method":"test/test-method","params":{"b":"2"}}`,
433
+ `Content-Length: 73\r\n\r\n{"jsonrpc":"2.0","id":"2","method":"test/test-method","params":{"c":"3"}}`
434
+ ]
435
+ const inputData = inputDataMessages.join("")
436
+
437
+ const expectedMessageObjs = [
438
+ {"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}},
439
+ {"jsonrpc":"2.0","id":"1","method":"test/test-method","params":{"b":"2"}},
440
+ {"jsonrpc":"2.0","id":"2","method":"test/test-method","params":{"c":"3"}}
441
+ ]
442
+ let actualMessageObjs = []
443
+ let actualError = null
444
+
445
+ beforeAll(() => {
446
+ const inputStream = new PassThrough()
447
+ const lspReader = new LspReader()
448
+ inputStream.pipe(lspReader)
449
+ lspReader.on('data', (message) => {
450
+ actualMessageObjs.push(message)
451
+ })
452
+ lspReader.on('parse-error', (error) => {
453
+ actualError = error
454
+ })
455
+
456
+ inputStream.write(inputData)
457
+ })
458
+
459
+ it("should not emit an error", () => {
460
+ expect(actualError).toBeNull()
461
+ })
462
+
463
+ it("should emit each message in the order it was received", () => {
464
+ expect(actualMessageObjs).to.deep.equal(expectedMessageObjs)
465
+ })
466
+ })
467
+
468
+ describe("when buffering partial messages in a multi message chunk", () => {
469
+ describe("when the next message has an incomplete header", () => {
470
+ const inputData = [
471
+ `Content-Length: 73\r\n\r\n{"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}}Content-Length: 73\r\n\r\n{"jsonrpc":"2.0","id":"1","method":"test/test-method","params":{"b":"2"}}Content-Lengt`,
472
+ `h: 73\r\n\r\n{"jsonrpc":"2.0","id":"2","method":"test/test-method","params":{"c":"3"}}`
473
+ ]
474
+
475
+ const expectedMessageObjs = [
476
+ {"jsonrpc":"2.0","id":"0","method":"test/test-method","params":{"a":"1"}},
477
+ {"jsonrpc":"2.0","id":"1","method":"test/test-method","params":{"b":"2"}},
478
+ {"jsonrpc":"2.0","id":"2","method":"test/test-method","params":{"c":"3"}}
479
+ ]
480
+ let actualMessageObjs = []
481
+ let actualError = null
482
+
483
+ beforeAll(() => {
484
+ const inputStream = new PassThrough()
485
+ const lspReader = new LspReader()
486
+ inputStream.pipe(lspReader)
487
+ lspReader.on('data', (message) => {
488
+ actualMessageObjs.push(message)
489
+ })
490
+ lspReader.on('parse-error', (error) => {
491
+ actualError = error
492
+ })
493
+
494
+ inputData.forEach((dataChunk) => {
495
+ inputStream.write(dataChunk)
496
+ })
497
+ })
498
+
499
+ it("should not emit an error", () => {
500
+ expect(actualError).toBeNull()
501
+ })
502
+
503
+ it("should emit each message in the order it was received", () => {
504
+ expect(actualMessageObjs).to.deep.equal(expectedMessageObjs)
505
+ })
506
+ })
507
+ })
508
+ })
@@ -0,0 +1,44 @@
1
+ import { headerKey } from './constants.js'
2
+
3
+ const jsonrpc = "2.0"
4
+
5
+ // exported for testing purposes
6
+ export const createHeader = (messageStr) => {
7
+ const byteLength = Buffer.byteLength(messageStr)
8
+ return `${headerKey}: ${byteLength}\r\n\r\n`
9
+ }
10
+
11
+ // exported for testing purposes
12
+ export const writeToOutput = (outputStream, messageObj) => {
13
+ const outputStr = JSON.stringify(messageObj)
14
+ const header = createHeader(outputStr)
15
+ outputStream.write(header)
16
+ outputStream.write(outputStr)
17
+ }
18
+
19
+ export const writeResponse = (outputStream, id, error, result) => {
20
+ const response = {jsonrpc, id}
21
+ if (error) {
22
+ response.error = error
23
+ } else {
24
+ response.result = result
25
+ }
26
+ writeToOutput(outputStream, response)
27
+ }
28
+
29
+ export const writeNotification = (outputStream, method, params) => {
30
+ const message = {jsonrpc, method, params}
31
+ writeToOutput(outputStream, message)
32
+ }
33
+
34
+ let requestId = 0
35
+ const nextRequestId = () => String(requestId++)
36
+ // only to be used for testing!
37
+ export const TESTONLY_resetRequestId = () => requestId = 0
38
+
39
+ export const writeRequest = (outputStream, method, params) => {
40
+ const id = nextRequestId()
41
+ const request = {jsonrpc, id, method, params}
42
+ writeToOutput(outputStream, request)
43
+ return id
44
+ }