import { toTransformStream } from "../streams/to_transform_stream.ts";import type { JsonValue, ParseStreamOptions } from "./common.ts";import { parse } from "./_common.ts";
function isBlankChar(char: string | undefined) { return char !== undefined && [" ", "\t", "\r", "\n"].includes(char);}
const primitives = new Map( (["null", "true", "false"] as const).map((v) => [v[0], v]),);
export class ConcatenatedJsonParseStream implements TransformStream<string, JsonValue> { readonly writable: WritableStream<string>; readonly readable: ReadableStream<JsonValue>;
constructor({ writableStrategy, readableStrategy }: ParseStreamOptions = {}) { const { writable, readable } = toTransformStream( this.#concatenatedJSONIterator, writableStrategy, readableStrategy, ); this.writable = writable; this.readable = readable; }
async *#concatenatedJSONIterator(src: AsyncIterable<string>) { let targetString = ""; let hasValue = false; let nestCount = 0; let readingString = false; let escapeNext = false; let readingPrimitive: false | "null" | "true" | "false" = false; let positionInPrimitive = 0; for await (const string of src) { let sliceStart = 0; for (let i = 0; i < string.length; i++) { const char = string[i];
if (readingPrimitive) { if (char === readingPrimitive[positionInPrimitive]) { positionInPrimitive++;
if (positionInPrimitive === readingPrimitive.length) { yield parse(targetString + string.slice(sliceStart, i + 1)); hasValue = false; readingPrimitive = false; positionInPrimitive = 0; targetString = ""; sliceStart = i + 1; } } else { readingPrimitive = false; positionInPrimitive = 0; } continue; }
if (readingString) { if (char === '"' && !escapeNext) { readingString = false;
if (nestCount === 0 && hasValue) { yield parse(targetString + string.slice(sliceStart, i + 1)); hasValue = false; targetString = ""; sliceStart = i + 1; } } escapeNext = !escapeNext && char === "\\"; continue; }
if ( hasValue && nestCount === 0 && (char === "{" || char === "[" || char === '"' || char === " " || char === "n" || char === "t" || char === "f") ) { yield parse(targetString + string.slice(sliceStart, i)); hasValue = false; readingString = false; targetString = ""; sliceStart = i; i--; continue; }
switch (char) { case '"': readingString = true; escapeNext = false; break; case "{": case "[": nestCount++; break; case "}": case "]": nestCount--; break; }
if (nestCount === 0 && primitives.has(char)) { readingPrimitive = primitives.get(char)!; positionInPrimitive = 1; }
if ( hasValue && nestCount === 0 && (char === "}" || char === "]") ) { yield parse(targetString + string.slice(sliceStart, i + 1)); hasValue = false; targetString = ""; sliceStart = i + 1; continue; }
if (!hasValue && !isBlankChar(char)) { hasValue = true; } } targetString += string.slice(sliceStart); } if (hasValue) { yield parse(targetString); } }}