ai-json-fixer
Version:
A simple JSON parser designed to handle malformed JSON from Large Language Models
123 lines • 4.94 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.addMissingCommas = addMissingCommas;
/**
* Adds missing commas between array elements and object properties
*/
function addMissingCommas(input) {
// Check if this is single-line JSON that needs special handling
if (input.indexOf('\n') === -1) {
return handleSingleLineJson(input);
}
const lines = input.split('\n');
const result = [];
let depth = 0;
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
const line = lines[lineIndex];
let processedLine = '';
// Reset string state for each line (this is a simplification)
// In practice, strings can span multiple lines, but for typical JSON they don't
let lineInString = false;
let lineEscapeNext = false;
for (let i = 0; i < line.length; i++) {
const char = line[i];
// Handle escape sequences
if (lineEscapeNext) {
processedLine += char;
lineEscapeNext = false;
continue;
}
if (char === '\\' && lineInString) {
processedLine += char;
lineEscapeNext = true;
continue;
}
// Handle string boundaries
if (char === '"' && !lineEscapeNext) {
lineInString = !lineInString;
processedLine += char;
continue;
}
// Skip processing inside strings
if (lineInString) {
processedLine += char;
continue;
}
// Track depth
if (char === '{' || char === '[') {
depth++;
}
else if (char === '}' || char === ']') {
depth--;
}
processedLine += char;
}
// Check if this line needs a comma at the end
const trimmedLine = processedLine.trim();
// Skip empty lines and lines that already end with comma, colon, or are container starts
if (trimmedLine === '' ||
trimmedLine.endsWith(',') ||
trimmedLine.endsWith(':') ||
trimmedLine.endsWith('{') ||
trimmedLine.endsWith('[')) {
result.push(processedLine);
continue;
}
// Check if we need to add a comma
let needsComma = false;
if (depth > 0 && lineIndex < lines.length - 1) {
// Look at the next non-empty line
let nextLineIndex = lineIndex + 1;
while (nextLineIndex < lines.length && lines[nextLineIndex].trim() === '') {
nextLineIndex++;
}
if (nextLineIndex < lines.length) {
const nextLine = lines[nextLineIndex].trim();
// We need a comma if the next line starts a new value or key
// and doesn't start with a container closer
if (nextLine !== '' && !nextLine.startsWith('}') && !nextLine.startsWith(']')) {
// Check if this line ends with a complete value
const endsWithValue = /["\d}\]]$/.test(trimmedLine) || /\b(true|false|null)$/.test(trimmedLine);
if (endsWithValue) {
needsComma = true;
}
}
}
}
if (needsComma) {
// Add comma to the end of the line, preserving whitespace
const match = processedLine.match(/^(\s*)(.*?)(\s*)$/);
if (match && match[1] !== undefined && match[2] !== undefined && match[3] !== undefined) {
const [, leadingWhitespace, content, trailingWhitespace] = match;
result.push(leadingWhitespace + content + ',' + trailingWhitespace);
}
else {
result.push(processedLine + ',');
}
}
else {
result.push(processedLine);
}
}
return result.join('\n');
}
/**
* Handles single-line JSON comma insertion using regex patterns
*/
function handleSingleLineJson(input) {
let result = input;
// Apply fixes iteratively until no more changes
let changed = true;
while (changed) {
const before = result;
// Pattern 1: Object properties - value followed by quote (new key)
// Match: number/string/boolean/null followed by space and quote
result = result.replace(/(["\d}\]]|true|false|null)\s+(")/g, '$1, $2');
// Pattern 2: Array elements - value followed by value
// Match: number/string/boolean/null followed by space and number/quote/bracket/negative
result = result.replace(/(["\d}\]]|true|false|null)\s+(["\d[{-]|true|false|null)/g, '$1, $2');
changed = result !== before;
}
return result;
}
//# sourceMappingURL=missing-comma-detection.js.map