neptune-privacy/TAURI_WASM_SETUP.md

6.4 KiB

Tauri + WASM Setup Guide

🐛 Problem

Symptom:

  • WASM works in browser (npm run dev)
  • WASM fails in Tauri (npm run tauri:dev)
  • Error: Cannot assign to read only property 'toString'

Root Cause: Tauri webview requires special configuration to load WASM files correctly.


Solution: Vite Plugins for WASM

1. Install Required Plugins

pnpm add -D vite-plugin-wasm vite-plugin-top-level-await

Why these plugins?

  • vite-plugin-wasm: Handles WASM file loading and initialization
  • vite-plugin-top-level-await: Enables top-level await (required by WASM)

2. Update vite.config.ts

import wasm from 'vite-plugin-wasm'
import topLevelAwait from 'vite-plugin-top-level-await'

export default defineConfig({
  plugins: [
    vue(),
    tailwindcss(),
    wasm(),             // ✅ Add WASM support
    topLevelAwait(),    // ✅ Add top-level await support
    // ... other plugins
  ],
  
  // Rest of config...
})

3. Tauri CSP Configuration

File: src-tauri/tauri.conf.json

Ensure CSP includes:

{
  "security": {
    "csp": {
      "script-src": "'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval'"
    }
  }
}

Key directive: 'wasm-unsafe-eval' is REQUIRED for WASM in Tauri 2.x


🎯 How It Works

Before (Without Plugins)

Tauri loads index.html
  ↓
Vite bundles JS (WASM as regular asset)
  ↓
Browser tries to load WASM
  ↓
💥 WASM initialization fails
  ↓
Error: Cannot assign to read only property

Why it fails:

  • Vite doesn't know how to bundle WASM for Tauri
  • WASM file is treated as regular asset
  • Tauri webview can't initialize WASM correctly

After (With Plugins)

Tauri loads index.html
  ↓
vite-plugin-wasm handles WASM bundling
  ↓
WASM file served with correct headers
  ↓
vite-plugin-top-level-await enables async init
  ↓
✅ WASM loads successfully

Why it works:

  • vite-plugin-wasm handles WASM as special asset type
  • Correct MIME type (application/wasm)
  • Proper initialization order
  • Compatible with Tauri's security model

📊 Comparison

Aspect Browser (dev) Tauri (without plugins) Tauri (with plugins)
WASM Loading Works Fails Works
MIME Type Auto Wrong Correct
Initialization Success Conflict Success
CSP Compatibility N/A Issues Compatible

🔍 Debugging

Check if WASM is Loading

In Browser DevTools (F12 in Tauri window):

  1. Network Tab:

    Look for: neptune_wasm_bg.wasm
    Status: 200 OK
    Type: application/wasm
    
  2. Console Tab:

    Should see: ✅ WASM initialized successfully
    Should NOT see: ❌ WASM init error
    
  3. Sources Tab:

    Check if WASM file is listed under "webpack://" or "(no domain)"
    

Common Issues

Issue 1: WASM file not found (404)

Cause: WASM not bundled correctly
Fix: Ensure vite-plugin-wasm is installed and configured

Issue 2: CSP violation

Cause: Missing 'wasm-unsafe-eval' in CSP
Fix: Add to script-src in tauri.conf.json

Issue 3: Module initialization error

Cause: Top-level await not supported
Fix: Install vite-plugin-top-level-await


🧪 Testing Steps

1. Test in Browser (Should Work)

npm run dev
  • Open http://localhost:5173
  • Navigate to /auth → Create Wallet
  • Check console: Should see " WASM initialized successfully"

2. Test in Tauri (Now Should Work)

npm run tauri:dev
  • Tauri window opens
  • Navigate to /auth → Create Wallet
  • Open DevTools (F12)
  • Check console: Should see " WASM initialized successfully"
  • Should NOT see any toString errors

3. Test Wallet Generation

// In CreateWalletFlow.vue
const { generateWallet } = useNeptuneWallet()

// Click "Create Wallet" button
const result = await generateWallet()

// Should return:
{
  receiver_identifier: "...",
  seed_phrase: ["word1", "word2", ..., "word18"]
}

📦 Package Versions

Installed:

{
  "devDependencies": {
    "vite-plugin-wasm": "^3.5.0",
    "vite-plugin-top-level-await": "^1.6.0"
  }
}

Compatible with:

  • Vite 7.x
  • Tauri 2.x
  • Vue 3.x

🔧 Configuration Files

vite.config.ts

import wasm from 'vite-plugin-wasm'
import topLevelAwait from 'vite-plugin-top-level-await'

export default defineConfig({
  plugins: [
    vue(),
    wasm(),
    topLevelAwait(),
    // ... other plugins
  ],
})

tauri.conf.json

{
  "app": {
    "security": {
      "csp": {
        "script-src": "'self' 'unsafe-inline' 'unsafe-eval' 'wasm-unsafe-eval'"
      }
    }
  }
}

package.json

{
  "dependencies": {
    "@neptune/wasm": "file:./packages/neptune-wasm"
  },
  "devDependencies": {
    "vite-plugin-wasm": "^3.5.0",
    "vite-plugin-top-level-await": "^1.6.0"
  }
}

💡 Why Browser Works But Tauri Doesn't

Browser (Chrome/Firefox)

  • Permissive WASM loading: Browsers automatically handle WASM
  • Built-in support: No special config needed
  • Dev server: Vite dev server serves WASM with correct headers

Tauri (Webview)

  • Strict security: CSP enforced by default
  • Custom protocol: Assets loaded via tauri:// protocol
  • WASM restrictions: Requires 'wasm-unsafe-eval' in CSP
  • Asset handling: Needs proper Vite configuration

Tauri = Embedded Browser + Rust Backend

  • More secure (CSP enforced)
  • More restrictive (needs explicit config)
  • Different asset loading (custom protocol)

🚀 Result

Before:

npm run dev        # ✅ WASM works
npm run tauri:dev  # ❌ WASM fails (toString error)

After:

npm run dev        # ✅ WASM works
npm run tauri:dev  # ✅ WASM works! 🎉

📚 Resources


🎯 Summary

Problem: Tauri can't load WASM without proper Vite configuration
Solution: Install vite-plugin-wasm + vite-plugin-top-level-await
Result: WASM works in both browser AND Tauri! 🚀