Generated: 2025-02-25 06:00 CST
Commit: ef66553
Branch: main
Tauri plugin for libsql (Turso's SQLite fork) with AES-256-CBC encryption, Drizzle ORM support, and browser-safe migrations. Enables SQLite in Tauri apps without Node.js runtime dependency.
.
├── src/ # Rust plugin (Tauri commands, libsql wrapper)
├── guest-js/ # TypeScript API (Database class, Drizzle proxy, migrations)
├── permissions/ # Tauri permission definitions
├── examples/ # Demo apps (todo-list)
├── Cargo.toml # Rust dependencies & features
├── package.json # JS package config
└── SKILL.md # Comprehensive AI skill reference
| Task | Location | Notes |
|---|---|---|
| Add Tauri command | src/commands.rs |
Follow existing patterns, add to lib.rs invoke_handler |
| Modify database operations | src/wrapper.rs |
DbConnection handles local/replica/remote modes |
| Change encryption | src/models.rs, src/wrapper.rs |
EncryptionConfig → libsql cipher |
| Add JS API method | guest-js/index.ts |
Database class, use invoke("plugin:libsql|cmd") |
| Drizzle integration | guest-js/drizzle.ts |
Proxy pattern for sqlite-proxy driver |
| Migration runner | guest-js/migrate.ts |
Parses import.meta.glob bundled SQL |
| Error types | src/error.rs |
thiserror enum, serializable for Tauri |
| Config | src/desktop.rs |
base_path, default encryption |
| Permissions | permissions/ |
Add to default.toml for new commands |
| Symbol | Type | Location | Role |
|---|---|---|---|
init / init_with_config |
fn | src/lib.rs:25 |
Plugin entry |
load / execute / select |
cmd | src/commands.rs |
Tauri command handlers |
DbConnection |
struct | src/wrapper.rs:16 |
libsql wrapper with catch_unwind |
DbInstances |
struct | src/wrapper.rs:290 |
Connection pool (Arc<Mutex>) |
Database |
class | guest-js/index.ts:54 |
Frontend API |
createDrizzleProxy |
fn | guest-js/drizzle.ts:80 |
sqlite-proxy callback |
migrate |
fn | guest-js/migrate.ts:79 |
Browser-safe migration runner |
Error |
enum | src/error.rs:5 |
Plugin error types |
Rust:
- Use
thiserrorfor errors (must implSerializefor Tauri IPC) - Wrap libsql builder in
catch_unwind(malformed URLs panic internally) - Release mutex locks before awaiting (
let conn = { ... lock().await ... }.clone()) - Feature-gate:
#[cfg(feature = "replication")]/#[cfg(feature = "encryption")] - Path resolution:
sqlite:prefix stripped,..normalized, checked againstbase_path
TypeScript:
- Use
invoke<T>("plugin:libsql|command", args)pattern - Drizzle proxy transforms
IndexMaprows to array-per-row format - Migrations use Vite
import.meta.globat build time (no runtime fs access)
- DON'T use
drizzle-orm/sqlite-proxy/migrator— it reads filesystem at runtime (WebView has no fs) - DON'T query before
migrate()— causes "no such table" errors - DON'T use
execute_batchfor embedded replicas — use explicitBEGIN/COMMITtransaction - DON'T pass encryption keys from frontend if avoidable — use plugin-level config instead
- DON'T let libsql builder panic unhandled — always wrap in
catch_unwind
| Feature | Default | Description |
|---|---|---|
core |
✅ | Local SQLite databases |
encryption |
✅ | AES-256-CBC encryption |
replication |
❌ | Turso embedded replica sync |
remote |
❌ | Pure remote connections |
# Build JS package
npm run build # or: rollup -c
# Generate Drizzle migrations
npx drizzle-kit generate
# Dev (from example)
cd examples/todo-list && bun run tauri dev// 1. Load database
await Database.load('sqlite:app.db');
// 2. Run migrations (before any queries!)
await migrate('sqlite:app.db', migrations);
// 3. Now safe to use Drizzle
const db = drizzle(createDrizzleProxy('sqlite:app.db'), { schema });- SKILL.md exists — comprehensive reference for AI assistants; copy to
.claude/skills/for Claude Code - Encryption: 32-byte key required (AES-256-CBC), can be plugin-level or per-database
- Turso: Embedded replica needs
replicationfeature; initial sync on load, manualdb.sync()for updates - Migrations: SQL bundled at build time via Vite — no runtime filesystem dependency
- Error handling: libsql internal panics caught via
catch_unwindand converted toError::InvalidDbUrl