refactor(sdk,wallet-lib,test-suite)!: convert dapi-client + wallet-lib + js-dash-sdk + platform-test-suite to ESM#3689
refactor(sdk,wallet-lib,test-suite)!: convert dapi-client + wallet-lib + js-dash-sdk + platform-test-suite to ESM#3689PastaPastaPasta wants to merge 11 commits into
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
✅ DashSDKFFI.xcframework built for this PR.
SwiftPM (host the zip at a stable URL, then use): .binaryTarget(
name: "DashSDKFFI",
url: "https://your.cdn.example/DashSDKFFI.xcframework.zip",
checksum: "d3a03c5dce08347513332117f46aa8601083d05f64eaeb7f09bf9f4b62651ebf"
)Xcode manual integration:
|
087ecec to
d131469
Compare
01db54e to
3af296d
Compare
… API
Adds lib/utils/bytes.js helper (hexToBytes/bytesToHex/base64ToBytes/bytesToBase64/concatBytes/bytesEqual) and converts all Buffer.* call sites in dapi-client lib/ to Uint8Array, with corresponding test updates. Package stays CJS.
Production exceptions where Buffer is retained: BlockHeadersReader passes Buffer to dashcore-lib's BlockHeader (its BufferReader needs .readInt32LE), and GetIdentitiesContractKeysResponse passes Buffer to wasm-dpp's Identifier.from (it explicitly requires Node Buffer).
createGrpcTransportError now handles both raw bytes (grpc-js path) and base64 strings (grpc-web path) for drive-error-data-bin, stack-bin, and dash-serialized-consensus-error-bin metadata fields, restoring the dual-format behavior that Buffer.from(x, 'base64') used to provide implicitly.
Test updates: spec files that construct expected protobuf requests now wrap .toBuffer() with new Uint8Array(...) to match production's normalization (sinon calledOnceWithExactly distinguishes Buffer from plain Uint8Array).
Breaking change for direct consumers: response object byte fields are now Uint8Array. Callers that do response.field.toString('hex') will fail — use bytesToHex(response.field) from lib/utils/bytes instead. Buffer.isBuffer(response.field) now returns false; use response.field instanceof Uint8Array.
Test results: dapi-client 315/315, wallet-lib 377/377, js-dash-sdk 60/60 — downstream consumers continue passing without modification (they exercise dapi-client mostly via mocks).
ca8a25e to
264012b
Compare
Identifier constructor and static from() now accept Uint8Array in addition to Buffer. Since Buffer extends Uint8Array in Node, existing Buffer callers continue to work unchanged — this is a widening, not a swap. Lets dapi-client and other web-facing consumers pass plain Uint8Array (e.g. from protobuf getters) without an explicit Buffer.from wrap. Error message updated from 'Identifier expects Buffer' to 'Identifier expects Uint8Array'.
wasm-dpp's Identifier.from now accepts Uint8Array directly, so the explicit Buffer.from wrap around entry.getIdentityId() is no longer needed. Removes one of two remaining Buffer escape hatches in dapi-client (the BlockHeader path through dashcore-lib remains, pending an upstream change there).
…ment
- Replace deprecated String.prototype.substr with slice in hexToBytes.
- Add light input validation to bytesToHex, base64ToBytes, bytesToBase64, concatBytes, and bytesEqual — they now throw informative TypeErrors instead of opaque runtime errors on non-bytes input.
- Export the helpers as DAPIClient.bytes so the recommended migration path (bytesToHex(field) instead of field.toString('hex')) is reachable via the package's public surface instead of a deep import into lib/utils/bytes.
- README: add a Browser usage section explaining that response fields are Uint8Array, that DAPIClient.bytes is the conversion helper, and that two internal paths (BlockHeadersProvider via dashcore-lib, Identifier.from via wasm-dpp) still construct Buffer — browser consumers must ensure Buffer is reachable at runtime via their bundler or an explicit polyfill until dashcore-lib's BufferReader accepts Uint8Array.
264012b to
78d87ae
Compare
Identifier no longer extends Buffer at the prototype level. It now extends Uint8Array, so new Identifier(x), id.toString('hex'), id.toString('base64'), id.toString('base58'), id.toBytes(), and id.equals(other) work without a Buffer polyfill at runtime. The wasm-dpp module loader also drops Buffer.from in favor of an inline atob-based decode.
toBuffer() is retained as a @deprecated compatibility shim that returns Buffer.from(this); it still requires the Buffer global to exist at call time but no longer at module load time. External callers migrating off Buffer should prefer toBytes() going forward.
BREAKING for any code that relied on Identifier extending Buffer: Buffer.isBuffer(id), id instanceof Buffer, and Buffer-specific methods on id (.readUInt32LE, .equals, etc.) no longer work. The one such internal callsite (wallet-lib's IdentitySyncWorker) is migrated to check instanceof Uint8Array. The error message string changed from 'Identifier expects Buffer' to 'Identifier expects Uint8Array' (accepted-type check itself remains a widening — Buffer extends Uint8Array).
bs58 (via safe-buffer) is the remaining transitive Buffer dependency in wasm-dpp; eliminating it requires replacing bs58 with a Uint8Array-native base58 library and is out of scope here.
…to .toBytes() Mechanical follow-up to the Identifier extends Uint8Array commit. All internal test code now uses the Buffer-free .toBytes() accessor on Identifier instances (.getId(), .getOwnerId(), .getContractId(), .getDataContractId(), and direct identityId/contractId/ownerId variables). The deprecated .toBuffer() method is retained for external compatibility but no longer used inside the monorepo. Also strips redundant new Uint8Array(...) wraps around .toBytes() — toBytes already returns a fresh Uint8Array, so the wrap was a copy of a copy.
78d87ae to
2d91cfc
Compare
2d91cfc to
0308e5b
Compare
Acted on real findings, dropped false positives.
Fixed:
- wallet-lib getTransaction: Buffer.from(response.getBlockHash()).toString('hex') instead of relying on Uint8Array.toString('hex') which ignores the encoding arg. After PR 2's migration, GetTransactionResponse.getBlockHash() returns a plain Uint8Array, so the previous code returned a comma-delimited decimal string instead of hex.
- GetProtocolVersionUpgradeVoteStatusResponse: use versionSignal.getProTxHash_asU8() instead of new Uint8Array(versionSignal.getProTxHash()). The default protobuf getter yields a base64 string under grpc-web; new Uint8Array(string) does not base64-decode, it iterates chars and produces zeros. _asU8 explicitly returns Uint8Array bytes in both grpc-js and grpc-web.
- wallet-lib Transport.d.ts: getIdentityByPublicKeyHash signature updated from (publicKeyHash: Buffer): Promise<Buffer[]> to (publicKeyHash: Uint8Array): Promise<Uint8Array>. Matches the post-migration runtime contract.
- wasm-dpp Document.spec.js #getDataContractId: was calling getOwnerId() and asserting against $ownerId. Now actually exercises getDataContractId.
- GetIdentityByPublicKeyHashResponse JSDoc: @param renamed from identities (plural) to identity (singular); @returns Uint8Array[] corrected to Uint8Array. Both predate this PR; cleaned up while touching the file.
- GetIdentityContractNonce.spec.js, GetIdentityNonce.spec.js: proof-only constructor-pass-through tests now use BigInt(0) (matches the actual nonce type) instead of new Uint8Array(0).
- GetIdentityKeys.spec.js: proof-only test now uses [] (matches the array-of-keys contract) instead of new Uint8Array(0).
- PlatformMethodsFacade.spec.js: getIdentityNonce second arg is now {} (options) instead of a second Uint8Array. Predates the migration.
- SimplifiedMasternodeListProvider catch block: guards bytesToHex call when simplifiedMNListDiffBytes is undefined, so the original error isn't masked by a secondary TypeError from the new bytesToHex input validation.
Skipped (false positives):
- GetDataContractResponse, GetIdentityResponse, GetIdentityByPublicKeyHashResponse createFromProto 'new Uint8Array(undefined)' findings: the existing tests explicitly assert new Uint8Array(0) for the proof-only path, and new Uint8Array(undefined) === new Uint8Array(0). Current behavior matches contract.
- wasm-dpp loadDpp atob fallback: atob has been a Node global since 16.0.0, and the package's engines.node is >=18.18 (PR 1). No fallback needed.
- validateDocumentsBatchTransitionBasicFactory.spec.js .toBuffer() nitpick: inside a describe.skip block; dead code.
Converts @dashevo/dapi-client to pure ESM: type: module, exports map with ./lib/* wildcard, all CJS require/module.exports replaced with import/export, .js extensions on relative imports. Engine bumped to >=18.18. Drops webpack.config.js, karma.conf.js, and lib/test/karma/. Removes the @babel/core, babel-loader, browser-polyfill, karma, and webpack devDeps. Consumers must use a modern bundler (Vite/esbuild/webpack 5) which handles ESM natively. Moves 'events' npm package from devDependencies to dependencies — used by DAPIClient, ReconnectableStream, and BlockHeadersProvider for EventEmitter, and resolves correctly in both Node and browser bundlers. BREAKING: CJS consumers (require) no longer work. Downstream consumers wallet-lib (PR 4) and js-dash-sdk (PR 5) are temporarily broken between this PR merging and PRs 4/5 merging — they must land as a sequence. Dashmate is already ESM and continues to work. Test results: dapi-client 315/315. wallet-lib + js-dash-sdk fail as expected (fixed by PRs 4 + 5).
Converts @dashevo/wallet-lib to pure ESM so it can consume the ESM dapi-client from PR 3. Adds 'type: module' to package.json. All src/, fixtures/, and tests/ files converted from CJS require/module.exports to ESM import/export with .js extensions on relative imports. Deletes webpack.config.js, karma/, src/test/karma/. Removes browser-polyfill devDeps (buffer, crypto-browserify, stream-browserify, etc.) and webpack/karma. Tests run via Mocha in Node 18+; browser builds are out of scope. Engines: >=18.18. CJS-named-import fixes: lodash, crypto-js, @dashevo/dashcore-lib, @dashevo/wasm-dpp, @dashevo/grpc-common all use default-import + destructure pattern because Node ESM cannot statically enumerate named exports of CJS modules. Surfaces three previously-silent CJS bugs that ESM strict mode catches: missing UnknownStrategy export from errors/index.js, missing named exports for coinSelection strategies, and a 'type=' implicit-global in DerivableKeyChain.spec.js. Defensive (loadDpp.default ?? loadDpp)() unwrap in IdentitySyncWorker.onStart for the same wasm-dpp NodeNext interop reason as PR 3's bootstrap. Test results: wallet-lib 377/377 passing. dapi-client unchanged (315/315). js-dash-sdk still broken (PR 5 fixes).
…SM dapi-client and wallet-lib
Final PR in the ESM migration stack. Converts the two remaining workspace packages that consumed dapi-client/wallet-lib, restoring monorepo-wide green tests after PR 3.
js-dash-sdk: TypeScript-based package with NodeNext module resolution. Adds 'type: module' + exports map. tsconfig.build.json → module: nodenext, moduleResolution: nodenext, target: es2020. All .ts files get .js extensions on relative imports (TS NodeNext compiles .ts to .js but the import string must already say .js). Drops ts-mock-imports by refactoring register.ts to accept an injectable randomBytes parameter (ts-mock-imports relies on mutable module exports which ESM forbids). Deletes webpack/karma + browser polyfills. Replaces winston with console-backed logger. CJS-named-import conversions for @dashevo/dashcore-lib, @dashevo/wasm-dpp, @dashevo/grpc-common, and the system-id imports from contract packages. Adds parallel 'import type' aliases (via 'import type { X as XType }; type X = XType;') in 22 .ts files where destructured value names are also used as types — works around TS2440 namespace collision under NodeNext.
platform-test-suite: converted ~35 files from CJS to ESM. Test bootstrap converted; .mocharc.yml uses 'import:' for ESM root hooks.
Defensive (loadDpp.default ?? loadDpp)() unwrap in 4 sites (Platform.initializeDppModule, createIdentityFixtureInAccount, contracts/get.spec.ts, contracts/history.spec.ts). createGrpcTransportError-style dual-format byte handling preserved.
Test results: 752 passing across the monorepo (dapi-client 315, wallet-lib 377, js-dash-sdk 60). platform-test-suite is end-to-end and requires live testnet credentials in .env (pre-existing constraint).
0308e5b to
1464665
Compare
Summary
Final PR in the ESM migration stack. Converts
@dashevo/dapi-client,@dashevo/wallet-lib,dash(@dashevo/js-dash-sdk), and@dashevo/platform-test-suiteto pure ESM. Removes the webpack/karma browser-bundle infrastructure across all four packages.This is PR 3 of 3 (after the originally-stacked PRs 3/4/5 were merged into one — see note below). Depends on #3679 (cleanup) and #3680 (Uint8Array).
Note
Originally split into three stacked PRs (#3681 dapi-client, #3682 wallet-lib, #3683 js-dash-sdk+PTS), each on a separate branch. CI fails on any of them in isolation because the monorepo build compiles every package on every PR, and dapi-client-ESM + wallet-lib-CJS is an incompatible mid-state (CJS cannot
require()ESM). Since they must land together regardless, they are combined here as a single PR with three commits preserved for review focus.What changes
@dashevo/dapi-client(commit 1:refactor(dapi-client)!: convert to ESM, drop webpack/karma)package.json:"type": "module","exports": { ".": "./lib/index.js", "./lib/*": "./lib/*" }, dropsmain. The wildcard preserves deep-import paths.require/module.exportsto ESMimport/export. Relative imports include.jsextension.eventsnpm package moved devDeps → deps. Used forEventEmitterinDAPIClient,ReconnectableStream,BlockHeadersProvider. Works in both Node and browser bundlers.webpack.config.js,karma.conf.js,lib/test/karma/. Removes all browser-polyfill devDeps (buffer,crypto-browserify,os-browserify,path-browserify,process,stream-browserify,string_decoder,url,util,webpack,webpack-cli,karma-*,babel-*)..mocharc.ymlusesrequire:for the ESM bootstrap with a defensiveloadDpp.default ?? loadDppunwrap.@dashevo/wallet-lib(commit 2:refactor(wallet-lib)!: convert to ESM, consume ESM dapi-client)package.json:"type": "module",engines.node >= 18.18. Dropsbuild:web,test:browsers*,prepublishOnly. Removes all browser-polyfill devDeps. Removes depswinston,node-inspect-extracted.src/,fixtures/, andtests/converted from CJS to ESM with.jsextensions on relative imports.src/logger/index.jsrewritten as a console-backed logger (matching dapi-client's pattern from PR 1).@dashevo/dapi-clientupdated to include.jsextensions.webpack.config.js,karma/,src/test/karma/..mocharc.ymlusesrequire:for ESM bootstrap.dash/@dashevo/js-dash-sdk(commit 3:refactor(js-dash-sdk,platform-test-suite)!: convert to ESM, consume ESM dapi-client and wallet-lib)package.json:"type": "module",exportsmap,engines.node >= 18.18. Dropsunpkg/browser,test:browsers*. Removeskarma-*,webpack, browser polyfills,ts-mock-imports(replaced with an injectable seam).tsconfig.build.jsonandtsconfig.json:module: nodenext,moduleResolution: nodenext,target: es2020..tsfiles get.jsextensions on relative imports (NodeNext requirement).src/SDK/Client/Platform/methods/names/register.tsto accept an injectablerandomBytesparameter (replaces ts-mock-imports for the existing test seam).winstonwith console-backed logger.import { X } from cjs-pkg→import pkg from cjs-pkg; const { X } = pkg;) fordashcore-lib,wasm-dpp,grpc-common, contract packages.import type { X as XType }; type X = XType;aliases in 22.tsfiles where destructured value names are also used in type positions (TS2440 workaround under NodeNext).(loadDpp.default ?? loadDpp)()unwrap in 4 sites.webpack.config.js,webpack.base.config.js,karma/.@dashevo/platform-test-suite(also in commit 3)package.json:"type": "module", dropstest:browsers, browser polyfills,karma-*..jsfiles converted to ESM.lib/test/bootstrap.jsrewritten as ESM withfileURLToPath-based__dirname..mocharc.ymlusesimport:for ESM root hooks.Test plan
yarn workspace @dashevo/dapi-client run test:unit— 315 passing ✅yarn workspace @dashevo/wallet-lib run test:unit— 377 passing, 2 pending ✅yarn workspace dash run test:unit— 60 passing, 3 pre-existing pending ✅yarn workspace @dashevo/platform-test-suite run lint— ESM bootstrap loads ✅.env(pre-existing constraint).Breaking changes
require()of them no longer works..jsextensions.Platform.names.register(...)gained an optional trailingrandomBytesparameter. Existing callers unaffected.winston,bs58,node-inspect-extractedfrom deps where unused.Monorepo state after this lands
@dashevo/dapi-client— ESM,Uint8ArrayAPI, browser-bundler-friendly@dashevo/wallet-lib— ESMdash— ESM TypeScript@dashevo/platform-test-suite— ESM@dashevo/dashmate— was already ESM, works as-is@dashevo/dapi-grpc— patched in PR 1 (chore(dapi): cleanup — drop unused deps, inline winston/fetch/promisify shims #3679); rest unchangedStack