diff --git a/src/protocol.test.ts b/src/protocol.test.ts index 06c2673..7735cb9 100644 --- a/src/protocol.test.ts +++ b/src/protocol.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { parseCommand } from './protocol.js'; +import { parseCommand, serializeResponse, successResponse } from './protocol.js'; // Helper to create command JSON string const cmd = (obj: object) => JSON.stringify(obj); @@ -1412,6 +1412,28 @@ describe('parseCommand', () => { }); }); + describe('serializeResponse - lone surrogate handling', () => { + it('should serialize response with lone surrogate to valid JSON', () => { + const response = successResponse('1', { + snapshot: 'button "test \uD800 click"', + }); + const json = serializeResponse(response); + expect(() => JSON.parse(json)).not.toThrow(); + const parsed = JSON.parse(json); + expect(parsed.data.snapshot).toContain('\uFFFD'); + expect(parsed.data.snapshot).not.toMatch(/[\uD800-\uDFFF]/); + }); + + it('should preserve valid emoji characters', () => { + const response = successResponse('1', { + snapshot: 'button "😀 Click"', + }); + const json = serializeResponse(response); + const parsed = JSON.parse(json); + expect(parsed.data.snapshot).toContain('😀'); + }); + }); + describe('invalid commands', () => { it('should reject unknown action', () => { const result = parseCommand(cmd({ id: '1', action: 'unknown' })); diff --git a/src/protocol.ts b/src/protocol.ts index 4b4200a..785d576 100644 --- a/src/protocol.ts +++ b/src/protocol.ts @@ -1149,8 +1149,12 @@ export function errorResponse(id: string, error: string): Response { } /** - * Serialize a response to JSON string + * Serialize a response to JSON string. + * Replaces lone Unicode surrogates with U+FFFD to prevent + * serde_json parsing errors on the Rust side. */ export function serializeResponse(response: Response): string { - return JSON.stringify(response); + return JSON.stringify(response, (_key, value) => + typeof value === 'string' && !value.isWellFormed() ? value.toWellFormed() : value + ); } diff --git a/tsconfig.json b/tsconfig.json index 93aa062..ff14e31 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,10 @@ { "compilerOptions": { - "target": "ES2022", + "target": "ES2024", "module": "NodeNext", "moduleResolution": "NodeNext", "lib": [ - "ES2022" + "ES2024" ], "outDir": "./dist", "rootDir": "./src",