Steem.js, Modernized & Published: `@blazeapps/steem` is Live on npm
This is the finale of my 7-part series on modernizing Steem.js. Over the past six days I walked through each engineering phase — today it ships. The modernized library is published to npm and running in production-ready form across every major JavaScript runtime.
- 📦 npm: https://www.npmjs.com/package/@blazeapps/steem
- 🛠️ Code: https://github.com/blazeapps007/steem-js/tree/BlazeDevelopment
- 📖 Docs: https://blazeapps007.github.io/steem-js/
What we did, in one screen
| Area | Before | After |
|---|---|---|
| Runtimes | Node + a browser bundle | Node 18+, browsers, Cloudflare/Vercel edge, Deno — one package |
| Crypto | bigi + ecurve + browserify-aes | audited, pure-JS @noble/curves + @noble/hashes + @noble/ciphers |
| Serializer | bytebuffer | in-repo bytebuffer-lite (Uint8Array + native BigInt) |
| Networking | cross-fetch, static ws | global fetch, lazy/optional ws |
| Promises | bluebird | tiny native-promise helper (dual callback/Promise API kept) |
| Build | webpack 4 + Babel | tsup (esbuild) → ESM + CJS + IIFE + types |
| Types | none | shipped dist/index.d.ts |
| Docs | partial | 100% generated from source + edge/Deno guides |
| Dependencies | ~25 legacy shims | a handful, all bundled into a self-contained dist |
Backwards compatible — byte for byte
The whole rewrite was gated by a golden-vector test that froze the exact outputs of the old library: WIFs, public keys, a canonical signature, serialized transaction hex, and an encrypted-memo roundtrip. Every phase kept those identical. Same keys, same signatures, same wire format — existing accounts and transactions just work.
The public API is unchanged, too: steem.api.*, steem.broadcast.*, steem.auth, steem.memo, steem.formatter, steem.utils, steem.config; callbacks and promises; the singleton and the Steem class.
import steem from '@blazeapps/steem'; // ESM — Vite, edge, Deno, modern Node
// const steem = require('@blazeapps/steem'); // CommonJS
const [account] = await steem.api.getAccountsAsync(['blaze.apps']);
const wif = steem.auth.toWif('alice', 'password', 'active'); // offline, every runtime
await steem.broadcast.voteAsync(wif, 'alice', 'author', 'permlink', 10000);
The journey (the six phases)
- Foundation & tooling — golden-vector safety net first, then tsup + Vitest.
- Crypto core — swapped to
@noble, preserving graphene's canonical RFC6979 signing byte-for-byte. - Serializer —
bytebuffer-litewith nativeBigInt, wire format untouched. - Transports & runtime — native
fetch, lazyws, feature-detected globals, Bluebird removed. - Types —
.d.tsgenerated from the same descriptors that drive the runtime, so they never drift. - Verification & docs — cross-runtime smoke tests, CI on Node 18/20/22 + Deno, and 100% generated docs.
Battle-tested on the way to release
Shipping surfaced — and fixed — the gnarly real-world edge cases:
- Node 18 has no default
globalThis.crypto→ a build-time banner wires it up via a non-literalnode:cryptospecifier, so edge/Deno bundles stay clean. @noblev2 is ESM-only → bundling deps intodistsorequire()works on Node 18 CJS.- Deno's permission model → the serializer no longer trips the env check; no
--allow-envneeded. - CI → npm → every push runs the full matrix + Deno, then auto-publishes a new version.
All verified on real Node 18.20.8, Node 20/22, and Deno.
Try it
npm install @blazeapps/steem
Then head to the docs — there are dedicated Cloudflare Workers and Deno guides with complete, worked examples.
Issues, PRs, and feedback against the fork are very welcome. If this work is useful to you, the best way to support continued development is below. 🙌
Support Secure Steem Development
If you value proactive engineering, UX polish, and performance optimizations for the STEEM ecosystem, please consider supporting my witness: blaze.apps
🗳️ Vote Here:
Vote for blaze.apps Witness