dsteem Modernization Plan: Bringing a Core Steem Library into Latest Compatibility
I am modernizing
dsteem— the TypeScript RPC client for Steem — from its 2019 state to a 2026-ready v0.12.0, preserving 100% API compatibility while updating the toolchain, security, and developer experience. This post outlines the phased plan, technical decisions, and how you can follow along or contribute.

Thanks ChatGPT for image
dsteem (originally by @jnordberg) has been a robust, dual-environment (Node.js + browser) RPC client for the Steem blockchain [[1]]. However, the last meaningful commit was in November 2019. Today, that means:
- 📦 Dependencies pinned to 2018–2019 versions
- 🔧 Deprecated build toolchain (browserify + tsify + babelify + uglifyjs)
- 🚫 Abandoned linter (
tslint) - ⚠️ High-severity vulnerability in
[email protected] - 🧪 Test pipeline targeting Node 8–10 (EOL)
For developers building on Steem in 2026 [[10]], this creates friction: security concerns, compatibility issues, and missed opportunities for modern TypeScript features.
Goal: Release v0.12.0 that is:
- ✅ API-compatible: Zero breaking changes for existing consumers
- 🔐 Secure: Replace vulnerable crypto dependencies with audited alternatives
- 🚀 Modern: ESM + CJS dual output, Node ≥22, ES2020+ browser target
- 🧪 Tested: 70%+ coverage gate, Playwright browser tests, GitHub Actions CI
Locked Technical Decisions
| Area | Decision | Rationale |
|---|---|---|
| Public API | Preserve exactly | Existing apps upgrade via version bump only |
| Crypto | @noble/curves + @noble/hashes | Audited, pure-JS, used by ethers/viem; removes native build risks |
| Module Format | Dual ESM + CJS | Supports modern bundlers and legacy CommonJS consumers |
| Node Floor | ≥22.0.0 | Leverages native fetch, modern V8, LTS support |
| Build Tool | tsup (esbuild wrapper) | Fast, emits ESM/CJS/.d.ts in one pass |
| Test Runner | Mocha 11 + c8 | Minimal test churn, built-in coverage |
| Browser Tests | Playwright headless | Replaces deprecated Karma + Sauce Labs |
| CI | GitHub Actions | Unified workflow, matrix testing on Node 22/24 |
| Buffer Polyfill | esbuild-plugin-polyfill-node | Zero consumer setup for browser builds |
| Test RPC | api.steemit.com (primary), api.moecki.online (fallback) | Reliable mainnet endpoints for CI [[17]] |
The Phased Modernization Plan
Each phase leaves CI green. All work is tracked in the blazeapps007/dsteem fork.
🔐 Phase 0: Capture Crypto Golden Fixtures (Load-bearing)
Before changing code, freeze current signing/hashing behavior:
- Generate deterministic test vectors using existing
[email protected]in a Node 16 container - Save to
test/fixtures/crypto-golden.json - Assert bit-identical output throughout the migration via
test/crypto-golden.ts
Why? Prevents silent signature drift that could break Steem network consensus.
🔄 Phase 1: CI Plumbing Only
- Add
.github/workflows/ci.ymlwith matrix: Node 22 + 24 - Jobs:
lint,test,build,coverage(upload to Codecov) - Delete
.travis.yml,.circleci/config.yml - Baseline: Run existing tests on Node 16 to establish "green" starting point
🧹 Phase 2: tslint → ESLint Flat Config
- Replace
tslint.jsonwitheslint.config.js(flat config) - Mirror existing rules: single quotes, no semicolons, 4-space indent
- Update disable comments in source files
- Update
package.jsonscripts:npm run lint→eslint src test
📦 Phase 3: TypeScript 3 → 5
- Bump
typescript@^5.6,@types/node@^22,@types/mocha@^10 - Modernize
tsconfig.json:target: ES2022,moduleResolution: Bundler,strict: true - Fix TS5 errors (e.g.,
global['fetch']→(globalThis as any).fetch) - Keep
ts-nodeworking temporarily for test execution
🔐 Phase 4: @noble Crypto Swap (Highest Risk)
Replace [email protected] + Node crypto.createHash with:
@noble/curves@^1for secp256k1 operations@noble/hashes@^1for SHA256/RIPEMD160
Critical details:
- Wrap new APIs to match old signatures exactly (internal helpers only)
- Pass
lowS: falseto maintain Steem's canonical signature format - Verify recovery-bit domain
{0,1,2,3}matches existingSignature.fromBufferlogic - Remove all native dependencies (
node-gyp, prebuild binaries)
✅ Gate: npm test -- --grep golden must pass — bit-identical output to Phase 0 fixtures.
🏗️ Phase 5: Build Modernization (tsup, Exports Map, Drop Dead Polyfills)
- Add
tsup@^8; remove browserify, babelify, uglify-js, dts-generator, etc. - New
tsup.config.ts: dual builds- Node:
src/index-node.ts→dist/index.{mjs,cjs,d.ts} - Browser:
src/index-browser.ts→dist/dsteem.browser.js(IIFE, minified)
- Node:
- Replace
src/version.tswith build-time__PACKAGE_VERSION__define - Gut
src/index-browser.ts: dropcore-js,regenerator-runtime,whatwg-fetch(Edge Legacy is dead) - Update
package.json:{ "type": "module", "engines": { "node": ">=22.0.0" }, "exports": { ".": { "types": "./dist/index.d.ts", "browser": "./dist/dsteem.browser.js", "import": "./dist/index.mjs", "require": "./dist/index.cjs" } } } - Delete
Makefile
🎯 Expected win: Browser bundle drops from ~500KB → ~150KB.
🧪 Phase 6: Tests + Coverage + Browser Tests
- Bump
mocha@^11, addc8@^10,tsx@^4,playwright@^1 - New
.mocharc.cjs: usetsxESM loader - New
playwright.config.ts: headless Chromium/Firefox/WebKit tests againstdist/dsteem.browser.js - Delete legacy test harnesses:
test/_node.js,test/_karma*.js, etc. - Add fallback RPC logic in
test/common.ts: retry againstapi.moecki.onlineif primary fails [[17]] - Gate testnet-dependent tests behind
TEST_TESTNET=1env flag
✅ Coverage gate: npm run coverage fails if line coverage < 70%.
📚 Phase 7: Docs
- Bump
typedoc@^0.28, addtypedoc.json npm run build:docs→docs/index.html- Remove BSD-sed post-processing (no longer needed in Typedoc 0.28)
🚀 Phase 8: Cleanup + Release
- Update
README.md: install instructions, CDN URL, Node 22 minimum, RPC endpoint notes - Update
CLAUDE.md: replace Makefile references withnpm run *scripts - Bump version to
0.12.0, tagv0.12.0 npm publish --dry-runfirst to inspect file list
Security & Dependency Outcome
Removed (deprecated/vulnerable)
secp256k1, @types/secp256k1, core-js, regenerator-runtime,
whatwg-fetch, node-fetch, tslint, browserify, tsify, babelify,
@babel/core, uglify-js, dts-generator, nyc, ts-node, karma*, coveralls
Added (modern/audited)
@noble/curves, @noble/hashes, tsup, esbuild-plugin-polyfill-node,
eslint, typescript-eslint, tsx, c8, playwright
Upgraded
typescript 3.1.6 → ^5.6
@types/node 10 → ^22
mocha 5 → ^11
typedoc 0.13 → ^0.28
bs58 4 → ^6
✅ Result: Eliminates the high-severity [email protected] advisory, removes native-build attack surface, and drops all deprecated/unmaintained packages.
Verification Checklist (Post-Release)
After all phases complete, we verify:
- ✅
npm testgreen on Node 22, ≥70% coverage, golden fixtures pass - ✅ Mainnet round-trip:
Client('https://api.steemit.com').database.getDynamicGlobalProperties()succeeds - ✅ Browser bundle works:
npm run test:browserpasses in headless Chromium - ✅ Dual-format install works for ESM and CJS consumers
- ✅ TypeScript types resolve correctly for
import {Client, PrivateKey} from 'dsteem' - ✅
dist/dsteem.browser.js< 200KB - ✅
npm auditclean — no known advisories in production deps - ✅ CI matrix green on Node 22 and 24
How You Can Help
This modernization benefits everyone building on Steem. Here's how to engage:
- 🔍 Review the plan: Check the
dsteemrepo and open issues for feedback - 🧪 Test early builds: Once Phase 1 lands, try the CI artifacts in your Steem apps
- 🐛 Report regressions: If your existing code breaks after upgrading to
v0.12.0, file an issue — API compatibility is non-negotiable - 💡 Suggest improvements: Especially around browser usage, RPC fallbacks, or Steem-specific edge cases
Final Thoughts
Modernizing core infrastructure is unglamorous but essential work. By bringing dsteem into 2026, we:
- Reduce security risks for all Steem developers
- Enable modern TypeScript features and tooling
- Improve bundle sizes and load times for browser apps
- Future-proof the client for the next era of Steem development [[21]]
This plan was developed with Claude AI assistance, but every technical decision reflects Steem ecosystem needs and backward-compatibility priorities. The work is ongoing — follow the repo for progress updates.
Built for Steem, by the community.
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
Disclaimer: This post describes a work-in-progress plan. All code changes are subject to review and testing before release.