Skip to content

Commit a147746

Browse files
author
Sahar Shemesh
committed
refactor: update ReadBuffer to store the JSON objects & improve filtering logic
1 parent 003d4f7 commit a147746

File tree

2 files changed

+26
-12
lines changed

2 files changed

+26
-12
lines changed

src/shared/stdio.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { JSONRPCMessage, JSONRPCMessageSchema } from '../types.js';
44
* Buffers a continuous stdio stream into discrete JSON-RPC messages.
55
*/
66
export class ReadBuffer {
7-
private _validLines: string[] = [];
7+
private _validLines: object[] = [];
88
private _lastIncompleteLine: string = '';
99

1010
append(chunk: Buffer): void {
@@ -31,28 +31,31 @@ export class ReadBuffer {
3131

3232
// The last element may be incomplete, so store it for the next chunk
3333
this._lastIncompleteLine = newLines.pop() ?? '';
34-
const completedLines = newLines.filter(looksLikeJson);
34+
const completedLines = newLines.map(safeJsonParse).filter(Boolean) as object[];
3535
this._validLines.push(...completedLines);
3636
}
3737
}
3838

3939
/**
40-
* Checks if a line looks like a JSON object.
41-
* @param line The line to check.
42-
* @returns True if the line looks like a JSON object, false otherwise.
40+
* Safely parses a JSON string, returning false if parsing fails.
41+
* @param line The JSON string to parse.
42+
* @returns The parsed object, or false if parsing failed.
4343
*/
44-
function looksLikeJson(line: string): boolean {
45-
const trimmed = line.trim();
46-
return trimmed.startsWith('{') && trimmed.endsWith('}');
44+
function safeJsonParse(line: string): object | false {
45+
try {
46+
return JSON.parse(line);
47+
} catch {
48+
return false;
49+
}
4750
}
4851

4952
/**
50-
* Deserializes a JSON-RPC message from a string.
51-
* @param line The string to deserialize.
53+
* Deserializes a JSON-RPC message from object.
54+
* @param line The object to deserialize.
5255
* @returns The deserialized JSON-RPC message.
5356
*/
54-
export function deserializeMessage(line: string): JSONRPCMessage | null {
55-
return JSONRPCMessageSchema.parse(JSON.parse(line));
57+
export function deserializeMessage(line: object): JSONRPCMessage | null {
58+
return JSONRPCMessageSchema.parse(line);
5659
}
5760

5861
/**

test/shared/stdio.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,17 @@ describe('non-JSON line filtering', () => {
110110
expect(readBuffer.readMessage()).toBeNull();
111111
});
112112

113+
test('should filter out lines which looks like JSON but are not valid JSON', () => {
114+
const readBuffer = new ReadBuffer();
115+
116+
const content = '{invalidJson: true}\n' + JSON.stringify(testMessage) + '\n';
117+
readBuffer.append(Buffer.from(content));
118+
119+
// Should only get the valid message, invalid JSON line filtered out
120+
expect(readBuffer.readMessage()).toEqual(testMessage);
121+
expect(readBuffer.readMessage()).toBeNull();
122+
});
123+
113124
test('should handle lines with leading/trailing whitespace around valid JSON', () => {
114125
const readBuffer = new ReadBuffer();
115126

0 commit comments

Comments
 (0)