UNPKG

@sergdudko/objectstream

Version:

Creates a stream to convert json from string or convert json to string.

235 lines (234 loc) 7.44 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Parser = void 0; const stream_1 = require("stream"); const global_js_1 = require("../utils/global.js"); /** * @class Parser * * Сreates an instance of Parser (String to Json conversion stream) */ class Parser extends stream_1.Transform { /** * * @param start - first separator * @param middle - middle separator * @param end - end separator */ constructor(start, middle, end) { super({ highWaterMark: 64 * 1024, objectMode: true }); if (typeof start !== "undefined" && (typeof start !== "string" || Buffer.byteLength(start) > 1 || start.match(/["{}]/))) throw new Error("Argument start require one byte String!"); if (typeof middle !== "undefined" && (typeof middle !== "string" || Buffer.byteLength(middle) > 1 || middle.match(/["{}]/))) throw new Error("Argument separator require one byte String!"); if (typeof end !== "undefined" && (typeof end !== "string" || Buffer.byteLength(end) > 1 || end.match(/["{}]/))) throw new Error("Argument end require one byte String!"); this.__separators = { start: Buffer.from(start ? start : "", "utf8")[0], middle: Buffer.from(middle ? middle : "", "utf8")[0], end: Buffer.from(end ? end : "", "utf8")[0], }; this.__clear(); this.__bytesRead = 0; this.__encoding = "utf8"; this.setDefaultEncoding(this.__encoding); this.__buffers = []; this.__leftBrace = 0; this.__rightBrace = 0; this.__openQuotes = false; } /** * clear buffer and reset counters * * @private */ __clear() { this.__buffers = []; this.__leftBrace = 0; this.__rightBrace = 0; this.__openQuotes = false; } /** * basic stream handler */ __handler(buffer, s, errors) { if (this.__buffers.length > 65536) { const _nbuffer = Buffer.concat(this.__buffers); this.__buffers = []; this.__buffers.push(_nbuffer); } if (this.__leftBrace !== 0) { this.__buffers.push(buffer.slice(s, s + 1)); } else if (this.__separators.start !== buffer[s] && this.__separators.end !== buffer[s] && this.__separators.middle !== buffer[s] && 0x20 !== buffer[s] && 0x0d !== buffer[s] && 0x0a !== buffer[s] && 0x09 !== buffer[s]) { errors.push(new Error("Unexpected token " + buffer.slice(s, s + 1).toString(this.__encoding) + " in JSON at position " + (this.__bytesRead + s))); } } /** * Data event handler * * @private * @param string - string or buffer data * @param encoding - stream encoding * @param callback - callback function */ _transform(string, // eslint-disable-next-line encoding = this.__encoding, callback = () => { return; }) { if (typeof string === "undefined") { callback(); return; } if (string === null) { this._final(() => { callback(); }); return; } const _buffer = typeof string === "string" ? Buffer.from(string, encoding) : string; if (!(_buffer instanceof Buffer)) { callback([ new Error("Incoming data type is " + typeof _buffer + ", require data type is String!"), ]); return; } if (Parser.__empty.equals(_buffer)) { callback(); return; } const errors = []; for (let s = 0; s < _buffer.length; s++) { switch (_buffer[s]) { case 0x7b: this.__leftBrace += 1; this.__handler(_buffer, s, errors); break; case 0x7d: this.__rightBrace += 1; this.__handler(_buffer, s, errors); break; case 0x08: case 0x09: case 0x0a: case 0x0c: case 0x0d: case 0x00: case 0x0b: if (this.__openQuotes && this.__leftBrace !== 0) this.__buffers.push(Buffer.from("\\u" + ("0000" + _buffer[s].toString(16)).slice(-4), "utf8")); break; case 0x22: if (_buffer[s - 1] !== 0x5c) if (this.__openQuotes) this.__openQuotes = false; else if (this.__leftBrace !== 0) this.__openQuotes = true; this.__handler(_buffer, s, errors); break; default: this.__handler(_buffer, s, errors); break; } if (this.__leftBrace !== 0 && this.__leftBrace === this.__rightBrace) { try { const _buf = Buffer.concat(this.__buffers); const _str = _buf.toString("utf8"); const _object = JSON.parse(_str); if ((0, global_js_1.validator)(_object, false)) { this.__clear(); this.push(_object); } else { this.__clear(); errors.push(new Error("Validation failed, incoming data type is not pure Object!")); } } catch (err) { this.__clear(); errors.push(err); } } else if (this.__leftBrace < this.__rightBrace) { this.__clear(); errors.push(new Error("Parsing error, clear buffer!")); } } if (errors.length > 0) callback(errors); else callback(); this.__bytesRead += _buffer.byteLength; } /** * Flush event handler * * @private * @param callback - callback function */ _flush(callback = () => { return; }) { this.__clear(); callback(); } /** * End event handler * * @private * @param callback - callback function */ _final(callback = () => { return; }) { if (this.__buffers.length === 0) { callback(); return; } try { const _buf = Buffer.concat(this.__buffers); const _str = _buf.toString("utf8"); JSON.parse(_str); callback([new Error("Raw object detected!")]); } catch (err) { callback([err]); } } /** * set stream encoding */ setEncoding(encoding) { this.__encoding = encoding; this.setDefaultEncoding(this.__encoding); return this; } } exports.Parser = Parser; /** * empty buffer * * @private */ Parser.__empty = Buffer.from("");