]> git.codecow.com Git - nano25519.git/commitdiff
Refactor API.
authorChris Duncan <chris@codecow.com>
Tue, 17 Mar 2026 20:47:27 +0000 (13:47 -0700)
committerChris Duncan <chris@codecow.com>
Tue, 17 Mar 2026 20:47:27 +0000 (13:47 -0700)
Refactor host code to compile synchronously, return both sync and async functions, merge trace logging function into wasm env, refactor worker listening process, add type checks to worker message parsing, and emit type definitions.
Update testing page to add NanoNaCl to global context in test page to enable testing ad hoc commands, add tests for sync functions using byte arrays, add pause when starting each test run to allow DOM to repaint status text, and always run self-check.
Rename main assembly file.
Improve documentation and reorganize functions.
Update dependencies.

assembly/index.ts [moved from assembly/nano-nacl.ts with 100% similarity]
index.html
index.ts
package-lock.json
package.json
tsconfig.json

similarity index 100%
rename from assembly/nano-nacl.ts
rename to assembly/index.ts
index 656709b443c5c1b42400d329898bf04fa164602a..8be9240ffc2c535d042872822e00ff3b7f3623d9 100644 (file)
@@ -31,6 +31,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
                                }
                        }
                }
+               window.NanoNaCl = NanoNaCl
                try {
                        ({ TEST_VECTORS } = await import('./env.mjs'))
                } catch (err) {
@@ -111,173 +112,205 @@ SPDX-License-Identifier: GPL-3.0-or-later
                        return [...bytes].map(b => b.toString(16).padStart(2, '0')).join('')
                }
 
-               export async function test (api, size, runs, isDebug, isSelfCheck) {
+               const derive = (sk, api) => {
+                       const sk8 = new Uint8Array(sk.match(/.{2}/g).map(b => parseInt(b, 16)))
+                       switch (api) {
+                               case 'NanoNaCl': {
+                                       const pk8 = NanoNaCl.derive(sk8)
+                                       return [...pk8].map(b => b.toString(16).padStart(2, '0')).join('')
+                               }
+                               case 'NanoNaCl (async)': {
+                                       return NanoNaCl.deriveAsync(sk)
+                               }
+                               case 'NanocurrencyWeb': {
+                                       return NanocurrencyWeb.wallet.legacyAccounts(sk)[0].publicKey
+                               }
+                               case 'Sodium': {
+                                       return sodium.crypto_sign_seed_keypair(sk8, 'hex').publicKey
+                               }
+                               case 'TweetNaCl.js': {
+                                       const sk8 = sk.match(/.{2}/g).map(b => parseInt(b, 16))
+                                       const pk8 = nacl.sign.keyPair.fromSeed(sk8).publicKey
+                                       return [...pk8].map(b => b.toString(16).padStart(2, '0')).join('')
+                               }
+                               default: {
+                                       return null
+                               }
+                       }
+               }
+
+               const sign = (h, sk, pk, api) => {
+                       const h8 = new Uint8Array(h.match(/.{2}/g).map(b => parseInt(b, 16)))
+                       const sk8 = new Uint8Array((sk + pk).match(/.{2}/g).map(b => parseInt(b, 16)))
+                       switch (api) {
+                               case 'NanoNaCl': {
+                                       const s8 = NanoNaCl.sign(h8, sk8)
+                                       return [...s8].map(b => b.toString(16).padStart(2, '0')).join('')
+                               }
+                               case 'NanoNaCl (async)': {
+                                       return NanoNaCl.signAsync(h, sk + pk)
+                               }
+                               case 'NanocurrencyWeb': {
+                                       const { privateKey } = NanocurrencyWeb.wallet.legacyAccounts(sk)[0]
+                                       return NanocurrencyWeb.tools.sign(privateKey, h)
+                               }
+                               case 'Sodium': {
+                                       return sodium.crypto_sign_detached(new Uint8Array(h8), new Uint8Array(sk8), 'hex')
+                               }
+                               case 'TweetNaCl.js': {
+                                       const s8 = nacl.sign.detached(new Uint8Array(h8), new Uint8Array(sk8))
+                                       return [...s8].map(b => b.toString(16).padStart(2, '0')).join('')
+                               }
+                               default: {
+                                       return null
+                               }
+                       }
+               }
+
+               const verify = (s, h, pk, api) => {
+                       const h8 = new Uint8Array(h.match(/.{2}/g).map(b => parseInt(b, 16)))
+                       const s8 = new Uint8Array(s.match(/.{2}/g).map(b => parseInt(b, 16)))
+                       const pk8 = new Uint8Array(pk.match(/.{2}/g).map(b => parseInt(b, 16)))
+                       switch (api) {
+                               case 'NanoNaCl': {
+                                       return NanoNaCl.verify(s8, h8, pk8)
+                               }
+                               case 'NanoNaCl (async)': {
+                                       return NanoNaCl.verifyAsync(s, h, pk)
+                               }
+                               case 'NanocurrencyWeb': {
+                                       return NanocurrencyWeb.tools.verify(pk, s, h)
+                               }
+                               case 'Sodium': {
+                                       return sodium.crypto_sign_verify_detached(s8, h8, pk8)
+                               }
+                               case 'TweetNaCl.js': {
+                                       return nacl.sign.detached.verify(h8, s8, pk8)
+                               }
+                               default: {
+                                       return null
+                               }
+                       }
+               }
+
+               export async function test (api, size, runs, isDebug) {
                        if (typeof size !== 'number' || size < 1) {
                                size = 1
                        }
                        if (typeof runs !== 'number' || runs < 1) {
                                runs = 1
                        }
-                       const selectedApi = api
-                       // Execute once on load to initialize worker and WASM
-                       const h = random(), k = random(64)
-                       await NanoNaCl.sign(h, k)
 
-                       const derive = sk => {
-                               const sk8 = new Uint8Array(sk.match(/.{2}/g).map(b => parseInt(b, 16)))
-                               switch (api) {
-                                       case 'NanoNaCl': {
-                                               const pk8 = NanoNaCl.deriveSync(sk8)
-                                               return [...pk8].map(b => b.toString(16).padStart(2, '0')).join('')
-                                       }
-                                       case 'NanocurrencyWeb': {
-                                               return NanocurrencyWeb.wallet.legacyAccounts(sk)[0].publicKey
-                                       }
-                                       case 'Sodium': {
-                                               return sodium.crypto_sign_seed_keypair(sk8, 'hex').publicKey
-                                       }
-                                       case 'TweetNaCl.js': {
-                                               const sk8 = sk.match(/.{2}/g).map(b => parseInt(b, 16))
-                                               const pk8 = nacl.sign.keyPair.fromSeed(sk8).publicKey
-                                               return [...pk8].map(b => b.toString(16).padStart(2, '0')).join('')
-                                       }
-                                       default: {
-                                               return null
-                                       }
-                               }
-                       }
-                       const sign = (h, sk, pk) => {
-                               const h8 = new Uint8Array(h.match(/.{2}/g).map(b => parseInt(b, 16)))
-                               const sk8 = new Uint8Array((sk + pk).match(/.{2}/g).map(b => parseInt(b, 16)))
-                               switch (api) {
-                                       case 'NanoNaCl': {
-                                               const s8 = NanoNaCl.signSync(h8, sk8)
-                                               return [...s8].map(b => b.toString(16).padStart(2, '0')).join('')
-                                       }
-                                       case 'NanocurrencyWeb': {
-                                               const { privateKey } = NanocurrencyWeb.wallet.legacyAccounts(sk)[0]
-                                               return NanocurrencyWeb.tools.sign(privateKey, h)
-                                       }
-                                       case 'Sodium': {
-                                               return sodium.crypto_sign_detached(new Uint8Array(h8), new Uint8Array(sk8), 'hex')
-                                       }
-                                       case 'TweetNaCl.js': {
-                                               const s8 = nacl.sign.detached(new Uint8Array(h8), new Uint8Array(sk8))
-                                               return [...s8].map(b => b.toString(16).padStart(2, '0')).join('')
-                                       }
-                                       default: {
-                                               return null
-                                       }
-                               }
-                       }
-                       const verify = (s, h, pk) => {
-                               const h8 = new Uint8Array(h.match(/.{2}/g).map(b => parseInt(b, 16)))
-                               const s8 = new Uint8Array(s.match(/.{2}/g).map(b => parseInt(b, 16)))
-                               const pk8 = new Uint8Array(pk.match(/.{2}/g).map(b => parseInt(b, 16)))
-                               switch (api) {
-                                       case 'NanoNaCl': {
-                                               const v = NanoNaCl.verifySync(s8, h8, pk8)
-                                               return v[0] === 0
-                                       }
-                                       case 'NanocurrencyWeb': {
-                                               return NanocurrencyWeb.tools.verify(pk, s, h)
-                                       }
-                                       case 'Sodium': {
-                                               return sodium.crypto_sign_verify_detached(s8, h8, pk8)
-                                       }
-                                       case 'TweetNaCl.js': {
-                                               return nacl.sign.detached.verify(h8, s8, pk8)
-                                       }
-                                       default: {
-                                               return null
-                                       }
-                               }
+                       // self-check
+                       document.getElementById('status').innerHTML = `RUNNING SELF-CHECK`
+                       console.log(`%cNanoNaCl`, 'color:green', 'Checking validation against known values')
+                       // https://docs.nano.org/integration-guides/key-management/#success-response_2
+                       TEST_VECTORS ??= {
+                               blockHash: 'BB569136FA05F8CBF65CEF2EDE368475B289C4477342976556BA4C0DDF216E45',
+                               blockHashBytes: new Uint8Array([0xBB, 0x56, 0x91, 0x36, 0xFA, 0x05, 0xF8, 0xCB, 0xF6, 0x5C, 0xEF, 0x2E, 0xDE, 0x36, 0x84, 0x75, 0xB2, 0x89, 0xC4, 0x47, 0x73, 0x42, 0x97, 0x65, 0x56, 0xBA, 0x4C, 0x0D, 0xDF, 0x21, 0x6E, 0x45]),
+                               privateKey: '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3',
+                               privateKeyBytes: new Uint8Array([0x78, 0x11, 0x86, 0xFB, 0x9E, 0xF1, 0x7D, 0xB6, 0xE3, 0xD1, 0x05, 0x65, 0x50, 0xD9, 0xFA, 0xE5, 0xD5, 0xBB, 0xAD, 0xA6, 0xA6, 0xBC, 0x37, 0x0E, 0x4C, 0xBB, 0x93, 0x8B, 0x1D, 0xC7, 0x1D, 0xA3]),
+                               publicKey: '3068BB1CA04525BB0E416C485FE6A67FD52540227D267CC8B6E8DA958A7FA039',
+                               publicKeyBytes: new Uint8Array([0x30, 0x68, 0xBB, 0x1C, 0xA0, 0x45, 0x25, 0xBB, 0x0E, 0x41, 0x6C, 0x48, 0x5F, 0xE6, 0xA6, 0x7F, 0xD5, 0x25, 0x40, 0x22, 0x7D, 0x26, 0x7C, 0xC8, 0xB6, 0xE8, 0xDA, 0x95, 0x8A, 0x7F, 0xA0, 0x39]),
+                               secretKey: '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA33068BB1CA04525BB0E416C485FE6A67FD52540227D267CC8B6E8DA958A7FA039',
+                               secretKeyBytes: new Uint8Array([0x78, 0x11, 0x86, 0xFB, 0x9E, 0xF1, 0x7D, 0xB6, 0xE3, 0xD1, 0x05, 0x65, 0x50, 0xD9, 0xFA, 0xE5, 0xD5, 0xBB, 0xAD, 0xA6, 0xA6, 0xBC, 0x37, 0x0E, 0x4C, 0xBB, 0x93, 0x8B, 0x1D, 0xC7, 0x1D, 0xA3, 0x30, 0x68, 0xBB, 0x1C, 0xA0, 0x45, 0x25, 0xBB, 0x0E, 0x41, 0x6C, 0x48, 0x5F, 0xE6, 0xA6, 0x7F, 0xD5, 0x25, 0x40, 0x22, 0x7D, 0x26, 0x7C, 0xC8, 0xB6, 0xE8, 0xDA, 0x95, 0x8A, 0x7F, 0xA0, 0x39]),
+                               signature: '74BCC59DBA39A1E34A5F75F96D6DE9154E3477AAD7DE30EA563DFCFE501A804228008F98DDF4A15FD35705102785C50EF76732C3A74B0FEC5B0DD67B574A5900',
+                               signatureBytes: new Uint8Array([0x74, 0xBC, 0xC5, 0x9D, 0xBA, 0x39, 0xA1, 0xE3, 0x4A, 0x5F, 0x75, 0xF9, 0x6D, 0x6D, 0xE9, 0x15, 0x4E, 0x34, 0x77, 0xAA, 0xD7, 0xDE, 0x30, 0xEA, 0x56, 0x3D, 0xFC, 0xFE, 0x50, 0x1A, 0x80, 0x42, 0x28, 0x00, 0x8F, 0x98, 0xDD, 0xF4, 0xA1, 0x5F, 0xD3, 0x57, 0x05, 0x10, 0x27, 0x85, 0xC5, 0x0E, 0xF7, 0x67, 0x32, 0xC3, 0xA7, 0x4B, 0x0F, 0xEC, 0x5B, 0x0D, 0xD6, 0x7B, 0x57, 0x4A, 0x59, 0x00]),
+                               badSignature: '74BCC59DBA39A1E34A5F75F96D6DE9154E3477AAD7DE30EA563DFCFE501A804215d484f5f757b4b7a9f4fcb2057fa423f76732c3a74b0fec5b0dd67b574a5910'
                        }
+                       const zeroes = '0000000000000000000000000000000000000000000000000000000000000000'
+                       const expect = []
+                       let result
 
-                       if (isSelfCheck) {
-                               api = 'NanoNaCl'
-                               document.getElementById('status').innerHTML = `RUNNING SELF-CHECK`
-                               console.log(`%cNanoNaCl`, 'color:green', 'Checking validation against known values')
-                               // https://docs.nano.org/integration-guides/key-management/#success-response_2
-                               TEST_VECTORS ??= {
-                                       blockHash: 'BB569136FA05F8CBF65CEF2EDE368475B289C4477342976556BA4C0DDF216E45',
-                                       privateKey: '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3',
-                                       publicKey: '3068BB1CA04525BB0E416C485FE6A67FD52540227D267CC8B6E8DA958A7FA039',
-                                       signature: '74BCC59DBA39A1E34A5F75F96D6DE9154E3477AAD7DE30EA563DFCFE501A804228008F98DDF4A15FD35705102785C50EF76732C3A74B0FEC5B0DD67B574A5900',
-                                       badSignature: '74BCC59DBA39A1E34A5F75F96D6DE9154E3477AAD7DE30EA563DFCFE501A804215d484f5f757b4b7a9f4fcb2057fa423f76732c3a74b0fec5b0dd67b574a5910'
-                               }
-                               const zeroes = '0000000000000000000000000000000000000000000000000000000000000000'
-                               const expect = []
-                               let result
+                       // PASS
+                       result = NanoNaCl.derive(TEST_VECTORS.privateKeyBytes)
+                       console.log('expected: ', TEST_VECTORS.publicKeyBytes)
+                       console.log('actual:', result)
+                       result = [...result].map(b => b.toString(16).padStart(2, '0')).join('').toUpperCase() === TEST_VECTORS.publicKey.toUpperCase()
+                       console.log(`derive() output for good private key is ${result === true ? 'correct' : 'incorrect'}`)
+                       expect.push(result)
 
-                               // PASS
-                               result = await derive(TEST_VECTORS.privateKey)
-                               console.log('expected: ', TEST_VECTORS.publicKey.toUpperCase())
-                               console.log('actual', result?.toUpperCase?.())
-                               result = result.toUpperCase() === TEST_VECTORS.publicKey.toUpperCase()
-                               console.log(`derive() output for good private key is ${result === true ? 'correct' : 'incorrect'}`)
-                               expect.push(result)
+                       result = NanoNaCl.sign(TEST_VECTORS.blockHashBytes, TEST_VECTORS.secretKeyBytes)
+                       console.log('expected: ', TEST_VECTORS.signatureBytes)
+                       console.log('actual:', result)
+                       result = [...result].map(b => b.toString(16).padStart(2, '0')).join('').toUpperCase() === TEST_VECTORS.signature.toUpperCase()
+                       console.log(`sign() output for good block hash and secret key is ${result === true ? 'correct' : 'incorrect'}`)
+                       expect.push(result)
 
-                               result = await sign(TEST_VECTORS.blockHash, TEST_VECTORS.privateKey, TEST_VECTORS.publicKey)
-                               console.log('expected: ', TEST_VECTORS.signature.toUpperCase())
-                               console.log('actual', result?.toUpperCase?.())
-                               result = result.toUpperCase() === TEST_VECTORS.signature.toUpperCase()
-                               console.log(`sign() output for good block hash and secret key is ${result === true ? 'correct' : 'incorrect'}`)
-                               expect.push(result)
+                       result = NanoNaCl.verify(TEST_VECTORS.signatureBytes, TEST_VECTORS.blockHashBytes, TEST_VECTORS.publicKeyBytes)
+                       console.log('expected:', true)
+                       console.log('actual:', result)
+                       result = result === true
+                       console.log(`verify() output for good block hash, signature, and private key is ${result === true ? 'correct' : 'incorrect'}`)
+                       expect.push(result)
 
-                               result = await verify(TEST_VECTORS.signature, TEST_VECTORS.blockHash, TEST_VECTORS.publicKey)
-                               console.log(result)
-                               result = result === true
-                               console.log(`verify() output for good block hash, signature, and private key is ${result === true ? 'correct' : 'incorrect'}`)
-                               expect.push(result)
+                       result = await NanoNaCl.deriveAsync(TEST_VECTORS.privateKey)
+                       console.log('expected: ', TEST_VECTORS.publicKey.toUpperCase())
+                       console.log('actual:', result?.toUpperCase?.())
+                       result = result.toUpperCase() === TEST_VECTORS.publicKey.toUpperCase()
+                       console.log(`deriveAsync() output for good private key is ${result === true ? 'correct' : 'incorrect'}`)
+                       expect.push(result)
 
-                               // XFAIL
-                               result = await verify(TEST_VECTORS.badSignature, TEST_VECTORS.blockHash, TEST_VECTORS.publicKey)
-                               console.log(result)
-                               result = result === false
-                               console.log(`verify() output for non-canonical signature is ${result === true ? 'correct' : 'incorrect'}`)
-                               expect.push(result)
+                       result = await NanoNaCl.signAsync(TEST_VECTORS.blockHash, TEST_VECTORS.secretKey)
+                       console.log('expected: ', TEST_VECTORS.signature.toUpperCase())
+                       console.log('actual:', result?.toUpperCase?.())
+                       result = result.toUpperCase() === TEST_VECTORS.signature.toUpperCase()
+                       console.log(`signAsync() output for good block hash and secret key is ${result === true ? 'correct' : 'incorrect'}`)
+                       expect.push(result)
 
-                               result = await verify(TEST_VECTORS.signature, random(), TEST_VECTORS.publicKey)
-                               console.log(result)
-                               result = result === false
-                               console.log(`verify() output for random block hash is ${result === true ? 'correct' : 'incorrect'}`)
-                               expect.push(result)
+                       result = await NanoNaCl.verifyAsync(TEST_VECTORS.signature, TEST_VECTORS.blockHash, TEST_VECTORS.publicKey)
+                       console.log('expected:', true)
+                       console.log('actual:', result)
+                       result = result === true
+                       console.log(`verifyAsync() output for good block hash, signature, and private key is ${result === true ? 'correct' : 'incorrect'}`)
+                       expect.push(result)
 
-                               result = await verify(TEST_VECTORS.signature, zeroes, TEST_VECTORS.publicKey)
-                               console.log(result)
-                               result = result === false
-                               console.log(`verify() output for bad block hash is ${result === true ? 'correct' : 'incorrect'}`)
-                               expect.push(result)
+                       // XFAIL
+                       result = await NanoNaCl.verifyAsync(TEST_VECTORS.badSignature, TEST_VECTORS.blockHash, TEST_VECTORS.publicKey)
+                       console.log(result)
+                       result = result === false
+                       console.log(`verify() output for non-canonical signature is ${result === true ? 'correct' : 'incorrect'}`)
+                       expect.push(result)
 
-                               result = await verify(`${zeroes}${zeroes}`, TEST_VECTORS.blockHash, TEST_VECTORS.publicKey)
-                               console.log(result)
-                               result = result === false
-                               console.log(`verify() output for bad signature is ${result === true ? 'correct' : 'incorrect'}`)
-                               expect.push(result)
+                       result = await NanoNaCl.verifyAsync(TEST_VECTORS.signature, random(), TEST_VECTORS.publicKey)
+                       console.log(result)
+                       result = result === false
+                       console.log(`verify() output for random block hash is ${result === true ? 'correct' : 'incorrect'}`)
+                       expect.push(result)
 
-                               result = await verify(TEST_VECTORS.signature, TEST_VECTORS.blockHash, zeroes)
-                               console.log(result)
-                               result = result === false
-                               console.log(`verify() output for bad public key is ${result === true ? 'correct' : 'incorrect'}`)
-                               expect.push(result)
+                       result = await NanoNaCl.verifyAsync(TEST_VECTORS.signature, zeroes, TEST_VECTORS.publicKey)
+                       console.log(result)
+                       result = result === false
+                       console.log(`verify() output for bad block hash is ${result === true ? 'correct' : 'incorrect'}`)
+                       expect.push(result)
 
-                               try {
-                                       if (!expect.every(result => result)) throw new Error(`Validation is not working`)
-                               } catch (err) {
-                                       document.getElementById('status').innerHTML = `FAILED TO VALIDATE KNOWN VALUES`
-                                       document.getElementById('output').innerHTML += `Error: ${err.message}<br/>`
-                                       console.error(err)
-                                       return
-                               } finally {
-                                       api = selectedApi
-                               }
+                       result = await NanoNaCl.verifyAsync(`${zeroes}${zeroes}`, TEST_VECTORS.blockHash, TEST_VECTORS.publicKey)
+                       console.log(result)
+                       result = result === false
+                       console.log(`verify() output for bad signature is ${result === true ? 'correct' : 'incorrect'}`)
+                       expect.push(result)
+
+                       result = await NanoNaCl.verifyAsync(TEST_VECTORS.signature, TEST_VECTORS.blockHash, zeroes)
+                       console.log(result)
+                       result = result === false
+                       console.log(`verify() output for bad public key is ${result === true ? 'correct' : 'incorrect'}`)
+                       expect.push(result)
+
+                       try {
+                               if (!expect.every(result => result)) throw new Error(`Validation is not working`)
+                       } catch (err) {
+                               document.getElementById('status').innerHTML = `FAILED TO VALIDATE KNOWN VALUES`
+                               document.getElementById('output').innerHTML += `Error: ${err.message}<br/>`
+                               console.error(err)
+                               return
                        }
 
+                       // run tests
                        console.log(`%c${api}`, 'color:green', `Calculate truncated harmonic mean of the truncated arithmetic rate of signing random block hashes across ${runs} runs of ${size} samples.`)
                        const rates = []
                        let start = 0, end = 0, publicKey = null, signature = null, isValid = false
                        for (let i = 0; i < runs; i++) {
+                               await new Promise(r => setTimeout(r))
                                const times = []
                                for (let j = 0; j < size; j++) {
                                        document.getElementById('status').innerHTML = `TESTING IN PROGRESS. THIS MAY TAKE A LONG TIME. ${i}/${runs} ${j}/${size}<br/>`
@@ -285,9 +318,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
                                        const privateKey = blockHash
                                        try {
                                                start = performance.now()
-                                               publicKey = await derive(privateKey)
-                                               signature = await sign(blockHash, privateKey, publicKey)
-                                               isValid = await verify(signature, blockHash, publicKey)
+                                               publicKey = await derive(privateKey, api)
+                                               signature = await sign(blockHash, privateKey, publicKey, api)
+                                               isValid = await verify(signature, blockHash, publicKey, api)
                                                end = performance.now()
                                        } catch (err) {
                                                document.getElementById('output').innerHTML += `Error: ${err.message}<br/>`
@@ -349,11 +382,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
                        const size = document.getElementById('size')
                        const runs = document.getElementById('runs')
                        const isDebug = document.getElementById('isDebug')
-                       const isSelfCheck = document.getElementById('isSelfCheck')
-                       test(api.value, +size.value, +runs.value, isDebug.checked, isSelfCheck.checked)
+                       test(api.value, +size.value, +runs.value, isDebug.checked)
                                .then(() => {
                                        event.target.disabled = false
-                                       isSelfCheck.checked = false
                                })
                }
 
@@ -387,6 +418,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
                <label for="api">API</label>
                <select id="api">
                        <option>NanoNaCl</option>
+                       <option>NanoNaCl (async)</option>
                        <option>NanocurrencyWeb</option>
                        <option>Sodium</option>
                        <option>TweetNaCl.js</option>
@@ -400,10 +432,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
                <label for="isDebug">Debug?</label>
                <input id="isDebug" type="checkbox" />
        </span>
-       <span>
-               <label for="isSelfCheck">Run self-check?</label>
-               <input id="isSelfCheck" type="checkbox" checked />
-       </span>
        <button id="btnStartTest">Go</button>
        <hr />
        <h3 id="status">WAITING</h3>
index 92d9644bcb0039728f7b4ef92311d7568e613ef9..9a1935b3a117a5c4c4fc866fd70abd357a00a5bf 100644 (file)
--- a/index.ts
+++ b/index.ts
@@ -1,12 +1,9 @@
 //! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
 //! SPDX-License-Identifier: GPL-3.0-or-later
 
+//@ts-expect-error
 import nacl from './build/nano-nacl.wasm'
-type Derive = () => void
-type Sign = () => void
-type Verify = () => number
-type GetInputPointer = () => number
-type GetOutputPointer = () => number
+
 type Data = {
        action: string
        blockHash?: string
@@ -15,132 +12,122 @@ type Data = {
        secretKey?: string
        signature?: string
 }
+type Exports = {
+       exports: {
+               derive: () => void,
+               sign: () => void,
+               verify: () => void,
+               getInputPointer: () => number,
+               getOutputPointer: () => number,
+               memory: WebAssembly.Memory
+       }
+}
 
-export const NanoNaCl = async (bytes: number[]): Promise<any> => {
-       let isReady = false
-       let wasm
-       let module
-       let instance
-       let memory: WebAssembly.Memory
-       let derive: (k: Uint8Array) => Uint8Array<ArrayBuffer>
-       let sign: (h: Uint8Array, k: Uint8Array) => Uint8Array<ArrayBuffer>
-       let verify: (s: Uint8Array, h: Uint8Array, k: Uint8Array) => Uint8Array<ArrayBuffer>
-
-       async function setup (): Promise<void> {
-               try {
-                       wasm = Uint8Array.from(bytes)
-                       module = await WebAssembly.compile(wasm)
-                       instance = await WebAssembly.instantiate(module, {
-                               env: {
-                                       abort: (msg: any, file: any, row: any, col: any) => {
-                                               // ~lib/builtins/abort(~lib/string/String | null?, ~lib/string/String | null?, u32?, u32?) => void
-                                               // msg = __liftString(msg >>> 0)
-                                               // file = __liftString(file >>> 0)
-                                               row = row >>> 0
-                                               col = col >>> 0
-                                               console.error('wasm abort:', `msg ${msg}`, `file ${file}`, `row ${row}`, `col ${col}`)
-                                               throw new Error(msg, { cause: { file, row, col } })
-                                       },
-                                       "performance.now" () {
-                                               // ~lib/bindings/dom/performance.now() => f64
-                                               return performance.now()
-                                       },
-                                       trace: (message: any, n?: number, a0?: number, a1?: number, a2?: number, a3?: number, a4?: number) => {
-                                               // ~lib/builtins/trace(~lib/string/String, i32?, f64?, f64?, f64?, f64?, f64?) => void
-                                               message = __liftString(message >>> 0);
-                                               (() => {
-                                                       // @external.js
-                                                       console.log(message, ...[a0, a1, a2, a3, a4].slice(0, n))
-                                               })()
-                                       },
-                                       memory: new WebAssembly.Memory({ initial: 4, maximum: 4 })
-                               }
-                       })
-                       const { exports } = instance as { exports: { derive: Derive, sign: Sign, verify: Verify, getInputPointer: GetInputPointer, getOutputPointer: GetOutputPointer, memory: WebAssembly.Memory } }
-                       memory = exports.memory
-
-                       function __liftString (pointer: number) {
-                               if (!pointer) return null
-                               const
-                                       //@ts-ignore
-                                       end = pointer + new Uint32Array(memory.buffer)[pointer - 4 >>> 2] >>> 1,
-                                       //@ts-ignore
-                                       memoryU16 = new Uint16Array(memory.buffer)
-                               let
-                                       start = pointer >>> 1,
-                                       string = ""
-                               while (end - start > 1024) string += String.fromCharCode(...memoryU16.subarray(start, start += 1024))
-                               return string + String.fromCharCode(...memoryU16.subarray(start, end))
-                       }
-
-                       derive = function (k: Uint8Array): Uint8Array<ArrayBuffer> {
-                               // assembly/nano-nacl/derive() => void
-                               let buffer: DataView | undefined = new DataView(memory.buffer)
-                               let inPtr = exports.getInputPointer()
-                               for (let i = 0; i < 32; i++) {
-                                       buffer.setUint8(inPtr + i, k[i])
-                               }
-                               exports.derive()
-                               const outPtr = exports.getOutputPointer()
-                               const pk = new Uint8Array(32)
-                               buffer = new DataView(memory.buffer)
-                               for (let i = 0; i < 32; i++) {
-                                       pk[i] = buffer.getUint8(outPtr + i)
+const startNanoNaCl = (bytes: number[]): { derive: typeof derive, sign: typeof sign, verify: typeof verify } => {
+       const wasm: Uint8Array<ArrayBuffer> = Uint8Array.from(bytes)
+       const module = new WebAssembly.Module(wasm)
+       const { exports } = new WebAssembly.Instance(module, {
+               env: {
+                       abort: (msg: any, file: any, row: any, col: any) => {
+                               // ~lib/builtins/abort(~lib/string/String | null?, ~lib/string/String | null?, u32?, u32?) => void
+                               // msg = __liftString(msg >>> 0)
+                               // file = __liftString(file >>> 0)
+                               row = row >>> 0
+                               col = col >>> 0
+                               console.error('wasm abort:', `msg ${msg}`, `file ${file}`, `row ${row}`, `col ${col}`)
+                               throw new Error(msg, { cause: { file, row, col } })
+                       },
+                       "performance.now" () {
+                               // ~lib/bindings/dom/performance.now() => f64
+                               return performance.now()
+                       },
+                       // ~lib/builtins/trace(~lib/string/String, i32?, f64?, f64?, f64?, f64?, f64?) => void
+                       trace: (message: any, n?: number, a0?: number, a1?: number, a2?: number, a3?: number, a4?: number): void => {
+                               let string = ''
+                               const pointer: number = message >>> 0
+                               if (pointer) {
+                                       const end = pointer + new Uint32Array(exports.memory.buffer)[pointer - 4 >>> 2] >>> 1
+                                       const memU16 = new Uint16Array(exports.memory.buffer)
+                                       let start = pointer >>> 1
+                                       while (end - start > 1024) {
+                                               string += String.fromCharCode(...memU16.subarray(start, start += 1024))
+                                       }
+                                       message = string + String.fromCharCode(...memU16.subarray(start, end))
                                }
-                               buffer = undefined
-                               return pk
-                       }
+                               (() => {
+                                       // @external.js
+                                       console.log(message, ...[a0, a1, a2, a3, a4].slice(0, n))
+                               })()
+                       },
+                       memory: new WebAssembly.Memory({ initial: 4, maximum: 4 })
+               }
+       }) as Exports
 
-                       sign = function (h: Uint8Array, k: Uint8Array): Uint8Array<ArrayBuffer> {
-                               // assembly/nano-nacl/sign() => void
-                               let buffer: DataView | undefined = new DataView(memory.buffer)
-                               let inPtr = exports.getInputPointer()
-                               for (let i = 0; i < 32; i++) {
-                                       buffer.setUint8(inPtr + i, h[i])
-                               }
-                               inPtr += 32
-                               for (let i = 0; i < 64; i++) {
-                                       buffer.setUint8(inPtr + i, k[i])
-                               }
-                               exports.sign()
-                               const outPtr = exports.getOutputPointer()
-                               const s = new Uint8Array(64)
-                               buffer = new DataView(memory.buffer)
-                               for (let i = 0; i < 64; i++) {
-                                       s[i] = buffer.getUint8(outPtr + i)
-                               }
-                               buffer = undefined
-                               return s
-                       }
+       function derive (k: unknown): Uint8Array<ArrayBuffer> {
+               validate('private key', 32, k)
+               let buffer: DataView | undefined = new DataView(exports.memory.buffer)
+               let inPtr = exports.getInputPointer()
+               for (let i = 0; i < 32; i++) {
+                       buffer.setUint8(inPtr + i, k[i])
+               }
+               exports.derive()
+               const outPtr = exports.getOutputPointer()
+               const pk = new Uint8Array(32)
+               buffer = new DataView(exports.memory.buffer)
+               for (let i = 0; i < 32; i++) {
+                       pk[i] = buffer.getUint8(outPtr + i)
+               }
+               buffer = undefined
+               return pk
+       }
 
-                       verify = function (s: Uint8Array, h: Uint8Array, k: Uint8Array): Uint8Array<ArrayBuffer> {
-                               // assembly/nano-nacl/verify() => void
-                               let buffer: DataView | undefined = new DataView(memory.buffer)
-                               let inPtr = exports.getInputPointer()
-                               for (let i = 0; i < 64; i++) {
-                                       buffer.setUint8(inPtr + i, s[i])
-                               }
-                               inPtr += 64
-                               for (let i = 0; i < 32; i++) {
-                                       buffer.setUint8(inPtr + i, h[i])
-                               }
-                               inPtr += 32
-                               for (let i = 0; i < 32; i++) {
-                                       buffer.setUint8(inPtr + i, k[i])
-                               }
-                               exports.verify()
-                               const outPtr = exports.getOutputPointer()
-                               const v = new Uint8Array(1)
-                               buffer = new DataView(memory.buffer)
-                               v[0] = buffer.getUint8(outPtr)
-                               buffer = undefined
-                               return v
-                       }
+       function sign (h: unknown, k: unknown): Uint8Array<ArrayBuffer> {
+               validate('block hash', 32, h)
+               validate('secret key', 64, k)
+               let buffer: DataView | undefined = new DataView(exports.memory.buffer)
+               let inPtr = exports.getInputPointer()
+               for (let i = 0; i < 32; i++) {
+                       buffer.setUint8(inPtr + i, h[i])
+               }
+               inPtr += 32
+               for (let i = 0; i < 64; i++) {
+                       buffer.setUint8(inPtr + i, k[i])
+               }
+               exports.sign()
+               const outPtr = exports.getOutputPointer()
+               const s = new Uint8Array(64)
+               buffer = new DataView(exports.memory.buffer)
+               for (let i = 0; i < 64; i++) {
+                       s[i] = buffer.getUint8(outPtr + i)
+               }
+               buffer = undefined
+               return s
+       }
 
-                       isReady = true
-               } catch (err) {
-                       throw new Error('Error instantiating WebAssembly', { cause: err })
+       function verify (s: unknown, h: unknown, k: unknown): Uint8Array<ArrayBuffer> {
+               validate('signature', 64, s)
+               validate('block hash', 32, h)
+               validate('public key', 32, k)
+               let buffer: DataView | undefined = new DataView(exports.memory.buffer)
+               let inPtr = exports.getInputPointer()
+               for (let i = 0; i < 64; i++) {
+                       buffer.setUint8(inPtr + i, s[i])
+               }
+               inPtr += 64
+               for (let i = 0; i < 32; i++) {
+                       buffer.setUint8(inPtr + i, h[i])
                }
+               inPtr += 32
+               for (let i = 0; i < 32; i++) {
+                       buffer.setUint8(inPtr + i, k[i])
+               }
+               exports.verify()
+               const outPtr = exports.getOutputPointer()
+               const v = new Uint8Array(1)
+               buffer = new DataView(exports.memory.buffer)
+               v[0] = buffer.getUint8(outPtr)
+               buffer = undefined
+               return v
        }
 
        function hex2bytes (type: string, byteLength: number, hex?: unknown): Uint8Array<ArrayBuffer> {
@@ -161,17 +148,42 @@ export const NanoNaCl = async (bytes: number[]): Promise<any> => {
                return bytes
        }
 
-       async function handleMessage (msg: any): Promise<void> {
+       function validate (type: string, byteLength: number, bytes: unknown): asserts bytes is Uint8Array<ArrayBuffer> {
+               if (!(bytes instanceof Uint8Array)) {
+                       throw new TypeError(`${type} must be Uint8Array`)
+               }
+               if (!('buffer' in bytes)) {
+                       throw new TypeError(`${type} must be backed by an ArrayBuffer`)
+               }
+               if (bytes.buffer.byteLength !== byteLength) {
+                       throw new TypeError(`${type} must be ${byteLength} bytes`)
+               }
+       }
+
+       let isListening = false
+
+       /**
+        * Parses inbound data when NanoNaCl is started as a Web Worker.
+        * @param {object} message.data - String of worker commands and related data
+        */
+       async function handleMessage (message: unknown): Promise<void> {
                let result: any = null
                try {
-                       if (!isReady) await setup()
-
+                       if (message == null
+                               || typeof message !== 'object'
+                               || !('data' in message)
+                               || typeof message.data !== 'string'
+                       ) {
+                               throw new TypeError('Invalid NanoNaClWorker request')
+                       }
+                       const msg = message as { [key: string]: string }
                        if (msg.data === 'start') {
-                               result = 'started'
+                               isListening = true
+                               postMessage('started')
                        } else if (msg.data === 'stop') {
-                               removeEventListener('message', handleMessage)
-                               result = 'stopped'
-                       } else {
+                               isListening = false
+                               postMessage('stopped')
+                       } else if (isListening) {
                                const data: Data = JSON.parse(msg.data)
                                const { action } = data
                                if (action === 'derive') {
@@ -202,6 +214,7 @@ export const NanoNaCl = async (bytes: number[]): Promise<any> => {
                                        }
                                        result = verification
                                }
+                               postMessage(result)
                        }
                } catch (err: unknown) {
                        if (typeof err === 'object' && err != null) {
@@ -212,24 +225,23 @@ export const NanoNaCl = async (bytes: number[]): Promise<any> => {
                        } else {
                                result = JSON.stringify(err)
                        }
-               } finally {
                        postMessage(result)
-                       addEventListener('message', handleMessage)
                }
        }
        addEventListener('message', handleMessage)
-}
 
-const NanoNaClWorker = `;await (${NanoNaCl})([${nacl}])`
+       return { derive, sign, verify }
+}
 
 /**
- * HOST CODE
+ * Host code for asynchronous Web Worker
  */
-// Initialize CPU
 let isWorkerReady: boolean = false
+let isWorkerListening: boolean = false
 let worker: Worker
 let url: string
 
+// Create worker module
 function init (): void {
        try {
                url = URL.createObjectURL(new Blob([NanoNaClWorker], { type: 'text/javascript' }))
@@ -242,6 +254,7 @@ function init (): void {
        }
 }
 
+// Reconstruct worker when errors occur
 function reset (): void {
        console.warn(`NanoNaCl encountered an error. Reinitializing...`)
        isWorkerReady = false
@@ -249,15 +262,28 @@ function reset (): void {
        init()
 }
 
+// Check that the worker is running and listening before sending messages
 async function start (): Promise<void> {
        if (!isWorkerReady) init()
-       return new Promise(async (ready, fail): Promise<void> => {
-               worker.onmessage = msg => msg.data === 'started' ? ready(msg.data) : fail(msg.data)
-               worker.onerror = err => fail(err.message)
-               worker.postMessage('start')
-       })
+       if (!isWorkerListening) {
+               return new Promise(async (resolve, reject): Promise<void> => {
+                       worker.onerror = err => reject(err.message)
+                       worker.onmessage = (msg): void => {
+                               const result = msg.data
+                               if (result === 'started') {
+                                       console.log('worker started successfully')
+                                       isWorkerListening = true
+                                       resolve(result)
+                               } else {
+                                       reject(result)
+                               }
+                       }
+                       worker.postMessage('start')
+               })
+       }
 }
 
+// Send command and relevant data to NanoNaCl worker
 async function dispatch (data: { [key: string]: string }): Promise<Uint8Array<ArrayBuffer>> {
        return new Promise((resolve, reject) => {
                worker.onerror = reject
@@ -271,6 +297,7 @@ async function dispatch (data: { [key: string]: string }): Promise<Uint8Array<Ar
        })
 }
 
+// Request that the worker stop listening without terminating
 async function stop (): Promise<void> {
        return new Promise((resolve, reject): void => {
                worker.onerror = reject
@@ -278,6 +305,7 @@ async function stop (): Promise<void> {
                        const result = msg.data
                        if (result === 'stopped') {
                                console.log('worker stopped successfully')
+                               isWorkerListening = false
                                resolve()
                        } else {
                                reject(result)
@@ -294,7 +322,6 @@ async function run (data: Record<string, string>): Promise<string> {
                console.error(err)
                throw new Error('Error initializing worker')
        }
-
        try {
                const result = await dispatch(data)
                if (!(result instanceof Uint8Array)) {
@@ -314,147 +341,65 @@ async function run (data: Record<string, string>): Promise<string> {
        }
 }
 
-let isReady = false
-let wasm
-let module
-let instance
-let memory: WebAssembly.Memory
-let deriveSync: (k: Uint8Array) => Uint8Array<ArrayBuffer>
-let signSync: (h: Uint8Array, k: Uint8Array) => Uint8Array<ArrayBuffer>
-let verifySync: (s: Uint8Array, h: Uint8Array, k: Uint8Array) => Uint8Array<ArrayBuffer>
-try {
-       wasm = Uint8Array.from(nacl)
-       module = await WebAssembly.compile(wasm)
-       instance = await WebAssembly.instantiate(module, {
-               env: {
-                       abort: (msg: any, file: any, row: any, col: any) => {
-                               // ~lib/builtins/abort(~lib/string/String | null?, ~lib/string/String | null?, u32?, u32?) => void
-                               // msg = __liftString(msg >>> 0)
-                               // file = __liftString(file >>> 0)
-                               row = row >>> 0
-                               col = col >>> 0
-                               console.error('wasm abort:', `msg ${msg}`, `file ${file}`, `row ${row}`, `col ${col}`)
-                               throw new Error(msg, { cause: { file, row, col } })
-                       },
-                       "performance.now" () {
-                               // ~lib/bindings/dom/performance.now() => f64
-                               return performance.now()
-                       },
-                       trace: (message: any, n?: number, a0?: number, a1?: number, a2?: number, a3?: number, a4?: number) => {
-                               // ~lib/builtins/trace(~lib/string/String, i32?, f64?, f64?, f64?, f64?, f64?) => void
-                               message = __liftString(message >>> 0);
-                               (() => {
-                                       // @external.js
-                                       console.log(message, ...[a0, a1, a2, a3, a4].slice(0, n))
-                               })()
-                       },
-                       memory: new WebAssembly.Memory({ initial: 4, maximum: 4 })
-               }
-       })
-       const { exports } = instance as { exports: { derive: Derive, sign: Sign, verify: Verify, getInputPointer: GetInputPointer, getOutputPointer: GetOutputPointer, memory: WebAssembly.Memory } }
-       memory = exports.memory
-
-       function __liftString (pointer: number) {
-               if (!pointer) return null
-               const
-                       //@ts-ignore
-                       end = pointer + new Uint32Array(memory.buffer)[pointer - 4 >>> 2] >>> 1,
-                       //@ts-ignore
-                       memoryU16 = new Uint16Array(memory.buffer)
-               let
-                       start = pointer >>> 1,
-                       string = ""
-               while (end - start > 1024) string += String.fromCharCode(...memoryU16.subarray(start, start += 1024))
-               return string + String.fromCharCode(...memoryU16.subarray(start, end))
-       }
+const NanoNaCl = startNanoNaCl(nacl)
+const NanoNaClWorker = `;(${startNanoNaCl})([${nacl}]);`
 
-       deriveSync = function (k: Uint8Array): Uint8Array<ArrayBuffer> {
-               // assembly/nano-nacl/derive() => void
-               let buffer: DataView | undefined = new DataView(memory.buffer)
-               let inPtr = exports.getInputPointer()
-               for (let i = 0; i < 32; i++) {
-                       buffer.setUint8(inPtr + i, k[i])
-               }
-               exports.derive()
-               const outPtr = exports.getOutputPointer()
-               const pk = new Uint8Array(32)
-               buffer = new DataView(memory.buffer)
-               for (let i = 0; i < 32; i++) {
-                       pk[i] = buffer.getUint8(outPtr + i)
-               }
-               buffer = undefined
-               return pk
-       }
+/**
+ * Nano public key derivation using WebAssembly.
+ * @param {Uint8Array<ArrayBuffer>} k - 32-byte private key
+ * @returns 32-byte public key
+ */
+export function derive (k: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer> {
+       return NanoNaCl.derive(k)
+}
 
-       signSync = function (h: Uint8Array, k: Uint8Array): Uint8Array<ArrayBuffer> {
-               // assembly/nano-nacl/sign() => void
-               let buffer: DataView | undefined = new DataView(memory.buffer)
-               let inPtr = exports.getInputPointer()
-               for (let i = 0; i < 32; i++) {
-                       buffer.setUint8(inPtr + i, h[i])
-               }
-               inPtr += 32
-               for (let i = 0; i < 64; i++) {
-                       buffer.setUint8(inPtr + i, k[i])
-               }
-               exports.sign()
-               const outPtr = exports.getOutputPointer()
-               const s = new Uint8Array(64)
-               buffer = new DataView(memory.buffer)
-               for (let i = 0; i < 64; i++) {
-                       s[i] = buffer.getUint8(outPtr + i)
-               }
-               buffer = undefined
-               return s
-       }
+/**
+ * Nano block signing using WebAssembly.
+ * @param {Uint8Array<ArrayBuffer>} h - 32-byte block hash
+ * @param {Uint8Array<ArrayBuffer>} k - 64-byte secret key (prv + pub)
+ * @returns 64-byte detached signature
+ */
+export function sign (h: Uint8Array<ArrayBuffer>, k: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer> {
+       return NanoNaCl.sign(h, k)
+}
 
-       verifySync = function (s: Uint8Array, h: Uint8Array, k: Uint8Array): Uint8Array<ArrayBuffer> {
-               // assembly/nano-nacl/verify() => void
-               let buffer: DataView | undefined = new DataView(memory.buffer)
-               let inPtr = exports.getInputPointer()
-               for (let i = 0; i < 64; i++) {
-                       buffer.setUint8(inPtr + i, s[i])
-               }
-               inPtr += 64
-               for (let i = 0; i < 32; i++) {
-                       buffer.setUint8(inPtr + i, h[i])
-               }
-               inPtr += 32
-               for (let i = 0; i < 32; i++) {
-                       buffer.setUint8(inPtr + i, k[i])
-               }
-               exports.verify()
-               const outPtr = exports.getOutputPointer()
-               const v = new Uint8Array(1)
-               buffer = new DataView(memory.buffer)
-               v[0] = buffer.getUint8(outPtr)
-               buffer = undefined
-               return v
-       }
-       isReady = true
-} catch (err) {
-       throw new Error('Error instantiating WebAssembly', { cause: err })
+/**
+ * Nano block signature verification using WebAssembly.
+ * @param {Uint8Array<ArrayBuffer>} s - 64-byte detached signature
+ * @param {Uint8Array<ArrayBuffer>} h - 32-byte block hash
+ * @param {Uint8Array<ArrayBuffer>} k - 32-byte public key
+ * @returns true if signature matches block hash and public key, else false
+ */
+export function verify (s: Uint8Array<ArrayBuffer>, h: Uint8Array<ArrayBuffer>, k: Uint8Array<ArrayBuffer>): boolean {
+       return NanoNaCl.verify(s, h, k)[0] === 0
 }
-export { deriveSync, signSync, verifySync }
 
 /**
-* Nano public key derivation using WebAssembly.
-*/
-export async function derive (privateKey: string): Promise<string> {
+ * Asynchronous Nano public key derivation using WebAssembly.
+ * @param {Uint8Array<ArrayBuffer>} k - 64-character private key hex string
+ * @returns Promise for 64-character public key hex string
+ */
+export async function deriveAsync (privateKey: string): Promise<string> {
        return await run({ action: 'derive', privateKey })
 }
 
 /**
-* Nano block signature using WebAssembly.
-*/
-export async function sign (blockHash: string, secretKey: string): Promise<string> {
+ * Asynchronous Nano block signing using WebAssembly.
+ * @param {Uint8Array<ArrayBuffer>} h - 64-character block hash hex string
+ * @param {Uint8Array<ArrayBuffer>} k - 128-character secret key (prv + pub) hex string
+ * @returns Promise for 128-character detached signature hex string
+ */
+export const signAsync = async function (blockHash: string, secretKey: string): Promise<string> {
        return await run({ action: 'sign', blockHash, secretKey })
 }
 
 /**
-* Nano block signature verification using WebAssembly.
-*/
-export async function verify (signature: string, blockHash: string, publicKey: string): Promise<boolean> {
-       const result = await run({ action: 'verify', signature, blockHash, publicKey })
-       return result === '00'
+ * Asynchronous Nano block signature verification using WebAssembly.
+ * @param {Uint8Array<ArrayBuffer>} s - 128-character detached signature hex string
+ * @param {Uint8Array<ArrayBuffer>} h - 64-character block hash hex string
+ * @param {Uint8Array<ArrayBuffer>} k - 64-character public key hex string
+ * @returns Promise resolving to true if signature matches block hash and public key, else false
+ */
+export const verifyAsync = async function (signature: string, blockHash: string, publicKey: string): Promise<boolean> {
+       return await run({ action: 'verify', signature, blockHash, publicKey }) === '00'
 }
index 6258df7aabc8f983225e9229422aa7b9e221715d..9f21649bf988902db2510bdc22932a01109fa8ba 100644 (file)
@@ -9,9 +9,9 @@
                        "version": "0.0.1",
                        "license": "(GPL-3.0-or-later AND MIT)",
                        "devDependencies": {
-                               "@types/node": "^25.2.3",
-                               "assemblyscript": "^0.28.9",
-                               "esbuild": "^0.27.3",
+                               "@types/node": "^25.5.0",
+                               "assemblyscript": "^0.28.10",
+                               "esbuild": "^0.27.4",
                                "typescript": "^5.9.3"
                        },
                        "funding": {
@@ -20,9 +20,9 @@
                        }
                },
                "node_modules/@esbuild/aix-ppc64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
-                       "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz",
+                       "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==",
                        "cpu": [
                                "ppc64"
                        ],
@@ -37,9 +37,9 @@
                        }
                },
                "node_modules/@esbuild/android-arm": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
-                       "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz",
+                       "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==",
                        "cpu": [
                                "arm"
                        ],
@@ -54,9 +54,9 @@
                        }
                },
                "node_modules/@esbuild/android-arm64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
-                       "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz",
+                       "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==",
                        "cpu": [
                                "arm64"
                        ],
@@ -71,9 +71,9 @@
                        }
                },
                "node_modules/@esbuild/android-x64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
-                       "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz",
+                       "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==",
                        "cpu": [
                                "x64"
                        ],
@@ -88,9 +88,9 @@
                        }
                },
                "node_modules/@esbuild/darwin-arm64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
-                       "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz",
+                       "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==",
                        "cpu": [
                                "arm64"
                        ],
                        }
                },
                "node_modules/@esbuild/darwin-x64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
-                       "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz",
+                       "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==",
                        "cpu": [
                                "x64"
                        ],
                        }
                },
                "node_modules/@esbuild/freebsd-arm64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
-                       "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz",
+                       "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==",
                        "cpu": [
                                "arm64"
                        ],
                        }
                },
                "node_modules/@esbuild/freebsd-x64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
-                       "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz",
+                       "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==",
                        "cpu": [
                                "x64"
                        ],
                        }
                },
                "node_modules/@esbuild/linux-arm": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
-                       "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz",
+                       "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==",
                        "cpu": [
                                "arm"
                        ],
                        }
                },
                "node_modules/@esbuild/linux-arm64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
-                       "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz",
+                       "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==",
                        "cpu": [
                                "arm64"
                        ],
                        }
                },
                "node_modules/@esbuild/linux-ia32": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
-                       "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz",
+                       "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==",
                        "cpu": [
                                "ia32"
                        ],
                        }
                },
                "node_modules/@esbuild/linux-loong64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
-                       "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz",
+                       "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==",
                        "cpu": [
                                "loong64"
                        ],
                        }
                },
                "node_modules/@esbuild/linux-mips64el": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
-                       "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz",
+                       "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==",
                        "cpu": [
                                "mips64el"
                        ],
                        }
                },
                "node_modules/@esbuild/linux-ppc64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
-                       "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz",
+                       "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==",
                        "cpu": [
                                "ppc64"
                        ],
                        }
                },
                "node_modules/@esbuild/linux-riscv64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
-                       "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz",
+                       "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==",
                        "cpu": [
                                "riscv64"
                        ],
                        }
                },
                "node_modules/@esbuild/linux-s390x": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
-                       "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz",
+                       "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==",
                        "cpu": [
                                "s390x"
                        ],
                        }
                },
                "node_modules/@esbuild/linux-x64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
-                       "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz",
+                       "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==",
                        "cpu": [
                                "x64"
                        ],
                        }
                },
                "node_modules/@esbuild/netbsd-arm64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
-                       "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz",
+                       "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==",
                        "cpu": [
                                "arm64"
                        ],
                        }
                },
                "node_modules/@esbuild/netbsd-x64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
-                       "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz",
+                       "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==",
                        "cpu": [
                                "x64"
                        ],
                        }
                },
                "node_modules/@esbuild/openbsd-arm64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
-                       "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz",
+                       "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==",
                        "cpu": [
                                "arm64"
                        ],
                        }
                },
                "node_modules/@esbuild/openbsd-x64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
-                       "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz",
+                       "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==",
                        "cpu": [
                                "x64"
                        ],
                        }
                },
                "node_modules/@esbuild/openharmony-arm64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
-                       "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz",
+                       "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==",
                        "cpu": [
                                "arm64"
                        ],
                        }
                },
                "node_modules/@esbuild/sunos-x64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
-                       "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz",
+                       "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==",
                        "cpu": [
                                "x64"
                        ],
                        }
                },
                "node_modules/@esbuild/win32-arm64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
-                       "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz",
+                       "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==",
                        "cpu": [
                                "arm64"
                        ],
                        }
                },
                "node_modules/@esbuild/win32-ia32": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
-                       "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz",
+                       "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==",
                        "cpu": [
                                "ia32"
                        ],
                        }
                },
                "node_modules/@esbuild/win32-x64": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
-                       "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz",
+                       "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==",
                        "cpu": [
                                "x64"
                        ],
                        }
                },
                "node_modules/@types/node": {
-                       "version": "25.2.3",
-                       "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz",
-                       "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==",
+                       "version": "25.5.0",
+                       "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
+                       "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
                        "dev": true,
                        "license": "MIT",
                        "dependencies": {
-                               "undici-types": "~7.16.0"
+                               "undici-types": "~7.18.0"
                        }
                },
                "node_modules/assemblyscript": {
-                       "version": "0.28.9",
-                       "resolved": "https://registry.npmjs.org/assemblyscript/-/assemblyscript-0.28.9.tgz",
-                       "integrity": "sha512-UQqIYSUuJzqQP6fKTs1CtanyxmnDk7K+Kmj/J67agimxDqxjdMnTF/y9rEHUBL+K/VIEV4QYu57RMrL5LwbAvw==",
+                       "version": "0.28.10",
+                       "resolved": "https://registry.npmjs.org/assemblyscript/-/assemblyscript-0.28.10.tgz",
+                       "integrity": "sha512-MGqjjNlPMmcWjfnme3cGFKmbBY/rj0bmE4M1Wg8tNqmHusA1PAjOMgWK67KWBK63lmvotFDoP8e5N0URxKlR/Q==",
                        "dev": true,
                        "license": "Apache-2.0",
                        "dependencies": {
                        }
                },
                "node_modules/esbuild": {
-                       "version": "0.27.3",
-                       "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
-                       "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
+                       "version": "0.27.4",
+                       "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz",
+                       "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==",
                        "dev": true,
                        "hasInstallScript": true,
                        "license": "MIT",
                                "node": ">=18"
                        },
                        "optionalDependencies": {
-                               "@esbuild/aix-ppc64": "0.27.3",
-                               "@esbuild/android-arm": "0.27.3",
-                               "@esbuild/android-arm64": "0.27.3",
-                               "@esbuild/android-x64": "0.27.3",
-                               "@esbuild/darwin-arm64": "0.27.3",
-                               "@esbuild/darwin-x64": "0.27.3",
-                               "@esbuild/freebsd-arm64": "0.27.3",
-                               "@esbuild/freebsd-x64": "0.27.3",
-                               "@esbuild/linux-arm": "0.27.3",
-                               "@esbuild/linux-arm64": "0.27.3",
-                               "@esbuild/linux-ia32": "0.27.3",
-                               "@esbuild/linux-loong64": "0.27.3",
-                               "@esbuild/linux-mips64el": "0.27.3",
-                               "@esbuild/linux-ppc64": "0.27.3",
-                               "@esbuild/linux-riscv64": "0.27.3",
-                               "@esbuild/linux-s390x": "0.27.3",
-                               "@esbuild/linux-x64": "0.27.3",
-                               "@esbuild/netbsd-arm64": "0.27.3",
-                               "@esbuild/netbsd-x64": "0.27.3",
-                               "@esbuild/openbsd-arm64": "0.27.3",
-                               "@esbuild/openbsd-x64": "0.27.3",
-                               "@esbuild/openharmony-arm64": "0.27.3",
-                               "@esbuild/sunos-x64": "0.27.3",
-                               "@esbuild/win32-arm64": "0.27.3",
-                               "@esbuild/win32-ia32": "0.27.3",
-                               "@esbuild/win32-x64": "0.27.3"
+                               "@esbuild/aix-ppc64": "0.27.4",
+                               "@esbuild/android-arm": "0.27.4",
+                               "@esbuild/android-arm64": "0.27.4",
+                               "@esbuild/android-x64": "0.27.4",
+                               "@esbuild/darwin-arm64": "0.27.4",
+                               "@esbuild/darwin-x64": "0.27.4",
+                               "@esbuild/freebsd-arm64": "0.27.4",
+                               "@esbuild/freebsd-x64": "0.27.4",
+                               "@esbuild/linux-arm": "0.27.4",
+                               "@esbuild/linux-arm64": "0.27.4",
+                               "@esbuild/linux-ia32": "0.27.4",
+                               "@esbuild/linux-loong64": "0.27.4",
+                               "@esbuild/linux-mips64el": "0.27.4",
+                               "@esbuild/linux-ppc64": "0.27.4",
+                               "@esbuild/linux-riscv64": "0.27.4",
+                               "@esbuild/linux-s390x": "0.27.4",
+                               "@esbuild/linux-x64": "0.27.4",
+                               "@esbuild/netbsd-arm64": "0.27.4",
+                               "@esbuild/netbsd-x64": "0.27.4",
+                               "@esbuild/openbsd-arm64": "0.27.4",
+                               "@esbuild/openbsd-x64": "0.27.4",
+                               "@esbuild/openharmony-arm64": "0.27.4",
+                               "@esbuild/sunos-x64": "0.27.4",
+                               "@esbuild/win32-arm64": "0.27.4",
+                               "@esbuild/win32-ia32": "0.27.4",
+                               "@esbuild/win32-x64": "0.27.4"
                        }
                },
                "node_modules/long": {
                        }
                },
                "node_modules/undici-types": {
-                       "version": "7.16.0",
-                       "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
-                       "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+                       "version": "7.18.2",
+                       "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
+                       "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
                        "dev": true,
                        "license": "MIT"
                }
index cc7a86581a2fabca4400333abed864f8ba1af560..c198f0e9077926ba6663a17a9f3bda4fbedb3520 100644 (file)
        "scripts": {
                "build": "npm run clean && npm run compile && node ./esbuild.mjs",
                "clean": "rm -rf {build,dist}",
-               "compile": "asc ./assembly/nano-nacl.ts",
+               "compile": "asc ./assembly/index.ts && tsc",
                "prepublishOnly": "npm run build",
                "test": "npm run build && node ./test.mjs"
        },
        "devDependencies": {
-               "@types/node": "^25.2.3",
-               "assemblyscript": "^0.28.9",
-               "esbuild": "^0.27.3",
+               "@types/node": "^25.5.0",
+               "assemblyscript": "^0.28.10",
+               "esbuild": "^0.27.4",
                "typescript": "^5.9.3"
        },
        "type": "module",
        "exports": {
                ".": {
-                       "types": "./dist/types.d.ts",
+                       "types": "./dist/index.d.ts",
                        "default": "./dist/browser.js"
                }
        },
-       "types": "./dist/types.d.ts",
+       "types": "./dist/index.d.ts",
        "unpkg": "./dist/browser.js"
 }
index 792a371bf8404233c2403cee8a8f24e6a962d7d0..323a13d76b3f33f387305e3db946b5660a9aab3b 100644 (file)
@@ -8,12 +8,16 @@
                "noErrorTruncation": true,
                "noFallthroughCasesInSwitch": true,
                "strict": true,
+               "declaration": true,
+               "emitDeclarationOnly": true,
+               "outDir": "dist",
                "lib": [
                        "DOM",
                        "ESNext"
                ]
        },
        "exclude": [
-               "assembly"
+               "assembly",
+               "dist"
        ]
 }