Nozzle: A streaming library that emits when possible and buffers where necessary 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. A text streaming library that emits when possible and buffers where necessary. Quickstart quickstart • Recipes recipes • API api-at-a-glance • Guarantees guarantees • Reference reference Nozzle 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. npm i nozzle-js or pnpm / bun / yarn js import { nz } from "nozzle-js" const parts = nz textStream .parse /img- \w+ /g, m = { src: m 1 } for await const part of parts console.log part / in: "Here is im" "g-abc" "123 and im" "g-xyz" "789." out: "Here is " { src: "abc123" } " and " { src: "xyz789" } "." / nz accepts any sync or async iterable. Getting a text stream from your provider // Vercel AI SDK const { textStream } = streamText { model, prompt } nz textStream // OpenAI const stream = await openai.chat.completions.create { ...args, stream: true } nz stream .map chunk = chunk.choices 0 ?.delta?.content ?? "" // Anthropic const stream = anthropic.messages.stream { ...args } nz stream .map e = e.type === "content block delta" && e.delta.type === "text delta" ? e.delta.text : "" // fetch const res = await fetch url nz res.body .pipeThrough new TextDecoderStream Stream just the code block to a preview while the model is still writing, then save it. js const code = await nz stream .after " ts\n" .before " " .tap sendToPreview .consume await saveSnippet code.string / in: "Sure \n " " ts\nconst x = " "await f \n" "return x\n " " \nDone " out: "const x = " "await f \n" "return x\n" / Providers often deliver text in bursts. Re-chunk into words and release them at a steady pace. js const smooth = nz stream .splitAfter " " .compact .minInterval 40 / in: The quick ...... brown fox jumps over ................ the lazy dog. out: The .. quick .. brown .. fox .. jumps .. over .. the .. lazy .. dog. / - Pipelines are lazy: nothing runs until you for await or .consume . Generator sources are single-use. - Breaking out of a loop early closes upstream iterators. Cancelling in-flight I/O is up to the source/provider. - Errors from the source, including errors raised in timers, surface where you await , so try/catch works. - Generator return values pass through transforms see mapReturn , consume .return . | Group | Functions | |---|---| | Regex | parse parse · scan scan · match match · replace replace | | Splitting | split split · splitAfter splitafter · splitBefore splitbefore · after after · before before · chunk chunk | | Timing | minInterval mininterval · throttle throttle | | Elements | map map · asyncMap asyncmap · filter filter · find find | | Transformation | flatMap flatmap · flatten flatten | | Filtering | compact compact · takeWhile takewhile · takeUntil takeuntil | | Indexing | first first · last last · at at · head head · tail tail · initial initial · slice slice | | Accumulation | accumulate accumulate · diff diff · reduce reduce | | Buffering | batch batch · buffer buffer · withContext withcontext · aperture aperture | | Side effects | tap tap · tee tee | | Errors | recover recover · wrap wrap · unwrap unwrap | | Conversion | consume consume · toReadableStream toreadablestream · fromList fromlist · mapReturn mapreturn | These functions use JavaScript regular expressions to search, match, and transform streaming text. Nozzle 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. These features throw an error because they cannot work reliably with streaming: | Feature | Example | Why | |---|---|---| | Lookaheads | ?=... , ? ... | Content to look ahead may not have arrived yet | | Lookbehinds | ?<=... , ?< ... | Content to look behind may have already been yielded | | Backreferences | \1 , \k