From 09b5239f58213b24f2e13116cb2384748bf8913f Mon Sep 17 00:00:00 2001 From: kaitranntt Date: Mon, 2 Feb 2026 23:40:36 -0500 Subject: [PATCH] fix(jsonl): add explicit UTF-8 BOM stripping Strip BOM character before JSON parsing to ensure robust cross-platform JSONL file handling. --- src/web-server/jsonl-parser.ts | 6 ++++-- tests/unit/jsonl-parser.test.ts | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/web-server/jsonl-parser.ts b/src/web-server/jsonl-parser.ts index 4bb21aee..cb8aa98a 100644 --- a/src/web-server/jsonl-parser.ts +++ b/src/web-server/jsonl-parser.ts @@ -70,10 +70,12 @@ export interface ParserOptions { * Returns null for non-assistant entries or entries without usage data */ export function parseUsageEntry(line: string, projectPath: string): RawUsageEntry | null { - if (!line.trim()) return null; + // Strip UTF-8 BOM if present (can occur on first line of some files) + const cleanLine = line.replace(/^\uFEFF/, '').trim(); + if (!cleanLine) return null; try { - const entry = JSON.parse(line); + const entry = JSON.parse(cleanLine); // Only process assistant entries with usage data if (entry.type !== 'assistant') return null; diff --git a/tests/unit/jsonl-parser.test.ts b/tests/unit/jsonl-parser.test.ts index c663d518..5fa00305 100644 --- a/tests/unit/jsonl-parser.test.ts +++ b/tests/unit/jsonl-parser.test.ts @@ -133,6 +133,16 @@ describe('parseUsageEntry', () => { expect(parseUsageEntry('not json at all', '/test')).toBeNull(); }); + test('strips UTF-8 BOM from line before parsing', () => { + // UTF-8 BOM character (\uFEFF) can appear at start of files + const bomEntry = '\uFEFF' + VALID_ASSISTANT_ENTRY; + const result = parseUsageEntry(bomEntry, '/test'); + + expect(result).not.toBeNull(); + expect(result!.model).toBe('claude-sonnet-4-5'); + expect(result!.inputTokens).toBe(1000); + }); + test('includes project path in result', () => { const result = parseUsageEntry(VALID_ASSISTANT_ENTRY, '/custom/project/path'); expect(result!.projectPath).toBe('/custom/project/path');