Open-source project
cfworker/cfworker avatar
cfworker/cfworker

cfworker: a package family sized for the Workers runtime

A collection of packages optimized for Cloudflare Workers and service workers.

704 stars66 forksTypeScriptMIT

At a glance

What is it?
cfworker is an MIT licensed monorepo of small TypeScript packages built for Cloudflare Workers, covering a web framework, a JWT library, a JSON schema validator, an Azure Cosmos DB client, a Sentry client, a UUID codec and a streaming CSV encoder, each published separately with a bundle size badge. The judgement is that the bundle discipline is the product, and that the readme you see on the repository is generated rather than written.
Who is it for?
cfworker fits a Cloudflare Workers developer who has discovered that the usual npm packages carry too much weight for a worker, because each of these is built for the runtime and publishes its own size, dependency count and tree-shaking figures.
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?
Activity is slowing. The repository last received commits 6 months 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 idea: small packages for a constrained runtime

cfworker is a collection of packages optimized for Cloudflare Workers and service workers, and the important word in that sentence is packages, plural, each published on its own.

The reason a collection like this exists is the runtime. A worker has a startup cost, a memory limit and a cold start, so a dependency that pulls in three megabytes of code is not a neutral choice. A library that assumes Node built-ins that a worker does not have does not work at all. And a package that ships a bundler, a polyfill and a large transitive tree is a liability inside a worker bundle.

So the collection is a set of small, specific packages, each solving one problem in a way that fits the platform, and each with its own version and its own release cadence. The releases listed are the proof of that, with @cfworker/web at 5.1.0 from 2025-08-12, @cfworker/web at 5.0.6 from January 2025 and @cfworker/uuid at 4.0.4 from the same day, which is what independent versioning looks like in practice.

The project is MIT licensed, its primary language is TypeScript, and the home page is cfworker.dev. The last push to the repository was on 2026-04-10, and the release workflow badge at the top of the readme points at the automation that publishes them.

The author is Jeremy Danyow, and the repository is a monorepo with a packages directory, a Lerna configuration, npm workspaces and Changesets for the releases.

Every package carries five size badges

The most unusual thing about the readme is that every package section is preceded by the same five badges, and they are the reason to read it that way.

Each package shows the minimum zip size, the minimum size, the dependency count, whether the package supports tree-shaking, and whether it ships TypeScript types. All five come from Bundlephobia, and they are shown for every one of the seven packages in the collection.

This is a small design decision with a large effect on how a package is used. A developer choosing a dependency for a worker is making a bundle budget decision, and the numbers that matter for that decision are not the star count or the download count, they are the size on disk, the number of things it pulls in, and whether the bundler can drop the parts you do not use. A badge that says the dependency count is one is more useful than a badge that says the library is popular.

It also means the collection is self-policing. If a change adds a dependency to one of these packages, the badge for that package changes, and a reviewer looking at a pull request can see the cost of the change in the readme the release will publish.

The badges are generated rather than maintained, and the generation is the subject of one of the later sections, because the readme in the repository is produced by a script.

Seven packages, one problem each

The collection covers the parts of an application that a worker is most often asked to do and that the general purpose libraries do badly.

The Cosmos package is an Azure Cosmos DB client for Cloudflare Workers and service workers, which is a specific enough problem that a general purpose client rarely fits, because the client libraries assume a long-lived Node process and a connection pool that a worker does not have.

The CSV package does streaming CSV encoding, so a worker that has to produce a large export does not have to build the whole file in memory first. The JSON schema package is a validator that runs on Cloudflare Workers and supports drafts 4, 7, 2019-09 and 2020-12, which is the useful detail, since the drafts differ and a validator that quietly implements the wrong one is worse than none.

The JWT package is described as a tiny lib for decoding JWTs and verifying signatures using the native crypto APIs, so it uses the Web Crypto that the runtime already has instead of a Node library. The Sentry package is a minimalist Sentry client for the same runtime. The UUID package serialises and deserialises version 4 UUIDs from a Uint8Array, which is a small job that is easy to get subtly wrong and easy to do correctly with a few lines.

And the web package is a web framework for Cloudflare Workers and service workers, inspired by Koa and fastify, which is the one most projects would reach for.

The web framework borrows from Koa and fastify

The web framework is the package with the highest version number in the collection, 5.1.0, and it is the one that gives the other packages their context.

The description says it is inspired by Koa and fastify, which is a useful pair of references because the two traditions are different. A framework for workers that takes inspiration from both is aiming at a middleware pipeline that stays small and a request handling path that stays fast, which are the two constraints that matter on that platform.

The version number is also a signal about how much of the collection depends on it. A 5.x line means the API has been iterated on for a while, and the fact that the web package and the uuid package are versioned separately means a project can take the small packages without taking the framework.

That separation is the point of the whole arrangement. A worker application often needs a JWT check in one route, a schema validation in another and a Cosmos read in a third, and each of those is a small problem with a small solution, so the framework is only needed if you want it.

The readme links to the per package readme for the details, and those documents are the actual API documentation, since the top level readme is deliberately a summary.

Workspaces, Lerna and Changesets, three tools for one job

The monorepo uses three pieces of tooling that overlap, and the readme explains what each one is for.

Npm workspaces do the linking. The contributing section says to install the dependencies with npm, and that npm workspaces will ensure the cfworker packages that depend on other cfworker packages are linked properly. The root manifest declares the workspaces as packages/*, and the engines field asks for npm 7.18.1 or newer and Node 16.14 or newer.

Lerna is present as well, in a lerna.json at the root, which is the older tool for the same problem and is often kept for its version and publish commands.

Changesets handles the releases, with a.changeset directory and a publish script that runs changeset publish, and the release workflow badge in the readme points at the GitHub Actions workflow that does it. The changesets model is the one that suits this collection, because a developer working on the uuid package writes a changeset saying which package changed and by how much, and the publish step then versions and publishes exactly those packages, which is what makes independent versioning across a monorepo practical rather than a manual chore.

Other root scripts are housekeeping, with a build that runs across the workspaces, a test that runs across the workspaces, a clean, a start for the examples workspace and a deploy for the site workspace.

The readme is generated, and the links are per package

The readme in the repository is produced by a script, and the root manifest has a readme-gen entry that runs readme-gen.js.

That explains the shape of the document. Every package section is a one line description, a block of five badges, and then two links, one to the package's own readme in the repository and one to the package on npm. There is no usage example, no API listing and no installation snippet for the packages in the top level readme, because all of that lives in the per package documents.

The reason to generate it is the same reason to generate the badges. A monorepo with seven packages has seven sets of size numbers, seven version numbers and seven npm links, and all of them change when a release happens. Generating the summary means the numbers in the readme cannot drift from the packages, and a contributor who adds a package gets the right block of badges by running the script.

The per package readmes are the real documentation, and the repository also carries a THIRD_PARTY_NOTICES.md at the root, which is the file to look at if you are thinking about the licences of the code that the packages build on.

The result is a readme that is not meant to be read end to end. It is an index, and it is kept honest by a script.

Testing across the workspaces, with keys for the integration tests

The contributing section lists three commands, and the third one is the useful detail.

Installing is npm install, which links the packages. Running everything is npm test, which runs the test script in every workspace. Running the tests of one package is npm run test with the workspace flag, and the example given is the base64url package, so the naming convention is that the npm package name and the workspace name match.

bash
npm install
npm test
npm run test --workspace=@cfworker/base64url

And then the note that some packages require a .env file, which is gitignored, containing the Cloudflare or Cosmos DB keys for integration testing. That is the difference between the packages that can be tested in isolation and the ones that need a real account behind them, and the readme is upfront about it.

The repository has the pieces for that. There is a test-worker.ts and a test.mjs at the root, which look like the harness for running a worker locally, and the prettier configuration and the lint-staged hook take care of the formatting, with the root lint script being a prettier check rather than a linter, so the collection relies on the formatter and on the type checker for style.

There is a .husky directory and a prepare script that installs the hooks, and an update-deps script that runs npm-check-updates with a deep upgrade and a rejection for path-to-regexp, which is the kind of exception a maintainer pins when a transitive dependency has caused a problem.

Editorial conclusion

cfworker fits a Cloudflare Workers developer who has discovered that the usual npm packages carry too much weight for a worker, because each of these is built for the runtime and publishes its own size, dependency count and tree-shaking figures. It is the wrong choice if you are writing code that runs on a normal Node server, since the whole point of the collection is the constraints of the Workers runtime, and it is the wrong choice if you want one package that does everything, because these are independent packages with independent versions. Before you adopt one, read its own readme rather than the top level one, because the generated summary will not tell you the API, and check the published version, since the web package and the uuid package are versioned independently and a project that takes both has to think about them separately.

Frequently asked questions

What is the cfworker collection?

It is a set of separate npm packages built for Cloudflare Workers and service workers, each published on its own and each solved in a way that fits the runtime. The packages include a web framework, a JWT decoder and verifier, a JSON schema validator, an Azure Cosmos DB client, a Sentry client, a UUID codec and a streaming CSV encoder.

Which JSON schema drafts does the cfworker validator support?

Drafts 4, 7, 2019-09 and 2020-12, according to the package description, which matters because the drafts differ and a validator implementing the wrong one fails silently.

How are the packages released?

The repository uses npm workspaces to link the packages that depend on each other, a Lerna configuration, and Changesets for versioning, with the publish step running changeset publish from a release workflow. Because the packages are versioned independently, a change to one of them does not release the others.

How do I run the tests?

Run npm install to link the workspaces, then npm test to run every package's tests, or npm run test with the workspace flag for a single package. Some packages need a gitignored .env file with Cloudflare or Cosmos DB keys for their integration tests.

Official sources

  1. cfworker/cfworker on GitHub
  2. License: MIT
  3. Project website
  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/cfworker-cfworker.svg)](https://hysenlabs.com/projects/cfworker-cfworker)

Was this useful?