UNPKG

@sapientpro/json-stream

Version:
313 lines (312 loc) 12.3 kB
"use strict"; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var _JsonStream_instances, _JsonStream_observers, _JsonStream_resolveDesc; Object.defineProperty(exports, "__esModule", { value: true }); exports.JsonStream = exports.Rest = exports.Any = void 0; const node_stream_1 = require("node:stream"); const rxjs_1 = require("rxjs"); exports.Any = Symbol('Any'); exports.Rest = Symbol('Rest'); class JsonStream extends node_stream_1.Writable { constructor(start = '', collectJson = false) { let buffer = ''; let pos = 0; let continuation = Promise.withResolvers(); let process = Promise.withResolvers(); const next = async (shift = 0, callback) => { while (pos + shift >= buffer.length) { callback?.(); const { resolve } = continuation; continuation = Promise.withResolvers(); resolve(); await process.promise; } //Cleanup buffer if it's too big if (!collectJson && pos > 512) { buffer = buffer.substring(pos); pos = 0; } return this.writable || pos < buffer.length; }; const waitStart = async () => { const length = start?.length ?? 0; if (!length) return; while (await next(length)) { const startPos = buffer.indexOf(start, pos); if (startPos >= 0) { pos = startPos + start.length; await skipSpaces(); buffer = buffer.substring(pos); pos = 0; break; } pos = buffer.length - length + 1; } }; const skipSpaces = async () => { while (await next()) { if (buffer.at(pos).trim() === '') { ++pos; continue; } break; } }; const parse = async (path = []) => { await skipSpaces(); let value; switch (buffer.at(pos)) { case '{': { pos++; value = {}; while (await next()) { await skipSpaces(); if (buffer.at(pos) === '}') { ++pos; break; } const name = await parseString(); await skipSpaces(); if (buffer.at(pos) !== ':') { throw new SyntaxError('Json syntax error at ' + pos); } ++pos; value[name] = await parse([...path, name]); await skipSpaces(); if (buffer.at(pos) === ',') { ++pos; } } break; } case '[': { ++pos; let index = 0; value = []; while (await next()) { await skipSpaces(); if (buffer.at(pos) === ']') { ++pos; break; } value.push(await parse([...path, index])); ++index; await skipSpaces(); if (buffer.at(pos) === ',') { ++pos; } } break; } case '"': value = await parseString(path); break; case "t": await next(3); if (buffer.substring(pos, pos + 4) !== 'true') { throw new SyntaxError('Json syntax error at ' + pos); } value = true; pos += 4; break; case "f": await next(4); if (buffer.substring(pos, pos + 5) !== 'false') { throw new SyntaxError('Json syntax error at ' + pos); } value = false; pos += 5; break; case "n": await next(3); if (buffer.substring(pos, pos + 4) !== 'null') { throw new SyntaxError('Json syntax error at ' + pos); } value = null; pos += 4; break; default: do { const str = buffer.substring(pos); const match = str.match(/^(-?\d+(\.\d+)?([eE][+-]?\d+)?)([^.eE])?/); if ((this.writable || this.writableLength > 0) && match && match[4] === void 0) { await next(str.length + 1); continue; } if (!match) { throw new SyntaxError('Json syntax error at ' + pos); } value = Number(match[1]); pos += match[1].length; } while (false); } pushValue(__classPrivateFieldGet(this, _JsonStream_observers, "f"), path, value); return value; }; const parseString = async (path) => { let value = ''; let chunk = ''; ++pos; const stream = path && __classPrivateFieldGet(this, _JsonStream_instances, "m", _JsonStream_resolveDesc).call(this, path, false)?.stream; loop: while (await next(0, () => { value += chunk; stream?.push(chunk); chunk = ''; })) { switch (buffer.at(pos)) { case '"': ++pos; break loop; case '\\': ++pos; await next(); switch (buffer.at(pos)) { case 't': chunk += '\t'; ++pos; break; case 'r': chunk += '\r'; ++pos; break; case 'n': chunk += '\n'; ++pos; break; case 'b': chunk += '\b'; ++pos; break; case 'f': chunk += '\f'; ++pos; break; case 'u': await next(4); chunk += String.fromCharCode(parseInt(buffer.substring(pos + 1, pos + 5), 16)); pos += 5; break; default: chunk += buffer.at(pos); ++pos; break; } break; default: chunk += buffer.at(pos); ++pos; break; } } if (chunk) { value += chunk; stream?.push(chunk); } stream?.push(null); return value; }; const pushValue = (observers, path, value, originalPath = [...path]) => { if (path.length === 0) { observers.observer?.next({ path: originalPath, value }); return; } const key = path.shift(); if (observers.children[key]) { pushValue(observers.children[key], path, value, originalPath); } if (observers.children[exports.Any]) { pushValue(observers.children[exports.Any], path, value, originalPath); } if (observers.children[exports.Rest]) { pushValue(observers.children[exports.Rest], [], value, originalPath); } }; const cleanup = (observer) => { observer.stream?.push(null); observer.observer?.complete(); for (const child of Object.values(observer.children)) { cleanup(child); } }; super({ defaultEncoding: 'utf-8', construct(callback) { waitStart() .then(() => parse()) .then(async (value) => { this.emit('value', value); continuation.resolve(); }) .catch((e) => { this.emit('error', e); }); callback(); }, async write(chunk, encoding, callback) { buffer += chunk.toString('utf-8'); continuation.promise.then(() => { callback(); }); const { resolve } = process; process = Promise.withResolvers(); resolve(); }, final(callback) { continuation.promise.then(() => { callback(); }, callback); }, destroy(error, callback) { cleanup(__classPrivateFieldGet(this, _JsonStream_observers, "f")); callback(error); } }); _JsonStream_instances.add(this); _JsonStream_observers.set(this, { children: {} }); } observe(path = []) { var _a; return ((_a = __classPrivateFieldGet(this, _JsonStream_instances, "m", _JsonStream_resolveDesc).call(this, path)).observer ?? (_a.observer = new rxjs_1.Subject())); } stream(path) { if (__classPrivateFieldGet(this, _JsonStream_instances, "m", _JsonStream_resolveDesc).call(this, path).stream) { throw new Error('Stream already exists'); } return (__classPrivateFieldGet(this, _JsonStream_instances, "m", _JsonStream_resolveDesc).call(this, path).stream = new node_stream_1.Readable({ encoding: 'utf-8', read() { } })); } async value(path = []) { const { value } = await (0, rxjs_1.firstValueFrom)(this.observe(path)); return value; } } exports.JsonStream = JsonStream; _JsonStream_observers = new WeakMap(), _JsonStream_instances = new WeakSet(), _JsonStream_resolveDesc = function _JsonStream_resolveDesc(path, create = true) { if (typeof path === 'string') { path = path.split('.'); } let observer = __classPrivateFieldGet(this, _JsonStream_observers, "f"); for (const key of path) { if (Object.hasOwn(observer.children, key)) { observer = observer.children[key]; } else if (create) { observer = observer.children[key] = { children: {} }; } else { return null; } } return observer; };