UNPKG

@init-kz/jsonparse-ts

Version:

Library for parsing partial jsons inspired by original jsonparse written on js

1,194 lines (1,183 loc) 36.9 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { CONSTANTS: () => constants_exports, Parser: () => Parser }); module.exports = __toCommonJS(index_exports); // src/constants/constants.ts var constants_exports = {}; __export(constants_exports, { CHARS: () => CHARS, PARSER_MODES: () => PARSER_MODES, PARSER_STATES: () => PARSER_STATES, STRING_BUFFER_SIZE: () => STRING_BUFFER_SIZE, TOKENIZER_STATES: () => TOKENIZER_STATES, TOKENS: () => TOKENS, UTF8_BOUNDS: () => UTF8_BOUNDS }); var TOKENS = { LEFT_BRACE: 1, RIGHT_BRACE: 2, LEFT_BRACKET: 3, RIGHT_BRACKET: 4, COLON: 5, COMMA: 6, TRUE: 7, FALSE: 8, NULL: 9, STRING: 10, NUMBER: 11 }; var TOKENIZER_STATES = { START: 17, STOP: 18, TRUE1: 33, TRUE2: 34, TRUE3: 35, FALSE1: 49, FALSE2: 50, FALSE3: 51, FALSE4: 52, NULL1: 65, NULL2: 66, NULL3: 67, NUMBER1: 81, NUMBER3: 83, STRING1: 97, // After open quote STRING2: 98, // After backslash // unicode hex codes STRING3: 99, STRING4: 100, STRING5: 101, STRING6: 102 }; var PARSER_STATES = { VALUE: 113, KEY: 114, COLON: 58, // : COMMA: 44 // , }; var PARSER_MODES = { OBJECT: 129, ARRAY: 130 }; var CHARS = { BACKSLASH: 92, // '\' FORWARD_SLASH: 47, // '/' BACKSPACE: 8, // \b FORM_FEED: 12, // \f NEWLINE: 10, // \n CARRIAGE_RETURN: 13, // \r TAB: 9, // \t SPACE: 32, // ' ' LEFT_BRACE: 123, // { RIGHT_BRACE: 125, // } LEFT_BRACKET: 91, // [ RIGHT_BRACKET: 93, // ] COLON: 58, // : COMMA: 44, // , QUOTE: 34, // " MINUS: 45, // - PLUS: 43, // + DOT: 46, // . E: 101, // e BIG_E: 69, // E T: 116, // t F: 102, // f N: 110, // n L: 108, // l S: 115, // s B: 98, // b (Backspace) R: 114, // r (Carriage Return) U: 117, // u (Unicode sequence start) A: 97, // a BIG_A: 65, // A BIG_F: 70, // F ZERO: 48, // 0 NINE: 57, // 9 WHITESPACES: [ 32, // ' ' 9, // \t 10, // \n 13 // \r ] }; var UTF8_BOUNDS = { MIN_MULTI_BYTE: 128, INVALID_LOWER: 193, BYTE_2_MIN: 194, BYTE_2_MAX: 223, BYTE_3_MIN: 224, BYTE_3_MAX: 239, BYTE_4_MIN: 240, BYTE_4_MAX: 244, HIGH_SURROGATE_START: 55296, HIGH_SURROGATE_END: 56319, LOW_SURROGATE_START: 56320, LOW_SURROGATE_END: 57343 }; var STRING_BUFFER_SIZE = 64 * 1024; // src/utils/utils.ts function alloc(size) { if (typeof Buffer !== "undefined" && Buffer.alloc) { return Buffer.alloc(size); } return new Uint8Array(size); } function isPrimitive(token) { return token === TOKENS.STRING || token === TOKENS.NUMBER || token === TOKENS.TRUE || token === TOKENS.FALSE || token === TOKENS.NULL; } function tokenName(code) { const _constants = { ...TOKENS, ...TOKENIZER_STATES, ...PARSER_STATES, ...PARSER_MODES }; for (const key of Object.keys(_constants)) { if (_constants[key] === code) { return key; } } return code ? `0x${code.toString(16)}` : ""; } function omitEmptyArrayOrObject(value) { if (Array.isArray(value)) { return value.map(omitEmptyArrayOrObject).filter((item) => !(Array.isArray(item) && item.length === 0) && !(typeof item === "object" && item !== null && Object.keys(item).length === 0)); } else if (typeof value === "object" && value !== null) { return Object.fromEntries( Object.entries(value).map(([key, val]) => [key, omitEmptyArrayOrObject(val)]).filter(([, val]) => !(Array.isArray(val) && val.length === 0) && !(typeof val === "object" && val !== null && Object.keys(val).length === 0)) ); } return value; } // src/handlers/error-handler.ts var ErrorHandler = class { constructor(eventEmitter, stateHandler) { this.eventEmitter = eventEmitter; this.stateHandler = stateHandler; } /** * Handles unexpected token errors. */ parseError(token, value) { this.stateHandler.setTokenizerState(TOKENIZER_STATES.STOP); const error = new Error( `Unexpected ${tokenName(token)}${value ? ` (${JSON.stringify(value)})` : ""} in state ${tokenName(this.stateHandler.getParsingState())}` ); this.eventEmitter.handleError(error); return error; } /** * Handles character-related errors. */ charError(buffer, i) { this.stateHandler.setTokenizerState(TOKENIZER_STATES.STOP); const error = new Error( `Unexpected ${JSON.stringify( String.fromCharCode(buffer[i]) )} at position ${i} in state ${tokenName(this.stateHandler.getTokenizerState())}` ); this.eventEmitter.handleError( error ); return error; } }; // src/handlers/event-emitter.ts var EventEmitter = class { valueHandlers = []; tokenHandlers = []; errorHandlers = []; /** * Subscribes a value event callback. */ onValue(callback) { this.valueHandlers.push(callback); } /** * Emits a parsed value event. */ emitValue(value) { this.valueHandlers.forEach((callback) => callback(value)); return this.valueHandlers.length > 0; } /** * Unsubscribes a value event callback. */ offValue(callback) { this.valueHandlers = this.valueHandlers.filter((cb) => cb !== callback); } /** * Returns true if length of value subscribers more than zero */ hasValueHandler() { return this.valueHandlers.length > 0; } /** * Subscribes a callback to token events. * @param callback - Function to handle tokens. */ onToken(callback) { this.tokenHandlers.push(callback); } /** * Subscribes a callback to token events. * @param callback - Function to handle tokens. */ offToken(callback) { this.tokenHandlers = this.tokenHandlers.filter((cb) => cb !== callback); } /** * Emits a token event to all subscribers. * @param token - The token type. * @param value - The associated value. */ emitToken(token, value) { if (this.tokenHandlers.length > 0) { this.tokenHandlers.forEach((callback) => callback(token, value)); } return this.tokenHandlers.length > 0; } /** * Subscribes an error handler callback. * @param callback - A function to handle errors. */ onError(callback) { this.errorHandlers.push(callback); } /** * Subscribes an error handler callback. * @param callback - A function to handle errors. */ offError(callback) { this.errorHandlers = this.errorHandlers.filter((cb) => cb !== callback); } /** * Handles errors by either notifying subscribers or throwing the error. * @param err - The error to handle. */ handleError(err) { if (this.errorHandlers.length > 0) { this.errorHandlers.forEach((callback) => callback(err)); } else { throw err; } } }; // src/handlers/state-handler.ts var StateHandler = class { eventEmitter; stringHandler; /** Tokenizer State - Tracks the current state of the tokenizer */ tState = TOKENIZER_STATES.START; /** Current Parsed Value */ value = void 0; lastValue = void 0; /** Parser Mode (OBJECT, ARRAY) */ mode = void 0; /** Stack to maintain parsing context */ stack; /** Current Parsing State (VALUE, KEY) */ state = PARSER_STATES.VALUE; /** Current byte offset in the stream */ offset = -1; /** Current key being processed (for objects) */ key = void 0; constructor(eventEmitter, stringHandler) { this.eventEmitter = eventEmitter; this.stringHandler = stringHandler; this.stack = []; } getTokenizerState() { return this.tState; } incTokenizerState() { this.tState++; return this; } setOffset(n) { this.offset = n; return this; } getOffset() { return this.offset; } addOffset(n) { this.offset += n; return this; } setTokenizerState(n) { this.tState = n; return this; } getParsingState() { return this.state; } getStack() { return this.stack; } /** * Pushes the current parsing context onto the stack. */ push() { this.stack.push({ value: this.value, key: this.key, mode: this.mode }); } /** * Pops the last parsing context from the stack and emits a value. */ pop() { const value = this.value; const parent = this.stack.pop(); this.value = parent?.value; this.key = parent?.key; this.mode = parent?.mode; if (typeof parent?.value !== void 0) { this.lastValue = value; } if (this.mode) { this.state = PARSER_STATES.COMMA; } this.eventEmitter.emitValue(value); if (!this.mode) { this.state = PARSER_STATES.VALUE; } } incOffset() { this.offset++; return this; } getValue() { return this.value; } getLastIncompleteValue(omitEmpty = false) { if (typeof this.lastValue === "undefined" || this.stack.length > 0) { let currentValue = void 0; let settedCurrentValue = false; if (this.mode) { switch (this.tState) { case TOKENIZER_STATES.NULL1: case TOKENIZER_STATES.NULL2: case TOKENIZER_STATES.NULL3: currentValue = null; break; case TOKENIZER_STATES.TRUE1: case TOKENIZER_STATES.TRUE2: case TOKENIZER_STATES.TRUE3: currentValue = true; break; case TOKENIZER_STATES.FALSE1: case TOKENIZER_STATES.FALSE2: case TOKENIZER_STATES.FALSE3: case TOKENIZER_STATES.FALSE4: currentValue = false; break; case TOKENIZER_STATES.STRING1: case TOKENIZER_STATES.STRING2: case TOKENIZER_STATES.STRING3: case TOKENIZER_STATES.STRING4: case TOKENIZER_STATES.STRING5: case TOKENIZER_STATES.STRING6: currentValue = this.stringHandler.flushBuffer().getString(); break; } if (typeof currentValue !== "undefined" && typeof this.key !== "undefined" && typeof this.value[this.key] === "undefined") { this.value[this.key] = currentValue; settedCurrentValue = true; } } const newStack = [...this.stack, { value: this.value, key: this.key, mode: this.mode }]; let parent = null; for (let i = newStack.length - 1; i >= 0; i--) { const currentStack = newStack[i]; if (typeof currentStack?.value !== "undefined") { parent = currentStack?.value; } } if (parent) { parent = JSON.parse(JSON.stringify(parent)); } if (typeof currentValue !== "undefined" && typeof this.key !== "undefined" && settedCurrentValue) delete this.value[this.key]; if (parent) return omitEmpty ? omitEmptyArrayOrObject(parent) : parent; } return this.lastValue ?? null; } getLastValue() { if (typeof this.lastValue === "undefined" || this.stack.length > 0) { const newStack = [...this.stack, { value: this.value, key: this.key, mode: this.mode }]; let parent = null; for (let i = newStack.length - 1; i >= 0; i--) { const currentStack = newStack[i]; if (typeof currentStack?.value !== "undefined") { parent = currentStack?.value; } } if (parent) { return JSON.parse(JSON.stringify(parent)); } } return this.lastValue ?? null; } getKey() { return this.key; } getTokenizerStateName() { return tokenName(this.getTokenizerState()); } getMode() { return this.mode; } setValue(value) { this.value = value; return this; } setKey(value) { this.key = value; return this; } setKeyValue(value) { if (this.value && (this.key || this.key === 0)) { this.value[this.key] = value; } } setState(n) { this.state = n; return this; } setMode(n) { this.mode = n; return this; } }; // src/handlers/string-handler.ts var StringHandler = class { /** Current string being parsed */ string = void 0; /** Buffer to store string data */ stringBuffer; /** Offset within the string buffer */ stringBufferOffset = 0; /** Unicode processing variables */ unicode = void 0; highSurrogate = void 0; constructor() { this.stringBuffer = alloc(STRING_BUFFER_SIZE); } /** * Appends a character to the string buffer. */ appendStringChar(char) { if (this.stringBufferOffset >= STRING_BUFFER_SIZE) { if (this.string === void 0) this.string = ""; this.string += new TextDecoder().decode( this.stringBuffer.subarray(0, this.stringBufferOffset) ); this.stringBufferOffset = 0; } this.stringBuffer[this.stringBufferOffset++] = char; } /** * Appends a portion of a buffer to the internal string buffer. * @param buf - The source Uint8Array buffer. * @param start - The starting index (optional, defaults to 0). * @param end - The ending index (optional, defaults to `buf.length`). */ appendStringBuf(buf, start = 0, end) { let size = buf.length; if (typeof start === "number") { if (typeof end === "number") { if (end < 0) { size = buf.length - start + end; } else { size = end - start; } } else { size = buf.length - start; } } if (size < 0) size = 0; if (this.stringBufferOffset + size > STRING_BUFFER_SIZE) { if (this.string === void 0) this.string = ""; this.string += new TextDecoder().decode( this.stringBuffer.subarray(0, this.stringBufferOffset) ); this.stringBufferOffset = 0; } this.stringBuffer.set( buf.subarray(start, start + size), this.stringBufferOffset ); this.stringBufferOffset += size; } /** * Flushes the current buffer into the string. */ flushBuffer() { if (this.stringBufferOffset > 0) { if (this.string === void 0) { this.string = ""; } this.string += new TextDecoder().decode( this.stringBuffer.subarray(0, this.stringBufferOffset) ); this.stringBufferOffset = 0; } return this; } /** * Gets the final string value. * Ensures that if no string has been accumulated, it returns an empty string. * @returns {string} The current accumulated string. */ getString() { return this.string || ""; } /** * Resets the string buffer and optionally sets a new initial string value. * This is useful when starting a new string parsing session. * @param {string} [stringValue] - Optional initial string value after reset. */ reset(stringValue) { this.stringBufferOffset = 0; this.string = stringValue; } /** * Resets only the stored string without affecting the buffer offset. * This can be useful when clearing the current string but keeping buffer state. * @param {string} [stringValue] - Optional new string value. */ resetString(stringValue) { this.string = stringValue; } /** * Sets the string value from a single character's char code. * Useful when initializing a string with a character (e.g., starting a number or string). * @param {number} n - The character code to set as the string. */ setFromCharcode(n) { this.string = String.fromCharCode(n); } /** * Appends a single character (from its char code) to the existing string. * Useful for dynamically building strings one character at a time. * @param {number} n - The character code to append to the string. */ addFromCharCode(n) { if (this.string === void 0) { this.string = ""; } this.string += String.fromCharCode(n); return this; } setUnicode(unicode) { this.unicode = unicode; return this; } getUnicode() { return this.unicode || ""; } setHighSurrogate(n) { this.highSurrogate = n; return this; } getHighSurrogate() { return this.highSurrogate; } addUnicodeFromCharCode(n) { if (this.unicode === void 0) { this.unicode = ""; } this.unicode += String.fromCharCode(n); return this; } }; // src/handlers/token-handler.ts var TokenHandler = class { constructor(eventEmitter, stateHandler, errorHandler) { this.eventEmitter = eventEmitter; this.stateHandler = stateHandler; this.errorHandler = errorHandler; } /** * Handles incoming tokens and builds the JSON structure. */ handleToken(token, value) { switch (this.stateHandler.getParsingState()) { case PARSER_STATES.VALUE: this.handleValueToken(token, value); break; case PARSER_STATES.KEY: this.handleKeyToken(token, value); break; case PARSER_STATES.COLON: this.handleColonToken(token); break; case PARSER_STATES.COMMA: this.handleCommaToken(token); break; default: this.errorHandler.parseError(token, value); } this.eventEmitter.emitToken(token, value); } /** * Handles tokens when in VALUE state. */ handleValueToken(token, value) { if (isPrimitive(token)) { this.stateHandler.setKeyValue(value); if (this.stateHandler.getMode()) { this.stateHandler.setState(PARSER_STATES.COMMA); } this.eventEmitter.emitValue(value); } else if (token === TOKENS.LEFT_BRACE) { this.startObject(); } else if (token === TOKENS.LEFT_BRACKET) { this.startArray(); } else if (token === TOKENS.RIGHT_BRACE && this.stateHandler.getMode() === PARSER_MODES.OBJECT || token === TOKENS.RIGHT_BRACKET && this.stateHandler.getMode() === PARSER_MODES.ARRAY) { this.stateHandler.pop(); } else { this.errorHandler.parseError(token, value); } } /** * Handles tokens when in KEY state. */ handleKeyToken(token, value) { if (token === TOKENS.STRING) { this.stateHandler.setKey(value); this.stateHandler.setState(PARSER_STATES.COLON); } else if (token === TOKENS.RIGHT_BRACE) { this.stateHandler.pop(); } else { this.errorHandler.parseError(token, value); } } /** * Handles tokens when in COLON state. */ handleColonToken(token) { if (token === TOKENS.COLON) { this.stateHandler.setState(PARSER_STATES.VALUE); } else { this.errorHandler.parseError(token); } } /** * Handles tokens when in COMMA state. */ handleCommaToken(token) { if (token === TOKENS.COMMA) { const mode = this.stateHandler.getMode(); if (mode === PARSER_MODES.ARRAY) { const key = this.stateHandler.getKey() || 0; this.stateHandler.setKey(key + 1); this.stateHandler.setState(PARSER_STATES.VALUE); } else if (mode === PARSER_MODES.OBJECT) { this.stateHandler.setState(PARSER_STATES.KEY); } } else if (token === TOKENS.RIGHT_BRACKET && this.stateHandler.getMode() === PARSER_MODES.ARRAY || token === TOKENS.RIGHT_BRACE && this.stateHandler.getMode() === PARSER_MODES.OBJECT) { this.stateHandler.pop(); } else { this.errorHandler.parseError(token); } } /** * Starts a new object `{}`. */ startObject() { this.stateHandler.push(); let value = this.stateHandler.getValue(); if (value) { value = value[this.stateHandler.getKey()] = {}; } else { value = {}; } this.stateHandler.setValue(value).setKey(void 0).setState(PARSER_STATES.KEY).setMode(PARSER_MODES.OBJECT); } /** * Starts a new array `[]`. */ startArray() { this.stateHandler.push(); let value = this.stateHandler.getValue(); if (value) { value = value[this.stateHandler.getKey()] = []; } else { value = []; } this.stateHandler.setValue(value).setKey(0).setMode(PARSER_MODES.ARRAY).setState(PARSER_STATES.VALUE); } }; // src/handlers/utf8-handler.ts var UTF8Handler = class { /** Remaining bytes for a multi-byte UTF-8 character, number of bytes remaining in multi byte utf8 char to read after split boundary */ bytes_remaining = 0; /** Total bytes in the current UTF-8 character sequence, bytes in multi byte utf8 char to read */ bytes_in_sequence = 0; /** Temporary buffers for rebuilding chars split before boundary is reached */ temp_buffs; constructor() { this.temp_buffs = { 2: alloc(2), 3: alloc(3), 4: alloc(4) }; } /** Get the remaining bytes for a multi-byte character */ getBytesRemaining() { return this.bytes_remaining; } getRemainingBytesInBuff(buffer) { for (let i = 0; i < this.bytes_remaining; i++) { this.temp_buffs[this.bytes_in_sequence][this.bytes_in_sequence - this.bytes_remaining + i] = buffer[i]; } const remainingBytes = this.temp_buffs[this.bytes_in_sequence] || alloc(0); this.bytes_in_sequence = this.bytes_remaining = 0; return remainingBytes; } handleBoundarySplit(i, buffer) { for (let j = 0; j <= buffer.length - 1 - i; j++) { this.temp_buffs[this.bytes_in_sequence][j] = buffer[i + j]; } this.bytes_remaining = i + this.bytes_in_sequence - buffer.length; } /** Set the remaining bytes, ensuring it's not negative */ setBytesRemaining(value) { if (value < 0) throw new Error("bytes_remaining cannot be negative"); this.bytes_remaining = value; } /** Get the total bytes in the current UTF-8 character sequence */ getBytesInSequence() { return this.bytes_in_sequence; } /** Set the total bytes, ensuring it's not negative */ setBytesInSequence(value) { if (value < 0) throw new Error("bytes_in_sequence cannot be negative"); this.bytes_in_sequence = value; } /** Get the temporary buffers for multi-byte characters */ getTempBuffs() { return this.temp_buffs; } /** Set the temporary buffers */ setTempBuffs(buffers) { this.temp_buffs = buffers; } hasBytesRemaining() { return this.bytes_remaining > 0; } }; // src/parsers/base-parser.ts var BaseParser = class { /** Event Emitter */ eventEmitter; /** Handler for current state */ stateHandler; /** Handler for strings, including multybytes */ stringHandler; /** Handler for utf8 symbols */ utf8Handler; errorHandler; tokenHandler; encoder; constructor() { this.eventEmitter = new EventEmitter(); this.stringHandler = new StringHandler(); this.stateHandler = new StateHandler(this.eventEmitter, this.stringHandler); this.errorHandler = new ErrorHandler(this.eventEmitter, this.stateHandler); this.utf8Handler = new UTF8Handler(); this.tokenHandler = new TokenHandler(this.eventEmitter, this.stateHandler, this.errorHandler); this.encoder = new TextEncoder(); } /** * Parses and processes numeric values. * Can be overridden for custom number handling. * @param text - The numeric text to parse. */ numberReviver(text) { const result = Number(text); if (isNaN(result)) { const error = new Error(`Invalid number: ${text}`); this.eventEmitter.handleError(error); return error; } if (/^[0-9]+$/.test(text) && result.toString() !== text) { this.tokenHandler.handleToken(TOKENS.STRING, text); } else { this.tokenHandler.handleToken(TOKENS.NUMBER, result); } } }; // src/parsers/parser.ts var Parser = class extends BaseParser { getEmitter() { return this.eventEmitter; } onToken(callback) { this.eventEmitter.onToken(callback); } onValue(callback) { this.eventEmitter.onValue(callback); } onError(callback) { this.eventEmitter.onError(callback); } getLastValue() { return this.stateHandler.getLastValue(); } getLastIncompleteValue(omitEmpty = false) { return this.stateHandler.getLastIncompleteValue(omitEmpty); } getCurrentKey() { return this.stateHandler.getKey(); } getStack() { return this.stateHandler.getStack(); } getOffset() { return this.stateHandler.getOffset(); } /** * Processes an incoming buffer of JSON data. * @param buffer - The input JSON chunk. */ write(buffer) { if (typeof buffer === "string") { buffer = this.encoder.encode(buffer); } let n; let res = 0; let hasError; for (let i = 0; i < buffer.length; i++) { n = buffer[i]; switch (this.stateHandler.getTokenizerState()) { case TOKENIZER_STATES.START: hasError = this.processStartState(n, buffer, i); if (typeof hasError !== "undefined") return hasError; break; case TOKENIZER_STATES.STRING1: res = this.processStringStartingState(n, buffer, i); if (typeof res !== "number") return res; i = res; break; case TOKENIZER_STATES.STRING2: res = this.processStringBackslashState(n, buffer, i); if (typeof res !== "number") return res; i = res; break; case TOKENIZER_STATES.STRING3: case TOKENIZER_STATES.STRING4: case TOKENIZER_STATES.STRING5: case TOKENIZER_STATES.STRING6: res = this.processStringUnicodeState(n, buffer, i); if (typeof res !== "number") return res; i = res; break; case TOKENIZER_STATES.NUMBER1: case TOKENIZER_STATES.NUMBER3: res = this.processNumberState(n, i); if (typeof res !== "number") return res; i = res; break; case TOKENIZER_STATES.TRUE1: case TOKENIZER_STATES.TRUE2: case TOKENIZER_STATES.TRUE3: hasError = this.processTrueState(n, buffer, i); if (typeof hasError !== "undefined") return hasError; break; case TOKENIZER_STATES.FALSE1: case TOKENIZER_STATES.FALSE2: case TOKENIZER_STATES.FALSE3: case TOKENIZER_STATES.FALSE4: hasError = this.processFalseState(n, buffer, i); if (typeof hasError !== "undefined") return hasError; break; case TOKENIZER_STATES.NULL1: case TOKENIZER_STATES.NULL2: case TOKENIZER_STATES.NULL3: this.processNullState(n, buffer, i); break; default: this.errorHandler.charError(buffer, i); } } } /** * Processes tokens at the root level (e.g. `{`, `[`, `:`, `,`). */ processStartState(n, buffer, i) { this.stateHandler.incOffset(); if (n === CHARS.LEFT_BRACE) this.tokenHandler.handleToken(TOKENS.LEFT_BRACE, "{"); else if (n === CHARS.RIGHT_BRACE) this.tokenHandler.handleToken(TOKENS.RIGHT_BRACE, "}"); else if (n === CHARS.LEFT_BRACKET) this.tokenHandler.handleToken(TOKENS.LEFT_BRACKET, "["); else if (n === CHARS.RIGHT_BRACKET) this.tokenHandler.handleToken(TOKENS.RIGHT_BRACKET, "]"); else if (n === CHARS.COLON) this.tokenHandler.handleToken(TOKENS.COLON, ":"); else if (n === CHARS.COMMA) this.tokenHandler.handleToken(TOKENS.COMMA, ","); else if (n === CHARS.T) this.stateHandler.setTokenizerState(TOKENIZER_STATES.TRUE1); else if (n === CHARS.F) this.stateHandler.setTokenizerState(TOKENIZER_STATES.FALSE1); else if (n === CHARS.N) this.stateHandler.setTokenizerState(TOKENIZER_STATES.NULL1); else if (n === CHARS.QUOTE) { this.stringHandler.reset(""); this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1); } else if (n === CHARS.MINUS) { this.stringHandler.resetString("-"); this.stateHandler.setTokenizerState(TOKENIZER_STATES.NUMBER1); } else if (n >= CHARS.ZERO && n <= CHARS.NINE) { this.stringHandler.setFromCharcode(n); this.stateHandler.setTokenizerState(TOKENIZER_STATES.NUMBER3); } else if (!CHARS.WHITESPACES.includes(n)) { return this.errorHandler.charError(buffer, i); } } processStringUnicodeState(n, buffer, i) { const isNumber = n >= CHARS.ZERO && n <= CHARS.NINE; const isBigHexLetter = n >= CHARS.BIG_A && n <= CHARS.BIG_F; const isHexLetter = n >= CHARS.A && n <= CHARS.F; if (isNumber || isBigHexLetter || isHexLetter) { this.stringHandler.addUnicodeFromCharCode(n); const currentState = this.stateHandler.getTokenizerState(); this.stateHandler.incTokenizerState(); if (currentState === TOKENIZER_STATES.STRING6) { const intVal = parseInt(this.stringHandler.getUnicode(), 16); this.stringHandler.setUnicode(void 0); if (this.stringHandler.getHighSurrogate() !== void 0 && intVal >= UTF8_BOUNDS.LOW_SURROGATE_START && intVal <= UTF8_BOUNDS.LOW_SURROGATE_END) { this.stringHandler.appendStringBuf(this.encoder.encode(String.fromCharCode(this.stringHandler.getHighSurrogate() || 0, intVal))); this.stringHandler.setHighSurrogate(void 0); } else if (this.stringHandler.getHighSurrogate() === void 0 && intVal >= UTF8_BOUNDS.HIGH_SURROGATE_START && intVal <= UTF8_BOUNDS.HIGH_SURROGATE_END) { this.stringHandler.setHighSurrogate(intVal); } else { if (this.stringHandler.getHighSurrogate() !== void 0) { this.stringHandler.appendStringBuf(this.encoder.encode(String.fromCharCode(this.stringHandler.getHighSurrogate() || 0))); this.stringHandler.setHighSurrogate(void 0); } this.stringHandler.appendStringBuf(this.encoder.encode(String.fromCharCode(intVal))); } this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1); } } else { return this.errorHandler.charError(buffer, i); } return i; } processStringBackslashState(n, buffer, i) { n = buffer[i]; if (n === CHARS.QUOTE) { this.stringHandler.appendStringChar(n); this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1); } else if (n === CHARS.BACKSLASH) { this.stringHandler.appendStringChar(CHARS.BACKSLASH); this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1); } else if (n === CHARS.FORWARD_SLASH) { this.stringHandler.appendStringChar(CHARS.FORWARD_SLASH); this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1); } else if (n === CHARS.B) { this.stringHandler.appendStringChar(CHARS.BACKSPACE); this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1); } else if (n === CHARS.F) { this.stringHandler.appendStringChar(CHARS.FORM_FEED); this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1); } else if (n === CHARS.N) { this.stringHandler.appendStringChar(CHARS.NEWLINE); this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1); } else if (n === CHARS.R) { this.stringHandler.appendStringChar(CHARS.CARRIAGE_RETURN); this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1); } else if (n === CHARS.T) { this.stringHandler.appendStringChar(CHARS.TAB); this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING1); } else if (n === CHARS.U) { this.stringHandler.setUnicode(""); this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING3); } else { return this.errorHandler.charError(buffer, i); } return i; } processStringStartingState(n, buffer, i) { if (this.utf8Handler.hasBytesRemaining()) { const remainingBytes = this.utf8Handler.getBytesRemaining(); this.stringHandler.appendStringBuf( this.utf8Handler.getRemainingBytesInBuff(buffer) ); i = i + remainingBytes - 1; } else if (!this.utf8Handler.hasBytesRemaining() && n >= UTF8_BOUNDS.MIN_MULTI_BYTE) { if (n <= UTF8_BOUNDS.INVALID_LOWER || n > UTF8_BOUNDS.BYTE_4_MAX) { const error = new Error( "Invalid UTF-8 character at position " + i + " in state " + this.stateHandler.getTokenizerStateName() + " with value: " + n ); this.eventEmitter.handleError(error); return error; } if (n >= UTF8_BOUNDS.BYTE_2_MIN && n <= UTF8_BOUNDS.BYTE_2_MAX) this.utf8Handler.setBytesInSequence(2); if (n >= UTF8_BOUNDS.BYTE_3_MIN && n <= UTF8_BOUNDS.BYTE_3_MAX) this.utf8Handler.setBytesInSequence(3); if (n >= UTF8_BOUNDS.BYTE_4_MIN && n <= UTF8_BOUNDS.BYTE_4_MAX) this.utf8Handler.setBytesInSequence(4); if (this.utf8Handler.getBytesInSequence() + i > buffer.length) { this.utf8Handler.handleBoundarySplit(i, buffer); i = buffer.length - 1; } else { this.stringHandler.appendStringBuf(buffer, i, i + this.utf8Handler.getBytesInSequence()); i = i + this.utf8Handler.getBytesInSequence() - 1; } } else if (n === CHARS.QUOTE) { this.stateHandler.setTokenizerState(TOKENIZER_STATES.START); this.stringHandler.flushBuffer(); this.tokenHandler.handleToken(TOKENS.STRING, this.stringHandler.getString()); this.stateHandler.addOffset(this.encoder.encode(this.stringHandler.getString()).length + 1); this.stringHandler.resetString(); } else if (n === CHARS.BACKSLASH) { this.stateHandler.setTokenizerState(TOKENIZER_STATES.STRING2); } else if (n >= CHARS.SPACE) { this.stringHandler.appendStringChar(n); } else { return this.errorHandler.charError(buffer, i); } return i; } /** * Parses numeric values. */ processNumberState(n, i) { if (n >= CHARS.ZERO && n <= CHARS.NINE || n === CHARS.DOT || n === CHARS.E || n === CHARS.BIG_E || n === CHARS.PLUS || n === CHARS.MINUS) { this.stringHandler.addFromCharCode(n); this.stateHandler.setTokenizerState(TOKENIZER_STATES.NUMBER3); } else { this.stateHandler.setTokenizerState(TOKENIZER_STATES.START); const error = this.numberReviver(this.stringHandler.getString()); if (error) { return error; } this.stateHandler.addOffset(this.stringHandler.getString().length - 1); this.stringHandler.resetString(); i--; } return i; } processFalseState(n, buffer, i) { const tokenizerState = this.stateHandler.getTokenizerState(); if (tokenizerState === TOKENIZER_STATES.FALSE1 && n === CHARS.A) { this.stateHandler.setTokenizerState(TOKENIZER_STATES.FALSE2); return; } if (tokenizerState === TOKENIZER_STATES.FALSE2 && n === CHARS.L) { this.stateHandler.setTokenizerState(TOKENIZER_STATES.FALSE3); return; } if (tokenizerState === TOKENIZER_STATES.FALSE3 && n === CHARS.S) { this.stateHandler.setTokenizerState(TOKENIZER_STATES.FALSE4); return; } if (tokenizerState === TOKENIZER_STATES.FALSE4 && n === CHARS.E) { this.stateHandler.setTokenizerState(TOKENIZER_STATES.START); this.tokenHandler.handleToken(TOKENS.FALSE, false); this.stateHandler.addOffset(4); return; } return this.errorHandler.charError(buffer, i); } processTrueState(n, buffer, i) { const tokenizerState = this.stateHandler.getTokenizerState(); if (tokenizerState === TOKENIZER_STATES.TRUE1 && n === CHARS.R) { this.stateHandler.setTokenizerState(TOKENIZER_STATES.TRUE2); return; } if (tokenizerState === TOKENIZER_STATES.TRUE2 && n === CHARS.U) { this.stateHandler.setTokenizerState(TOKENIZER_STATES.TRUE3); return; } if (tokenizerState === TOKENIZER_STATES.TRUE3 && n === CHARS.E) { this.stateHandler.setTokenizerState(TOKENIZER_STATES.START); this.tokenHandler.handleToken(TOKENS.TRUE, true); this.stateHandler.addOffset(3); return; } return this.errorHandler.charError(buffer, i); } /** * Processes `null` value. */ processNullState(n, buffer, i) { const tokenizerState = this.stateHandler.getTokenizerState(); if (tokenizerState === TOKENIZER_STATES.NULL1 && n === CHARS.U) { this.stateHandler.setTokenizerState(TOKENIZER_STATES.NULL2); return; } if (tokenizerState === TOKENIZER_STATES.NULL2 && n === CHARS.L) { this.stateHandler.setTokenizerState(TOKENIZER_STATES.NULL3); return; } if (tokenizerState === TOKENIZER_STATES.NULL3 && n === CHARS.L) { this.stateHandler.setTokenizerState(TOKENIZER_STATES.START); this.tokenHandler.handleToken(TOKENS.NULL, null); this.stateHandler.addOffset(3); return; } return this.errorHandler.charError(buffer, i); } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { CONSTANTS, Parser }); //# sourceMappingURL=index.cjs.map