UNPKG

newline-async-iterator

Version:

Line-by-line async iterator for the browser and node

1 lines 9.51 kB
{"version":3,"file":"newline-async-iterator.min.cjs","sources":["../../src/decodeUTF8.ts","../../src/compat.ts","../../src/index.ts"],"sourcesContent":["import { uint8ArrayToArray } from './compat.ts';\n\n/**\n * Get the expected length of a UTF-8 sequence from its first byte\n */\nfunction getUTF8SequenceLength(byte: number): number {\n if (byte < 0x80) return 1; // 0xxxxxxx - ASCII\n if (byte < 0xc0) return 0; // 10xxxxxx - continuation byte (invalid as start)\n if (byte < 0xe0) return 2; // 110xxxxx\n if (byte < 0xf0) return 3; // 1110xxxx\n if (byte < 0xf8) return 4; // 11110xxx\n return 0; // Invalid UTF-8 start byte\n}\n\n/**\n * Decode a single UTF-8 code point from bytes\n */\nfunction decodeCodePoint(bytes: number[], start: number, length: number): string {\n let codePoint: number;\n\n switch (length) {\n case 1:\n codePoint = bytes[start];\n break;\n case 2:\n codePoint = ((bytes[start] & 0x1f) << 6) | (bytes[start + 1] & 0x3f);\n break;\n case 3:\n codePoint = ((bytes[start] & 0x0f) << 12) | ((bytes[start + 1] & 0x3f) << 6) | (bytes[start + 2] & 0x3f);\n break;\n case 4:\n codePoint = ((bytes[start] & 0x07) << 18) | ((bytes[start + 1] & 0x3f) << 12) | ((bytes[start + 2] & 0x3f) << 6) | (bytes[start + 3] & 0x3f);\n break;\n default:\n return '\\ufffd'; // Replacement character for invalid sequences\n }\n\n // Handle code points outside BMP (need surrogate pairs in JavaScript)\n if (codePoint > 0xffff) {\n // Convert to surrogate pair\n codePoint -= 0x10000;\n return String.fromCharCode(0xd800 + (codePoint >> 10), 0xdc00 + (codePoint & 0x3ff));\n }\n\n return String.fromCharCode(codePoint);\n}\n\n/**\n * Create a fallback UTF-8 streaming decoder with its own state\n */\nfunction createFallbackDecoder(): (uint8Array: Uint8Array) => string {\n let pendingBytes: number[] = [];\n\n return function decode(uint8Array: Uint8Array): string {\n // Combine pending bytes with new input\n const inputBytes = uint8ArrayToArray(uint8Array);\n const bytes = pendingBytes.length > 0 ? pendingBytes.concat(inputBytes) : inputBytes;\n pendingBytes = [];\n\n let result = '';\n let i = 0;\n\n while (i < bytes.length) {\n const byte = bytes[i];\n const sequenceLength = getUTF8SequenceLength(byte);\n\n if (sequenceLength === 0) {\n // Invalid start byte or continuation byte - emit replacement character\n result += '\\ufffd';\n i++;\n continue;\n }\n\n if (i + sequenceLength > bytes.length) {\n // Incomplete sequence - save for next chunk\n pendingBytes = bytes.slice(i);\n break;\n }\n\n result += decodeCodePoint(bytes, i, sequenceLength);\n i += sequenceLength;\n }\n\n return result;\n };\n}\n\n/**\n * Create a TextDecoder-based streaming decoder\n */\nfunction createTextDecoderDecoder(): (uint8Array: Uint8Array) => string {\n const decoder = new TextDecoder('utf8');\n return function decode(uint8Array: Uint8Array): string {\n return decoder.decode(uint8Array, { stream: true });\n };\n}\n\n/**\n * Create a new UTF-8 streaming decoder instance.\n * Each decoder maintains its own state for handling multi-byte\n * characters that span chunk boundaries.\n */\nexport default function createUTF8Decoder(): (uint8Array: Uint8Array) => string {\n if (typeof TextDecoder !== 'undefined') {\n return createTextDecoderDecoder();\n }\n return createFallbackDecoder();\n}\n","// Feature detection - no global modifications per polyfill-removal plans\n\n/**\n * Convert a Uint8Array to a number array\n * Compatible with Node 0.8+ (no Array.from)\n */\nexport function uint8ArrayToArray(uint8Array: Uint8Array): number[] {\n if (typeof Array.from === 'function') return Array.from(uint8Array);\n\n // Fallback for old environments without Array.from\n const arr: number[] = [];\n for (let i = 0; i < uint8Array.length; i++) {\n arr.push(uint8Array[i]);\n }\n return arr;\n}\n","import createUTF8Decoder from './decodeUTF8.ts';\n\nconst REGEX_NEW_LINE = /\\r?\\n|\\r/g;\n\nconst root = typeof window === 'undefined' ? global : window;\n// biome-ignore lint/suspicious/noShadowRestrictedNames: Legacy\nconst Symbol: SymbolConstructor = typeof root.Symbol === 'undefined' ? ({ asyncIterator: undefined } as unknown as SymbolConstructor) : root.Symbol;\n\n/**\n * Create a newline iterator recognizing CR, LF, and CRLF using the Symbol.asyncIterator interface\n *\n * @param source The async iterable of Uint8Array chunks to iterate through\n *\n * ```typescript\n * import newlineIterator from \"newline-async-iterator\";\n *\n * const iterator = newlineIterator(readableStream);\n * const results = [];\n * for await (const line of iterator) results.push(line);\n * console.log(results); // [\"some\", \"string\", \"combination\"];\n * ```\n */\n\nexport default function newlineIterator(source: AsyncIterable<Uint8Array> | AsyncIterator<Uint8Array>): AsyncIterableIterator<string> {\n const decodeUTF8 = createUTF8Decoder();\n const lines = [];\n let last = '';\n let done = false;\n\n const sourceIterator = Symbol.asyncIterator ? source[Symbol.asyncIterator]() : source;\n\n function generateNext(): Promise<IteratorResult<string, boolean>> {\n return new Promise((resolve, reject) => {\n sourceIterator.next().then((next) => {\n if (next.done) done = true;\n else last += decodeUTF8(next.value);\n\n const end = last.length > 0 ? last[last.length - 1] : '';\n if (done || (end !== '\\r' && end !== '\\n')) {\n const moreLines = last.split(REGEX_NEW_LINE);\n last = moreLines.pop();\n moreLines.forEach((line) => {\n lines.unshift(line);\n });\n if (done && last.length > 0) {\n lines.unshift(last);\n last = '';\n }\n }\n\n if (lines.length > 0) {\n const value = lines.pop();\n if (done && lines.length === 0 && value.length === 0) return resolve({ value: null, done: true });\n return resolve({ value, done: false });\n }\n if (done) return resolve({ value: null, done: true });\n generateNext().then(resolve).catch(reject); // get more\n });\n });\n }\n\n const iterator = {\n next(): Promise<IteratorResult<string, boolean>> {\n return generateNext();\n },\n [Symbol.asyncIterator](): AsyncIterator<string> {\n return this;\n },\n };\n\n return iterator as AsyncIterableIterator<string>;\n}\n"],"names":["getUTF8SequenceLength","byte","decodeCodePoint","bytes","start","length","codePoint","String","fromCharCode","createFallbackDecoder","pendingBytes","uint8Array","inputBytes","Array","from","arr","i","push","uint8ArrayToArray","concat","result","sequenceLength","slice","createUTF8Decoder","TextDecoder","decoder","decode","stream","REGEX_NEW_LINE","root","window","global","Symbol","asyncIterator","undefined","source","decodeUTF8","lines","last","done","sourceIterator","generateNext","Promise","resolve","reject","next","then","value","end","moreLines","split","pop","forEach","line","unshift","catch","this"],"mappings":"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"}