diff --git a/src/lib/seedblend.ts b/src/lib/seedblend.ts index 62a2248..7f2e067 100644 --- a/src/lib/seedblend.ts +++ b/src/lib/seedblend.ts @@ -23,16 +23,28 @@ */ import wordlistTxt from '../bip39_wordlist.txt?raw'; -import { webcrypto } from 'crypto'; // --- Isomorphic Crypto Setup --- -// Use browser crypto if available, otherwise fallback to Node.js webcrypto. -// This allows the library to run in both the browser and the test environment (Node.js). -const subtle = (typeof window !== 'undefined' && window.crypto?.subtle) - ? window.crypto.subtle - : webcrypto.subtle; - +let cryptoPromise: Promise; +/** + * Asynchronously gets the appropriate SubtleCrypto interface, using a singleton + * pattern to ensure the module is loaded only once. + * This approach uses a dynamic import() to prevent Vite from bundling the + * Node.js 'crypto' module in browser builds. + */ +function getCrypto(): Promise { + if (!cryptoPromise) { + cryptoPromise = (async () => { + if (typeof window !== 'undefined' && window.crypto?.subtle) { + return window.crypto.subtle; + } + const { webcrypto } = await import('crypto'); + return webcrypto.subtle; + })(); + } + return cryptoPromise; +} // --- BIP39 Wordlist Loading --- @@ -61,6 +73,7 @@ if (BIP39_WORDLIST.length !== 2048) { * @returns A promise that resolves to the hash as a Uint8Array. */ async function sha256(data: Uint8Array): Promise { + const subtle = await getCrypto(); const hashBuffer = await subtle.digest('SHA-256', data); return new Uint8Array(hashBuffer); } @@ -72,6 +85,7 @@ async function sha256(data: Uint8Array): Promise { * @returns A promise that resolves to the HMAC tag. */ async function hmacSha256(key: Uint8Array, data: Uint8Array): Promise { + const subtle = await getCrypto(); const cryptoKey = await subtle.importKey( 'raw', key,