From 035a62ae77be1c72ae7976adb4de17c65f4db7c3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 Aug 2025 03:22:07 +0000 Subject: [PATCH 1/2] Initial plan From b029d411b88fcc8ff688e8f5a5e27fc3cb3afd1c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 Aug 2025 03:32:55 +0000 Subject: [PATCH 2/2] Fix issue #12: Improve error handling for malformed JSON Co-authored-by: CNSeniorious000 <74518716+CNSeniorious000@users.noreply.github.com> --- src/index.ts | 38 +++++++++++++++++++++++++++++++++----- tests/issue12.test.js | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 tests/issue12.test.js diff --git a/src/index.ts b/src/index.ts index ea6977d..b41cbf1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -65,7 +65,13 @@ const _parseJSON = (jsonString: string, allow: number) => { index += 3; return NaN; } - return parseNum(); + // Check if we have a valid number character before calling parseNum + const char = jsonString[index]; + if (char === "-" || (char >= "0" && char <= "9")) { + return parseNum(); + } + // If we get here, it's an invalid token + throwMalformedError(`Unexpected token '${char}'`); }; const parseStr: () => string = () => { @@ -108,6 +114,10 @@ const _parseJSON = (jsonString: string, allow: number) => { const value = parseAny(); obj[key] = value; } catch (e) { + // If it's a malformed JSON error, let it bubble up + if (e instanceof MalformedJSON) { + throw e; + } if (Allow.OBJ & allow) return obj; else throw e; } @@ -115,6 +125,10 @@ const _parseJSON = (jsonString: string, allow: number) => { if (jsonString[index] === ",") index++; // skip comma } } catch (e) { + // If it's a malformed JSON error, let it bubble up + if (e instanceof MalformedJSON) { + throw e; + } if (Allow.OBJ & allow) return obj; else markPartialJSON("Expected '}' at end of object"); } @@ -124,6 +138,7 @@ const _parseJSON = (jsonString: string, allow: number) => { const parseArr = () => { index++; // skip initial bracket + skipBlank(); // skip whitespace at start of array const arr = []; try { while (jsonString[index] !== "]") { @@ -131,9 +146,14 @@ const _parseJSON = (jsonString: string, allow: number) => { skipBlank(); if (jsonString[index] === ",") { index++; // skip comma + skipBlank(); // skip whitespace after comma } } } catch (e) { + // If it's a malformed JSON error, let it bubble up + if (e instanceof MalformedJSON) { + throw e; + } if (Allow.ARR & allow) { return arr; } @@ -168,11 +188,19 @@ const _parseJSON = (jsonString: string, allow: number) => { return JSON.parse(jsonString.substring(start, index)); } catch (e) { if (jsonString.substring(start, index) === "-") markPartialJSON("Not sure what '-' is"); - try { - return JSON.parse(jsonString.substring(start, jsonString.lastIndexOf("e"))); - } catch (e) { - throwMalformedError(String(e)); + // If the number is partial and we allow partial numbers, try to parse up to last 'e' + if (Allow.NUM & allow) { + const numberStr = jsonString.substring(start, index); + const lastE = numberStr.lastIndexOf("e"); + if (lastE > 0) { + try { + return JSON.parse(numberStr.substring(0, lastE)); + } catch (e2) { + // Still invalid, fall through to error + } + } } + throwMalformedError(String(e)); } }; diff --git a/tests/issue12.test.js b/tests/issue12.test.js new file mode 100644 index 0000000..5c41e88 --- /dev/null +++ b/tests/issue12.test.js @@ -0,0 +1,43 @@ +import { parse, MalformedJSON } from "../src/index"; +import { test, expect } from "vitest"; + +test("issue #12 - invalid number starting with dot should throw error", () => { + // This should throw an error instead of silently failing + expect(() => parse(`{ + "vector": [1, 2, 3, .0516156161551515, 7] + }`)).toThrow(MalformedJSON); + + // Standalone invalid numbers should also throw + expect(() => parse(".123")).toThrow(MalformedJSON); + expect(() => parse("[1, .123, 3]")).toThrow(MalformedJSON); +}); + +test("issue #12 - invalid tokens should throw error instead of returning empty", () => { + // Should throw error instead of returning [] + expect(() => parse("[abc")).toThrow(MalformedJSON); + expect(() => parse("[invalid")).toThrow(MalformedJSON); +}); + +test("issue #12 - empty array with spaces should not stop parsing", () => { + // This should parse the complete JSON, not stop at the empty array + const input = `[ + { "id":1,"arr":["hello"]}, + {"id":2, "arr": [ ],"more":"yaya"}, + {"id":3,"arr":["!"]} + ]`; + + const result = parse(input); + expect(result).toHaveLength(3); + expect(result[0]).toEqual({ "id": 1, "arr": ["hello"] }); + expect(result[1]).toEqual({ "id": 2, "arr": [], "more": "yaya" }); + expect(result[2]).toEqual({ "id": 3, "arr": ["!"] }); +}); + +test("valid edge cases should still work", () => { + // These should continue to work as before + expect(parse("[]")).toEqual([]); + expect(parse("[ ]")).toEqual([]); + expect(parse("[1, 2, 3]")).toEqual([1, 2, 3]); + expect(parse("0.123")).toBe(0.123); + expect(parse("[0.123]")).toEqual([0.123]); +}); \ No newline at end of file