UNPKG

newline-async-iterator

Version:

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

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