Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
8653bdb
Initial draft for tauri plugin
simolus3 Mar 9, 2026
d75e4ee
Support queries for tauri
simolus3 Mar 17, 2026
0a6c7d0
Move example to demos
simolus3 Mar 17, 2026
27f6048
Get simple queries to work
simolus3 Mar 17, 2026
c649ce7
Demo: Implement connect in Rust
simolus3 Mar 17, 2026
09709c6
Emit status updates
simolus3 Mar 17, 2026
8ee26a0
Support subscribing to sync streams
simolus3 Mar 17, 2026
5a0e90e
Scaffold demo app
simolus3 Mar 18, 2026
d97a7b5
Open items in new window
simolus3 Mar 18, 2026
8a36ffa
Update rusqlite
simolus3 Mar 18, 2026
7af9c2b
Support directories
simolus3 Mar 18, 2026
7935367
Small fixes
simolus3 Mar 18, 2026
9775f5e
Add release instructions
simolus3 Mar 19, 2026
ca79002
Attempt to fix demo install
simolus3 Mar 19, 2026
a395832
Remove dependency patch
simolus3 Mar 23, 2026
2e37a66
Add integration tests
simolus3 Mar 23, 2026
de93b6d
Avoid webdriver for tests
simolus3 Mar 24, 2026
616ceb9
Show cargo logs
simolus3 Mar 24, 2026
a15b617
Install GTK libraries for Taurix
simolus3 Mar 24, 2026
3fa7832
More Tauri system libraries
simolus3 Mar 24, 2026
341977e
Build test runner in pretest to avoid timeouts
simolus3 Mar 24, 2026
4e5f5bd
Don't test Tauri plugin by default
simolus3 Mar 24, 2026
aa66656
Try testing Tauri on macOS
simolus3 Mar 24, 2026
503d8db
Delete unused files
simolus3 Mar 24, 2026
664cd27
Try demo in release mode
simolus3 Mar 25, 2026
eee39b9
Review feedback
simolus3 Mar 25, 2026
2b430ac
Use postversion hack to automate cargo publishing
simolus3 Mar 25, 2026
b281eeb
Update author to powersync
simolus3 Mar 26, 2026
a01cf94
Prepare changesets publishing for Tauri
simolus3 Mar 26, 2026
d6dd459
Fix tauri plugin version in demo app
simolus3 Mar 26, 2026
da15192
Add Tauri plugin to docs
simolus3 Mar 26, 2026
0dfbcd4
Merge remote-tracking branch 'origin/main' into tauri-plugin
simolus3 Mar 26, 2026
ca6468a
Update tauri demo dependencies
simolus3 Mar 26, 2026
1b40780
Use Tauri SDK terms in readme
simolus3 Mar 26, 2026
6226449
Add cargo metadata
simolus3 Mar 26, 2026
ce76670
Merge remote-tracking branch 'origin/main' into tauri-plugin
simolus3 Mar 30, 2026
433dda6
Update handlebars to fix audit
simolus3 Mar 30, 2026
7336002
Update diagnostics file build
simolus3 Mar 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["!@powersync/*"],
"ignore": ["import-tests", "powersynctests"],
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"onlyUpdatePeerDependentsWhenOutOfRange": true,
"updateInternalDependents": "out-of-range"
Expand Down
6 changes: 6 additions & 0 deletions .changeset/thick-hats-joke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'tauri-plugin-powersync': patch
'@powersync/tauri-plugin': patch
---

Initial release
21 changes: 21 additions & 0 deletions .github/workflows/release-tauri-crate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: release
on:
push:
tags:
- tauri-plugin-powersync@[0-9]+.[0-9]+.[0-9]+

jobs:
publish_crates_io:
runs-on: ubuntu-latest
permissions:
id-token: write # Required for OIDC token exchange
steps:
- uses: actions/checkout@v6
- uses: rust-lang/crates-io-auth-action@v1
id: auth

- name: Publish powersync crate
run: cargo publish
working-directory: powersync
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
50 changes: 50 additions & 0 deletions .github/workflows/test-tauri.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Tauri Integration tests
on:
pull_request: # triggered for any PR updates (including new pushes to PR branch)

jobs:
check-changes:
name: Check for relevant changes
runs-on: ubuntu-latest
outputs:
should_run: ${{ steps.check.outputs.should_run }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Check for changes
id: check
run: |
git fetch origin ${{ github.base_ref }}
if git diff --quiet origin/${{ github.base_ref }} -- packages/common packages/tauri; then
echo "should_run=false" >> $GITHUB_OUTPUT
else
echo "should_run=true" >> $GITHUB_OUTPUT
fi

test-tauri:
name: Tauri integration tests macOS
runs-on: macos-latest
needs: check-changes
if: needs.check-changes.outputs.should_run == 'true'
timeout-minutes: 15

steps:
- uses: actions/checkout@v6
- name: Enable Corepack
run: corepack enable
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
cache: pnpm

- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm run -r --filter "@powersync/tauri-plugin..." build
- name: Compile test runner
run: cargo build -p test-runner
- name: Test
working-directory: packages/tauri
run: pnpm test
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,7 @@ demos/**/.pnpmfile.cjs
demos/**/.branches
demos/**/.temp

Cargo.lock
target/
# Large files generated by Tauri
**/gen/schemas/
13 changes: 13 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Having a top-level Cargo.toml makes it easier to use rust-analyzer in VS Code when opening
# this project.

[workspace]
resolver = "3"
members = [
"packages/tauri",
"packages/tauri/test-runner",
"demos/tauri-app/src-tauri"
]

[workspace.package]
repository = "https://github.com/powersync-ja/powersync-js"
24 changes: 24 additions & 0 deletions demos/tauri-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# PowerSync Tauri demo

This demonstrates a minimal Tauri app using PowerSync for local persistence and data sync.

It is designed to run with a [self-hosted instance](https://github.com/powersync-ja/self-host-demo/). To get started:

1. Clone the [self-host-demo](https://github.com/powersync-ja/self-host-demo/) repository.
2. Change `config/sync-config.yaml` to the Sync Config from this readme.
3. Run `pnpm tauri dev` to start the app.

```yaml
# config/sync-config.yaml
config:
edition: 3

streams:
lists:
query: SELECT * FROM lists
auto_subscribe: true
todos:
query: SELECT * FROM todos WHERE list_id = subscription.parameter('list')
```

When opening the app, you should see a list displaying all `lists` rows from the server. Clicking on the `Open in new window` button opens the list in a new window. All windows have their own JavaScript context, but share queries and sync in real-time thanks to the PowerSync Tauri plugin.
13 changes: 13 additions & 0 deletions demos/tauri-app/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PowerSync Tauri Demo</title>
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
30 changes: 30 additions & 0 deletions demos/tauri-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "tauri-app",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"tauri": "tauri"
},
"dependencies": {
"@powersync/common": "1.50.0",
"@powersync/tauri-plugin": "0.0.1",
"@powersync/react": "1.9.1",
"@tauri-apps/api": "^2.0.0",
"@tanstack/react-router": "^1.167.4",
"react": "^19.2.4",
"react-dom": "^19.2.4"
},
"devDependencies": {
"@tauri-apps/cli": "^2.0.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"@tanstack/router-plugin": "^1.166.13",
"typescript": "^5.9.3",
"vite": "^8.0.0"
}
}
2 changes: 2 additions & 0 deletions demos/tauri-app/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
packages:
- .
29 changes: 29 additions & 0 deletions demos/tauri-app/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[package]
name = "tauri-app"
version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
license = ""
repository = ""
edition = "2021"
rust-version = "1.77.2"

[lib]
name = "tauri_app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[build-dependencies]
tauri-build = { version = "2.5.6", default-features = false , features = [] }

[dependencies]
tauri = { version = "2.10.3", features = [] }
tauri-plugin-powersync = { path = "../../../packages/tauri" }
powersync = "0.0.5"
async-trait = "0.1.89"
serde = "1"
serde_json = "1"
reqwest = { version = "0.13.0", features = ["json"] }
log = "0.4"
futures-lite = "2"
3 changes: 3 additions & 0 deletions demos/tauri-app/src-tauri/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
tauri_build::build()
}
14 changes: 14 additions & 0 deletions demos/tauri-app/src-tauri/capabilities/default.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "enables the default permissions",
"windows": [
"*"
],
"permissions": [
"core:default",
"powersync:default",
"core:webview:allow-create-webview-window",
"core:window:allow-set-title"
]
}
Binary file added demos/tauri-app/src-tauri/icons/128x128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demos/tauri-app/src-tauri/icons/128x128@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demos/tauri-app/src-tauri/icons/32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demos/tauri-app/src-tauri/icons/icon.icns
Binary file not shown.
Binary file added demos/tauri-app/src-tauri/icons/icon.ico
Binary file not shown.
Binary file added demos/tauri-app/src-tauri/icons/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
115 changes: 115 additions & 0 deletions demos/tauri-app/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use async_trait::async_trait;
use futures_lite::StreamExt;
use log::warn;
use powersync::error::PowerSyncError;
use powersync::{
BackendConnector, PowerSyncCredentials, PowerSyncDatabase, SyncOptions, UpdateType,
};
use reqwest::StatusCode;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use tauri::{AppHandle, Runtime};
use tauri_plugin_powersync::PowerSyncExt;

#[tauri::command]
async fn connect<R: Runtime>(
app: AppHandle<R>,
handle: usize,
) -> tauri_plugin_powersync::Result<()> {
let ps = app.powersync();
let database = ps.database_from_javascript_handle(handle)?;

let options = SyncOptions::new(MyRustConnector {
db: database.clone(),
});
database.connect(options).await;

Ok(())
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![connect])
.plugin(tauri_plugin_powersync::init())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

/// A backend connector implementation based on the
/// [self-hosted demo](https://github.com/powersync-ja/self-host-demo/tree/main/demos/nodejs).
struct MyRustConnector {
db: PowerSyncDatabase,
}

#[async_trait]
impl BackendConnector for MyRustConnector {
async fn fetch_credentials(&self) -> Result<PowerSyncCredentials, PowerSyncError> {
let response = reqwest::get("http://localhost:6060/api/auth/token").await?;

#[derive(Deserialize)]
struct TokenResponse {
token: String,
}

let token: TokenResponse = response.json().await?;
Ok(PowerSyncCredentials {
endpoint: "http://localhost:8080".to_string(),
token: token.token,
})
}

async fn upload_data(&self) -> Result<(), PowerSyncError> {
let mut transactions = self.db.crud_transactions();
let mut last_tx = None;

while let Some(mut tx) = transactions.try_next().await? {
#[derive(Serialize)]
struct BackendEntry {
op: UpdateType,
table: String,
id: String,
data: Option<Map<String, Value>>,
}

#[derive(Serialize)]
struct BackendBatch {
batch: Vec<BackendEntry>,
}

let mut entries = vec![];
for crud in std::mem::take(&mut tx.crud) {
entries.push(BackendEntry {
op: crud.update_type,
table: crud.table,
id: crud.id,
data: crud.data,
});
}

let serialized = serde_json::to_string(&BackendBatch { batch: entries })?;

let client = reqwest::Client::new();
let response = client
.post("http://localhost:6060/api/data")
.header("Content-Type", "application/json")
.body(serialized)
.send()
.await?;

if response.status() != StatusCode::OK {
let status = response.status();
let body = response.text().await?;
warn!("Received {} from /api/data: {}", status, body);
}

last_tx = Some(tx);
}

if let Some(tx) = last_tx {
tx.complete().await?;
}

Ok(())
}
}
6 changes: 6 additions & 0 deletions demos/tauri-app/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

fn main() {
tauri_app_lib::run();
}
37 changes: 37 additions & 0 deletions demos/tauri-app/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"productName": "tauri-app",
"version": "0.1.0",
"identifier": "com.powersync.demo.tauri.selfhosted",
"build": {
"beforeDevCommand": "pnpm dev",
"beforeBuildCommand": "pnpm build",
"devUrl": "http://localhost:1420",
"frontendDist": "../dist"
},
"app": {
"withGlobalTauri": false,
"security": {
"csp": null
},
"windows": [
{
"fullscreen": false,
"height": 600,
"resizable": true,
"title": "tauri-app",
"width": 800
}
]
},
"bundle": {
"active": true,
"targets": "all",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
]
}
}
Loading
Loading