nozzle-js
v0.11.0
Published
Chainable stream processing for async iterators. Transform LLM responses, event streams, and more with a fluent API.
Maintainers
Readme
Nozzle is a small TypeScript library for transforming async iterables, especially streamed text from LLMs.
It helps when provider chunks are not the chunks your app wants: parse structured markers, split text into useful pieces, fan out one stream to multiple consumers, extract generated sections, or smooth token timing without waiting for the whole response.
Quickstart
npm i nozzle-js # or pnpm / bun / yarnNozzle has ESM and CJS builds and works with any sync or async iterable.
import { nz } from "nozzle-js"
const pacedWords = nz(llmTextStream)
.splitAfter(" ")
.compact()
.minInterval(40)
for await (const word of pacedWords) {
process.stdout.write(word)
}Recipes
Parse structured markers as they stream
const stream = await openai.chat.completions.create({ ...args, stream: true })
const parts = nz(stream)
.map((chunk) => chunk.choices[0]?.delta?.content ?? "")
.parse(/img-(\w+)/g, (match) => ({ type: "image", id: match[1] }))
// yields: "Here is ", { type: "image", id: "abc123" }, " for you"
Branch one stream for UI and storage
const [displayStream, storageStream] = nz(stream).tee(2)
const displayPromise = (async () => {
for await (const chunk of displayStream) process.stdout.write(chunk)
})()
const [, consumed] = await Promise.all([displayPromise, storageStream.consume()])
conversation.push({ role: "assistant", content: consumed.string() })
Extract a generated section
const code = await nz(stream)
.after("```ts\n")
.before("```")
.tap(sendToPreview)
.consume()
await saveSnippet(code.string())
Smooth chunky provider output
const smoothStream = nz(stream)
.splitAfter(" ")
.compact()
.minInterval(40)
Behavior
- Pipelines are consumed with
for awaitor.consume(). Generator sources are single-use. .consume()retains all values; use.list(),.string(), or.return()on its result.- Early exit closes upstream iterators. Pending I/O needs cancellation from the source/provider.
buffer(),tee, andasyncMapcan buffer without a limit; concurrency is not a queue limit.- Regexes match across chunks. Anchors, word boundaries, lookarounds, backreferences,
Unicode escapes (
\uXXXX,\u{…}),m/y/v, and nullable patterns are rejected. The explicit empty separator splits UTF-16 code units. Unfinished matches buffer input; match indices are buffer-local.
Reference
{{reference}}
Development
pnpm install --frozen-lockfile
pnpm run checkRun pnpm run docs after editing API comments or this README's template.
Run pnpm run release:dry to validate package contents without publishing.
The release:patch, release:minor, and release:major scripts publish and push tags.
License
This library is licensed under the MIT license.
