From 2cf9c42df61d05e1cc7c6db3e7f06fa9883b9f08 Mon Sep 17 00:00:00 2001 From: ShuaixinHuang <1836656514@qq.com> Date: Sat, 7 Nov 2026 01:28:59 +0800 Subject: [PATCH] fix(desktop): recognise JSON scalar files with mixed-case suffixes (#9152) * fix(desktop): recognise JSON scalar files with mixed-case suffixes * test(setup): use the current registration command in CodeBuddy fixture * test(cli): split doctor help and timing boundary groups * style(test): format split MCP doctor boundary groups --- src/domain/desktop/appExportPython.ts | 2 +- src/domain/desktop/stateFormats.test.ts | 11 +++++++++++ src/domain/desktop/stateFormats.ts | 2 +- tests/boundary/providers/desktopExportPython.test.ts | 3 ++- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/domain/desktop/appExportPython.ts b/src/domain/desktop/appExportPython.ts index f0b7a6e699..185932d6f4 100644 --- a/src/domain/desktop/appExportPython.ts +++ b/src/domain/desktop/appExportPython.ts @@ -396,7 +396,7 @@ def _text_view(name: str, text: str, size: int) -> dict: """The format REA's state view detects for decoded text of size bytes, and its view; a JSON or key file over the parsed-view limit is refused before its value is built.""" trimmed = re.sub(r"^[\s]+", "", text) - if name.endswith(".json") or trimmed[:1] in ("[", "{"): + if name.lower().endswith(".json") or trimmed[:1] in ("[", "{"): view = _json_view(text, size) if view is not None: return view diff --git a/src/domain/desktop/stateFormats.test.ts b/src/domain/desktop/stateFormats.test.ts index c2558f6119..60377217b1 100644 --- a/src/domain/desktop/stateFormats.test.ts +++ b/src/domain/desktop/stateFormats.test.ts @@ -5,12 +5,23 @@ import { jsonView, keyfileView, textView, + textSyntaxFormat, xmlView, } from "./stateFormats.js"; const bytes = (text: string) => new TextEncoder().encode(text); describe("detectStateFormat", () => { + it("recognises JSON scalars with mixed-case suffixes and still validates their syntax", () => { + for (const text of ["42", "true", "null", '"value"']) { + expect(detectStateFormat("/h/a.JsOn", bytes(text))).toBe("json"); + expect(textSyntaxFormat("/h/a.JSON", text)).toBe("json"); + expect(detectStateFormat("/h/a.txt", bytes(text))).toBe("text"); + } + expect(detectStateFormat("/h/a.JSON", bytes("42 trailing"))).toBe("text"); + expect(textSyntaxFormat("/h/a.JSON", "42 trailing")).toBe("text"); + }); + it("recognises SQLite and dconf headers before decoding text", () => { expect(detectStateFormat("/h/a.db", bytes("SQLite format 3\0rest"))).toBe( "sqlite", diff --git a/src/domain/desktop/stateFormats.ts b/src/domain/desktop/stateFormats.ts index 2e868563da..d4ee0a3855 100644 --- a/src/domain/desktop/stateFormats.ts +++ b/src/domain/desktop/stateFormats.ts @@ -210,7 +210,7 @@ export const isJsonText = (text: string): boolean => { /** Whether a file's name or leading text marks it as JSON, which it is if it parses. */ const namedJson = (path: string, trimmed: string): boolean => - path.endsWith(".json") || /^[[{]/u.test(trimmed); + path.toLowerCase().endsWith(".json") || /^[[{]/u.test(trimmed); /** Whether a file's name or XML declaration marks it as XML, which it is if it parses. */ const namedXml = (path: string, trimmed: string): boolean => diff --git a/tests/boundary/providers/desktopExportPython.test.ts b/tests/boundary/providers/desktopExportPython.test.ts index 7783989ea2..4cbaa9f5f9 100644 --- a/tests/boundary/providers/desktopExportPython.test.ts +++ b/tests/boundary/providers/desktopExportPython.test.ts @@ -91,13 +91,14 @@ it("keeps JSON Lines holding a number it cannot read exactly as text, as REA doe const files = [ ["events.jsonl", lines], ["events.json", lines], + ["events.JSON", "42"], ["big.json", '{"id":9007199254740993}'], ["long.json", `{"id":${"9".repeat(5000)}}`], ] as const; const formats = pythonFormats(files); - expect(formats).toEqual(["text", "text", "json", "json"]); + expect(formats).toEqual(["text", "text", "json", "json", "json"]); expect(formats).toEqual( files.map(([name, text]) => detectStateFormat(name, new TextEncoder().encode(text)),