Skip to content

Commit 3b539e3

Browse files
thephezclaude
andauthored
chore: fix tsconfig to check setupDashClient with correct ESM settings (#64)
* chore: fix tsconfig to check setupDashClient with correct ESM settings Update tsconfig.json to use module/moduleResolution node16 for proper .mjs ESM support, scope include to setupDashClient.mjs only, and set maxNodeModuleJsDepth to 0. Add @types/node and @types/mocha devDeps. Add JSDoc type annotations to setupDashClient.mjs to pass strict tsc check. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: replace any types with precise SDK types in setupDashClient Add JSDoc typedef imports for Identity, IdentityPublicKey, PlatformAddress, PlatformAddressInfo, and NetworkLike. Define DerivedKeyEntry and AddressEntry typedefs. Replace all any params/returns with real SDK types. Add missing @returns to create(), convenience signer methods, and exported functions. Add identity-not-found guard in getSigner() with corresponding test. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b3cc55e commit 3b539e3

5 files changed

Lines changed: 160 additions & 23 deletions

File tree

package-lock.json

Lines changed: 47 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
"dotenv": "17.3.1"
2828
},
2929
"devDependencies": {
30+
"@types/mocha": "10.0.10",
31+
"@types/node": "25.5.0",
3032
"chai": "6.2.2",
3133
"eslint": "8.45.0",
3234
"mocha": "11.7.5"

setupDashClient.mjs

Lines changed: 87 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,27 @@ try {
2121
/* dotenv not installed */
2222
}
2323

24+
/** @typedef {import('@dashevo/evo-sdk').Identity} Identity */
25+
/** @typedef {import('@dashevo/evo-sdk').IdentityPublicKey} IdentityPublicKey */
26+
/** @typedef {import('@dashevo/evo-sdk').PlatformAddress} PlatformAddress */
27+
/** @typedef {import('@dashevo/evo-sdk').PlatformAddressInfo} PlatformAddressInfo */
28+
/** @typedef {import('@dashevo/evo-sdk').NetworkLike} NetworkLike */
29+
30+
/**
31+
* @typedef {Object} DerivedKeyEntry
32+
* @property {number} keyId
33+
* @property {string} privateKeyWif
34+
* @property {string} [publicKey] - Only present via createForNewIdentity()
35+
*/
36+
37+
/**
38+
* @typedef {Object} AddressEntry
39+
* @property {PlatformAddress} address
40+
* @property {string} bech32m
41+
* @property {string} privateKeyWif
42+
* @property {string} path
43+
*/
44+
2445
// ⚠️ Tutorial helper — holds WIFs in memory for convenience.
2546
// Do not use this pattern as-is for production key management.
2647

@@ -44,6 +65,10 @@ const clientConfig = {
4465
* Build a DIP-13 identity key derivation path.
4566
* Returns the full 7-level hardened path:
4667
* m/9'/{coin}'/5'/0'/0'/{identityIndex}'/{keyIndex}'
68+
* @param {string} network
69+
* @param {number} identityIndex
70+
* @param {number} keyIndex
71+
* @returns {Promise<string>}
4772
*/
4873
export async function dip13KeyPath(network, identityIndex, keyIndex) {
4974
const base =
@@ -57,12 +82,18 @@ export async function dip13KeyPath(network, identityIndex, keyIndex) {
5782
// SDK client helpers
5883
// ---------------------------------------------------------------------------
5984

85+
/**
86+
* Create and connect an EvoSDK client for the selected network.
87+
*
88+
* @param {string} [network='testnet']
89+
* @returns {Promise<EvoSDK>}
90+
*/
6091
export async function createClient(network = 'testnet') {
61-
const factories = {
92+
const factories = /** @type {Record<string, () => EvoSDK>} */ ({
6293
testnet: () => EvoSDK.testnetTrusted(),
6394
mainnet: () => EvoSDK.mainnetTrusted(),
6495
local: () => EvoSDK.localTrusted(),
65-
};
96+
});
6697

6798
const factory = factories[network];
6899
if (!factory) {
@@ -71,7 +102,7 @@ export async function createClient(network = 'testnet') {
71102
);
72103
}
73104

74-
const sdk = factory();
105+
const sdk = /** @type {EvoSDK} */ (factory());
75106
await sdk.connect();
76107
return sdk;
77108
}
@@ -125,6 +156,12 @@ const KEY_SPECS = [
125156
* Key 4 = ENCRYPTION MEDIUM (encrypted messaging/data)
126157
*/
127158
class IdentityKeyManager {
159+
/**
160+
* @param {EvoSDK} sdk
161+
* @param {string|null|undefined} identityId
162+
* @param {Record<string, DerivedKeyEntry>} keys
163+
* @param {number} identityIndex
164+
*/
128165
constructor(sdk, identityId, keys, identityIndex) {
129166
this.sdk = sdk;
130167
this.id = identityId;
@@ -141,12 +178,13 @@ class IdentityKeyManager {
141178
* Derives all standard identity keys using DIP-9 paths.
142179
*
143180
* @param {object} opts
144-
* @param {object} opts.sdk - Connected EvoSDK instance
181+
* @param {EvoSDK} opts.sdk - Connected EvoSDK instance
145182
* @param {string} [opts.identityId] - Identity ID. If omitted, auto-resolved
146183
* from the mnemonic by looking up the master key's public key hash on-chain.
147184
* @param {string} opts.mnemonic - BIP39 mnemonic
148185
* @param {string} [opts.network='testnet'] - 'testnet' or 'mainnet'
149186
* @param {number} [opts.identityIndex=0] - Which identity derived from this mnemonic
187+
* @returns {Promise<IdentityKeyManager>}
150188
*/
151189
static async create({
152190
sdk,
@@ -155,7 +193,7 @@ class IdentityKeyManager {
155193
network = 'testnet',
156194
identityIndex = 0,
157195
}) {
158-
const derive = async (keyIndex) =>
196+
const derive = async (/** @type {number} */ keyIndex) =>
159197
wallet.deriveKeyFromSeedWithPath({
160198
mnemonic,
161199
path: await dip13KeyPath(network, identityIndex, keyIndex),
@@ -211,7 +249,7 @@ class IdentityKeyManager {
211249
* Find the first unused DIP-9 identity index for a mnemonic.
212250
* Scans indices starting at 0 until no on-chain identity is found.
213251
*
214-
* @param {object} sdk - Connected EvoSDK instance
252+
* @param {EvoSDK} sdk - Connected EvoSDK instance
215253
* @param {string} mnemonic - BIP39 mnemonic
216254
* @param {string} [network='testnet'] - 'testnet' or 'mainnet'
217255
* @returns {Promise<number>} The first unused identity index
@@ -240,7 +278,7 @@ class IdentityKeyManager {
240278
* If identityIndex is omitted, auto-selects the next unused index.
241279
*
242280
* @param {object} opts
243-
* @param {object} opts.sdk - Connected EvoSDK instance
281+
* @param {EvoSDK} opts.sdk - Connected EvoSDK instance
244282
* @param {string} opts.mnemonic - BIP39 mnemonic
245283
* @param {string} [opts.network='testnet'] - 'testnet' or 'mainnet'
246284
* @param {number} [opts.identityIndex] - Identity index (auto-scanned if omitted)
@@ -255,7 +293,7 @@ class IdentityKeyManager {
255293
const idx =
256294
identityIndex ??
257295
(await IdentityKeyManager.findNextIndex(sdk, mnemonic, network));
258-
const derive = async (keyIndex) =>
296+
const derive = async (/** @type {number} */ keyIndex) =>
259297
wallet.deriveKeyFromSeedWithPath({
260298
mnemonic,
261299
path: await dip13KeyPath(network, idx, keyIndex),
@@ -339,7 +377,7 @@ class IdentityKeyManager {
339377
/**
340378
* Fetch identity and build { identity, identityKey, signer } for a given key.
341379
* @param {string} keyName - One of: master, auth, authHigh, transfer, encryption
342-
* @returns {{ identity, identityKey, signer }}
380+
* @returns {Promise<{ identity: Identity, identityKey: IdentityPublicKey | undefined, signer: IdentitySigner }>}
343381
*/
344382
async getSigner(keyName) {
345383
if (!this.id) {
@@ -348,42 +386,60 @@ class IdentityKeyManager {
348386
'or create/register the identity first and then set the ID.',
349387
);
350388
}
351-
const key = this.keys[keyName];
389+
const key = /** @type {Record<string, DerivedKeyEntry>} */ (this.keys)[
390+
keyName
391+
];
352392
if (!key) {
353393
throw new Error(
354394
`Unknown key "${keyName}". Use: ${Object.keys(this.keys).join(', ')}`,
355395
);
356396
}
357397
const identity = await this.sdk.identities.fetch(this.id);
398+
if (!identity) {
399+
throw new Error(`Identity "${this.id}" not found on-chain.`);
400+
}
358401
const identityKey = identity.getPublicKeyById(key.keyId);
359402
const signer = new IdentitySigner();
360403
signer.addKeyFromWif(key.privateKeyWif);
361404
return { identity, identityKey, signer };
362405
}
363406

364-
/** CRITICAL auth (key 2) — contracts, documents, names. */
407+
/**
408+
* CRITICAL auth (key 2) — contracts, documents, names.
409+
* @returns {Promise<{ identity: Identity, identityKey: IdentityPublicKey | undefined, signer: IdentitySigner }>}
410+
*/
365411
async getAuth() {
366412
return this.getSigner('auth');
367413
}
368414

369-
/** HIGH auth (key 1) — documents, names. */
415+
/**
416+
* HIGH auth (key 1) — documents, names.
417+
* @returns {Promise<{ identity: Identity, identityKey: IdentityPublicKey | undefined, signer: IdentitySigner }>}
418+
*/
370419
async getAuthHigh() {
371420
return this.getSigner('authHigh');
372421
}
373422

374-
/** TRANSFER — credit transfers, withdrawals. */
423+
/**
424+
* TRANSFER — credit transfers, withdrawals.
425+
* @returns {Promise<{ identity: Identity, identityKey: IdentityPublicKey | undefined, signer: IdentitySigner }>}
426+
*/
375427
async getTransfer() {
376428
return this.getSigner('transfer');
377429
}
378430

379-
/** ENCRYPTION MEDIUM — encrypted messaging/data. */
431+
/**
432+
* ENCRYPTION MEDIUM — encrypted messaging/data.
433+
* @returns {Promise<{ identity: Identity, identityKey: IdentityPublicKey | undefined, signer: IdentitySigner }>}
434+
*/
380435
async getEncryption() {
381436
return this.getSigner('encryption');
382437
}
383438

384439
/**
385440
* MASTER — identity updates (add/disable keys).
386441
* @param {string[]} [additionalKeyWifs] - WIFs for new keys being added
442+
* @returns {Promise<{ identity: Identity, identityKey: IdentityPublicKey | undefined, signer: IdentitySigner }>}
387443
*/
388444
async getMaster(additionalKeyWifs) {
389445
const result = await this.getSigner('master');
@@ -409,6 +465,11 @@ class IdentityKeyManager {
409465
* that hold credits directly, independent of identities.
410466
*/
411467
class AddressKeyManager {
468+
/**
469+
* @param {EvoSDK} sdk
470+
* @param {AddressEntry[]} addresses
471+
* @param {string} network
472+
*/
412473
constructor(sdk, addresses, network) {
413474
this.sdk = sdk;
414475
this.addresses = addresses; // [{ address, bech32m, privateKeyWif, path }]
@@ -425,10 +486,11 @@ class AddressKeyManager {
425486
* Derives platform address keys using BIP44 paths.
426487
*
427488
* @param {object} opts
428-
* @param {object} opts.sdk - Connected EvoSDK instance
489+
* @param {EvoSDK} opts.sdk - Connected EvoSDK instance
429490
* @param {string} opts.mnemonic - BIP39 mnemonic
430491
* @param {string} [opts.network='testnet'] - 'testnet' or 'mainnet'
431492
* @param {number} [opts.count=1] - Number of addresses to derive
493+
* @returns {Promise<AddressKeyManager>}
432494
*/
433495
static async create({ sdk, mnemonic, network = 'testnet', count = 1 }) {
434496
const addresses = [];
@@ -452,7 +514,9 @@ class AddressKeyManager {
452514

453515
addresses.push({
454516
address: platformAddress,
455-
bech32m: platformAddress.toBech32m(network),
517+
bech32m: platformAddress.toBech32m(
518+
/** @type {NetworkLike} */ (network),
519+
),
456520
privateKeyWif: obj.privateKeyWif,
457521
path,
458522
});
@@ -488,7 +552,7 @@ class AddressKeyManager {
488552

489553
/**
490554
* Fetch current balance and nonce for the primary address.
491-
* @returns {Promise<PlatformAddressInfo|undefined>}
555+
* @returns {Promise<PlatformAddressInfo | undefined>}
492556
*/
493557
async getInfo() {
494558
return this.sdk.addresses.get(this.primaryAddress.bech32m);
@@ -497,7 +561,7 @@ class AddressKeyManager {
497561
/**
498562
* Fetch current balance and nonce for an address by index.
499563
* @param {number} index - Address index
500-
* @returns {Promise<PlatformAddressInfo|undefined>}
564+
* @returns {Promise<PlatformAddressInfo | undefined>}
501565
*/
502566
async getInfoAt(index) {
503567
const entry = this.addresses[index];
@@ -514,9 +578,13 @@ class AddressKeyManager {
514578
// setupDashClient — convenience wrapper
515579
// ---------------------------------------------------------------------------
516580

581+
/**
582+
* @param {{requireIdentity?: boolean, identityIndex?: number}} opts
583+
* @returns {Promise<{ sdk: EvoSDK, keyManager: IdentityKeyManager | undefined, addressKeyManager: AddressKeyManager | undefined }>}
584+
*/
517585
export async function setupDashClient({
518586
requireIdentity = true,
519-
identityIndex,
587+
identityIndex = undefined,
520588
} = {}) {
521589
const { network, mnemonic } = clientConfig;
522590

test/setupDashClient.test.mjs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,26 @@ describe('IdentityKeyManager', function suite() {
444444
expect(err.message).to.include('Unknown key "bogus"');
445445
}
446446
});
447+
448+
it('should throw when identity is not found on-chain', async function () {
449+
const fakeSdk = {
450+
identities: {
451+
fetch: async () => undefined,
452+
byPublicKeyHash: async () => ({ id: 'fake-id' }),
453+
},
454+
};
455+
const km = await IdentityKeyManager.create({
456+
sdk: fakeSdk,
457+
identityId: 'nonexistent-id',
458+
mnemonic: TEST_MNEMONIC,
459+
});
460+
try {
461+
await km.getAuth();
462+
expect.fail('should have thrown');
463+
} catch (err) {
464+
expect(err.message).to.include('not found on-chain');
465+
}
466+
});
447467
});
448468

449469
describe('create() error paths', function () {

0 commit comments

Comments
 (0)