base-x
Fast base encoding and decoding for any custom alphabet, using bitcoin-style leading zero compression.
Repository Health
Technical Analysis
base-x is a minimal JavaScript/TypeScript library for encoding and decoding byte arrays into strings using any custom alphabet, not just the standard bases. It powers base58 encoding used throughout Bitcoin and other cryptocurrencies, but works for base2 through base67 (and beyond) by accepting any alphabet string of unique characters.
The implementation is a direct derivation of the base58 algorithm from Bitcoin Core’s C++ source, generalized to work with arbitrary alphabets rather than a hardcoded one. It correctly handles bitcoin-style leading zero compression, throws on ambiguous or invalid characters, and ships as both ESM and CommonJS builds with full TypeScript types.
What You Get
- Alphabet-driven factory - call
base(ALPHABET)with any string of unique characters and get back{encode, decode, decodeUnsafe}for that base. - Bitcoin-compatible leading zero handling - leading zero bytes are preserved and compressed using the same convention as Bitcoin Core’s base58.cpp.
- Dual ESM/CJS builds with types - published with separate
src/esmandsrc/cjsentry points plus generated.d.tsdeclarations, selected automatically via package.jsonexports. - Safe and unsafe decode variants -
decode()throws on invalid input,decodeUnsafe()returnsundefinedinstead, so callers can choose the error-handling style that fits.
Common Use Cases
- Cryptocurrency address encoding - encoding/decoding Bitcoin-style base58 addresses and other blockchain identifiers.
- Custom identifier formats - generating compact, URL-safe or human-friendly IDs using a custom alphabet (e.g. base62 for short links).
- Binary-to-text encoding - converting arbitrary byte arrays (hashes, keys, binary payloads) into printable strings without alphabet-specific standard library support.
- Building higher-level codecs - used as the underlying primitive by other packages that wrap a specific alphabet (e.g. base58-only wrappers).
Under The Hood
Architecture base-x is a single-file, closure-based module (ts_src/index.ts, ~166 lines) compiled twice via plain tsc, once to ESM (src/esm) and once to CommonJS (src/cjs, with a postbuild rename step from .js to .cjs), with package.json’s exports map routing consumers to the right build automatically. The entire library is one factory function that closes over a byte-lookup table (BASE_MAP) built from the caller’s alphabet string, returning a small object of pure encode/decode/decodeUnsafe functions with no shared mutable state and no I/O. There’s effectively no internal layering to describe: everything a consumer touches is the single BaseConverter object returned from the factory, and nothing else exists to break if that abstraction changes.
Tech Stack Written in TypeScript (^5.4.5) with a strict compiler configuration (strict, noImplicitAny, noUnusedLocals) and built with the plain tsc compiler rather than a bundler, split across two tsconfig files for the ESM and CJS targets. It ships zero runtime dependencies; devDependencies are limited to standard for linting/formatting, tape for unit testing, rimraf for the clean script, and @types/node. CI (GitHub Actions) runs on every push and pull request across three jobs: unit tests, standard lint, and a gitdiff check that fails the build if the committed, pre-built output has drifted from source.
Code Quality Test coverage comes from a tape-driven suite (test/index.js) run against a shared fixtures.json covering encode and decode round-trips across every supported alphabet (base2 through base67 and beyond), plus dedicated cases for invalid-alphabet and invalid-input errors and correct Uint8Array return types. Error handling is explicit and typed, throwing TypeError/Error with specific, testable messages, and a decodeUnsafe variant is offered for callers who prefer a non-throwing check. Variable naming leans heavily on the original Bitcoin Core C++ source it was ported from (pbegin, pend, psz), which trades a little readability for direct traceability to that reference implementation, documented via inline comments.
API Design The public surface is a single factory call, basex(ALPHABET), returning {encode, decode, decodeUnsafe}, about as little boilerplate as an encoding library can ask of its callers. The README documents the pattern with a runnable example and a reference table of common alphabet strings (base2 through base67), so most users can copy an alphabet and go without reading source. Shipped TypeScript types give autocomplete and compile-time safety, though the decode/decodeUnsafe distinction and other nuances aren’t documented inline via JSDoc, only in the README and type declarations.
Used by 5 apps in this directory
Dub
Marketing · Analytics
The open-source link attribution platform for short links, conversion tracking, and affiliate programs — powering 100M+ clicks monthly.
Formizee
Forms Surveys
An open-source form backend and builder platform — design forms, collect submissions, and get analytics without hosting your own server, positioned as an alternative to Formspree and Typeform.
overleaf
Collaboration · Productivity
Open-source, real-time collaborative LaTeX editor with sandboxed compilation and full TeXLive support for self-hosted academic and research teams.
Papermark
Digital Signiture · File Storage
Open-source DocSend alternative with page-by-page analytics, secure data rooms, and custom domains for document sharing.
Unkey
Developer Tools
An open-source developer platform for API infrastructure — issue and verify API keys, enforce global rate limits, route traffic through a gateway, and get per-key analytics and audit logs.