{"slug": "nozzle-a-streaming-library-that-emits-when-possible-and-buffers-where-necessary", "title": "Nozzle: A streaming library that emits when possible and buffers where necessary", "summary": "Nozzle, a new TypeScript library published to npm as nozzle-js, provides streaming text utilities for LLM output that buffer only where necessary and emit chunks as soon as possible, such as holding a potential regex match only until it is confirmed or rejected. The library accepts any sync or async iterable and ships functions grouped into regex (parse, scan, match, replace), splitting (split, splitAfter, splitBefore, after, before, chunk), timing (minInterval, throttle), buffering (batch, buffer, withContext, aperture) and conversion (consume, toReadableStream, fromList, mapReturn) categories, with documented integrations for the Vercel AI SDK, OpenAI, Anthropic and fetch streams. Nozzle's pipelines are lazy until iterated with for await or .consume(), and breaking out of a loop early closes upstream iterators, though cancelling in-flight I/O remains the source provider's responsibility.", "body_md": "**A text streaming library that emits when possible and buffers where necessary.**\n\n[Quickstart](#quickstart)•\n\n[Recipes](#recipes)•\n\n[API](#api-at-a-glance)•\n\n[Guarantees](#guarantees)•\n\n[Reference](#reference)\n\nNozzle is a small TypeScript library for handling text streamed from LLMs. Its functions buffer only where necessary and otherwise emit chunks as soon as possible. For example, regexes buffer a potential match only until it's confirmed or rejected, and emit everything around it immediately.\n\n```\nnpm i nozzle-js # or pnpm / bun / yarn\njs\nimport { nz } from \"nozzle-js\"\n\nconst parts = nz(textStream).parse(/img-(\\w+)/g, (m) => ({ src: m[1] }))\n\nfor await (const part of parts) console.log(part)\n\n/*\n * in:   \"Here is im\"  \"g-abc\"  \"123 and im\"                \"g-xyz\"  \"789.\"\n * out:  \"Here is \"             { src: \"abc123\" }  \" and \"           { src: \"xyz789\" }  \".\"\n */\n```\n\n`nz()` accepts any sync or async iterable.\n\n## Getting a text stream from your provider\n\n```\n// Vercel AI SDK\nconst { textStream } = streamText({ model, prompt })\nnz(textStream)\n\n// OpenAI\nconst stream = await openai.chat.completions.create({ ...args, stream: true })\nnz(stream).map((chunk) => chunk.choices[0]?.delta?.content ?? \"\")\n\n// Anthropic\nconst stream = anthropic.messages.stream({ ...args })\nnz(stream).map((e) => (e.type === \"content_block_delta\" && e.delta.type === \"text_delta\" ? e.delta.text : \"\"))\n\n// fetch\nconst res = await fetch(url)\nnz(res.body!.pipeThrough(new TextDecoderStream()))\n```\n\nStream just the code block to a preview while the model is still writing, then save it.\n\n``` js\nconst code = await nz(stream)\n  .after(\"``` ts\\n\")\n  .before(\"```\")\n  .tap(sendToPreview)\n  .consume()\n\nawait saveSnippet(code.string())\n\n/*\n * in:   \"Sure!\\n``\"  \"` ts\\nconst x = \"  \"await f()\\n\"  \"return x\\n``\"  \"`\\nDone!\"\n * out:               \"const x = \"       \"await f()\\n\"  \"return x\\n\"\n */\n```\n\nProviders often deliver text in bursts. Re-chunk into words and release them at a steady pace.\n\n``` js\nconst smooth = nz(stream)\n  .splitAfter(\" \")\n  .compact()\n  .minInterval(40)\n\n/*\n * in:   The quick ...... brown fox jumps over ................ the lazy dog.\n * out:  The .. quick .. brown .. fox .. jumps .. over .. the .. lazy .. dog.\n */\n```\n\n- Pipelines are lazy: nothing runs until you `for await` or`.consume()` . Generator sources are single-use.\n- Breaking out of a loop early closes upstream iterators. Cancelling in-flight I/O is up to the source/provider.\n- Errors from the source, including errors raised in timers, surface where you `await` , so`try/catch` works.\n- Generator return values pass through transforms (see `mapReturn` ,`consume().return()` ).\n\n| Group | Functions | \n|---|---|\n| **Regex** | [`parse`](#parse) ·[` scan`](#scan) ·[` match`](#match) ·[` replace`](#replace) | \n| **Splitting** | [`split`](#split) ·[` splitAfter`](#splitafter) ·[` splitBefore`](#splitbefore) ·[` after`](#after) ·[` before`](#before) ·[` chunk`](#chunk) | \n| **Timing** | [`minInterval`](#mininterval) ·[` throttle`](#throttle) | \n| **Elements** | [`map`](#map) ·[` asyncMap`](#asyncmap) ·[` filter`](#filter) ·[` find`](#find) | \n| **Transformation** | [`flatMap`](#flatmap) ·[` flatten`](#flatten) | \n| **Filtering** | [`compact`](#compact) ·[` takeWhile`](#takewhile) ·[` takeUntil`](#takeuntil) | \n| **Indexing** | [`first`](#first) ·[` last`](#last) ·[` at`](#at) ·[` head`](#head) ·[` tail`](#tail) ·[` initial`](#initial) ·[` slice`](#slice) | \n| **Accumulation** | [`accumulate`](#accumulate) ·[` diff`](#diff) ·[` reduce`](#reduce) | \n| **Buffering** | [`batch`](#batch) ·[` buffer`](#buffer) ·[` withContext`](#withcontext) ·[` aperture`](#aperture) | \n| **Side effects** | [`tap`](#tap) ·[` tee`](#tee) | \n| **Errors** | [`recover`](#recover) ·[` wrap`](#wrap) ·[` unwrap`](#unwrap) | \n| **Conversion** | [`consume`](#consume) ·[` toReadableStream`](#toreadablestream) ·[` fromList`](#fromlist) ·[` mapReturn`](#mapreturn) | \n\nThese functions use JavaScript regular expressions to search, match, and transform streaming text.\n\nNozzle handles matching patterns across chunk boundaries by buffering text internally. Non-matching text is yielded as soon as it's certain not to be part of a match, while potential matches are held back until confirmed.\n\nThese features throw an error because they cannot work reliably with streaming:\n\n| Feature | Example | Why | \n|---|---|---|\n| Lookaheads | `(?=...)` ,`(?!...)` | Content to look ahead may not have arrived yet | \n| Lookbehinds | `(?<=...)` ,`(?<!...)` | Content to look behind may have already been yielded | \n| Backreferences | `\\1` ,`\\k<name>` | Referenced group may span chunks or be partially buffered | \n| Multiline mode | `/pattern/m` | `^` /`$` would behave inconsistently at arbitrary chunk boundaries | \n| Anchors | `^` ,`$` | The start and end of the stream are not known mid-stream; use `before` /`after` | \n| Word boundaries | `\\b` ,`\\B` | The neighboring character may be in a chunk that has not arrived | \n| Unicode escapes | `\\uXXXX` ,`\\u{...}` | Not analyzed for partial matches; use literal characters | \n| Sticky and Unicode-sets flags | `/pattern/y` ,`/pattern/v` | Not supported by the partial matcher | \n| Empty matches | `/x?/` ,`/a{0,}/` | A pattern that can match nothing would match between every character | \n\nSome patterns force nozzle to buffer text longer than you might expect:\n\n- \n**Open-ended quantifiers at pattern end** :`/hello.+/g` buffers everything after \"hello\" until the stream ends—`.+` can always match more. Use a delimiter instead:`/hello[^!]+/g` matches until`!` , allowing earlier output.\n- \n**Alternations with shared prefixes** :`/cat|caterpillar/g` buffers \"cat\" until enough text arrives to rule out \"caterpillar\". Put longer alternatives first:`/caterpillar|cat/g` .\n- \n**Optional suffixes** :`/items?/g` buffers \"item\" to check for a trailing \"s\". This is usually fine, but`/data.*?end/g` buffers from \"data\" until \"end\" appears.\n\n- **Global (`/pattern/g`)** : Finds all matches throughout the stream\n- **Non-global (`/pattern/`)** : Finds only the first match, then passes remaining text through unchanged\n\n``` js\nnz([\"a\", \"b\", \"b\", \"a\"]).match(/a([ab]*)a/g) // => [\"abba\", \"bb\"] (match arrays with capture groups)\n```\n\nExtracts matches of a regex pattern from the input stream.\n\nUses earliestPossibleMatchIndex to efficiently skip tokens as soon as we know they don't match the regex, while holding back potential matches until we can determine if they match.\n\nBuilt on: `scan(input, regex).filter(x => 'match' in x).map(x => x.match)`\n\n## Details\n\n```\nfunction match<R = any>(input: AsyncIterable<string, R>, regex: RegExp): AsyncGenerator<RegExpExecArray, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `input` | AsyncIterable<string, R> | An asynchronous iterable of strings. | \n| `regex` | RegExp | The regular expression pattern to match. | \n\n```\n// Extract UUIDs from text\nnz([\"Now I'm taking uuid-asdf-flkj and adding it to uuid-fslkj-alkjlsf.\"])\n  .parse(/uuid-(?<id>\\w+)-\\w+/g, m => ({ id: m.groups!.id }))\n// yields: \"Now I'm taking \", { id: \"asdf\" }, \" and adding it to \", { id: \"fslkj\" }, \".\"\n\n// Parse numbers from text\nnz([\"The answer is 42 and also 123\"])\n  .parse(/\\d+/g, m => parseInt(m[0], 10))\n// yields: \"The answer is \", 42, \" and also \", 123\n```\n\nParses input for regex matches, yielding text as-is and transforming matches.\n\nThis transform is useful for extracting structured data from text streams. Non-matching text is passed through as strings, while matches are transformed using the provided function.\n\n## Details\n\n```\nfunction parse<T, R = any>(input: AsyncIterable<string, R>, regex: RegExp, transform: (match: RegExpExecArray) => T): AsyncGenerator<string | T, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `input` | AsyncIterable<string, R> | An asynchronous iterable of strings. | \n| `regex` | RegExp | The regular expression pattern to match. | \n| `transform` | (match: RegExpExecArray) => T | A function that transforms each match into a desired type. | \n\n``` js\nnz([\"a\", \"b\", \"b\", \"a\"]).replace(/a[ab]*a/g, \"X\") // => \"X\"\n```\n\nReplaces matches of a regex pattern with a replacement string in the input stream.\n\nUses earliestPossibleMatchIndex to efficiently yield tokens as soon as we know they don't match the regex, while holding back potential matches until we can determine if they should be replaced.\n\nBuilt on: `scan(input, regex).map(x => 'text' in x ? x.text : x.match[0].replace(regex, replacement))`\n\n## Details\n\n```\nfunction replace<R = any>(input: AsyncIterable<string, R>, regex: RegExp, replacement: string): AsyncGenerator<string, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `input` | AsyncIterable<string, R> | An asynchronous iterable of strings. | \n| `regex` | RegExp | The regular expression pattern to match. | \n| `replacement` | string | The string to replace matches with. | \n\n```\nnz([\"hello world\"]).scan(/\\w+/g)\n// yields: { match: [...] }, { text: \" \" }, { match: [...] }\n\nnz([\"Now I'm taking uuid-asdf-flkj...\"]).scan(/uuid-(\\w+)-\\w+/g)\n// yields: { text: \"Now I'm taking \" }, { match: [...] }, { text: \"...\" }\n```\n\nScans input for regex matches, yielding interleaved text and match results.\n\nThis is the foundational regex transform that other transforms build on. It efficiently yields non-matching text as soon as we're certain it can't match, while holding back potential matches until we can determine their boundaries.\n\nNote: Empty text strings are never yielded.\n\n## Details\n\n```\nfunction scan<R = any>(input: AsyncIterable<string, R>, regex: RegExp): AsyncGenerator<ScanResult, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `input` | AsyncIterable<string, R> | An asynchronous iterable of strings. | \n| `regex` | RegExp | The regular expression pattern to match. | \n\n``` js\nnz([\"a\", \"b\", \"c\", \"d\", \"e\"]).after(/bc/) // => \"d\", \"e\"\n```\n\nEmit everything **after** the accumulated prefix that matches `pattern`.\n\nBuilt on: `scan(source, regex)` skipping until first match, then yielding everything after\n\n## Details\n\n```\nfunction after<R = any>(source: StringIterable<R>, pattern: string | RegExp): AsyncGenerator<string, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `source` | StringIterable<R> | stream or iterable to scan | \n| `pattern` | string \\| RegExp | first `RegExp` that marks the cut-off | \n\n``` js\nnz([\"a\", \"b\", \"c\", \"d\", \"e\"]).before(\"cd\") // => \"a\", \"b\"\n```\n\nEmit everything **before** the accumulated prefix that contains `separator`.\n\nBuilt on: `scan(source, regex)` taking text until first match\n\nEarly termination may return undefined instead of the source's final value.\n\n## Details\n\n```\nfunction before<R = any>(source: StringIterable<R>, separator: string | RegExp): AsyncGenerator<string, R | undefined, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `source` | StringIterable<R> | stream or iterable to scan | \n| `separator` | string \\| RegExp | string that marks the cut-off | \n\n``` js\nnz([\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"]).chunk(3) // => \"abc\", \"def\"\n```\n\nGroups input tokens into chunks of the specified size and yields the joined result. Takes N input items and yields N/size output items, where each output is the concatenation of size input items.\n\n## Details\n\n```\nfunction chunk<R = any>(source: AsyncIterable<string, R>, size: number): AsyncGenerator<string, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `source` | AsyncIterable<string, R> | The async iterable source of strings (tokens). | \n| `size` | number | The number of input tokens to group together in each output chunk. | \n\n``` js\nnz([\"hello,world,test\"]).split(\",\") // => \"hello\", \"world\", \"test\"\n```\n\nTakes incoming chunks, merges them, and then splits them by a string separator.\n\nBuilt on: `scan(source, regex)` accumulating text between matches\n\n## Details\n\n```\nfunction split<R = any>(source: AsyncIterable<string, R>, separator: string | RegExp): AsyncGenerator<string, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `source` | AsyncIterable<string, R> | The async iterable source of strings. | \n| `separator` | string \\| RegExp | The string separator to split by. | \n\n``` js\nnz([\"hello,world,test\"]).splitAfter(\",\") // => \"hello,\", \"world,\", \"test\"\n```\n\nTakes incoming chunks, merges them, and then splits them by a string separator, keeping the separator at the end of each part (except the last).\n\nBuilt on: `scan(source, regex)` with separator appended to each segment\n\n## Details\n\n```\nfunction splitAfter<R = any>(source: AsyncIterable<string, R>, separator: string | RegExp): AsyncGenerator<string, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `source` | AsyncIterable<string, R> | The async iterable source of strings. | \n| `separator` | string \\| RegExp | The string separator to split by. | \n\n``` js\nnz([\"hello,world,test\"]).splitBefore(\",\") // => \"hello\", \",world\", \",test\"\n```\n\nTakes incoming chunks, merges them, and then splits them by a string separator, keeping the separator at the beginning of each part (except the first).\n\nBuilt on: `scan(source, regex)` with separator prepended to each segment after first\n\n## Details\n\n```\nfunction splitBefore<R = any>(source: AsyncIterable<string, R>, separator: string | RegExp): AsyncGenerator<string, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `source` | AsyncIterable<string, R> | The async iterable source of strings. | \n| `separator` | string \\| RegExp | The string separator to split by. | \n\n``` js\nnz([\"a\", \"b\", \"c\"]).minInterval(100) // => \"a\" (0ms), \"b\" (100ms), \"c\" (200ms)\n```\n\nEnforces a minimum delay between adjacent tokens in a stream. The first token is yielded immediately, then subsequent tokens are delayed to ensure at least `delayMs` milliseconds pass between each yield.\n\n## Details\n\n```\nfunction minInterval<T, R = any>(source: AsyncIterable<T, R>, delayMs: number): AsyncGenerator<T, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `source` | AsyncIterable<T, R> | The async iterable source of tokens. | \n| `delayMs` | number | The minimum delay in milliseconds between adjacent tokens. | \n\n``` js\nnz([\"a\", \"b\", \"c\", \"d\"]).throttle(100, chunks => chunks.join(\"\")) // => \"abcd\" (100ms)\n```\n\nBatches values into time windows, then yields the merged batch. The first value opens a window lasting intervalMs; it is not emitted immediately. Values arriving within that window are merged and emitted when its timer expires. After an idle period, the next value opens a new window. A final partial batch waits for its existing timer. An interval of zero passes values through unchanged. Source errors are surfaced immediately after already-emitted batches; an unfinished batch is discarded. The source is consumed eagerly, so slow consumers can build up an unbounded queue of merged batches.\n\n## Details\n\n```\nfunction throttle<T, R = any>(source: AsyncIterable<T, R>, intervalMs: number, merge: (values: T[]) => T): AsyncGenerator<T, R>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `source` | AsyncIterable<T, R> | The async iterable source of values. | \n| `intervalMs` | number | The throttling interval in milliseconds. | \n| `merge` | (values: T[]) => T | - | \n\n``` js\nnz([\"hello\", \"world\"]).asyncMap(async x => x.toUpperCase()) // => \"HELLO\", \"WORLD\"\nnz([\"api/users\", \"api/posts\"]).asyncMap(async url => fetch(url).then(r => r.json())) // => [userData], [postsData]\nnz(urls).asyncMap(fetchJson, { concurrency: 4 })\n```\n\nTransforms each value from the input stream using the provided async function. Starts the async function for each item as soon as it comes off the source iterator, up to the configured concurrency limit. Results are yielded in source order, not completion order, so a later item can finish first but will not be yielded or thrown until all earlier items have settled. Concurrency limits active calls, not queued results. Slow consumers can cause unbounded buffering. Cancellation does not abort already-started mapper calls.\n\n## Details\n\n```\nfunction asyncMap<T, U, R = any>(iterator: AsyncIterable<T, R>, fn: (value: T) => Promise<U>, options?: AsyncMapOptions): AsyncGenerator<U, R>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<T, R> | An asynchronous iterable of strings. | \n| `fn` | (value: T) => Promise<U> | An async function that transforms each string value. | \n| `options` | AsyncMapOptions | Optional configuration. | \n\n``` js\nnz([\"Hello\", \"Hi\", \"World\"]).filter(chunk => chunk.length > 4) // => \"Hello\", \"World\"\n```\n\nFilters the input stream based on a predicate function.\n\n## Details\n\n```\nfunction filter<T, R = any>(iterator: AsyncIterable<T, R>, predicate: (chunk: T) => boolean): AsyncGenerator<T, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<T, R> | An asynchronous iterable of strings. | \n| `predicate` | (chunk: T) => boolean | A function that returns true for items to keep. | \n\n``` js\nawait nz([\"apple\", \"banana\", \"cherry\"]).find(chunk => chunk.startsWith(\"b\")) // => \"banana\"\n```\n\nFinds the first value from the input stream that matches the predicate.\n\n## Details\n\n``` js\nfunction find<T>(iterator: AsyncIterable<T>, predicate: (chunk: T) => boolean): Promise<T | undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<T> | An asynchronous iterable of values. | \n| `predicate` | (chunk: T) => boolean | A function that returns true for the item to find. | \n\n``` js\nnz([\"hello\", \"world\"]).map(x => x.toUpperCase()) // => \"HELLO\", \"WORLD\"\n```\n\nTransforms each value from the input stream using the provided function.\n\n## Details\n\n``` js\nfunction map<T, U, R = any>(iterator: AsyncIterable<T, R>, fn: (value: T) => U): AsyncGenerator<U, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<T, R> | An asynchronous iterable of strings. | \n| `fn` | (value: T) => U | A function that transforms each string value. | \n\n``` js\nnz([\"hi\", \"ok\"]).flatMap(word => word.split(\"\")) // => \"h\", \"i\", \"o\", \"k\"\nnz([1, 2, 3]).flatMap(n => Array(n).fill(n)) // => 1, 2, 2, 3, 3, 3\n```\n\nTransforms each value from the input stream into zero or more output values.\n\n## Details\n\n``` js\nfunction flatMap<T, U, R = any>(source: AsyncIterable<T, R>, fn: (value: T) => Iterable<U, any, any> | AsyncIterable<U, any, any>): AsyncGenerator<U, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `source` | AsyncIterable<T, R> | An asynchronous iterable of values. | \n| `fn` | (value: T) => Iterable<U, any, any> \\| AsyncIterable<U, any, any> | A function that returns sync or async iterable values for each input. | \n\n``` js\nnz([[\"a\", \"b\"], [\"c\", \"d\"], [\"e\"]]).flatten() // => \"a\", \"b\", \"c\", \"d\", \"e\"\n```\n\nFlattens nested arrays or iterables into a single stream.\n\n## Details\n\n```\nfunction flatten<T, R = any>(src: Iterable<Iterable<T> | T[], R>): AsyncGenerator<T, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `src` | Iterable<Iterable<T> \\| T[], R> | The source iterable containing nested arrays or iterables. | \n\n``` js\nnz([\"Hello\", \"\", \"World\", \"\"]).compact() // => \"Hello\", \"World\"\n```\n\nFilters out empty strings from the input stream.\n\n## Details\n\n```\nfunction compact<R = any>(iterator: AsyncIterable<string, R>): AsyncGenerator<string, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<string, R> | An asynchronous iterable of strings. | \n\n``` js\nnz([1, 2, 3, 4]).takeUntil(n => n === 3) // => 1, 2\n```\n\nYields values until the predicate matches, excluding the matching value.\n\nEarly termination may return undefined instead of the source's final value.\n\n## Details\n\n```\nfunction takeUntil<T, R = any>(source: AsyncIterable<T, R>, predicate: (value: T) => boolean): AsyncGenerator<T, R | undefined, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `source` | AsyncIterable<T, R> | An asynchronous iterable of values. | \n| `predicate` | (value: T) => boolean | A function that returns true for the value that should stop the stream. | \n\n``` js\nnz([1, 2, 3, 1]).takeWhile(n => n < 3) // => 1, 2\n```\n\nYields values while the predicate matches, excluding the first non-matching value.\n\nEarly termination may return undefined instead of the source's final value.\n\n## Details\n\n```\nfunction takeWhile<T, R = any>(source: AsyncIterable<T, R>, predicate: (value: T) => boolean): AsyncGenerator<T, R | undefined, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `source` | AsyncIterable<T, R> | An asynchronous iterable of values. | \n| `predicate` | (value: T) => boolean | A function that returns true for values to keep. | \n\n``` js\nawait nz([\"a\", \"b\", \"c\", \"d\", \"e\"]).at(2) // => \"c\"\nawait nz([\"a\", \"b\", \"c\", \"d\", \"e\"]).at(-1) // => \"e\"\n```\n\nReturns the element at the specified index in the input stream. Supports negative indices to count from the end.\n\n## Details\n\n```\nfunction at<T>(iterator: AsyncIterable<T>, index: number): Promise<T | undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<T> | An asynchronous iterable of values. | \n| `index` | number | The index to access. Negative values count from the end. | \n\n``` js\nawait nz([\"Hello\", \"World\", \"!\"]).first() // => \"Hello\"\n```\n\nReturns the first value from the input stream.\n\n## Details\n\n```\nfunction first<T>(iterator: AsyncIterable<T>): Promise<T | undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<T> | An asynchronous iterable of values. | \n\n``` js\nnz([\"Hello\", \"World\", \"!\"]).head() // => \"Hello\"\n```\n\nYields only the first value from the input stream.\n\nEarly termination may return undefined instead of the source's final value.\n\n## Details\n\n```\nfunction head<T, R = any>(iterator: AsyncIterable<T, R>): AsyncGenerator<T, R | undefined, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<T, R> | An asynchronous iterable of values. | \n\nSee also: [`at`](#at), [` tail`](#tail), [` initial`](#initial), `last`\n\n``` js\nnz([\"Hello\", \"World\", \"!\"]).initial() // => \"Hello\", \"World\"\n```\n\nYields all values except the last from the input stream.\n\n## Details\n\n```\nfunction initial<T, R = any>(iterator: AsyncIterable<T, R>): AsyncGenerator<T, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<T, R> | An asynchronous iterable of values. | \n\n``` js\nawait nz([\"Hello\", \"World\", \"!\"]).last() // => \"!\"\n```\n\nReturns the last value from the input stream.\n\n## Details\n\n```\nfunction last<T>(iterator: AsyncIterable<T>): Promise<T | undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<T> | An asynchronous iterable of values. | \n\n``` js\nnz([\"a\", \"b\", \"c\", \"d\", \"e\"]).slice(1, 3) // => \"b\", \"c\"\nnz([\"a\", \"b\", \"c\", \"d\", \"e\"]).slice(-2) // => \"d\", \"e\"\n```\n\nYields a slice of the input stream between start and end indices. Supports negative indices by maintaining an internal buffer. A bounded slice may stop early and return undefined. Without an end, it preserves the source return value on completion.\n\n## Details\n\n```\nfunction slice<T, R = any>(iterator: AsyncIterable<T, R>, start: number, end?: undefined): AsyncGenerator<T, R, undefined>;\nfunction slice<T, R = any>(iterator: AsyncIterable<T, R>, start: number, end: number | undefined): AsyncGenerator<T, R | undefined, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<T, R> | The async iterable to slice | \n| `start` | number | Starting index (inclusive). Negative values count from end. | \n| `end` | number \\| undefined | Ending index (exclusive). Negative values count from end. If undefined, slices to end. | \n\n``` js\nnz([\"Hello\", \"World\", \"!\"]).tail() // => \"World\", \"!\"\n```\n\nYields all values except the first from the input stream.\n\n## Details\n\n```\nfunction tail<T, R = any>(iterator: AsyncIterable<T, R>): AsyncGenerator<T, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<T, R> | An asynchronous iterable of values. | \n\n``` js\nnz([\"This \", \"is \", \"a \", \"test!\"]).accumulate() // => \"This \", \"This is \", \"This is a \", \"This is a test!\"\n```\n\nYields a cumulative prefix of the input stream.\n\n## Details\n\n```\nfunction accumulate<R = any>(iterator: AsyncIterable<string, R>): AsyncGenerator<string, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<string, R> | An asynchronous iterable of strings. | \n\n``` js\nnz([\"This \", \"This is \", \"This is a \", \"This is a test!\"]).diff().value() // => \"This \", \"is \", \"a \", \"test!\"\n```\n\nYields the difference between the current and previous string in the input stream.\n\n## Details\n\n```\nfunction diff<R = any>(iterator: AsyncIterable<string, R>): AsyncGenerator<string, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<string, R> | An asynchronous iterable of strings. | \n\n``` js\nnz([1, 2, 3, 4]).reduce((acc, n) => acc + n, 0) // => 1, 3, 6, 10\n```\n\nYields progressive accumulated values using a reducer function.\n\n## Details\n\n```\nfunction reduce<T, A, R = any>(source: AsyncIterable<T, R>, reducer: (accumulator: A, current: T, index: number) => A, initial: A): AsyncGenerator<A, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `source` | AsyncIterable<T, R> | An asynchronous iterable of values. | \n| `reducer` | (accumulator: A, current: T, index: number) => A | A function that combines the accumulator with each value. | \n| `initial` | A | The initial accumulator value. | \n\n``` js\nnz([1, 2, 3, 4, 5]).aperture(3) // => [1, 2, 3], [2, 3, 4], [3, 4, 5]\n```\n\nCreates a sliding window of size n over the input stream, yielding arrays of consecutive elements.\n\n## Details\n\n```\nfunction aperture<T, R = any>(source: Iterable<T, R>, n: number): AsyncGenerator<T[], R | undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `source` | Iterable<T, R> | An iterable to create windows over. | \n| `n` | number | The size of each window. | \n\n``` js\nnz([1, 2, 3, 4, 5]).batch(2) // => [1, 2], [3, 4], [5]\nnz([\"a\", \"b\", \"c\"]).batch(2).map(xs => xs.join(\"\")) // => \"ab\", \"c\"\n```\n\nGroups input values into arrays of the specified size.\n\n## Details\n\n```\nfunction batch<T, R = any>(source: AsyncIterable<T, R>, size: number): AsyncGenerator<T[], R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `source` | AsyncIterable<T, R> | The async iterable source of values. | \n| `size` | number | The number of input values to include in each batch. | \n\n``` js\nnz([\"a\", \"b\", \"c\"]).buffer(2) // => \"a\", \"b\", \"c\"\n// While a slow consumer handles \"a\", the source is read ahead:\n// consumed: a, yielded: a, consumed: b, consumed: c, yielded: b, yielded: c\n```\n\nBuffers up to N items from the source iterator, consuming them eagerly and yielding them on demand. If n is undefined, buffers unlimited items.\n\nThe buffer() function \"slurps up\" as much of the input iterator as it can as fast as it can, storing items in an internal buffer. When items are requested from the buffer, they are yielded from this pre-filled buffer. This creates a decoupling between the consumption rate and the production rate.\n\n## Details\n\n```\nfunction buffer<T, R = any>(source: AsyncIterable<T, R>, n?: number): AsyncGenerator<T, R>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `source` | AsyncIterable<T, R> | The async iterable source of values. | \n| `n` | number | The maximum number of items to buffer. If undefined, buffers unlimited items. | \n\n```\nnz([1, 2, 3]).withContext({ before: 1, after: 1 })\n// { past: [], current: 1, upcoming: [2], index: 0 }\n// { past: [1], current: 2, upcoming: [3], index: 1 }\n// { past: [2], current: 3, upcoming: [], index: 2 }\n```\n\nYields each item with bounded neighboring context in source order. `before` and `after` default to zero and must be nonnegative safe integers. Lookahead delays emission until `after` items arrive or the source ends. Context is shorter at stream boundaries. Each output has independent arrays; the items themselves are shared. The source's return value is preserved.\n\n## Details\n\n```\nfunction withContext<T, R = any>(source: Iterable<T, R>, options: WithContextOptions): AsyncGenerator<ItemContext<T>, R>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `source` | Iterable<T, R> | - | \n| `options` | WithContextOptions | - | \n\n``` js\nnz([\"Hello\", \"World\", \"!\"]).tap(x => console.log(`yielded: ${x}`)) // => \"Hello\", \"World\", \"!\" (logs: yielded: Hello, yielded: World, yielded: !)\n```\n\nExecutes a side effect for each value without modifying the stream.\n\n## Details\n\n``` js\nfunction tap<T, R = any>(iterator: AsyncIterable<T, R>, fn: (value: T) => void): AsyncGenerator<T, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<T, R> | An asynchronous iterable of strings. | \n| `fn` | (value: T) => void | A function to execute for each value. | \n\n``` js\nconst [stream1, stream2] = nz([\"a\", \"b\", \"c\"]).tee(2) // => Two independent streams of \"a\", \"b\", \"c\"\n```\n\nSplits a single iterator into N independent iterables. Starts reading eagerly on the first branch read. Branch queues are unbounded; consume every branch or close unused branches with iterator.return().\n\n## Details\n\n```\nfunction tee<T, R = any>(iterator: AsyncIterator<T, R>, n: number): AsyncGenerator<T, R, any>[];\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterator<T, R> | The source async iterator to split. | \n| `n` | number | Number of independent iterables to create. | \n\n``` js\nnz(stream).recover(() => [\"[stream failed]\"])\nnz(stream).recover(() => []) // swallow the error and end\n```\n\nCatches an upstream error and optionally yields replacement values.\n\n## Details\n\n```\nfunction recover<T, R = any>(source: AsyncIterable<T, R>, handler: (error: unknown) => RecoverResult<T>): AsyncGenerator<T, R | undefined, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `source` | AsyncIterable<T, R> | An asynchronous iterable of values. | \n| `handler` | (error: unknown) => RecoverResult<T> | A function that returns replacement values, or nothing to end the stream. | \n\n``` js\nnz([\"hello\", \"world\"]).wrap().unwrap() // => \"hello\", \"world\"\n```\n\nUnwraps results from wrap() back into a normal iterator that throws/returns/yields. The opposite of wrap() - takes wrapped result objects and converts them back to normal iterator behavior.\n\n## Details\n\n```\nfunction unwrap<T, R = any>(iterator: AsyncIterable<WrappedResult<T, R>>): AsyncGenerator<T, R, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<WrappedResult<T, R>> | An asynchronous iterable of wrapped result objects. | \n\n``` js\nnz([\"hello\", \"world\"]).wrap()\n// => {type: \"value\", value: \"hello\"}, {type: \"value\", value: \"world\"}, {type: \"return\", value: undefined}\n```\n\nWraps an iterator to catch any errors and return them in a result object format. Instead of throwing, errors are yielded as `{type: \"error\", error}` and successful values as `{type: \"value\", value}`.\n\n## Details\n\n```\nfunction wrap<T, R = any>(iterator: AsyncIterable<T, R>): AsyncGenerator<WrappedResult<T, R>>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<T, R> | An asynchronous iterable. | \n\n``` js\nconst consumed = await nz([\"a\", \"b\"]).consume()\nconsumed.list()   // => [\"a\", \"b\"]\nconsumed.return() // => undefined (or iterator's return value)\n```\n\nConsumes an async iterator completely, collecting both yielded values and the return value.\n\nReturns a ConsumedPipeline which provides access to both yielded values and return values:\n\n- `.list()` - Returns all yielded values as an array (`T[]` )\n- `.return()` - Returns the iterator's return value (`R | undefined` )\n- `.string()` - Concatenates string values\n\n## Details\n\n```\nfunction consume<T, R>(iterator: AsyncIterable<T, R>): Promise<ConsumedPipeline<T, R>>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<T, R> | An asynchronous iterable to consume | \n\n``` js\nnz([\"Hello\", \"World\", \"!\"]) // => \"Hello\", \"World\", \"!\"\n```\n\nConverts an array to an async iterator.\n\n```\nreturn new Response(nz(llmTextStream).minInterval(40).toReadableStream().pipeThrough(new TextEncoderStream()))\n```\n\nConverts an async iterable into a Web `ReadableStream`.\n\nThe stream pulls from the source only when its reader asks for a value, so nothing is read ahead and timing transforms keep their pacing. Cancelling the stream closes the source iterator. The source's return value is discarded, because Web Streams have no return value.\n\nAdded in 0.12.0, along with `ReadableStream` sources for `nz()`.\n\n## Details\n\n```\nfunction toReadableStream<T>(source: AsyncIterable<T>): ReadableStream<T>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `source` | AsyncIterable<T> | The async iterable to read from. | \n\n``` js\nnz([\"a\", \"b\"]).mapReturn(returnValue => returnValue?.toString() ?? \"default\") // => \"a\", \"b\" (with mapped return value)\n```\n\nMaps the return type of an iterator while preserving all yielded values unchanged.\n\n## Details\n\n``` js\nfunction mapReturn<T, R, U>(iterator: AsyncIterable<T, R>, fn: (value: R) => U): AsyncGenerator<T, U, undefined>;\n```\n\n| Parameter | Type | Description | \n|---|---|---|\n| `iterator` | AsyncIterable<T, R> | An asynchronous iterable. | \n| `fn` | (value: R) => U | A function that transforms the return value. | \n\n```\npnpm install --frozen-lockfile\npnpm run check\n```\n\nRun `pnpm run docs` after editing API comments or the README template.\nRun `pnpm run release:dry` to validate package contents without publishing.\n\nMIT", "url": "https://wpnews.pro/news/nozzle-a-streaming-library-that-emits-when-possible-and-buffers-where-necessary", "canonical_source": "https://github.com/Robert-Cunningham/nozzle", "published_at": "2026-11-15 01:13:52+00:00", "updated_at": "2026-11-15 01:23:27.966330+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "large-language-models", "generative-ai"], "entities": ["Nozzle", "nozzle-js", "TypeScript", "Vercel AI SDK", "OpenAI", "Anthropic"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/nozzle-a-streaming-library-that-emits-when-possible-and-buffers-where-necessary", "markdown": "https://wpnews.pro/news/nozzle-a-streaming-library-that-emits-when-possible-and-buffers-where-necessary.md", "text": "https://wpnews.pro/news/nozzle-a-streaming-library-that-emits-when-possible-and-buffers-where-necessary.txt", "jsonld": "https://wpnews.pro/news/nozzle-a-streaming-library-that-emits-when-possible-and-buffers-where-necessary.jsonld"}}