Library / SDK
0no-co/wonka avatar
0no-co/wonka

wonka: a tiny stream library that treats iterables and observables as one thing

🎩 A tiny but capable push & pull stream library for TypeScript and Flow

740 stars30 forksTypeScriptMIT

At a glance

What is it?
Wonka is a small push and pull stream library for TypeScript and Flow that follows the callbag specification, and its central idea is that a source of multiple values can be consumed either synchronously as an iterable or asynchronously as an observable without the consumer caring. It has also changed shape twice, and the version line you pick determines which languages it supports.
Who is it for?
Adopt wonka if you are in TypeScript and want one stream abstraction covering both the synchronous and the asynchronous case, since that is the design it is built around and the callbag spec keeps the semantics tight. Do not adopt it if you need OCaml or Reason support, because the current v6 line is TypeScript, Flow and JavaScript only and the older line is where that support lives.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 157 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One abstraction for values that arrive now and values that arrive later

Wonka is a lightweight iterable and observable library loosely based on the callbag spec. It exposes a set of helpers to create streams, which are sources of multiple values, which allow you to create, transform and consume event streams or iterable sets of data.

That sentence contains the whole design in a slightly awkward order. A stream in Wonka is a source of multiple values, and the same source can be consumed as an event stream or as an iterable set of data. The push and pull in the package description is the same distinction seen from the consumer's side: a pull consumer asks for the next value, a push consumer is handed values as they arrive.

Most stream libraries in the JavaScript ecosystem specialise in one of those. Most of the async iteration libraries are pull-only. Wonka's claim is that the two are not separate problems, and the callbag spec it loosely follows is what makes the claim implementable, because the callbag protocol is a small negotiation between a consumer and a source about how values will be delivered.

The topics on the repository make the same point: callbag, events, iterable, observable, stream. All of them are first class, which is unusual.

The name is a Willy Wonka quote from the top of the README, which is a reasonable mood for a small library, and the author is 0no.co with a README that is otherwise short and to the point.

Two prior versions, and why the version number tells you which languages you get

The most practically important paragraph in this README is a note, and it is about versions rather than about streams.

The currently released version v6 is only compatible now with TypeScript, Flow, and JavaScript. If you are looking for Reason/OCaml/esy/dune support, please check v5, and if you are looking for the legacy version of this library check v4.

So the library has been through at least two structural changes and each one changed which languages it could target. v4 is called the legacy version. v5 is the one to use if you are on Reason, OCaml, esy or dune, which is a set of toolchains that are largely OCaml and JavaScript adjacent. v6 is the current line and it is TypeScript, Flow and JavaScript.

This matters more than it sounds for a library whose whole reason to exist is small, well-typed, portable code. A project that wanted to share a stream pipeline between an OCaml service and a TypeScript front end had a v4 or v5 to work with, and the same project today has to pick a side. The note is written plainly and the branches are named, so anyone arriving from an older version can find their way.

The release history is consistent with that. v6.3.6 came on 2026-03-24, v6.3.5 on 2025-03-03 and v6.3.4 on 2023-08-03. So the current line has been patching for a while, and the 6.3.x series has been stable across a long gap between v6.3.4 and v6.3.5, which suggests the API settled and the work moved to fixes.

The packaging, and a good example of a modern exports map

The package.json is worth reading because it is a compact demonstration of what a library that wants to be consumable everywhere has to do.

The entry points are the interesting part:

json
  "source": "./src/index.ts",
  "main": "./dist/wonka",
  "module": "./dist/wonka.mjs",
  "types": "./dist/wonka.d.ts",

And the exports map handles the rest. The main entry has types, import, require and source conditions, so a bundler that understands the source condition can consume the TypeScript directly, a Node ESM import gets the .mjs, a CommonJS require gets the .js, and a type checker gets the declarations.

The source condition is the part that distinguishes a library written in TypeScript from a library ported to it. Because the source entry is exposed, a consumer whose bundler can handle TypeScript gets the original code rather than the compiled output, which is a smaller and faster build.

Two other fields matter. sideEffects is false, which tells a bundler it can drop the whole module if nothing is imported from it, and that claim is only safe if the library has no import-time side effects, which for a pure stream library is a reasonable thing to be able to say. The files array publishes src, dist, the docs markdown, index.js.flow and the top-level markdown files, so the documentation and the Flow definitions ship with the package rather than living only in the repository.

That last entry is worth noting. index.js.flow sits at the repository root and is published, which is how the Flow types are distributed. The Flow support is not a build step over the TypeScript source, it is a separate hand-written definition file that travels with the package.

How the project is built and tested, and what the scripts reveal

The tooling is a small, current set, and the scripts describe how a library of this kind should be worked on.

The test runner is vitest, the type check is tsc, the linter is eslint with a shared preset in the scripts directory, and the build is rollup with a config at scripts/rollup.config.mjs. The clean step uses rimraf, and the publish hook runs the whole chain: clean, build, check and test, so a package cannot be published without passing everything.

Versioning uses changesets, with a changeset:version script that runs the version command and then updates the lockfile with pnpm install --lockfile-only, and a separate changeset:publish script. The .changeset/ directory at the root is where the pending version notes live, which is the standard changesets workflow and a good fit for a library that has to ship a note for every patch.

Git hooks are handled with husky running lint-staged on pre-commit, and the lint-staged config runs eslint with the shared preset on JavaScript and TypeScript files, and prettier on JSON and markdown. The prettier configuration is in the package.json itself with singleQuote set to true, a tab width of 2 and a print width of 100, and the eslint configuration is also in the package.json, extending the shared preset from the scripts directory.

Two things stand out about that arrangement. The lint and format configuration lives in package.json rather than in separate dotfiles, so it travels with the package, and the shared eslint preset lives in the repository's scripts directory rather than being published, which keeps the published tarball small while still letting contributors and CI use the same rules.

The repository layout, and the fact that performance is a tracked concern

The top level of the repository is short. There is a src/ directory for the implementation, a docs/ directory for the documentation source, a scripts/ directory for the build and lint configuration, a perf/ directory, a .changeset/ directory, a CHANGELOG.md, a CONTRIBUTING.md, a LICENSE.md, a tsconfig.json and a pnpm-lock.yaml.

The perf/ directory is the one that tells you something about the project. A library this small, with a bundle size badge and a test coverage badge, has a performance directory next to its source, which means the author treats throughput as something to measure rather than something to assume. For a stream library that is the right instinct, because stream libraries tend to be judged on two axes, how many values per second you can push through a pipeline and how much memory a pipeline holds while doing it, and both are easy to regress with a change that looks harmless.

The documentation lives in the repository and is published with the package. The raw markdown files are in the docs folder, and the site is wonka.kitten.sh, with an introduction, a getting started page, a basics section and an API reference. The README links to the site and to the four sections, and the note about the raw markdown being in the repository is there so that the docs can be corrected without going through the site.

The badges tell the rest. There is an npm version badge and an MIT licence badge, a coveralls badge for test coverage pointing at the kitten/wonka repository on coveralls, and a bundlephobia badge for the minified gzip size, which is the number that matters for a library that competes on being tiny.

The last push to the repository was on 2026-05-19, and the repository is not archived. The project uses pnpm, with a pnpm-lock.yaml at the root.

Editorial conclusion

Adopt wonka if you are in TypeScript and want one stream abstraction covering both the synchronous and the asynchronous case, since that is the design it is built around and the callbag spec keeps the semantics tight. Do not adopt it if you need OCaml or Reason support, because the current v6 line is TypeScript, Flow and JavaScript only and the older line is where that support lives. Before you upgrade from v4, check the version note, because the library was restructured across v5 and v6 and the API you know is not the API you will get.

Frequently asked questions

What is Wonka used for?

It is a lightweight iterable and observable library loosely based on the callbag spec. It exposes helpers to create streams, which are sources of multiple values, so you can create, transform and consume event streams or iterable sets of data with the same set of primitives.

Which languages does the current version of Wonka support?

The currently released v6 is compatible with TypeScript, Flow and JavaScript. If you need Reason, OCaml, esy or dune support you should check v5, and the legacy version of the library is v4.

How is Wonka packaged for different module systems?

The package exports a main entry with types, import, require and source conditions, pointing at the declaration file, the .mjs build, the .js build and the original TypeScript source. The package also publishes index.js.flow for Flow users, and marks itself sideEffects false.

How do you run the Wonka tests and checks?

The test script runs vitest, the check script runs tsc, and the lint script runs eslint. The publish hook runs clean, build, check and test in sequence, and versions are cut with changesets rather than by hand.

Official sources

  1. 0no-co/wonka on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/0no-co-wonka.svg)](https://hysenlabs.com/projects/0no-co-wonka)

Was this useful?