DEV Community

Pierre Moati
Pierre Moati

Posted on

Why I built a unified TypeScript utility ecosystem (and what I learned)

Every TypeScript project I've worked on in the last few years had the same stack of utility dependencies: Lodash for data manipulation, Zod for validation, and something like Neverthrow for error handling. Three libraries, three install commands, three sets of docs, three bundle impacts.

They all work fine individually. But they don't talk to each other. And I kept running into the same frustrations:

  • "Where did I put that utility again?"
  • Code constantly rewritten, never truly battle-tested.
  • Great snippets scattered across projects, never improving.
  • Fear of supply-chain surprises from external dependencies.

After 20 years of building web and mobile apps for clients like Michelin, Clarins, and Roland Garros, I decided to stop rewriting the same helpers for the fifth time and centralize everything into one place.

That's how Pithos started. First as a personal utility library, then it grew into a complete ecosystem when I merged two standalone projects: Kanon (schema validation) and Zygos (functional error handling).

Why "Pithos"?

Pandora's "box" was actually a large jar: a pithos in Greek. This project embraces that story. A single jar holding both the pains we faced and the solutions we crafted to solve them.

What's inside the jar

One package, five modules, zero dependencies:

  • Arkhe: data utilities (arrays, objects, strings, numbers). Think Lodash, but modern-first, tree-shakable, and ~21x smaller.
  • Kanon: schema validation. Like Zod, but 5-11x faster and ~4x smaller. Comes with a z shim so your existing Zod schemas work with just an import change.
  • Zygos: Result and Option types for typed error handling. Drop-in replacement for Neverthrow (~3x smaller, ~1.6x faster).
  • Sphalma: typed, hierarchical error classes with hex codes and structured metadata.
  • Taphos: migration bridge for Lodash/es-toolkit, with IDE deprecation warnings guiding you toward native JS or Arkhe.

Every module name comes from Greek, keeping with the mythology theme. Arkhe (origin), Kanon (rule), Zygos (balance), Sphalma (error), Taphos (tomb, where deprecated utilities come to rest).

The real point: they compose

Any library can claim good benchmarks. What makes Pithos different is that the modules were designed to work together.

ensure() takes a Kanon schema and returns a Zygos Result. That means you can validate input, transform it with Arkhe, and handle domain errors with Sphalma in a single typed pipeline. No try/catch, no glue code, full type inference from start to finish.

One unified philosophy: trust TypeScript at compile-time, validate at boundaries.

Kanon says "is this data well-formed?". Sphalma says "is this operation valid?". Zygos chains everything. Arkhe transforms the data. One pipeline, four modules, zero exceptions.

You don't have to go all-in

This was a deliberate design decision. I didn't want Pithos to be an all-or-nothing bet.

  • Using Lodash? Taphos wraps your existing utilities and shows deprecation warnings in your IDE, guiding you toward native JS or Arkhe at your own pace. Even during the transition, you already get smaller bundles.
  • Using Zod? The z shim gives you near-complete API compatibility. Swap your import, keep your schemas. A few features like .pipe() and .brand() are intentionally left out by design.
  • Using Neverthrow? Zygos is a drop-in replacement.

Each migration path is independent. Replace one library at a time, or don't replace anything at all.

Even if you don't use Pithos

The site might still be useful to you. I spent a lot of time building detailed comparison pages: Arkhe vs Lodash, Kanon vs Zod, Zygos vs Neverthrow, with real benchmarks and API equivalence tables.

There's also a use-case explorer that helps you find the right utility for a given need, regardless of which library you end up choosing.

I built the docs I wished existed when I was evaluating these libraries myself.

What's next

Pithos is open-source, MIT licensed, and actively maintained.
I'd love feedback on the API design, the docs, and anything that feels missing.

If you've ever felt the friction of gluing together utility libraries that weren't designed to talk to each other, I'd be curious to hear how you solved it.

Top comments (0)