{"version":3,"file":"jsonl.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/jsonl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG5C;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAExD;AAED,MAAM,WAAW,sBAAsB;IACtC;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACpC,MAAM,EAAE,QAAQ,EAChB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EAC9B,OAAO,GAAE,sBAA2B,GAClC,MAAM,IAAI,CA+EZ","sourcesContent":["import type { Readable } from \"node:stream\";\nimport { StringDecoder } from \"node:string_decoder\";\n\n/**\n * Serialize a single strict JSONL record.\n *\n * Framing is LF-only. Payload strings may contain other Unicode separators such as\n * U+2028 and U+2029. Clients must split records on `\\n` only.\n */\nexport function serializeJsonLine(value: unknown): string {\n\treturn `${JSON.stringify(value)}\\n`;\n}\n\nexport interface JsonlLineReaderOptions {\n\t/**\n\t * Cap (in UTF-16 code units) on the un-terminated line buffer. A line that\n\t * grows past this without a newline is dropped in its entirety (the rest of\n\t * it is skipped up to the next newline) instead of being buffered without\n\t * bound — a misbehaving writer must not be able to OOM the reader. Omit for\n\t * an unbounded buffer (trusted peers, e.g. the RPC channel).\n\t */\n\tmaxBuffer?: number;\n}\n\n/**\n * Attach an LF-only JSONL reader to a stream.\n *\n * This intentionally does not use Node readline. Readline splits on additional\n * Unicode separators that are valid inside JSON strings and therefore does not\n * implement strict JSONL framing.\n *\n * The reader detaches itself on stream `error` (which would otherwise crash the\n * process as an unhandled 'error' event) and flushes + detaches on `close`, so\n * a child that dies mid-line cannot leak listeners or buffered data. The\n * returned function detaches manually.\n */\nexport function attachJsonlLineReader(\n\tstream: Readable,\n\tonLine: (line: string) => void,\n\toptions: JsonlLineReaderOptions = {},\n): () => void {\n\tconst decoder = new StringDecoder(\"utf8\");\n\tconst maxBuffer = options.maxBuffer;\n\tlet buffer = \"\";\n\t/** True while skipping the remainder of a line that overflowed maxBuffer. */\n\tlet discarding = false;\n\tlet flushed = false;\n\tlet detached = false;\n\n\tconst emitLine = (line: string) => {\n\t\tonLine(line.endsWith(\"\\r\") ? line.slice(0, -1) : line);\n\t};\n\n\tconst onData = (chunk: string | Buffer) => {\n\t\tbuffer += typeof chunk === \"string\" ? chunk : decoder.write(chunk);\n\n\t\twhile (true) {\n\t\t\tconst newlineIndex = buffer.indexOf(\"\\n\");\n\t\t\tif (newlineIndex === -1) {\n\t\t\t\tif (maxBuffer !== undefined && buffer.length > maxBuffer) {\n\t\t\t\t\tbuffer = \"\";\n\t\t\t\t\tdiscarding = true;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst line = buffer.slice(0, newlineIndex);\n\t\t\tbuffer = buffer.slice(newlineIndex + 1);\n\t\t\tif (discarding) {\n\t\t\t\t// The extracted piece is the tail of an oversized, already-dropped line.\n\t\t\t\tdiscarding = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\temitLine(line);\n\t\t}\n\t};\n\n\tconst flush = () => {\n\t\tif (flushed) return;\n\t\tflushed = true;\n\t\tbuffer += decoder.end();\n\t\tif (buffer.length > 0 && !discarding) {\n\t\t\temitLine(buffer);\n\t\t}\n\t\tbuffer = \"\";\n\t};\n\n\tconst detach = () => {\n\t\tif (detached) return;\n\t\tdetached = true;\n\t\tstream.off(\"data\", onData);\n\t\tstream.off(\"end\", onEnd);\n\t\tstream.off(\"close\", onClose);\n\t\tstream.off(\"error\", onError);\n\t\tbuffer = \"\";\n\t};\n\n\tconst onEnd = () => {\n\t\tflush();\n\t};\n\n\tconst onClose = () => {\n\t\tflush();\n\t\tdetach();\n\t};\n\n\tconst onError = () => {\n\t\t// The stream is dead; drop partial data and release the listeners. Having\n\t\t// this handler also keeps a stream 'error' from becoming an uncaught\n\t\t// exception in the parent.\n\t\tdetach();\n\t};\n\n\tstream.on(\"data\", onData);\n\tstream.on(\"end\", onEnd);\n\tstream.on(\"close\", onClose);\n\tstream.on(\"error\", onError);\n\n\treturn detach;\n}\n"]}