UNPKG

assistant-stream

Version:

Streaming utilities for AI assistants

297 lines (296 loc) • 7.51 kB
// src/utils/json/fix-json.ts const HEX_DIGIT = /[0-9a-fA-F]/; function fixJson(input) { const stack = ["ROOT"]; let lastValidIndex = -1; let literalStart = null; let unicodeEscapeDigits = 0; const path = []; let currentKey; function pushCurrentKeyToPath() { if (currentKey !== void 0) { path.push(JSON.parse(`"${currentKey}"`)); currentKey = void 0; } } function processValueStart(char, i, swapState) { switch (char) { case "\"": lastValidIndex = i; stack.pop(); stack.push(swapState); stack.push("INSIDE_STRING"); pushCurrentKeyToPath(); break; case "f": case "t": case "n": lastValidIndex = i; literalStart = i; stack.pop(); stack.push(swapState); stack.push("INSIDE_LITERAL"); break; case "-": stack.pop(); stack.push(swapState); stack.push("INSIDE_NUMBER"); pushCurrentKeyToPath(); break; case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9": lastValidIndex = i; stack.pop(); stack.push(swapState); stack.push("INSIDE_NUMBER"); pushCurrentKeyToPath(); break; case "{": lastValidIndex = i; stack.pop(); stack.push(swapState); stack.push("INSIDE_OBJECT_START"); pushCurrentKeyToPath(); break; case "[": lastValidIndex = i; stack.pop(); stack.push(swapState); stack.push("INSIDE_ARRAY_START"); pushCurrentKeyToPath(); } } function processAfterObjectValue(char, i) { switch (char) { case ",": stack.pop(); stack.push("INSIDE_OBJECT_AFTER_COMMA"); break; case "}": lastValidIndex = i; stack.pop(); currentKey = path.pop(); } } function processAfterArrayValue(char, i) { switch (char) { case ",": stack.pop(); stack.push("INSIDE_ARRAY_AFTER_COMMA"); currentKey = (Number(currentKey) + 1).toString(); break; case "]": lastValidIndex = i; stack.pop(); currentKey = path.pop(); } } for (let i = 0; i < input.length; i++) { const char = input[i]; switch (stack[stack.length - 1]) { case "ROOT": processValueStart(char, i, "FINISH"); break; case "INSIDE_OBJECT_START": switch (char) { case "\"": stack.pop(); stack.push("INSIDE_OBJECT_KEY"); currentKey = ""; break; case "}": lastValidIndex = i; stack.pop(); currentKey = path.pop(); } break; case "INSIDE_OBJECT_AFTER_COMMA": switch (char) { case "\"": stack.pop(); stack.push("INSIDE_OBJECT_KEY"); currentKey = ""; } break; case "INSIDE_OBJECT_KEY": switch (char) { case "\"": stack.pop(); stack.push("INSIDE_OBJECT_AFTER_KEY"); break; case "\\": stack.push("INSIDE_STRING_ESCAPE"); currentKey += char; break; default: currentKey += char; } break; case "INSIDE_OBJECT_AFTER_KEY": switch (char) { case ":": stack.pop(); stack.push("INSIDE_OBJECT_BEFORE_VALUE"); } break; case "INSIDE_OBJECT_BEFORE_VALUE": processValueStart(char, i, "INSIDE_OBJECT_AFTER_VALUE"); break; case "INSIDE_OBJECT_AFTER_VALUE": processAfterObjectValue(char, i); break; case "INSIDE_STRING": switch (char) { case "\"": stack.pop(); lastValidIndex = i; currentKey = path.pop(); break; case "\\": stack.push("INSIDE_STRING_ESCAPE"); break; default: lastValidIndex = i; } break; case "INSIDE_ARRAY_START": switch (char) { case "]": lastValidIndex = i; stack.pop(); currentKey = path.pop(); break; default: currentKey = "0"; processValueStart(char, i, "INSIDE_ARRAY_AFTER_VALUE"); } break; case "INSIDE_ARRAY_AFTER_VALUE": switch (char) { case ",": stack.pop(); stack.push("INSIDE_ARRAY_AFTER_COMMA"); currentKey = (Number(currentKey) + 1).toString(); break; case "]": lastValidIndex = i; stack.pop(); currentKey = path.pop(); break; default: lastValidIndex = i; } break; case "INSIDE_ARRAY_AFTER_COMMA": processValueStart(char, i, "INSIDE_ARRAY_AFTER_VALUE"); break; case "INSIDE_STRING_ESCAPE": { stack.pop(); const parent = stack[stack.length - 1]; if (char === "u") { stack.push("INSIDE_STRING_UNICODE_ESCAPE"); unicodeEscapeDigits = 0; } else if (parent === "INSIDE_STRING") lastValidIndex = i; if (parent === "INSIDE_OBJECT_KEY") currentKey += char; break; } case "INSIDE_STRING_UNICODE_ESCAPE": { const parent = stack[stack.length - 2]; if (!HEX_DIGIT.test(char)) { stack.pop(); i--; break; } unicodeEscapeDigits++; if (unicodeEscapeDigits === 4) { stack.pop(); if (parent === "INSIDE_STRING") lastValidIndex = i; } if (parent === "INSIDE_OBJECT_KEY") currentKey += char; break; } case "INSIDE_NUMBER": switch (char) { case "0": case "1": case "2": case "3": case "4": case "5": case "6": case "7": case "8": case "9": lastValidIndex = i; break; case "e": case "E": case "-": case "+": case ".": break; case ",": stack.pop(); currentKey = path.pop(); if (stack[stack.length - 1] === "INSIDE_ARRAY_AFTER_VALUE") processAfterArrayValue(char, i); if (stack[stack.length - 1] === "INSIDE_OBJECT_AFTER_VALUE") processAfterObjectValue(char, i); break; case "}": stack.pop(); currentKey = path.pop(); if (stack[stack.length - 1] === "INSIDE_OBJECT_AFTER_VALUE") processAfterObjectValue(char, i); break; case "]": stack.pop(); currentKey = path.pop(); if (stack[stack.length - 1] === "INSIDE_ARRAY_AFTER_VALUE") processAfterArrayValue(char, i); break; default: stack.pop(); currentKey = path.pop(); } break; case "INSIDE_LITERAL": { const partialLiteral = input.substring(literalStart, i + 1); if (!"false".startsWith(partialLiteral) && !"true".startsWith(partialLiteral) && !"null".startsWith(partialLiteral)) { stack.pop(); if (stack[stack.length - 1] === "INSIDE_OBJECT_AFTER_VALUE") processAfterObjectValue(char, i); else if (stack[stack.length - 1] === "INSIDE_ARRAY_AFTER_VALUE") processAfterArrayValue(char, i); } else lastValidIndex = i; break; } } } let result = input.slice(0, lastValidIndex + 1); for (let i = stack.length - 1; i >= 0; i--) switch (stack[i]) { case "INSIDE_STRING": result += "\""; break; case "INSIDE_OBJECT_KEY": case "INSIDE_OBJECT_AFTER_KEY": case "INSIDE_OBJECT_AFTER_COMMA": case "INSIDE_OBJECT_START": case "INSIDE_OBJECT_BEFORE_VALUE": case "INSIDE_OBJECT_AFTER_VALUE": result += "}"; break; case "INSIDE_ARRAY_START": case "INSIDE_ARRAY_AFTER_COMMA": case "INSIDE_ARRAY_AFTER_VALUE": result += "]"; break; case "INSIDE_LITERAL": { const partialLiteral = input.substring(literalStart, input.length); if ("true".startsWith(partialLiteral)) result += "true".slice(partialLiteral.length); else if ("false".startsWith(partialLiteral)) result += "false".slice(partialLiteral.length); else if ("null".startsWith(partialLiteral)) result += "null".slice(partialLiteral.length); } } return [result, path]; } export { fixJson }; //# sourceMappingURL=fix-json.js.map