Merge branch 'tauri-apps:v2' into v2

pull/2576/head
Jim 4 months ago committed by GitHub
commit 72fc99efe9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,6 @@
---
autostart: minor
autostart-js: minor
---
Add a `Builder` for more flexible settings

@ -0,0 +1,6 @@
---
http: patch
http-js: patch
---
Add `zstd` cargo feature flag to enable `reqwest/zstd` flag.

@ -0,0 +1,6 @@
---
'deep-link': 'patch:bug'
'deep-link-js': 'patch:bug'
---
Fix `is_registered` not being able to pickup deep link registered in `HKEY_LOCAL_MACHINE` on Windows

@ -0,0 +1,6 @@
---
'deep-link': 'patch:bug'
'deep-link-js': 'patch:bug'
---
Fix `unregister` not being able to remove deep link registered in `HKEY_LOCAL_MACHINE` on Windows

@ -0,0 +1,7 @@
---
"http": "patch"
"http-js": "patch"
---
Persist cookies to disk and load it on next app start.

@ -0,0 +1,6 @@
---
"fs": "patch:bug"
"fs-js": "patch:bug"
---
Fix `writeFile` ReadableStream handling due to missing async iterator support on macOS platform

@ -0,0 +1,6 @@
---
"updater": "patch:bug"
"updater-js": "patch:bug"
---
Fix `timeout` passed to `check` gets re-used by `download` and `downloadAndinstall`

@ -46,7 +46,7 @@ jobs:
node-version: 'lts/*' node-version: 'lts/*'
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
with: with:
version: 9.x.x version: 10.x.x
run_install: true run_install: true
- name: audit - name: audit
run: pnpm audit run: pnpm audit

@ -167,7 +167,7 @@ jobs:
node-version: 'lts/*' node-version: 'lts/*'
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
with: with:
version: 9.x.x version: 10.x.x
run_install: true run_install: true
- name: build api - name: build api

@ -39,7 +39,7 @@ jobs:
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
with: with:
version: 9.x.x version: 10.x.x
run_install: true run_install: true
- name: cargo login - name: cargo login
@ -75,7 +75,7 @@ jobs:
- name: Create Pull Request With Versions Bumped - name: Create Pull Request With Versions Bumped
id: cpr id: cpr
uses: tauri-apps/create-pull-request@v3 uses: peter-evans/create-pull-request@dd2324fc52d5d43c699a5636bcf19fceaa70c284 # 7.0.7
if: steps.covector.outputs.commandRan == 'version' if: steps.covector.outputs.commandRan == 'version'
with: with:
title: 'Publish New Versions (${{ github.ref_name }})' title: 'Publish New Versions (${{ github.ref_name }})'

@ -38,7 +38,7 @@ jobs:
node-version: 'lts/*' node-version: 'lts/*'
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
with: with:
version: 9.x.x version: 10.x.x
run_install: true run_install: true
- run: pnpm format:check - run: pnpm format:check

@ -49,7 +49,7 @@ jobs:
node-version: 'lts/*' node-version: 'lts/*'
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
with: with:
version: 9.x.x version: 10.x.x
run_install: true run_install: true
- name: eslint - name: eslint
run: pnpm lint run: pnpm lint

@ -38,7 +38,7 @@ jobs:
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
with: with:
version: 9.x.x version: 10.x.x
run_install: true run_install: true
- name: Build packages - name: Build packages

2
.gitignore vendored

@ -14,6 +14,8 @@ target/
# .vscode workspace settings file # .vscode workspace settings file
.vscode/settings.json .vscode/settings.json
.vscode/launch.json
.vscode/tasks.json
# npm, yarn and bun lock files # npm, yarn and bun lock files
package-lock.json package-lock.json

835
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -1,5 +1,42 @@
# Changelog # Changelog
## \[2.0.19]
### Dependencies
- Upgraded to `http-js@2.4.2`
- Upgraded to `updater-js@2.6.1`
## \[2.0.18]
### Dependencies
- Upgraded to `http-js@2.4.1`
## \[2.0.17]
### Dependencies
- Upgraded to `log-js@2.3.1`
## \[2.0.16]
### Dependencies
- Upgraded to `clipboard-manager-js@2.2.2`
- Upgraded to `notification-js@2.2.2`
- Upgraded to `os-js@2.2.1`
- Upgraded to `http-js@2.4.0`
- Upgraded to `log-js@2.3.0`
- Upgraded to `updater-js@2.6.0`
## \[2.0.15]
### Dependencies
- Upgraded to `log-js@2.2.3`
- Upgraded to `opener-js@2.2.6`
## \[2.0.14] ## \[2.0.14]
### Dependencies ### Dependencies

@ -1,7 +1,7 @@
{ {
"name": "api", "name": "api",
"private": true, "private": true,
"version": "2.0.14", "version": "2.0.19",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite --clearScreen false", "dev": "vite --clearScreen false",
@ -10,35 +10,35 @@
"tauri": "tauri" "tauri": "tauri"
}, },
"dependencies": { "dependencies": {
"@tauri-apps/api": "2.2.0", "@tauri-apps/api": "2.4.0",
"@tauri-apps/plugin-barcode-scanner": "^2.2.0", "@tauri-apps/plugin-barcode-scanner": "^2.2.0",
"@tauri-apps/plugin-biometric": "^2.2.0", "@tauri-apps/plugin-biometric": "^2.2.0",
"@tauri-apps/plugin-cli": "^2.2.0", "@tauri-apps/plugin-cli": "^2.2.0",
"@tauri-apps/plugin-clipboard-manager": "^2.2.1", "@tauri-apps/plugin-clipboard-manager": "^2.2.2",
"@tauri-apps/plugin-dialog": "^2.2.0", "@tauri-apps/plugin-dialog": "^2.2.0",
"@tauri-apps/plugin-fs": "^2.2.0", "@tauri-apps/plugin-fs": "^2.2.0",
"@tauri-apps/plugin-geolocation": "^2.2.0", "@tauri-apps/plugin-geolocation": "^2.2.0",
"@tauri-apps/plugin-global-shortcut": "^2.2.0", "@tauri-apps/plugin-global-shortcut": "^2.2.0",
"@tauri-apps/plugin-opener": "^2.2.5",
"@tauri-apps/plugin-haptics": "^2.2.0", "@tauri-apps/plugin-haptics": "^2.2.0",
"@tauri-apps/plugin-http": "^2.3.0", "@tauri-apps/plugin-http": "^2.4.2",
"@tauri-apps/plugin-nfc": "^2.2.0", "@tauri-apps/plugin-nfc": "^2.2.0",
"@tauri-apps/plugin-notification": "^2.2.1", "@tauri-apps/plugin-notification": "^2.2.2",
"@tauri-apps/plugin-os": "^2.2.0", "@tauri-apps/plugin-opener": "^2.2.6",
"@tauri-apps/plugin-os": "^2.2.1",
"@tauri-apps/plugin-process": "^2.2.0", "@tauri-apps/plugin-process": "^2.2.0",
"@tauri-apps/plugin-shell": "^2.2.0", "@tauri-apps/plugin-shell": "^2.2.0",
"@tauri-apps/plugin-store": "^2.2.0", "@tauri-apps/plugin-store": "^2.2.0",
"@tauri-apps/plugin-updater": "^2.5.1", "@tauri-apps/plugin-updater": "^2.6.1",
"@zerodevx/svelte-json-view": "1.0.11" "@zerodevx/svelte-json-view": "1.0.11"
}, },
"devDependencies": { "devDependencies": {
"@iconify-json/codicon": "^1.2.6", "@iconify-json/codicon": "^1.2.12",
"@iconify-json/ph": "^1.2.1", "@iconify-json/ph": "^1.2.2",
"@sveltejs/vite-plugin-svelte": "^5.0.1", "@sveltejs/vite-plugin-svelte": "^5.0.3",
"@tauri-apps/cli": "2.2.7", "@tauri-apps/cli": "2.4.0",
"@unocss/extractor-svelte": "^66.0.0", "@unocss/extractor-svelte": "^66.0.0",
"svelte": "^5.10.0", "svelte": "^5.20.4",
"unocss": "^66.0.0", "unocss": "^66.0.0",
"vite": "^6.0.3" "vite": "^6.2.0"
} }
} }

@ -1,5 +1,44 @@
# Changelog # Changelog
## \[2.0.23]
### Dependencies
- Upgraded to `http@2.4.2`
- Upgraded to `updater@2.6.1`
## \[2.0.22]
### Dependencies
- Upgraded to `http@2.4.1`
## \[2.0.21]
### Dependencies
- Upgraded to `log@2.3.1`
## \[2.0.20]
### Dependencies
- Upgraded to `clipboard-manager@2.2.2`
- Upgraded to `geolocation@2.2.4`
- Upgraded to `haptics@2.2.4`
- Upgraded to `notification@2.2.2`
- Upgraded to `os@2.2.1`
- Upgraded to `http@2.4.0`
- Upgraded to `log@2.3.0`
- Upgraded to `updater@2.6.0`
## \[2.0.19]
### Dependencies
- Upgraded to `log@2.2.3`
- Upgraded to `opener@2.2.6`
## \[2.0.18] ## \[2.0.18]
### Dependencies ### Dependencies

@ -1,7 +1,7 @@
[package] [package]
name = "api" name = "api"
publish = false publish = false
version = "2.0.18" version = "2.0.23"
description = "An example Tauri Application showcasing the api" description = "An example Tauri Application showcasing the api"
edition = "2021" edition = "2021"
rust-version = { workspace = true } rust-version = { workspace = true }
@ -18,22 +18,24 @@ tauri-build = { workspace = true, features = ["codegen", "isolation"] }
serde_json = { workspace = true } serde_json = { workspace = true }
serde = { workspace = true } serde = { workspace = true }
tiny_http = "0.12" tiny_http = "0.12"
time = "0.3"
log = { workspace = true } log = { workspace = true }
tauri-plugin-log = { path = "../../../plugins/log", version = "2.2.2" } tauri-plugin-log = { path = "../../../plugins/log", version = "2.3.1" }
tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.2.0", features = [ tauri-plugin-fs = { path = "../../../plugins/fs", version = "2.2.0", features = [
"watch", "watch",
] } ] }
tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.2.1" } tauri-plugin-clipboard-manager = { path = "../../../plugins/clipboard-manager", version = "2.2.2" }
tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.2.0" } tauri-plugin-dialog = { path = "../../../plugins/dialog", version = "2.2.0" }
tauri-plugin-http = { path = "../../../plugins/http", features = [ tauri-plugin-http = { path = "../../../plugins/http", features = [
"multipart", "multipart",
], version = "2.3.0" } "cookies",
tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.2.1", features = [ ], version = "2.4.2" }
tauri-plugin-notification = { path = "../../../plugins/notification", version = "2.2.2", features = [
"windows7-compat", "windows7-compat",
] } ] }
tauri-plugin-os = { path = "../../../plugins/os", version = "2.2.0" } tauri-plugin-os = { path = "../../../plugins/os", version = "2.2.1" }
tauri-plugin-process = { path = "../../../plugins/process", version = "2.2.0" } tauri-plugin-process = { path = "../../../plugins/process", version = "2.2.0" }
tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.2.5" } tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.2.6" }
tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.2.0" } tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.2.0" }
tauri-plugin-store = { path = "../../../plugins/store", version = "2.2.0" } tauri-plugin-store = { path = "../../../plugins/store", version = "2.2.0" }
@ -53,15 +55,15 @@ features = [
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] [target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
tauri-plugin-cli = { path = "../../../plugins/cli", version = "2.2.0" } tauri-plugin-cli = { path = "../../../plugins/cli", version = "2.2.0" }
tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut", version = "2.2.0" } tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut", version = "2.2.0" }
tauri-plugin-updater = { path = "../../../plugins/updater", version = "2.5.1" } tauri-plugin-updater = { path = "../../../plugins/updater", version = "2.6.1" }
tauri-plugin-window-state = { path = "../../../plugins/window-state", version = "2.2.0" } tauri-plugin-window-state = { path = "../../../plugins/window-state", version = "2.2.0" }
[target."cfg(any(target_os = \"android\", target_os = \"ios\"))".dependencies] [target."cfg(any(target_os = \"android\", target_os = \"ios\"))".dependencies]
tauri-plugin-barcode-scanner = { path = "../../../plugins/barcode-scanner/", version = "2.2.0" } tauri-plugin-barcode-scanner = { path = "../../../plugins/barcode-scanner/", version = "2.2.0" }
tauri-plugin-nfc = { path = "../../../plugins/nfc", version = "2.2.0" } tauri-plugin-nfc = { path = "../../../plugins/nfc", version = "2.2.0" }
tauri-plugin-biometric = { path = "../../../plugins/biometric/", version = "2.2.0" } tauri-plugin-biometric = { path = "../../../plugins/biometric/", version = "2.2.0" }
tauri-plugin-geolocation = { path = "../../../plugins/geolocation/", version = "2.2.3" } tauri-plugin-geolocation = { path = "../../../plugins/geolocation/", version = "2.2.4" }
tauri-plugin-haptics = { path = "../../../plugins/haptics/", version = "2.2.3" } tauri-plugin-haptics = { path = "../../../plugins/haptics/", version = "2.2.4" }
[features] [features]
prod = ["tauri/custom-protocol"] prod = ["tauri/custom-protocol"]

@ -102,9 +102,28 @@ pub fn run() {
if let Ok(mut request) = server.recv() { if let Ok(mut request) = server.recv() {
let mut body = Vec::new(); let mut body = Vec::new();
let _ = request.as_reader().read_to_end(&mut body); let _ = request.as_reader().read_to_end(&mut body);
let mut headers = request.headers().to_vec();
if !headers.iter().any(|header| header.field == tiny_http::HeaderField::from_bytes(b"Cookie").unwrap()) {
let expires = time::OffsetDateTime::now_utc() + time::Duration::days(1);
// RFC 1123 format
let format = time::macros::format_description!(
"[weekday repr:short], [day] [month repr:short] [year] [hour]:[minute]:[second] GMT"
);
let expires_str = expires.format(format).unwrap();
headers.push(
tiny_http::Header::from_bytes(
&b"Set-Cookie"[..],
format!("session-token=test-value; Secure; Path=/; Expires={expires_str}")
.as_bytes(),
)
.unwrap(),
);
}
let response = tiny_http::Response::new( let response = tiny_http::Response::new(
tiny_http::StatusCode(200), tiny_http::StatusCode(200),
request.headers().to_vec(), headers,
std::io::Cursor::new(body), std::io::Cursor::new(body),
request.body_length(), request.body_length(),
None, None,

@ -11,25 +11,29 @@
"example:api:dev": "pnpm run --filter \"api\" tauri dev" "example:api:dev": "pnpm run --filter \"api\" tauri dev"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "9.21.0", "@eslint/js": "9.23.0",
"@rollup/plugin-node-resolve": "16.0.0", "@rollup/plugin-node-resolve": "16.0.1",
"@rollup/plugin-terser": "0.4.4", "@rollup/plugin-terser": "0.4.4",
"@rollup/plugin-typescript": "11.1.6", "@rollup/plugin-typescript": "12.1.2",
"covector": "^0.12.3", "covector": "^0.12.4",
"eslint": "9.21.0", "eslint": "9.23.0",
"eslint-config-prettier": "10.0.1", "eslint-config-prettier": "10.1.1",
"eslint-plugin-security": "3.0.1", "eslint-plugin-security": "3.0.1",
"prettier": "3.5.2", "prettier": "3.5.3",
"rollup": "4.34.8", "rollup": "4.37.0",
"tslib": "2.8.1", "tslib": "2.8.1",
"typescript": "5.7.3", "typescript": "5.8.2",
"typescript-eslint": "8.25.0" "typescript-eslint": "8.28.0"
}, },
"resolutions": { "pnpm": {
"semver": ">=7.5.2", "overrides": {
"optionator": ">=0.9.3" "esbuild@<0.25.0": ">=0.25.0"
},
"onlyBuiltDependencies": [
"esbuild"
]
}, },
"engines": { "engines": {
"pnpm": "^9.0.0" "pnpm": "^10.0.0"
} }
} }

@ -57,11 +57,9 @@ First you need to register the core plugin with Tauri:
`src-tauri/src/lib.rs` `src-tauri/src/lib.rs`
```rust ```rust
use tauri_plugin_autostart::MacosLauncher;
fn main() { fn main() {
tauri::Builder::default() tauri::Builder::default()
.plugin(tauri_plugin_autostart::init(MacosLauncher::LaunchAgent, Some(vec!["--flag1", "--flag2"]) /* arbitrary number of args to pass to your app */)) .plugin(tauri_plugin_autostart::Builder::new().args((["--flag1", "--flag2"])).build()))
.run(tauri::generate_context!()) .run(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while running tauri application");
} }

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -14,7 +14,7 @@ use auto_launch::{AutoLaunch, AutoLaunchBuilder};
use serde::{ser::Serializer, Serialize}; use serde::{ser::Serializer, Serialize};
use tauri::{ use tauri::{
command, command,
plugin::{Builder, TauriPlugin}, plugin::{Builder as PluginBuilder, TauriPlugin},
Manager, Runtime, State, Manager, Runtime, State,
}; };
@ -22,8 +22,9 @@ use std::env::current_exe;
type Result<T> = std::result::Result<T, Error>; type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Default, Copy, Clone)]
pub enum MacosLauncher { pub enum MacosLauncher {
#[default]
LaunchAgent, LaunchAgent,
AppleScript, AppleScript,
} }
@ -71,10 +72,12 @@ impl AutoLaunchManager {
} }
pub trait ManagerExt<R: Runtime> { pub trait ManagerExt<R: Runtime> {
/// TODO: Rename these to `autostart` or `auto_start` in v3
fn autolaunch(&self) -> State<'_, AutoLaunchManager>; fn autolaunch(&self) -> State<'_, AutoLaunchManager>;
} }
impl<R: Runtime, T: Manager<R>> ManagerExt<R> for T { impl<R: Runtime, T: Manager<R>> ManagerExt<R> for T {
/// TODO: Rename these to `autostart` or `auto_start` in v3
fn autolaunch(&self) -> State<'_, AutoLaunchManager> { fn autolaunch(&self) -> State<'_, AutoLaunchManager> {
self.state::<AutoLaunchManager>() self.state::<AutoLaunchManager>()
} }
@ -95,59 +98,132 @@ async fn is_enabled(manager: State<'_, AutoLaunchManager>) -> Result<bool> {
manager.is_enabled() manager.is_enabled()
} }
#[derive(Default)]
pub struct Builder {
#[cfg(target_os = "macos")]
macos_launcher: MacosLauncher,
args: Vec<String>,
}
impl Builder {
/// Create a new auto start builder with default settings
pub fn new() -> Self {
Self::default()
}
/// Adds an argument to pass to your app on startup.
///
/// ## Examples
///
/// ```no_run
/// Builder::new()
/// .arg("--from-autostart")
/// .arg("--hey")
/// .build();
/// ```
pub fn arg<S: Into<String>>(mut self, arg: S) -> Self {
self.args.push(arg.into());
self
}
/// Adds multiple arguments to pass to your app on startup.
///
/// ## Examples
///
/// ```no_run
/// Builder::new()
/// .args(["--from-autostart", "--hey"])
/// .build();
/// ```
pub fn args<I, S>(mut self, args: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
for arg in args {
self = self.arg(arg);
}
self
}
/// Sets whether to use launch agent or apple script to be used to enable auto start,
/// the builder's default is [`MacosLauncher::LaunchAgent`]
#[cfg(target_os = "macos")]
pub fn macos_launcher(mut self, macos_launcher: MacosLauncher) -> Self {
self.macos_launcher = macos_launcher;
self
}
pub fn build<R: Runtime>(self) -> TauriPlugin<R> {
PluginBuilder::new("autostart")
.invoke_handler(tauri::generate_handler![enable, disable, is_enabled])
.setup(move |app, _api| {
let mut builder = AutoLaunchBuilder::new();
builder.set_app_name(&app.package_info().name);
builder.set_args(&self.args);
let current_exe = current_exe()?;
#[cfg(windows)]
builder.set_app_path(&current_exe.display().to_string());
#[cfg(target_os = "macos")]
{
builder.set_use_launch_agent(matches!(
self.macos_launcher,
MacosLauncher::LaunchAgent
));
// on macOS, current_exe gives path to /Applications/Example.app/MacOS/Example
// but this results in seeing a Unix Executable in macOS login items
// It must be: /Applications/Example.app
// If it didn't find exactly a single occurance of .app, it will default to
// exe path to not break it.
let exe_path = current_exe.canonicalize()?.display().to_string();
let parts: Vec<&str> = exe_path.split(".app/").collect();
let app_path = if parts.len() == 2
&& matches!(self.macos_launcher, MacosLauncher::AppleScript)
{
format!("{}.app", parts.first().unwrap())
} else {
exe_path
};
builder.set_app_path(&app_path);
}
#[cfg(target_os = "linux")]
if let Some(appimage) = app
.env()
.appimage
.and_then(|p| p.to_str().map(|s| s.to_string()))
{
builder.set_app_path(&appimage);
} else {
builder.set_app_path(&current_exe.display().to_string());
}
app.manage(AutoLaunchManager(
builder.build().map_err(|e| e.to_string())?,
));
Ok(())
})
.build()
}
}
/// Initializes the plugin. /// Initializes the plugin.
/// ///
/// `args` - are passed to your app on startup. /// `args` - are passed to your app on startup.
pub fn init<R: Runtime>( pub fn init<R: Runtime>(
macos_launcher: MacosLauncher, #[allow(unused)] macos_launcher: MacosLauncher,
args: Option<Vec<&'static str>>, args: Option<Vec<&'static str>>,
) -> TauriPlugin<R> { ) -> TauriPlugin<R> {
Builder::new("autostart") let mut builder = Builder::new();
.invoke_handler(tauri::generate_handler![enable, disable, is_enabled]) if let Some(args) = args {
.setup(move |app, _api| { builder = builder.args(args)
let mut builder = AutoLaunchBuilder::new(); }
builder.set_app_name(&app.package_info().name); #[cfg(target_os = "macos")]
if let Some(args) = args { {
builder.set_args(&args); builder = builder.macos_launcher(macos_launcher);
} }
builder.set_use_launch_agent(matches!(macos_launcher, MacosLauncher::LaunchAgent)); builder.build()
let current_exe = current_exe()?;
#[cfg(windows)]
builder.set_app_path(&current_exe.display().to_string());
#[cfg(target_os = "macos")]
{
// on macOS, current_exe gives path to /Applications/Example.app/MacOS/Example
// but this results in seeing a Unix Executable in macOS login items
// It must be: /Applications/Example.app
// If it didn't find exactly a single occurance of .app, it will default to
// exe path to not break it.
let exe_path = current_exe.canonicalize()?.display().to_string();
let parts: Vec<&str> = exe_path.split(".app/").collect();
let app_path =
if parts.len() == 2 && matches!(macos_launcher, MacosLauncher::AppleScript) {
format!("{}.app", parts.first().unwrap())
} else {
exe_path
};
builder.set_app_path(&app_path);
}
#[cfg(target_os = "linux")]
if let Some(appimage) = app
.env()
.appimage
.and_then(|p| p.to_str().map(|s| s.to_string()))
{
builder.set_app_path(&appimage);
} else {
builder.set_app_path(&current_exe.display().to_string());
}
app.manage(AutoLaunchManager(
builder.build().map_err(|e| e.to_string())?,
));
Ok(())
})
.build()
} }

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -1,5 +1,11 @@
# Changelog # Changelog
## \[2.2.2]
### bug
- [`d37bbdef`](https://github.com/tauri-apps/plugins-workspace/commit/d37bbdef8dc70e61e59f9fe0bb8b2a48999d0aa1) ([#2507](https://github.com/tauri-apps/plugins-workspace/pull/2507) by [@SquitchYT](https://github.com/tauri-apps/plugins-workspace/../../SquitchYT)) Fix clipboard-manager Wayland support.
## \[2.2.1] ## \[2.2.1]
- [`ce11079f`](https://github.com/tauri-apps/plugins-workspace/commit/ce11079f19852fbefdecf0e4c7d947af3624fee0) ([#2280](https://github.com/tauri-apps/plugins-workspace/pull/2280) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Explicitly drop `arboard::Clipboard` on exit. Add recommendation to not use read methods on the mainthread. - [`ce11079f`](https://github.com/tauri-apps/plugins-workspace/commit/ce11079f19852fbefdecf0e4c7d947af3624fee0) ([#2280](https://github.com/tauri-apps/plugins-workspace/pull/2280) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Explicitly drop `arboard::Clipboard` on exit. Add recommendation to not use read methods on the mainthread.

@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-clipboard-manager" name = "tauri-plugin-clipboard-manager"
version = "2.2.1" version = "2.2.2"
description = "Read and write to the system clipboard." description = "Read and write to the system clipboard."
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
@ -36,4 +36,4 @@ thiserror = { workspace = true }
tauri = { workspace = true, features = ["wry"] } tauri = { workspace = true, features = ["wry"] }
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] [target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
arboard = "3" arboard = { version = "3", features = ["wayland-data-control"] }

@ -91,7 +91,7 @@ async function writeImage(
* import { readImage } from '@tauri-apps/plugin-clipboard-manager'; * import { readImage } from '@tauri-apps/plugin-clipboard-manager';
* *
* const clipboardImage = await readImage(); * const clipboardImage = await readImage();
* const blob = new Blob([await clipboardImage.rbga()], { type: 'image' }) * const blob = new Blob([await clipboardImage.rgba()], { type: 'image' })
* const url = URL.createObjectURL(blob) * const url = URL.createObjectURL(blob)
* ``` * ```
* @since 2.0.0 * @since 2.0.0

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-clipboard-manager", "name": "@tauri-apps/plugin-clipboard-manager",
"version": "2.2.1", "version": "2.2.2",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
"Tauri Programme within The Commons Conservancy" "Tauri Programme within The Commons Conservancy"

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -10,12 +10,12 @@
"tauri": "tauri" "tauri": "tauri"
}, },
"dependencies": { "dependencies": {
"@tauri-apps/api": "2.2.0", "@tauri-apps/api": "2.4.0",
"@tauri-apps/plugin-deep-link": "2.2.0" "@tauri-apps/plugin-deep-link": "2.2.0"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "2.2.7", "@tauri-apps/cli": "2.4.0",
"typescript": "^5.2.2", "typescript": "^5.7.3",
"vite": "^6.0.0" "vite": "^6.2.0"
} }
} }

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -172,7 +172,7 @@ mod imp {
use tauri::Manager; use tauri::Manager;
use tauri::{AppHandle, Runtime}; use tauri::{AppHandle, Runtime};
#[cfg(windows)] #[cfg(windows)]
use windows_registry::CURRENT_USER; use windows_registry::{CLASSES_ROOT, CURRENT_USER, LOCAL_MACHINE};
/// Access to the deep-link APIs. /// Access to the deep-link APIs.
pub struct DeepLink<R: Runtime> { pub struct DeepLink<R: Runtime> {
@ -258,25 +258,23 @@ mod imp {
pub fn register<S: AsRef<str>>(&self, _protocol: S) -> crate::Result<()> { pub fn register<S: AsRef<str>>(&self, _protocol: S) -> crate::Result<()> {
#[cfg(windows)] #[cfg(windows)]
{ {
let key_base = format!("Software\\Classes\\{}", _protocol.as_ref()); let protocol = _protocol.as_ref();
let key_base = format!("Software\\Classes\\{protocol}");
let exe = dunce::simplified(&tauri::utils::platform::current_exe()?) let exe = dunce::simplified(&tauri::utils::platform::current_exe()?)
.display() .display()
.to_string(); .to_string();
let key_reg = CURRENT_USER.create(&key_base)?; let key_reg = CURRENT_USER.create(&key_base)?;
key_reg.set_string( key_reg.set_string("", format!("URL:{} protocol", self.app.config().identifier))?;
"",
&format!("URL:{} protocol", self.app.config().identifier),
)?;
key_reg.set_string("URL Protocol", "")?; key_reg.set_string("URL Protocol", "")?;
let icon_reg = CURRENT_USER.create(format!("{key_base}\\DefaultIcon"))?; let icon_reg = CURRENT_USER.create(format!("{key_base}\\DefaultIcon"))?;
icon_reg.set_string("", &format!("{exe},0"))?; icon_reg.set_string("", format!("{exe},0"))?;
let cmd_reg = CURRENT_USER.create(format!("{key_base}\\shell\\open\\command"))?; let cmd_reg = CURRENT_USER.create(format!("{key_base}\\shell\\open\\command"))?;
cmd_reg.set_string("", &format!("\"{exe}\" \"%1\""))?; cmd_reg.set_string("", format!("\"{exe}\" \"%1\""))?;
Ok(()) Ok(())
} }
@ -351,13 +349,21 @@ mod imp {
/// ///
/// ## Platform-specific: /// ## Platform-specific:
/// ///
/// - **Windows**: Requires admin rights if the protocol is registered on local machine
/// (this can happen when registered from the NSIS installer when the install mode is set to both or per machine)
/// - **Linux**: Can only unregister the scheme if it was initially registered with [`register`](`Self::register`). May not work on older distros. /// - **Linux**: Can only unregister the scheme if it was initially registered with [`register`](`Self::register`). May not work on older distros.
/// - **macOS / Android / iOS**: Unsupported, will return [`Error::UnsupportedPlatform`](`crate::Error::UnsupportedPlatform`). /// - **macOS / Android / iOS**: Unsupported, will return [`Error::UnsupportedPlatform`](`crate::Error::UnsupportedPlatform`).
pub fn unregister<S: AsRef<str>>(&self, _protocol: S) -> crate::Result<()> { pub fn unregister<S: AsRef<str>>(&self, _protocol: S) -> crate::Result<()> {
#[cfg(windows)] #[cfg(windows)]
{ {
CURRENT_USER.remove_tree(format!("Software\\Classes\\{}", _protocol.as_ref()))?; let protocol = _protocol.as_ref();
let path = format!("Software\\Classes\\{protocol}");
if LOCAL_MACHINE.open(&path).is_ok() {
LOCAL_MACHINE.remove_tree(&path)?;
}
if CURRENT_USER.open(&path).is_ok() {
CURRENT_USER.remove_tree(&path)?;
}
Ok(()) Ok(())
} }
@ -401,10 +407,11 @@ mod imp {
pub fn is_registered<S: AsRef<str>>(&self, _protocol: S) -> crate::Result<bool> { pub fn is_registered<S: AsRef<str>>(&self, _protocol: S) -> crate::Result<bool> {
#[cfg(windows)] #[cfg(windows)]
{ {
let cmd_reg = CURRENT_USER.open(format!( let protocol = _protocol.as_ref();
"Software\\Classes\\{}\\shell\\open\\command", let Ok(cmd_reg) = CLASSES_ROOT.open(format!("{protocol}\\shell\\open\\command"))
_protocol.as_ref() else {
))?; return Ok(false);
};
let registered_cmd = cmd_reg.get_string("")?; let registered_cmd = cmd_reg.get_string("")?;

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

File diff suppressed because one or more lines are too long

@ -1075,10 +1075,18 @@ async function writeFile(
if (data instanceof ReadableStream) { if (data instanceof ReadableStream) {
const file = await open(path, options) const file = await open(path, options)
for await (const chunk of data) { const reader = data.getReader()
await file.write(chunk)
try {
while (true) {
const { done, value } = await reader.read()
if (done) break
await file.write(value)
}
} finally {
reader.releaseLock()
await file.close()
} }
await file.close()
} else { } else {
await invoke('plugin:fs|write_file', data, { await invoke('plugin:fs|write_file', data, {
headers: { headers: {

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -53,10 +53,7 @@ impl FilePath {
#[inline] #[inline]
pub fn into_path(self) -> Result<PathBuf> { pub fn into_path(self) -> Result<PathBuf> {
match self { match self {
Self::Url(url) => url Self::Url(url) => url.to_file_path().map_err(|_| Error::InvalidPathUrl),
.to_file_path()
.map(PathBuf::from)
.map_err(|_| Error::InvalidPathUrl),
Self::Path(p) => Ok(p), Self::Path(p) => Ok(p),
} }
} }
@ -91,10 +88,7 @@ impl SafeFilePath {
#[inline] #[inline]
pub fn into_path(self) -> Result<PathBuf> { pub fn into_path(self) -> Result<PathBuf> {
match self { match self {
Self::Url(url) => url Self::Url(url) => url.to_file_path().map_err(|_| Error::InvalidPathUrl),
.to_file_path()
.map(PathBuf::from)
.map_err(|_| Error::InvalidPathUrl),
Self::Path(p) => Ok(p.as_ref().to_owned()), Self::Path(p) => Ok(p.as_ref().to_owned()),
} }
} }

@ -1,5 +1,9 @@
# Changelog # Changelog
## \[2.2.4]
- [`a1b3fa27`](https://github.com/tauri-apps/plugins-workspace/commit/a1b3fa27f11022c9b6622b4fab12d93239eb05de) ([#2515](https://github.com/tauri-apps/plugins-workspace/pull/2515) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Re-exported the `Geolocation`, `Haptics`, `Notification`, and `Os` structs so that they show up on docs.rs.
## \[2.2.3] ## \[2.2.3]
- [`406e6f48`](https://github.com/tauri-apps/plugins-workspace/commit/406e6f484cdc13d35c50fb949f7489ca9eeccc44) ([#2323](https://github.com/tauri-apps/plugins-workspace/pull/2323) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Fixed an issue that caused build failures when the `haptics` or `geolocation` plugin was used without their `specta` feature flag enabled. - [`406e6f48`](https://github.com/tauri-apps/plugins-workspace/commit/406e6f484cdc13d35c50fb949f7489ca9eeccc44) ([#2323](https://github.com/tauri-apps/plugins-workspace/pull/2323) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Fixed an issue that caused build failures when the `haptics` or `geolocation` plugin was used without their `specta` feature flag enabled.

@ -1,7 +1,7 @@
[package] [package]
name = "tauri-plugin-geolocation" name = "tauri-plugin-geolocation"
description = "Get and track the device's current position" description = "Get and track the device's current position"
version = "2.2.3" version = "2.2.4"
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
license = { workspace = true } license = { workspace = true }

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-geolocation", "name": "@tauri-apps/plugin-geolocation",
"version": "2.2.3", "version": "2.2.4",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
"Tauri Programme within The Commons Conservancy" "Tauri Programme within The Commons Conservancy"

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -21,9 +21,9 @@ mod models;
pub use error::{Error, Result}; pub use error::{Error, Result};
#[cfg(desktop)] #[cfg(desktop)]
use desktop::Geolocation; pub use desktop::Geolocation;
#[cfg(mobile)] #[cfg(mobile)]
use mobile::Geolocation; pub use mobile::Geolocation;
/// Extensions to [`tauri::App`], [`tauri::AppHandle`], [`tauri::WebviewWindow`], [`tauri::Webview`] and [`tauri::Window`] to access the geolocation APIs. /// Extensions to [`tauri::App`], [`tauri::AppHandle`], [`tauri::WebviewWindow`], [`tauri::Webview`] and [`tauri::Window`] to access the geolocation APIs.
pub trait GeolocationExt<R: Runtime> { pub trait GeolocationExt<R: Runtime> {

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -1,5 +1,9 @@
# Changelog # Changelog
## \[2.2.4]
- [`a1b3fa27`](https://github.com/tauri-apps/plugins-workspace/commit/a1b3fa27f11022c9b6622b4fab12d93239eb05de) ([#2515](https://github.com/tauri-apps/plugins-workspace/pull/2515) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Re-exported the `Geolocation`, `Haptics`, `Notification`, and `Os` structs so that they show up on docs.rs.
## \[2.2.3] ## \[2.2.3]
- [`406e6f48`](https://github.com/tauri-apps/plugins-workspace/commit/406e6f484cdc13d35c50fb949f7489ca9eeccc44) ([#2323](https://github.com/tauri-apps/plugins-workspace/pull/2323) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Fixed an issue that caused build failures when the `haptics` or `geolocation` plugin was used without their `specta` feature flag enabled. - [`406e6f48`](https://github.com/tauri-apps/plugins-workspace/commit/406e6f484cdc13d35c50fb949f7489ca9eeccc44) ([#2323](https://github.com/tauri-apps/plugins-workspace/pull/2323) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Fixed an issue that caused build failures when the `haptics` or `geolocation` plugin was used without their `specta` feature flag enabled.

@ -1,7 +1,7 @@
[package] [package]
name = "tauri-plugin-haptics" name = "tauri-plugin-haptics"
description = "Haptic feedback and vibrations on Android and iOS" description = "Haptic feedback and vibrations on Android and iOS"
version = "2.2.3" version = "2.2.4"
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
license = { workspace = true } license = { workspace = true }

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-haptics", "name": "@tauri-apps/plugin-haptics",
"version": "2.2.3", "version": "2.2.4",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
"Tauri Programme within The Commons Conservancy" "Tauri Programme within The Commons Conservancy"

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -21,9 +21,9 @@ mod models;
pub use error::{Error, Result}; pub use error::{Error, Result};
#[cfg(desktop)] #[cfg(desktop)]
use desktop::Haptics; pub use desktop::Haptics;
#[cfg(mobile)] #[cfg(mobile)]
use mobile::Haptics; pub use mobile::Haptics;
/// Extensions to [`tauri::App`], [`tauri::AppHandle`], [`tauri::WebviewWindow`], [`tauri::Webview`] and [`tauri::Window`] to access the haptics APIs. /// Extensions to [`tauri::App`], [`tauri::AppHandle`], [`tauri::WebviewWindow`], [`tauri::Webview`] and [`tauri::Window`] to access the haptics APIs.
pub trait HapticsExt<R: Runtime> { pub trait HapticsExt<R: Runtime> {

@ -1,5 +1,17 @@
# Changelog # Changelog
## \[2.4.2]
- [`a15eedf3`](https://github.com/tauri-apps/plugins-workspace/commit/a15eedf37854344f7ffbcb0d373d848563817011) ([#2535](https://github.com/tauri-apps/plugins-workspace/pull/2535) by [@amrbashir](https://github.com/tauri-apps/plugins-workspace/../../amrbashir)) Fix `fetch` occasionally throwing an error due to trying to close the underline stream twice.
## \[2.4.1]
- [`d3183aa9`](https://github.com/tauri-apps/plugins-workspace/commit/d3183aa99da7ca67e627394132ddeb3b85ccef06) ([#2522](https://github.com/tauri-apps/plugins-workspace/pull/2522) by [@adrieljss](https://github.com/tauri-apps/plugins-workspace/../../adrieljss)) Fix `fetch` blocking until the whole response is read even if it was a streaming response.
## \[2.4.0]
- [`cb38f54f`](https://github.com/tauri-apps/plugins-workspace/commit/cb38f54f4a4ef30995283cd82166c62da17bac44) ([#2479](https://github.com/tauri-apps/plugins-workspace/pull/2479) by [@adrieljss](https://github.com/tauri-apps/plugins-workspace/../../adrieljss)) Add stream support for HTTP stream responses.
## \[2.3.0] ## \[2.3.0]
- [`10513649`](https://github.com/tauri-apps/plugins-workspace/commit/105136494c5a5bf4b1f1cc06cc71815412d17ec8) ([#2204](https://github.com/tauri-apps/plugins-workspace/pull/2204) by [@RickeyWard](https://github.com/tauri-apps/plugins-workspace/../../RickeyWard)) Add `dangerous-settings` feature flag and new JS `danger` option to disable tls hostname/certificate validation. - [`10513649`](https://github.com/tauri-apps/plugins-workspace/commit/105136494c5a5bf4b1f1cc06cc71815412d17ec8) ([#2204](https://github.com/tauri-apps/plugins-workspace/pull/2204) by [@RickeyWard](https://github.com/tauri-apps/plugins-workspace/../../RickeyWard)) Add `dangerous-settings` feature flag and new JS `danger` option to disable tls hostname/certificate validation.

@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-http" name = "tauri-plugin-http"
version = "2.3.0" version = "2.4.2"
description = "Access an HTTP client written in Rust." description = "Access an HTTP client written in Rust."
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
@ -41,6 +41,8 @@ http = "1"
reqwest = { version = "0.12", default-features = false } reqwest = { version = "0.12", default-features = false }
url = { workspace = true } url = { workspace = true }
data-url = "0.3" data-url = "0.3"
cookie_store = { version = "0.21.1", optional = true, features = ["serde"] }
bytes = { version = "1.9", optional = true }
tracing = { workspace = true, optional = true } tracing = { workspace = true, optional = true }
[features] [features]
@ -62,10 +64,11 @@ rustls-tls-manual-roots = ["reqwest/rustls-tls-manual-roots"]
rustls-tls-webpki-roots = ["reqwest/rustls-tls-webpki-roots"] rustls-tls-webpki-roots = ["reqwest/rustls-tls-webpki-roots"]
rustls-tls-native-roots = ["reqwest/rustls-tls-native-roots"] rustls-tls-native-roots = ["reqwest/rustls-tls-native-roots"]
blocking = ["reqwest/blocking"] blocking = ["reqwest/blocking"]
cookies = ["reqwest/cookies"] cookies = ["reqwest/cookies", "dep:cookie_store", "dep:bytes"]
gzip = ["reqwest/gzip"] gzip = ["reqwest/gzip"]
brotli = ["reqwest/brotli"] brotli = ["reqwest/brotli"]
deflate = ["reqwest/deflate"] deflate = ["reqwest/deflate"]
zstd = ["reqwest/zstd"]
trust-dns = ["reqwest/trust-dns"] trust-dns = ["reqwest/trust-dns"]
socks = ["reqwest/socks"] socks = ["reqwest/socks"]
http2 = ["reqwest/http2"] http2 = ["reqwest/http2"]

@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_PLUGIN_HTTP__=function(e){"use strict";async function t(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}"function"==typeof SuppressedError&&SuppressedError;const r="Request canceled";return e.fetch=async function(e,n){const a=n?.signal;if(a?.aborted)throw new Error(r);const o=n?.maxRedirections,s=n?.connectTimeout,i=n?.proxy,d=n?.danger;n&&(delete n.maxRedirections,delete n.connectTimeout,delete n.proxy,delete n.danger);const c=n?.headers?n.headers instanceof Headers?n.headers:new Headers(n.headers):new Headers,u=new Request(e,n),f=await u.arrayBuffer(),_=0!==f.byteLength?Array.from(new Uint8Array(f)):null;for(const[e,t]of u.headers)c.get(e)||c.set(e,t);const h=(c instanceof Headers?Array.from(c.entries()):Array.isArray(c)?c:Object.entries(c)).map((([e,t])=>[e,"string"==typeof t?t:t.toString()]));if(a?.aborted)throw new Error(r);const l=await t("plugin:http|fetch",{clientConfig:{method:u.method,url:u.url,headers:h,data:_,maxRedirections:o,connectTimeout:s,proxy:i,danger:d}}),p=()=>t("plugin:http|fetch_cancel",{rid:l});if(a?.aborted)throw p(),new Error(r);a?.addEventListener("abort",(()=>{p()}));const{status:w,statusText:y,url:g,headers:T,rid:A}=await t("plugin:http|fetch_send",{rid:l}),R=await t("plugin:http|fetch_read_body",{rid:A}),b=new Response(R instanceof ArrayBuffer&&0!==R.byteLength?R:R instanceof Array&&R.length>0?new Uint8Array(R):null,{status:w,statusText:y});return Object.defineProperty(b,"url",{value:g}),Object.defineProperty(b,"headers",{value:new Headers(T)}),b},e}({});Object.defineProperty(window.__TAURI__,"http",{value:__TAURI_PLUGIN_HTTP__})} if("__TAURI__"in window){var __TAURI_PLUGIN_HTTP__=function(e){"use strict";function t(e,t,r,n){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?n:"a"===r?n.call(e):n?n.value:t.get(e)}function r(e,t,r,n,s){if("function"==typeof t||!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return t.set(e,r),r}var n,s,a;"function"==typeof SuppressedError&&SuppressedError;const i="__TAURI_TO_IPC_KEY__";class o{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,n.set(this,(()=>{})),s.set(this,0),a.set(this,[]),this.id=function(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}((({message:e,id:i})=>{if(i==t(this,s,"f"))for(t(this,n,"f").call(this,e),r(this,s,t(this,s,"f")+1);t(this,s,"f")in t(this,a,"f");){const e=t(this,a,"f")[t(this,s,"f")];t(this,n,"f").call(this,e),delete t(this,a,"f")[t(this,s,"f")],r(this,s,t(this,s,"f")+1)}else t(this,a,"f")[i]=e}))}set onmessage(e){r(this,n,e)}get onmessage(){return t(this,n,"f")}[(n=new WeakMap,s=new WeakMap,a=new WeakMap,i)](){return`__CHANNEL__:${this.id}`}toJSON(){return this[i]()}}async function c(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}const d="Request cancelled";return e.fetch=async function(e,t){const r=t?.signal;if(r?.aborted)throw new Error(d);const n=t?.maxRedirections,s=t?.connectTimeout,a=t?.proxy,i=t?.danger;t&&(delete t.maxRedirections,delete t.connectTimeout,delete t.proxy,delete t.danger);const h=t?.headers?t.headers instanceof Headers?t.headers:new Headers(t.headers):new Headers,f=new Request(e,t),_=await f.arrayBuffer(),u=0!==_.byteLength?Array.from(new Uint8Array(_)):null;for(const[e,t]of f.headers)h.get(e)||h.set(e,t);const l=(h instanceof Headers?Array.from(h.entries()):Array.isArray(h)?h:Object.entries(h)).map((([e,t])=>[e,"string"==typeof t?t:t.toString()]));if(r?.aborted)throw new Error(d);const w=await c("plugin:http|fetch",{clientConfig:{method:f.method,url:f.url,headers:l,data:u,maxRedirections:n,connectTimeout:s,proxy:a,danger:i}}),p=()=>c("plugin:http|fetch_cancel",{rid:w});if(r?.aborted)throw p(),new Error(d);r?.addEventListener("abort",(()=>{p()}));const{status:y,statusText:m,url:T,headers:g,rid:b}=await c("plugin:http|fetch_send",{rid:w}),A=new ReadableStream({start:e=>{const t=new o;t.onmessage=t=>{if(r?.aborted)return void e.error(d);const n=new Uint8Array(t),s=n[n.byteLength-1],a=n.slice(0,n.byteLength-1);1!=s?e.enqueue(a):e.close()},c("plugin:http|fetch_read_body",{rid:b,streamChannel:t}).catch((t=>{e.error(t)}))}}),R=new Response(A,{status:y,statusText:m});return Object.defineProperty(R,"url",{value:T}),Object.defineProperty(R,"headers",{value:new Headers(g)}),R},e}({});Object.defineProperty(window.__TAURI__,"http",{value:__TAURI_PLUGIN_HTTP__})}

@ -26,7 +26,7 @@
* @module * @module
*/ */
import { invoke } from '@tauri-apps/api/core' import { Channel, invoke } from '@tauri-apps/api/core'
/** /**
* Configuration of a proxy that a Client should pass requests to. * Configuration of a proxy that a Client should pass requests to.
@ -106,7 +106,7 @@ export interface DangerousSettings {
acceptInvalidHostnames?: boolean acceptInvalidHostnames?: boolean
} }
const ERROR_REQUEST_CANCELLED = 'Request canceled' const ERROR_REQUEST_CANCELLED = 'Request cancelled'
/** /**
* Fetch a resource from the network. It returns a `Promise` that resolves to the * Fetch a resource from the network. It returns a `Promise` that resolves to the
@ -229,31 +229,49 @@ export async function fetch(
rid rid
}) })
const body = await invoke<ArrayBuffer | number[]>( const readableStreamBody = new ReadableStream({
'plugin:http|fetch_read_body', start: (controller) => {
{ const streamChannel = new Channel<ArrayBuffer | number[]>()
rid: responseRid streamChannel.onmessage = (res: ArrayBuffer | number[]) => {
} // close early if aborted
) if (signal?.aborted) {
controller.error(ERROR_REQUEST_CANCELLED)
return
}
const resUint8 = new Uint8Array(res)
const lastByte = resUint8[resUint8.byteLength - 1]
const actualRes = resUint8.slice(0, resUint8.byteLength - 1)
// close when the signal to close (last byte is 1) is sent from the IPC.
if (lastByte == 1) {
controller.close()
return
}
controller.enqueue(actualRes)
}
const res = new Response( // run a non-blocking body stream fetch
body instanceof ArrayBuffer && body.byteLength !== 0 invoke('plugin:http|fetch_read_body', {
? body rid: responseRid,
: body instanceof Array && body.length > 0 streamChannel
? new Uint8Array(body) }).catch((e) => {
: null, controller.error(e)
{ })
status,
statusText
} }
) })
const res = new Response(readableStreamBody, {
status,
statusText
})
// url and headers are read only properties // Set `Response` properties that are ignored by the
// but seems like we can set them like this // constructor, like url and some headers
// //
// we define theme like this, because using `Response` // Since url and headers are read only properties
// constructor, it removes url and some headers // this is the only way to set them.
// like `set-cookie` headers
Object.defineProperty(res, 'url', { value: url }) Object.defineProperty(res, 'url', { value: url })
Object.defineProperty(res, 'headers', { Object.defineProperty(res, 'headers', {
value: new Headers(responseHeaders) value: new Headers(responseHeaders)

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-http", "name": "@tauri-apps/plugin-http",
"version": "2.3.0", "version": "2.4.2",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
"Tauri Programme within The Commons Conservancy" "Tauri Programme within The Commons Conservancy"

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
use tauri::{ use tauri::{
async_runtime::Mutex, async_runtime::Mutex,
command, command,
ipc::{CommandScope, GlobalScope}, ipc::{Channel, CommandScope, GlobalScope},
Manager, ResourceId, ResourceTable, Runtime, State, Webview, Manager, ResourceId, ResourceTable, Runtime, State, Webview,
}; };
use tokio::sync::oneshot::{channel, Receiver, Sender}; use tokio::sync::oneshot::{channel, Receiver, Sender};
@ -315,6 +315,7 @@ pub async fn fetch<R: Runtime>(
tracing::trace!("{:?}", request); tracing::trace!("{:?}", request);
let fut = async move { request.send().await.map_err(Into::into) }; let fut = async move { request.send().await.map_err(Into::into) };
let mut resources_table = webview.resources_table(); let mut resources_table = webview.resources_table();
let rid = resources_table.add_request(Box::pin(fut)); let rid = resources_table.add_request(Box::pin(fut));
@ -358,7 +359,7 @@ pub fn fetch_cancel<R: Runtime>(webview: Webview<R>, rid: ResourceId) -> crate::
Ok(()) Ok(())
} }
#[tauri::command] #[command]
pub async fn fetch_send<R: Runtime>( pub async fn fetch_send<R: Runtime>(
webview: Webview<R>, webview: Webview<R>,
rid: ResourceId, rid: ResourceId,
@ -410,17 +411,31 @@ pub async fn fetch_send<R: Runtime>(
}) })
} }
#[tauri::command] #[command]
pub(crate) async fn fetch_read_body<R: Runtime>( pub async fn fetch_read_body<R: Runtime>(
webview: Webview<R>, webview: Webview<R>,
rid: ResourceId, rid: ResourceId,
) -> crate::Result<tauri::ipc::Response> { stream_channel: Channel<tauri::ipc::InvokeResponseBody>,
) -> crate::Result<()> {
let res = { let res = {
let mut resources_table = webview.resources_table(); let mut resources_table = webview.resources_table();
resources_table.take::<ReqwestResponse>(rid)? resources_table.take::<ReqwestResponse>(rid)?
}; };
let res = Arc::into_inner(res).unwrap().0;
Ok(tauri::ipc::Response::new(res.bytes().await?.to_vec())) let mut res = Arc::into_inner(res).unwrap().0;
// send response through IPC channel
while let Some(chunk) = res.chunk().await? {
let mut chunk = chunk.to_vec();
// append 0 to indicate we are not done yet
chunk.push(0);
stream_channel.send(tauri::ipc::InvokeResponseBody::Raw(chunk))?;
}
// send 1 to indicate we are done
stream_channel.send(tauri::ipc::InvokeResponseBody::Raw(vec![1]))?;
Ok(())
} }
// forbidden headers per fetch spec https://fetch.spec.whatwg.org/#terminology-headers // forbidden headers per fetch spec https://fetch.spec.whatwg.org/#terminology-headers

@ -14,30 +14,77 @@ pub use error::{Error, Result};
mod commands; mod commands;
mod error; mod error;
#[cfg(feature = "cookies")]
mod reqwest_cookie_store;
mod scope; mod scope;
#[cfg(feature = "cookies")]
const COOKIES_FILENAME: &str = ".cookies";
pub(crate) struct Http { pub(crate) struct Http {
#[cfg(feature = "cookies")] #[cfg(feature = "cookies")]
cookies_jar: std::sync::Arc<reqwest::cookie::Jar>, cookies_jar: std::sync::Arc<crate::reqwest_cookie_store::CookieStoreMutex>,
} }
pub fn init<R: Runtime>() -> TauriPlugin<R> { pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::<R>::new("http") Builder::<R>::new("http")
.setup(|app, _| { .setup(|app, _| {
#[cfg(feature = "cookies")]
let cookies_jar = {
use crate::reqwest_cookie_store::*;
use std::fs::File;
use std::io::BufReader;
let cache_dir = app.path().app_cache_dir()?;
std::fs::create_dir_all(&cache_dir)?;
let path = cache_dir.join(COOKIES_FILENAME);
let file = File::options()
.create(true)
.append(true)
.read(true)
.open(&path)?;
let reader = BufReader::new(file);
CookieStoreMutex::load(path.clone(), reader).unwrap_or_else(|_e| {
#[cfg(feature = "tracing")]
tracing::warn!(
"failed to load cookie store: {_e}, falling back to empty store"
);
CookieStoreMutex::new(path, Default::default())
})
};
let state = Http { let state = Http {
#[cfg(feature = "cookies")] #[cfg(feature = "cookies")]
cookies_jar: std::sync::Arc::new(reqwest::cookie::Jar::default()), cookies_jar: std::sync::Arc::new(cookies_jar),
}; };
app.manage(state); app.manage(state);
Ok(()) Ok(())
}) })
.on_event(|app, event| {
#[cfg(feature = "cookies")]
if let tauri::RunEvent::Exit = event {
let state = app.state::<Http>();
match state.cookies_jar.request_save() {
Ok(rx) => {
let _ = rx.recv();
}
Err(_e) => {
#[cfg(feature = "tracing")]
tracing::error!("failed to save cookie jar: {_e}");
}
}
}
})
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![
commands::fetch, commands::fetch,
commands::fetch_cancel, commands::fetch_cancel,
commands::fetch_send, commands::fetch_send,
commands::fetch_read_body, commands::fetch_read_body
]) ])
.build() .build()
} }

@ -0,0 +1,133 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
// taken from https://github.com/pfernie/reqwest_cookie_store/blob/2ec4afabcd55e24d3afe3f0626ee6dc97bed938d/src/lib.rs
use std::{
path::PathBuf,
sync::{mpsc::Receiver, Mutex},
};
use cookie_store::{CookieStore, RawCookie, RawCookieParseError};
use reqwest::header::HeaderValue;
fn set_cookies(
cookie_store: &mut CookieStore,
cookie_headers: &mut dyn Iterator<Item = &HeaderValue>,
url: &url::Url,
) {
let cookies = cookie_headers.filter_map(|val| {
std::str::from_utf8(val.as_bytes())
.map_err(RawCookieParseError::from)
.and_then(RawCookie::parse)
.map(|c| c.into_owned())
.ok()
});
cookie_store.store_response_cookies(cookies, url);
}
fn cookies(cookie_store: &CookieStore, url: &url::Url) -> Option<HeaderValue> {
let s = cookie_store
.get_request_values(url)
.map(|(name, value)| format!("{}={}", name, value))
.collect::<Vec<_>>()
.join("; ");
if s.is_empty() {
return None;
}
HeaderValue::from_maybe_shared(bytes::Bytes::from(s)).ok()
}
/// A [`cookie_store::CookieStore`] wrapped internally by a [`std::sync::Mutex`], suitable for use in
/// async/concurrent contexts.
#[derive(Debug)]
pub struct CookieStoreMutex {
pub path: PathBuf,
store: Mutex<CookieStore>,
save_task: Mutex<Option<CancellableTask>>,
}
impl CookieStoreMutex {
/// Create a new [`CookieStoreMutex`] from an existing [`cookie_store::CookieStore`].
pub fn new(path: PathBuf, cookie_store: CookieStore) -> CookieStoreMutex {
CookieStoreMutex {
path,
store: Mutex::new(cookie_store),
save_task: Default::default(),
}
}
pub fn load<R: std::io::BufRead>(
path: PathBuf,
reader: R,
) -> cookie_store::Result<CookieStoreMutex> {
cookie_store::serde::load(reader, |c| serde_json::from_str(c))
.map(|store| CookieStoreMutex::new(path, store))
}
fn cookies_to_str(&self) -> Result<String, serde_json::Error> {
let mut cookies = Vec::new();
for cookie in self
.store
.lock()
.expect("poisoned cookie jar mutex")
.iter_unexpired()
{
if cookie.is_persistent() {
cookies.push(cookie.clone());
}
}
serde_json::to_string(&cookies)
}
pub fn request_save(&self) -> cookie_store::Result<Receiver<()>> {
let cookie_str = self.cookies_to_str()?;
let path = self.path.clone();
let (tx, rx) = std::sync::mpsc::channel();
let task = tauri::async_runtime::spawn(async move {
match tokio::fs::write(&path, &cookie_str).await {
Ok(()) => {
let _ = tx.send(());
}
Err(_e) => {
#[cfg(feature = "tracing")]
tracing::error!("failed to save cookie jar: {_e}");
}
}
});
self.save_task
.lock()
.unwrap()
.replace(CancellableTask(task));
Ok(rx)
}
}
impl reqwest::cookie::CookieStore for CookieStoreMutex {
fn set_cookies(&self, cookie_headers: &mut dyn Iterator<Item = &HeaderValue>, url: &url::Url) {
set_cookies(&mut self.store.lock().unwrap(), cookie_headers, url);
// try to persist cookies immediately asynchronously
if let Err(_e) = self.request_save() {
#[cfg(feature = "tracing")]
tracing::error!("failed to save cookie jar: {_e}");
}
}
fn cookies(&self, url: &url::Url) -> Option<HeaderValue> {
let store = self.store.lock().unwrap();
cookies(&store, url)
}
}
#[derive(Debug)]
struct CancellableTask(tauri::async_runtime::JoinHandle<()>);
impl Drop for CancellableTask {
fn drop(&mut self) {
self.0.abort();
}
}

@ -1,5 +1,19 @@
# Changelog # Changelog
## \[2.3.1]
- [`1bb1ced5`](https://github.com/tauri-apps/plugins-workspace/commit/1bb1ced53820127204aa7adf57510c1cbce55e12) ([#2524](https://github.com/tauri-apps/plugins-workspace/pull/2524) by [@elwerene](https://github.com/tauri-apps/plugins-workspace/../../elwerene)) enable TargetKind::LogDir on mobile
## \[2.3.0]
### feat
- [`02481501`](https://github.com/tauri-apps/plugins-workspace/commit/024815018fbc63a37afc716796a454925aa7d25e) ([#2377](https://github.com/tauri-apps/plugins-workspace/pull/2377) by [@3lpsy](https://github.com/tauri-apps/plugins-workspace/../../3lpsy)) Add a `is_skip_logger` flag to the Log Plugin `Builder` struct, a `skip_logger()` method to the Builder, and logic to avoid acquiring (creating) a logger and attaching it to the global logger. Since acquire_logger is pub, a `LoggerNotInitialized` is added and returned if it's called when the `is_skip_looger` flag is set. Overall, this feature permits a user to avoid calling `attach_logger` which can only be called once in a program's lifetime and allows the user to control the logger returned from `logger()`. Additionally, it also will allow users to generate multiple Tauri Mock apps in test suites that run and parallel and have the `log` plugin attached (assuming they use `skip_logger()`).
## \[2.2.3]
- [`1a984659`](https://github.com/tauri-apps/plugins-workspace/commit/1a9846599b6a71faf330845847a30f6bf9735898) ([#2469](https://github.com/tauri-apps/plugins-workspace/pull/2469) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Update `objc2` crate to 0.6. No user facing changes.
## \[2.2.2] ## \[2.2.2]
- [`6b4c3917`](https://github.com/tauri-apps/plugins-workspace/commit/6b4c3917389f4bc489d03b48a837557ac0584175) ([#2401](https://github.com/tauri-apps/plugins-workspace/pull/2401) by [@Seishiin](https://github.com/tauri-apps/plugins-workspace/../../Seishiin)) Fix timezone_strategy overwriting previously set LogLevels. - [`6b4c3917`](https://github.com/tauri-apps/plugins-workspace/commit/6b4c3917389f4bc489d03b48a837557ac0584175) ([#2401](https://github.com/tauri-apps/plugins-workspace/pull/2401) by [@Seishiin](https://github.com/tauri-apps/plugins-workspace/../../Seishiin)) Fix timezone_strategy overwriting previously set LogLevels.

@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-log" name = "tauri-plugin-log"
version = "2.2.2" version = "2.3.1"
description = "Configurable logging for your Tauri app." description = "Configurable logging for your Tauri app."
authors = { workspace = true } authors = { workspace = true }
license = { workspace = true } license = { workspace = true }
@ -35,12 +35,12 @@ time = { version = "0.3", features = ["formatting", "local-offset"] }
fern = "0.7" fern = "0.7"
[target."cfg(target_os = \"android\")".dependencies] [target."cfg(target_os = \"android\")".dependencies]
android_logger = "0.14" android_logger = "0.15"
[target."cfg(target_os = \"ios\")".dependencies] [target."cfg(target_os = \"ios\")".dependencies]
swift-rs = "1" swift-rs = "1"
objc2 = "0.5" objc2 = "0.6"
objc2-foundation = { version = "0.2", default-features = false, features = [ objc2-foundation = { version = "0.3", default-features = false, features = [
"std", "std",
"NSString", "NSString",
] } ] }

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-log", "name": "@tauri-apps/plugin-log",
"version": "2.2.2", "version": "2.3.1",
"description": "Configurable logging for your Tauri app.", "description": "Configurable logging for your Tauri app.",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -159,11 +159,12 @@ pub enum TargetKind {
/// ///
/// ### Platform-specific /// ### Platform-specific
/// ///
/// |Platform | Value | Example | /// |Platform | Value | Example |
/// | ------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------- | /// | --------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
/// | Linux | `$XDG_DATA_HOME/{bundleIdentifier}/logs` or `$HOME/.local/share/{bundleIdentifier}/logs` | `/home/alice/.local/share/com.tauri.dev/logs` | /// | Linux | `$XDG_DATA_HOME/{bundleIdentifier}/logs` or `$HOME/.local/share/{bundleIdentifier}/logs` | `/home/alice/.local/share/com.tauri.dev/logs` |
/// | macOS | `{homeDir}/Library/Logs/{bundleIdentifier}` | `/Users/Alice/Library/Logs/com.tauri.dev` | /// | macOS/iOS | `{homeDir}/Library/Logs/{bundleIdentifier}` | `/Users/Alice/Library/Logs/com.tauri.dev` |
/// | Windows | `{FOLDERID_LocalAppData}/{bundleIdentifier}/logs` | `C:\Users\Alice\AppData\Local\com.tauri.dev\logs` | /// | Windows | `{FOLDERID_LocalAppData}/{bundleIdentifier}/logs` | `C:\Users\Alice\AppData\Local\com.tauri.dev\logs` |
/// | Android | `{ConfigDir}/logs` | `/data/data/com.tauri.dev/files/logs` |
LogDir { file_name: Option<String> }, LogDir { file_name: Option<String> },
/// Forward logs to the webview (via the `log://log` event). /// Forward logs to the webview (via the `log://log` event).
/// ///
@ -451,9 +452,6 @@ impl Builder {
)?)? )?)?
.into() .into()
} }
#[cfg(mobile)]
TargetKind::LogDir { .. } => continue,
#[cfg(desktop)]
TargetKind::LogDir { file_name } => { TargetKind::LogDir { file_name } => {
let path = app_handle.path().app_log_dir()?; let path = app_handle.path().app_log_dir()?;
if !path.exists() { if !path.exists() {

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -1,5 +1,9 @@
# Changelog # Changelog
## \[2.2.2]
- [`a1b3fa27`](https://github.com/tauri-apps/plugins-workspace/commit/a1b3fa27f11022c9b6622b4fab12d93239eb05de) ([#2515](https://github.com/tauri-apps/plugins-workspace/pull/2515) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Re-exported the `Geolocation`, `Haptics`, `Notification`, and `Os` structs so that they show up on docs.rs.
## \[2.2.1] ## \[2.2.1]
- [`da5c59e2`](https://github.com/tauri-apps/plugins-workspace/commit/da5c59e2fe879d177e3cfd52fcacce85440423cb) ([#2271](https://github.com/tauri-apps/plugins-workspace/pull/2271) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated `zbus` dependency to version 5. No API changes. - [`da5c59e2`](https://github.com/tauri-apps/plugins-workspace/commit/da5c59e2fe879d177e3cfd52fcacce85440423cb) ([#2271](https://github.com/tauri-apps/plugins-workspace/pull/2271) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated `zbus` dependency to version 5. No API changes.

@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-notification" name = "tauri-plugin-notification"
version = "2.2.1" version = "2.2.2"
description = "Send desktop and mobile notifications on your Tauri application." description = "Send desktop and mobile notifications on your Tauri application."
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-notification", "name": "@tauri-apps/plugin-notification",
"version": "2.2.1", "version": "2.2.2",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
"Tauri Programme within The Commons Conservancy" "Tauri Programme within The Commons Conservancy"

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -18,6 +18,8 @@ pub fn init<R: Runtime, C: DeserializeOwned>(
} }
/// Access to the notification APIs. /// Access to the notification APIs.
///
/// You can get an instance of this type via [`NotificationExt`](crate::NotificationExt)
pub struct Notification<R: Runtime>(AppHandle<R>); pub struct Notification<R: Runtime>(AppHandle<R>);
impl<R: Runtime> crate::NotificationBuilder<R> { impl<R: Runtime> crate::NotificationBuilder<R> {

@ -34,9 +34,9 @@ mod models;
pub use error::{Error, Result}; pub use error::{Error, Result};
#[cfg(desktop)] #[cfg(desktop)]
use desktop::Notification; pub use desktop::Notification;
#[cfg(mobile)] #[cfg(mobile)]
use mobile::Notification; pub use mobile::Notification;
/// The notification builder. /// The notification builder.
#[derive(Debug)] #[derive(Debug)]
@ -120,7 +120,7 @@ impl<R: Runtime> NotificationBuilder<R> {
/// Identifier used to group multiple notifications. /// Identifier used to group multiple notifications.
/// ///
/// https://developer.apple.com/documentation/usernotifications/unmutablenotificationcontent/1649872-threadidentifier /// <https://developer.apple.com/documentation/usernotifications/unmutablenotificationcontent/1649872-threadidentifier>
pub fn group(mut self, group: impl Into<String>) -> Self { pub fn group(mut self, group: impl Into<String>) -> Self {
self.data.group.replace(group.into()); self.data.group.replace(group.into());
self self

@ -40,6 +40,8 @@ impl<R: Runtime> crate::NotificationBuilder<R> {
} }
/// Access to the notification APIs. /// Access to the notification APIs.
///
/// You can get an instance of this type via [`NotificationExt`](crate::NotificationExt)
pub struct Notification<R: Runtime>(PluginHandle<R>); pub struct Notification<R: Runtime>(PluginHandle<R>);
impl<R: Runtime> Notification<R> { impl<R: Runtime> Notification<R> {

@ -1,5 +1,10 @@
# Changelog # Changelog
## \[2.2.6]
- [`1a984659`](https://github.com/tauri-apps/plugins-workspace/commit/1a9846599b6a71faf330845847a30f6bf9735898) ([#2469](https://github.com/tauri-apps/plugins-workspace/pull/2469) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Update `objc2` crate to 0.6. No user facing changes.
- [`71f95c9f`](https://github.com/tauri-apps/plugins-workspace/commit/71f95c9f05b29cf1be586849614c0b007757c15d) ([#2445](https://github.com/tauri-apps/plugins-workspace/pull/2445) by [@renovate](https://github.com/tauri-apps/plugins-workspace/../../renovate)) Updated `windows` crate to 0.60 to match Tauri 2.3.0. No user facing changes.
## \[2.2.5] ## \[2.2.5]
- [`5b821181`](https://github.com/tauri-apps/plugins-workspace/commit/5b8211815825ddae2dcc0c00520e0cfdff002763) ([#2332](https://github.com/tauri-apps/plugins-workspace/pull/2332) by [@betamos](https://github.com/tauri-apps/plugins-workspace/../../betamos)) Fix broken JS commands `opener.openPath` and `opener.openUrl` on mobile. - [`5b821181`](https://github.com/tauri-apps/plugins-workspace/commit/5b8211815825ddae2dcc0c00520e0cfdff002763) ([#2332](https://github.com/tauri-apps/plugins-workspace/pull/2332) by [@betamos](https://github.com/tauri-apps/plugins-workspace/../../betamos)) Fix broken JS commands `opener.openPath` and `opener.openUrl` on mobile.

@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-opener" name = "tauri-plugin-opener"
version = "2.2.5" version = "2.2.6"
description = "Open files and URLs using their default application." description = "Open files and URLs using their default application."
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
@ -40,7 +40,7 @@ glob = { workspace = true }
dunce = { workspace = true } dunce = { workspace = true }
[target."cfg(windows)".dependencies.windows] [target."cfg(windows)".dependencies.windows]
version = "0.58" version = "0.60"
features = [ features = [
"Win32_Foundation", "Win32_Foundation",
"Win32_UI_Shell_Common", "Win32_UI_Shell_Common",
@ -54,12 +54,12 @@ zbus = { workspace = true }
url = { workspace = true } url = { workspace = true }
[target."cfg(target_os = \"macos\")".dependencies.objc2-app-kit] [target."cfg(target_os = \"macos\")".dependencies.objc2-app-kit]
version = "0.2" version = "0.3"
default-features = false default-features = false
features = ["std", "NSWorkspace"] features = ["std", "NSWorkspace"]
[target."cfg(target_os = \"macos\")".dependencies.objc2-foundation] [target."cfg(target_os = \"macos\")".dependencies.objc2-foundation]
version = "0.2" version = "0.3"
default-features = false default-features = false
features = ["std", "NSURL", "NSArray", "NSString"] features = ["std", "NSURL", "NSArray", "NSString"]

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-opener", "name": "@tauri-apps/plugin-opener",
"version": "2.2.5", "version": "2.2.6",
"description": "Open files and URLs using their default application.", "description": "Open files and URLs using their default application.",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -56,7 +56,7 @@ impl<R: Runtime> Opener<R> {
/// - **Android / iOS**: Always opens using default program. /// - **Android / iOS**: Always opens using default program.
#[cfg(desktop)] #[cfg(desktop)]
pub fn open_url(&self, url: impl Into<String>, with: Option<impl Into<String>>) -> Result<()> { pub fn open_url(&self, url: impl Into<String>, with: Option<impl Into<String>>) -> Result<()> {
crate::open::open(url.into(), with.map(Into::into)).map_err(Into::into) crate::open::open(url.into(), with.map(Into::into))
} }
/// Open a url with a default or specific program. /// Open a url with a default or specific program.
@ -108,7 +108,7 @@ impl<R: Runtime> Opener<R> {
path: impl Into<String>, path: impl Into<String>,
with: Option<impl Into<String>>, with: Option<impl Into<String>>,
) -> Result<()> { ) -> Result<()> {
crate::open::open(path.into(), with.map(Into::into)).map_err(Into::into) crate::open::open(path.into(), with.map(Into::into))
} }
/// Open a path with a default or specific program. /// Open a path with a default or specific program.

@ -185,7 +185,7 @@ mod imp {
let path = path.to_string_lossy(); let path = path.to_string_lossy();
let path = NSString::from_str(&path); let path = NSString::from_str(&path);
let urls = vec![NSURL::fileURLWithPath(&path)]; let urls = vec![NSURL::fileURLWithPath(&path)];
let urls = NSArray::from_vec(urls); let urls = NSArray::from_retained_slice(&urls);
let workspace = NSWorkspace::new(); let workspace = NSWorkspace::new();
workspace.activateFileViewerSelectingURLs(&urls); workspace.activateFileViewerSelectingURLs(&urls);

@ -1,5 +1,9 @@
# Changelog # Changelog
## \[2.2.1]
- [`a1b3fa27`](https://github.com/tauri-apps/plugins-workspace/commit/a1b3fa27f11022c9b6622b4fab12d93239eb05de) ([#2515](https://github.com/tauri-apps/plugins-workspace/pull/2515) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Re-exported the `Geolocation`, `Haptics`, `Notification`, and `Os` structs so that they show up on docs.rs.
## \[2.2.0] ## \[2.2.0]
- [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other. - [`3a79266b`](https://github.com/tauri-apps/plugins-workspace/commit/3a79266b8cf96a55b1ae6339d725567d45a44b1d) ([#2173](https://github.com/tauri-apps/plugins-workspace/pull/2173) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Bumped all plugins to `v2.2.0`. From now, the versions for the Rust and JavaScript packages of each plugin will be in sync with each other.

@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-os" name = "tauri-plugin-os"
version = "2.2.0" version = "2.2.1"
description = "Read information about the operating system." description = "Read information about the operating system."
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-os", "name": "@tauri-apps/plugin-os",
"version": "2.2.0", "version": "2.2.1",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
"Tauri Programme within The Commons Conservancy" "Tauri Programme within The Commons Conservancy"

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -88,7 +88,7 @@ pub fn exe_extension() -> &'static str {
std::env::consts::EXE_EXTENSION std::env::consts::EXE_EXTENSION
} }
/// Returns the current operating system locale with the `BCP-47` language tag. If the locale couldnt be obtained, `None` is returned instead. /// Returns the current operating system locale with the `BCP-47` language tag. If the locale couldn't be obtained, `None` is returned instead.
pub fn locale() -> Option<String> { pub fn locale() -> Option<String> {
sys_locale::get_locale() sys_locale::get_locale()
} }

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -75,7 +75,7 @@ impl<R: Runtime> Shell<R> {
#[deprecated(since = "2.1.0", note = "Use tauri-plugin-opener instead.")] #[deprecated(since = "2.1.0", note = "Use tauri-plugin-opener instead.")]
#[allow(deprecated)] #[allow(deprecated)]
pub fn open(&self, path: impl Into<String>, with: Option<open::Program>) -> Result<()> { pub fn open(&self, path: impl Into<String>, with: Option<open::Program>) -> Result<()> {
open::open(&self.open_scope, path.into(), with).map_err(Into::into) open::open(&self.open_scope, path.into(), with)
} }
/// Open a (url) path with a default or specific browser opening program. /// Open a (url) path with a default or specific browser opening program.

@ -9,6 +9,6 @@
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "2.2.7" "@tauri-apps/cli": "2.4.0"
} }
} }

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -8,8 +8,8 @@
"tauri": "tauri" "tauri": "tauri"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "2.2.7", "@tauri-apps/cli": "2.4.0",
"vite": "^6.0.0", "typescript": "^5.7.3",
"typescript": "^5.4.7" "vite": "^6.2.0"
} }
} }

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -1,5 +1,14 @@
# Changelog # Changelog
## \[2.6.1]
- [`12c4537b`](https://github.com/tauri-apps/plugins-workspace/commit/12c4537b8e4fed29b415ff817434b664c0596dac) ([#2541](https://github.com/tauri-apps/plugins-workspace/pull/2541) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Add support to the `riscv64` architecture.
## \[2.6.0]
- [`faefcc9f`](https://github.com/tauri-apps/plugins-workspace/commit/faefcc9fd8c61f709d491649e255a7fcac82c09a) ([#2430](https://github.com/tauri-apps/plugins-workspace/pull/2430) by [@goenning](https://github.com/tauri-apps/plugins-workspace/../../goenning)) Add `UpdaterBuilder::configure_client` method on Rust side, to configure the `reqwest` client used to check and download the update.
- [`ac60d589`](https://github.com/tauri-apps/plugins-workspace/commit/ac60d589eca2bbc4aed040feb18da148e66ec171) ([#2513](https://github.com/tauri-apps/plugins-workspace/pull/2513) by [@lucasfernog](https://github.com/tauri-apps/plugins-workspace/../../lucasfernog)) Enhance error logging.
## \[2.5.1] ## \[2.5.1]
- [`6f881293`](https://github.com/tauri-apps/plugins-workspace/commit/6f881293fcd67838f6f3f8063f536292431dd1f7) ([#2439](https://github.com/tauri-apps/plugins-workspace/pull/2439) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Fixed an issue that caused the plugin to emit a `ReleaseNotFound` error instead of a `Reqwest` error when the http request in `check()` failed. - [`6f881293`](https://github.com/tauri-apps/plugins-workspace/commit/6f881293fcd67838f6f3f8063f536292431dd1f7) ([#2439](https://github.com/tauri-apps/plugins-workspace/pull/2439) by [@FabianLars](https://github.com/tauri-apps/plugins-workspace/../../FabianLars)) Fixed an issue that caused the plugin to emit a `ReleaseNotFound` error instead of a `Reqwest` error when the http request in `check()` failed.

@ -1,6 +1,6 @@
[package] [package]
name = "tauri-plugin-updater" name = "tauri-plugin-updater"
version = "2.5.1" version = "2.6.1"
description = "In-app updates for Tauri applications." description = "In-app updates for Tauri applications."
edition = { workspace = true } edition = { workspace = true }
authors = { workspace = true } authors = { workspace = true }
@ -30,6 +30,7 @@ tauri = { workspace = true }
serde = { workspace = true } serde = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
log = { workspace = true }
tokio = "1" tokio = "1"
reqwest = { version = "0.12", default-features = false, features = [ reqwest = { version = "0.12", default-features = false, features = [
"json", "json",

@ -76,7 +76,7 @@ Afterwards all the plugin's APIs are available through the JavaScript guest bind
import { check } from '@tauri-apps/plugin-updater' import { check } from '@tauri-apps/plugin-updater'
import { relaunch } from '@tauri-apps/plugin-process' import { relaunch } from '@tauri-apps/plugin-process'
const update = await check() const update = await check()
if (update?.available) { if (update) {
await update.downloadAndInstall() await update.downloadAndInstall()
await relaunch() await relaunch()
} }

@ -133,7 +133,10 @@ async function check(options?: CheckOptions): Promise<Update | null> {
return await invoke<UpdateMetadata>('plugin:updater|check', { return await invoke<UpdateMetadata>('plugin:updater|check', {
...options ...options
}).then((meta) => (meta.available ? new Update(meta) : null)) }).then((meta) =>
// TODO: Handle this in the rust side
meta.available ? new Update(meta) : null
)
} }
export type { CheckOptions, DownloadOptions, DownloadEvent } export type { CheckOptions, DownloadOptions, DownloadEvent }

@ -1,6 +1,6 @@
{ {
"name": "@tauri-apps/plugin-updater", "name": "@tauri-apps/plugin-updater",
"version": "2.5.1", "version": "2.6.1",
"license": "MIT OR Apache-2.0", "license": "MIT OR Apache-2.0",
"authors": [ "authors": [
"Tauri Programme within The Commons Conservancy" "Tauri Programme within The Commons Conservancy"

@ -49,7 +49,7 @@
"minimum": 1.0 "minimum": 1.0
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"
@ -111,7 +111,7 @@
"type": "string" "type": "string"
}, },
"description": { "description": {
"description": "Human-readable description of what the permission does. Tauri internal convention is to use <h4> headings in markdown content for Tauri documentation generation purposes.", "description": "Human-readable description of what the permission does. Tauri internal convention is to use `<h4>` headings in markdown content for Tauri documentation generation purposes.",
"type": [ "type": [
"string", "string",
"null" "null"

@ -40,6 +40,8 @@ pub(crate) struct Metadata {
struct DownloadedBytes(pub Vec<u8>); struct DownloadedBytes(pub Vec<u8>);
impl Resource for DownloadedBytes {} impl Resource for DownloadedBytes {}
// TODO: Align this with the result of `updater.check` to Result<Option<Metadata>>
// and remove `available` instead of handling this in the js side
#[tauri::command] #[tauri::command]
pub(crate) async fn check<R: Runtime>( pub(crate) async fn check<R: Runtime>(
webview: Webview<R>, webview: Webview<R>,

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save