agsamantha/node_modules/openai/_vendor/partial-json-parser/parser.mjs
2024-10-02 15:15:21 -05:00

241 lines
No EOL
8.1 KiB
JavaScript

const STR = 0b000000001;
const NUM = 0b000000010;
const ARR = 0b000000100;
const OBJ = 0b000001000;
const NULL = 0b000010000;
const BOOL = 0b000100000;
const NAN = 0b001000000;
const INFINITY = 0b010000000;
const MINUS_INFINITY = 0b100000000;
const INF = INFINITY | MINUS_INFINITY;
const SPECIAL = NULL | BOOL | INF | NAN;
const ATOM = STR | NUM | SPECIAL;
const COLLECTION = ARR | OBJ;
const ALL = ATOM | COLLECTION;
const Allow = {
STR,
NUM,
ARR,
OBJ,
NULL,
BOOL,
NAN,
INFINITY,
MINUS_INFINITY,
INF,
SPECIAL,
ATOM,
COLLECTION,
ALL,
};
// The JSON string segment was unable to be parsed completely
class PartialJSON extends Error {
}
class MalformedJSON extends Error {
}
/**
* Parse incomplete JSON
* @param {string} jsonString Partial JSON to be parsed
* @param {number} allowPartial Specify what types are allowed to be partial, see {@link Allow} for details
* @returns The parsed JSON
* @throws {PartialJSON} If the JSON is incomplete (related to the `allow` parameter)
* @throws {MalformedJSON} If the JSON is malformed
*/
function parseJSON(jsonString, allowPartial = Allow.ALL) {
if (typeof jsonString !== 'string') {
throw new TypeError(`expecting str, got ${typeof jsonString}`);
}
if (!jsonString.trim()) {
throw new Error(`${jsonString} is empty`);
}
return _parseJSON(jsonString.trim(), allowPartial);
}
const _parseJSON = (jsonString, allow) => {
const length = jsonString.length;
let index = 0;
const markPartialJSON = (msg) => {
throw new PartialJSON(`${msg} at position ${index}`);
};
const throwMalformedError = (msg) => {
throw new MalformedJSON(`${msg} at position ${index}`);
};
const parseAny = () => {
skipBlank();
if (index >= length)
markPartialJSON('Unexpected end of input');
if (jsonString[index] === '"')
return parseStr();
if (jsonString[index] === '{')
return parseObj();
if (jsonString[index] === '[')
return parseArr();
if (jsonString.substring(index, index + 4) === 'null' ||
(Allow.NULL & allow && length - index < 4 && 'null'.startsWith(jsonString.substring(index)))) {
index += 4;
return null;
}
if (jsonString.substring(index, index + 4) === 'true' ||
(Allow.BOOL & allow && length - index < 4 && 'true'.startsWith(jsonString.substring(index)))) {
index += 4;
return true;
}
if (jsonString.substring(index, index + 5) === 'false' ||
(Allow.BOOL & allow && length - index < 5 && 'false'.startsWith(jsonString.substring(index)))) {
index += 5;
return false;
}
if (jsonString.substring(index, index + 8) === 'Infinity' ||
(Allow.INFINITY & allow && length - index < 8 && 'Infinity'.startsWith(jsonString.substring(index)))) {
index += 8;
return Infinity;
}
if (jsonString.substring(index, index + 9) === '-Infinity' ||
(Allow.MINUS_INFINITY & allow &&
1 < length - index &&
length - index < 9 &&
'-Infinity'.startsWith(jsonString.substring(index)))) {
index += 9;
return -Infinity;
}
if (jsonString.substring(index, index + 3) === 'NaN' ||
(Allow.NAN & allow && length - index < 3 && 'NaN'.startsWith(jsonString.substring(index)))) {
index += 3;
return NaN;
}
return parseNum();
};
const parseStr = () => {
const start = index;
let escape = false;
index++; // skip initial quote
while (index < length && (jsonString[index] !== '"' || (escape && jsonString[index - 1] === '\\'))) {
escape = jsonString[index] === '\\' ? !escape : false;
index++;
}
if (jsonString.charAt(index) == '"') {
try {
return JSON.parse(jsonString.substring(start, ++index - Number(escape)));
}
catch (e) {
throwMalformedError(String(e));
}
}
else if (Allow.STR & allow) {
try {
return JSON.parse(jsonString.substring(start, index - Number(escape)) + '"');
}
catch (e) {
// SyntaxError: Invalid escape sequence
return JSON.parse(jsonString.substring(start, jsonString.lastIndexOf('\\')) + '"');
}
}
markPartialJSON('Unterminated string literal');
};
const parseObj = () => {
index++; // skip initial brace
skipBlank();
const obj = {};
try {
while (jsonString[index] !== '}') {
skipBlank();
if (index >= length && Allow.OBJ & allow)
return obj;
const key = parseStr();
skipBlank();
index++; // skip colon
try {
const value = parseAny();
Object.defineProperty(obj, key, { value, writable: true, enumerable: true, configurable: true });
}
catch (e) {
if (Allow.OBJ & allow)
return obj;
else
throw e;
}
skipBlank();
if (jsonString[index] === ',')
index++; // skip comma
}
}
catch (e) {
if (Allow.OBJ & allow)
return obj;
else
markPartialJSON("Expected '}' at end of object");
}
index++; // skip final brace
return obj;
};
const parseArr = () => {
index++; // skip initial bracket
const arr = [];
try {
while (jsonString[index] !== ']') {
arr.push(parseAny());
skipBlank();
if (jsonString[index] === ',') {
index++; // skip comma
}
}
}
catch (e) {
if (Allow.ARR & allow) {
return arr;
}
markPartialJSON("Expected ']' at end of array");
}
index++; // skip final bracket
return arr;
};
const parseNum = () => {
if (index === 0) {
if (jsonString === '-' && Allow.NUM & allow)
markPartialJSON("Not sure what '-' is");
try {
return JSON.parse(jsonString);
}
catch (e) {
if (Allow.NUM & allow) {
try {
if ('.' === jsonString[jsonString.length - 1])
return JSON.parse(jsonString.substring(0, jsonString.lastIndexOf('.')));
return JSON.parse(jsonString.substring(0, jsonString.lastIndexOf('e')));
}
catch (e) { }
}
throwMalformedError(String(e));
}
}
const start = index;
if (jsonString[index] === '-')
index++;
while (jsonString[index] && !',]}'.includes(jsonString[index]))
index++;
if (index == length && !(Allow.NUM & allow))
markPartialJSON('Unterminated number literal');
try {
return JSON.parse(jsonString.substring(start, index));
}
catch (e) {
if (jsonString.substring(start, index) === '-' && Allow.NUM & allow)
markPartialJSON("Not sure what '-' is");
try {
return JSON.parse(jsonString.substring(start, jsonString.lastIndexOf('e')));
}
catch (e) {
throwMalformedError(String(e));
}
}
};
const skipBlank = () => {
while (index < length && ' \n\r\t'.includes(jsonString[index])) {
index++;
}
};
return parseAny();
};
// using this function with malformed JSON is undefined behavior
const partialParse = (input) => parseJSON(input, Allow.ALL ^ Allow.NUM);
export { partialParse, PartialJSON, MalformedJSON };
//# sourceMappingURL=parser.mjs.map