Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 33 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = () => {
Expand Down Expand Up @@ -108,13 +114,21 @@ 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;
}
skipBlank();
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");
}
Expand All @@ -124,16 +138,22 @@ 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] !== "]") {
arr.push(parseAny());
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;
}
Expand Down Expand Up @@ -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));
}
};

Expand Down
43 changes: 43 additions & 0 deletions tests/issue12.test.js
Original file line number Diff line number Diff line change
@@ -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]);
});