MCPcopy Create free account
hub / github.com/algolia/algoliasearch-client-javascript / iterLines

Function iterLines

packages/client-common/src/sse.ts:83–174  ·  view source on GitHub ↗

* Yields individual lines from a byte stream. * * Handles \r, \n, and \r\n line endings, including \r\n split across chunks. * Uses offset tracking within each decoded chunk to avoid O(n²) buffer growth. * Strips BOM (U+FEFF) from the very first line. * Throws if the internal line buffer exceed

(stream: ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>)

Source from the content-addressed store, hash-verified

81 * Throws if the internal line buffer exceeds 10MB.
82 */
83async function* iterLines(stream: ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>): AsyncGenerator<string> {
84 const decoder = new TextDecoder('utf-8');
85 const buffer: string[] = [];
86 let bufferSize = 0;
87 let trailingCR = false;
88 let isFirstLine = true;
89
90 for await (const chunk of toAsyncIterable(stream)) {
91 const text = decoder.decode(chunk, { stream: true });
92 let offset = 0;
93
94 // Handle \r\n split across chunks: if the previous chunk ended with \r
95 // and this one starts with \n, skip the \n (it's the second half of \r\n)
96 if (trailingCR) {
97 trailingCR = false;
98 if (text.length > 0 && text[0] === '\n') {
99 offset = 1;
100 }
101 }
102
103 while (offset < text.length) {
104 const crIdx = text.indexOf('\r', offset);
105 const lfIdx = text.indexOf('\n', offset);
106
107 // No more line endings in this chunk — buffer the rest
108 if (crIdx === -1 && lfIdx === -1) {
109 const remaining = text.slice(offset);
110 buffer.push(remaining);
111 bufferSize += remaining.length;
112 if (bufferSize > MAX_LINE_BUFFER_SIZE) {
113 throw new Error('SSE line buffer exceeded 10MB');
114 }
115 break;
116 }
117
118 let endIdx: number;
119 let skipLen: number;
120
121 if (crIdx !== -1 && (lfIdx === -1 || crIdx < lfIdx)) {
122 // \r found before \n (or no \n at all)
123 endIdx = crIdx;
124 if (crIdx + 1 < text.length) {
125 // Peek ahead: \r\n or bare \r
126 skipLen = text[crIdx + 1] === '\n' ? 2 : 1;
127 } else {
128 // \r at end of chunk — might be \r\n split across chunks
129 trailingCR = true;
130 skipLen = 1;
131 }
132 } else {
133 // \n found before \r (or no \r at all)
134 // Safe: at least one of crIdx/lfIdx is != -1, and we're in the else
135 // branch, so lfIdx must be != -1
136 endIdx = lfIdx;
137 skipLen = 1;
138 }
139
140 const segment = text.slice(offset, endIdx);

Callers 1

iterSSEEventsFunction · 0.85

Calls 2

toAsyncIterableFunction · 0.85
decodeMethod · 0.80

Tested by

no test coverage detected