chore: Merge branch v1 into v2 (#702)

pull/722/head
Fabian-Lars 2 years ago committed by GitHub
parent d4d1633c4d
commit 251852ccbc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,5 @@
---
"sql": patch
---
Fixed an issue where float-like values were decoded as `null`.

@ -0,0 +1,5 @@
---
"stronghold": patch
---
Added `Builder::with_argon2`.

@ -0,0 +1,5 @@
---
"positioner": patch
---
`TrayLeft`, `TrayRight` and `TrayCenter` will now position the window according to the tray position relative to the monitor dimensions to prevent windows being displayed partially off-screen.

@ -0,0 +1,5 @@
---
"upload": patch
---
Use `BufWriter` to reduce IO calls.

@ -0,0 +1,6 @@
---
"websocket": patch
"websocket-js": patch
---
Add support for custom request headers.

1544
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -13,18 +13,18 @@
"@rollup/plugin-node-resolve": "15.2.3",
"@rollup/plugin-terser": "0.4.4",
"@rollup/plugin-typescript": "11.1.5",
"@typescript-eslint/eslint-plugin": "6.8.0",
"@typescript-eslint/parser": "6.8.0",
"@typescript-eslint/eslint-plugin": "6.9.1",
"@typescript-eslint/parser": "6.9.1",
"covector": "^0.10.2",
"eslint": "8.51.0",
"eslint": "8.53.0",
"eslint-config-prettier": "9.0.0",
"eslint-config-standard-with-typescript": "39.1.1",
"eslint-plugin-import": "2.28.1",
"eslint-plugin-import": "2.29.0",
"eslint-plugin-n": "16.2.0",
"eslint-plugin-promise": "6.1.1",
"eslint-plugin-security": "1.7.1",
"prettier": "3.0.3",
"rollup": "4.1.4",
"rollup": "4.3.0",
"typescript": "5.2.2"
},
"resolutions": {
@ -33,5 +33,12 @@
},
"engines": {
"pnpm": ">=7.33.1"
},
"pnpm": {
"auditConfig": {
"ignoreCves": [
"CVE-2023-46115"
]
}
}
}

@ -10,11 +10,11 @@ _This plugin requires a Rust version of at least **1.70**_
There are three general methods of installation that we can recommend.
1. Use crates.io and npm (easiest, and requires you to trust that our publishing pipeline worked)
1. Use crates.io and npm (easiest and requires you to trust that our publishing pipeline worked)
2. Pull sources directly from Github using git tags / revision hashes (most secure)
3. Git submodule install this repo in your tauri project and then use file protocol to ingest the source (most secure, but inconvenient to use)
3. Git submodule install this repo in your tauri project and then use the file protocol to ingest the source (most secure, but inconvenient to use)
Install the Core plugin by adding the following to your `Cargo.toml` file:
Install the authenticator plugin by adding the following lines to your `Cargo.toml` file:
`src-tauri/Cargo.toml`
@ -50,7 +50,7 @@ yarn add https://github.com/tauri-apps/tauri-plugin-authenticator#v2
## Usage
First you need to register the core plugin with Tauri:
First, you need to register the authenticator plugin with Tauri:
`src-tauri/src/main.rs`
@ -67,7 +67,7 @@ fn main() {
}
```
Afterwards all the plugin's APIs are available through the JavaScript guest bindings:
Afterwards, all the plugin's APIs are available through the JavaScript guest bindings:
```javascript
import { Authenticator } from "@tauri-apps/plugin-authenticator";
@ -88,7 +88,7 @@ const domain = "https://tauri.app";
const json = await auth.register(challenge, domain);
const registerResult = JSON.parse(json);
// verify te registration was successfull
// verify the registration was successful
const r2 = await auth.verifyRegistration(
challenge,
app,

@ -25,7 +25,7 @@
"LICENSE"
],
"devDependencies": {
"tslib": "2.6.0"
"tslib": "2.6.2"
},
"dependencies": {
"@tauri-apps/api": "2.0.0-alpha.11"

@ -17,4 +17,4 @@ serde_json = { workspace = true }
tauri = { workspace = true }
log = { workspace = true }
thiserror = { workspace = true }
auto-launch = "0.4"
auto-launch = "0.5"

@ -24,7 +24,7 @@
"LICENSE"
],
"devDependencies": {
"tslib": "2.6.0"
"tslib": "2.6.2"
},
"dependencies": {
"@tauri-apps/api": "2.0.0-alpha.11"

@ -24,7 +24,7 @@
"LICENSE"
],
"devDependencies": {
"tslib": "^2.4.1"
"tslib": "2.6.2"
},
"dependencies": {
"@tauri-apps/api": "2.0.0-alpha.11"

File diff suppressed because it is too large Load Diff

@ -75,7 +75,7 @@ pub async fn watch(
let watcher = if let Some(delay) = options.delay_ms {
let (tx, rx) = channel();
let mut debouncer = new_debouncer(Duration::from_millis(delay), None, tx)?;
let mut debouncer = new_debouncer(Duration::from_millis(delay), tx)?;
let watcher = debouncer.watcher();
for path in &paths {
watcher.watch(path, mode)?;

@ -24,7 +24,7 @@
"LICENSE"
],
"devDependencies": {
"tslib": "^2.5.0"
"tslib": "2.6.2"
},
"dependencies": {
"@tauri-apps/api": "2.0.0-alpha.11"

@ -25,7 +25,7 @@
"LICENSE"
],
"devDependencies": {
"tslib": "2.6.0"
"tslib": "2.6.2"
},
"dependencies": {
"@tauri-apps/api": "2.0.0-alpha.11"

@ -17,7 +17,7 @@ serde_json = { workspace = true }
tauri = { workspace = true }
log = { workspace = true }
thiserror = { workspace = true }
aho-corasick = "1.0"
aho-corasick = "1"
bincode = "1"
tauri-plugin-fs = { path = "../fs", version = "2.0.0-alpha.4" }

@ -13,13 +13,13 @@
use aho_corasick::AhoCorasick;
use serde::{Deserialize, Serialize};
#[cfg(feature = "protocol-asset")]
use tauri::scope::fs::{Event as FsScopeEvent, Scope as FsScope};
use tauri::{
plugin::{Builder, TauriPlugin},
scope::fs::Pattern as GlobPattern,
Manager, Runtime,
};
#[cfg(feature = "protocol-asset")]
use tauri::{FsScope, FsScopeEvent};
use tauri_plugin_fs::{FsExt, Scope as FsPluginScope, ScopeEvent as FsPluginScopeEvent};
use std::{
@ -58,7 +58,7 @@ trait ScopeExt {
fn forbidden_patterns(&self) -> HashSet<GlobPattern>;
}
impl ScopeExt for &FsPluginScope {
impl ScopeExt for FsPluginScope {
fn allow_file(&self, path: &Path) {
let _ = FsPluginScope::allow_file(self, path);
}
@ -85,7 +85,7 @@ impl ScopeExt for &FsPluginScope {
}
#[cfg(feature = "protocol-asset")]
impl ScopeExt for &FsScope {
impl ScopeExt for FsScope {
fn allow_file(&self, path: &Path) {
let _ = FsScope::allow_file(self, path);
}
@ -171,7 +171,7 @@ fn fix_directory(path_str: &str) -> &Path {
path
}
fn allow_path(scope: impl ScopeExt, path: &str) {
fn allow_path(scope: &impl ScopeExt, path: &str) {
let target_type = detect_scope_type(path);
match target_type {
@ -189,7 +189,7 @@ fn allow_path(scope: impl ScopeExt, path: &str) {
}
}
fn forbid_path(scope: impl ScopeExt, path: &str) {
fn forbid_path(scope: &impl ScopeExt, path: &str) {
let target_type = detect_scope_type(path);
match target_type {
@ -205,7 +205,7 @@ fn forbid_path(scope: impl ScopeExt, path: &str) {
}
}
fn save_scopes(scope: impl ScopeExt, app_dir: &Path, scope_state_path: &Path) {
fn save_scopes(scope: &impl ScopeExt, app_dir: &Path, scope_state_path: &Path) {
let scope = Scope {
allowed_paths: scope
.allowed_patterns()
@ -243,7 +243,8 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
let asset_scope_state_path = app_dir.join(ASSET_SCOPE_STATE_FILENAME);
if let Some(fs_scope) = fs_scope {
let _ = fs_scope.forbid_file(&fs_scope_state_path);}
let _ = fs_scope.forbid_file(&fs_scope_state_path);
}
#[cfg(feature = "protocol-asset")]
let _ = asset_protocol_scope.forbid_file(&asset_scope_state_path);
@ -271,7 +272,7 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
// This is needed to fix broken .peristed-scope files in case the app doesn't update the scope itself.
save_scopes(fs_scope, &app_dir, &fs_scope_state_path);
}
}
}
#[cfg(feature = "protocol-asset")]
if asset_scope_state_path.exists() {
@ -303,15 +304,15 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
}
});
}
#[cfg(feature = "protocol-asset")]
{
let asset_protocol_scope_ = asset_protocol_scope.clone();
asset_protocol_scope.listen(move |event| {
if let FsScopeEvent::PathAllowed(_) = event {
save_scopes(&asset_protocol_scope_, &app_dir_, &asset_scope_state_path);
}
});}
if let FsScopeEvent::PathAllowed(_) = event {
save_scopes(&asset_protocol_scope_, &app_dir_, &asset_scope_state_path);
}
});
}
}
Ok(())
})

@ -25,7 +25,7 @@
"LICENSE"
],
"devDependencies": {
"tslib": "2.6.0"
"tslib": "2.6.2"
},
"dependencies": {
"@tauri-apps/api": "2.0.0-alpha.11"

@ -109,13 +109,20 @@ impl<R: Runtime> WindowExt for Window<R> {
},
#[cfg(feature = "tray-icon")]
TrayLeft => {
if let Some((tray_x, tray_y)) = tray_position {
PhysicalPosition {
x: tray_x,
y: tray_y - window_size.height,
}
if let (Some((tray_x, tray_y)), Some((_, _tray_height))) =
(tray_position, tray_size)
{
let y = tray_y - window_size.height;
// Choose y value based on the target OS
#[cfg(target_os = "windows")]
let y = if y < 0 { tray_y + _tray_height } else { y };
#[cfg(target_os = "macos")]
let y = if y < 0 { tray_y } else { y };
PhysicalPosition { x: tray_x, y }
} else {
panic!("tray position not set");
panic!("Tray position not set");
}
}
#[cfg(feature = "tray-icon")]
@ -131,11 +138,20 @@ impl<R: Runtime> WindowExt for Window<R> {
}
#[cfg(feature = "tray-icon")]
TrayRight => {
if let (Some((tray_x, tray_y)), Some((tray_width, _))) = (tray_position, tray_size)
if let (Some((tray_x, tray_y)), Some((tray_width, _tray_height))) =
(tray_position, tray_size)
{
let y = tray_y - window_size.height;
// Choose y value based on the target OS
#[cfg(target_os = "windows")]
let y = if y < 0 { tray_y + _tray_height } else { y };
#[cfg(target_os = "macos")]
let y = if y < 0 { tray_y } else { y };
PhysicalPosition {
x: tray_x + tray_width,
y: tray_y - window_size.height,
y,
}
} else {
panic!("Tray position not set");
@ -155,12 +171,19 @@ impl<R: Runtime> WindowExt for Window<R> {
}
#[cfg(feature = "tray-icon")]
TrayCenter => {
if let (Some((tray_x, tray_y)), Some((tray_width, _))) = (tray_position, tray_size)
if let (Some((tray_x, tray_y)), Some((tray_width, _tray_height))) =
(tray_position, tray_size)
{
PhysicalPosition {
x: tray_x + (tray_width / 2) - (window_size.width / 2),
y: tray_y - window_size.height,
}
let x = tray_x + tray_width / 2 - window_size.width / 2;
let y = tray_y - window_size.height;
// Choose y value based on the target OS
#[cfg(target_os = "windows")]
let y = if y < 0 { tray_y + _tray_height } else { y };
#[cfg(target_os = "macos")]
let y = if y < 0 { tray_y } else { y };
PhysicalPosition { x, y }
} else {
panic!("Tray position not set");
}

@ -19,11 +19,11 @@ tauri = { workspace = true }
log = { workspace = true }
thiserror = { workspace = true }
futures-core = "0.3"
sqlx = { version = "0.7", features = [ "runtime-tokio-rustls", "json", "time" ] }
sqlx = { version = "0.7", features = ["json", "time"] }
time = "0.3"
tokio = { version = "1", features = [ "sync" ] }
[features]
sqlite = [ "sqlx/sqlite" ]
mysql = [ "sqlx/mysql" ]
postgres = [ "sqlx/postgres" ]
sqlite = ["sqlx/sqlite", "sqlx/runtime-tokio"]
mysql = ["sqlx/mysql", "sqlx/runtime-tokio-rustls"]
postgres = ["sqlx/postgres", "sqlx/runtime-tokio-rustls"]

@ -114,7 +114,6 @@ export default class Database {
values: bindValues ?? [],
},
);
return {
lastInsertId,
rowsAffected,

@ -25,7 +25,7 @@
"LICENSE"
],
"devDependencies": {
"tslib": "2.6.0"
"tslib": "2.6.2"
},
"dependencies": {
"@tauri-apps/api": "2.0.0-alpha.11"

@ -21,7 +21,14 @@ pub(crate) fn to_json(v: MySqlValueRef) -> Result<JsonValue, Error> {
JsonValue::Null
}
}
"FLOAT" | "DOUBLE" => {
"FLOAT" => {
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<f32>() {
JsonValue::from(v)
} else {
JsonValue::Null
}
}
"DOUBLE" => {
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<f64>() {
JsonValue::from(v)
} else {

@ -21,14 +21,35 @@ pub(crate) fn to_json(v: PgValueRef) -> Result<JsonValue, Error> {
JsonValue::Null
}
}
"FLOAT4" | "FLOAT8" => {
"FLOAT4" => {
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<f32>() {
JsonValue::from(v)
} else {
JsonValue::Null
}
}
"FLOAT8" => {
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<f64>() {
JsonValue::from(v)
} else {
JsonValue::Null
}
}
"INT2" | "INT4" | "INT8" => {
"INT2" => {
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<i16>() {
JsonValue::Number(v.into())
} else {
JsonValue::Null
}
}
"INT4" => {
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<i32>() {
JsonValue::Number(v.into())
} else {
JsonValue::Null
}
}
"INT8" => {
if let Ok(v) = ValueRef::to_owned(&v).try_decode::<i64>() {
JsonValue::Number(v.into())
} else {

@ -25,7 +25,7 @@
"LICENSE"
],
"devDependencies": {
"tslib": "2.6.0"
"tslib": "2.6.2"
},
"dependencies": {
"@tauri-apps/api": "2.0.0-alpha.11"

@ -22,6 +22,15 @@ iota-crypto = "0.23"
hex = "0.4"
zeroize = { version = "1", features = [ "zeroize_derive" ] }
# kdf dependencies
rust-argon2 = { version = "1", optional = true }
rand_chacha = { version = "0.3.1", optional = true }
rand_core = { version = "0.6.4", features = ["getrandom"], optional = true }
[dev-dependencies]
rand = "0.8"
rusty-fork = "0.3"
[features]
default = ["kdf"]
kdf = ["dep:rust-argon2", "dep:rand_chacha", "dep:rand_core"]

@ -25,7 +25,7 @@
"LICENSE"
],
"devDependencies": {
"tslib": "2.6.0"
"tslib": "2.6.2"
},
"dependencies": {
"@tauri-apps/api": "2.0.0-alpha.11"

@ -0,0 +1,39 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use rand_chacha::ChaCha20Rng;
use rand_core::{RngCore, SeedableRng};
use std::path::Path;
/// NOTE: Hash supplied to Stronghold must be 32bits long.
/// This is a current limitation of Stronghold.
const HASH_LENGTH: usize = 32;
pub struct KeyDerivation {}
impl KeyDerivation {
/// Will create a key from [`password`] and a generated salt.
/// Salt will be generated to file [`salt_path`] or taken from it
/// if file already exists
pub fn argon2(password: &str, salt_path: &Path) -> Vec<u8> {
let mut salt = [0u8; HASH_LENGTH];
create_or_get_salt(&mut salt, salt_path);
argon2::hash_raw(password.as_bytes(), &salt, &Default::default())
.expect("Failed to generate hash for password")
}
}
fn create_or_get_salt(salt: &mut [u8], salt_path: &Path) {
if salt_path.is_file() {
// Get existing salt
let tmp = std::fs::read(salt_path).unwrap();
salt.clone_from_slice(&tmp);
} else {
// Generate new salt
let mut gen = ChaCha20Rng::from_entropy();
gen.fill_bytes(salt);
std::fs::write(salt_path, salt).expect("Failed to write salt for Stronghold")
}
}

@ -35,6 +35,9 @@ use tauri::{
};
use zeroize::Zeroize;
#[cfg(feature = "kdf")]
pub mod kdf;
pub mod stronghold;
type PasswordHashFn = dyn Fn(&str) -> Vec<u8> + Send + Sync;
@ -407,27 +410,72 @@ fn get_client(
}
}
enum PasswordHashFunctionKind {
#[cfg(feature = "kdf")]
Argon2(PathBuf),
Custom(Box<PasswordHashFn>),
}
pub struct Builder {
password_hash_function: Box<PasswordHashFn>,
password_hash_function: PasswordHashFunctionKind,
}
impl Builder {
pub fn new<F: Fn(&str) -> Vec<u8> + Send + Sync + 'static>(password_hash_function: F) -> Self {
Self {
password_hash_function: Box::new(password_hash_function),
password_hash_function: PasswordHashFunctionKind::Custom(Box::new(
password_hash_function,
)),
}
}
/// Initializes [`Self`] with argon2 as password hash function.
///
/// # Examples
///
/// ```rust
/// tauri::Builder::default()
/// .setup(|app| {
/// let salt_path = app
/// .path_resolver()
/// .app_local_data_dir()
/// .expect("could not resolve app local data path")
/// .join("salt.txt");
/// app.handle().plugin(tauri_plugin_stronghold::Builder::with_argon2(&salt_path).build())?;
/// Ok(())
/// });
/// ```
#[cfg(feature = "kdf")]
pub fn with_argon2(salt_path: &std::path::Path) -> Self {
Self {
password_hash_function: PasswordHashFunctionKind::Argon2(salt_path.to_owned()),
}
}
pub fn build<R: Runtime>(self) -> TauriPlugin<R> {
let password_hash_function = self.password_hash_function;
PluginBuilder::new("stronghold")
let plugin_builder = PluginBuilder::new("stronghold")
.js_init_script(include_str!("api-iife.js").to_string())
.setup(move |app, _api| {
app.manage(StrongholdCollection::default());
app.manage(PasswordHashFunction(password_hash_function));
app.manage(PasswordHashFunction(match password_hash_function {
#[cfg(feature = "kdf")]
PasswordHashFunctionKind::Argon2(path) => {
Box::new(move |p| kdf::KeyDerivation::argon2(p, &path))
}
PasswordHashFunctionKind::Custom(f) => f,
}));
Ok(())
})
});
Builder::invoke_stronghold_handlers_and_build(plugin_builder)
}
fn invoke_stronghold_handlers_and_build<R: Runtime>(
builder: PluginBuilder<R>,
) -> TauriPlugin<R> {
builder
.invoke_handler(tauri::generate_handler![
initialize,
destroy,

@ -25,7 +25,7 @@
"LICENSE"
],
"devDependencies": {
"tslib": "2.6.0"
"tslib": "2.6.2"
},
"dependencies": {
"@tauri-apps/api": "2.0.0-alpha.11"

@ -21,7 +21,10 @@ use tauri::{
plugin::{Builder as PluginBuilder, TauriPlugin},
Runtime,
};
use tokio::{fs::File, io::AsyncWriteExt};
use tokio::{
fs::File,
io::{AsyncWriteExt, BufWriter},
};
use tokio_util::codec::{BytesCodec, FramedRead};
use read_progress_stream::ReadProgressStream;
@ -74,7 +77,7 @@ async fn download(
let response = request.send().await?;
let total = response.content_length().unwrap_or(0);
let mut file = File::create(file_path).await?;
let mut file = BufWriter::new(File::create(file_path).await?);
let mut stream = response.bytes_stream();
while let Some(chunk) = stream.try_next().await? {
@ -84,6 +87,7 @@ async fn download(
total,
});
}
file.flush().await?;
Ok(())
}

@ -11,8 +11,8 @@
"tauri": "tauri"
},
"devDependencies": {
"@sveltejs/adapter-static": "2.0.3",
"@sveltejs/kit": "1.26.0",
"@sveltejs/adapter-auto": "2.1.1",
"@sveltejs/kit": "1.27.3",
"@tauri-apps/cli": "2.0.0-alpha.17",
"svelte": "4.2.2",
"svelte-check": "3.5.2",

@ -50,6 +50,10 @@ export default class WebSocket {
listeners.forEach((l) => l(message));
};
if (config?.headers) {
config.headers = Array.from(new Headers(config.headers).entries());
}
return await invoke<number>("plugin:websocket|connect", {
url,
onMessage,

@ -24,7 +24,7 @@
"LICENSE"
],
"devDependencies": {
"tslib": "2.6.0"
"tslib": "2.6.2"
},
"dependencies": {
"@tauri-apps/api": "2.0.0-alpha.11"

@ -1 +1 @@
if("__TAURI__"in window){var __TAURI_WEBSOCKET__=function(){"use strict";function e(e,t,r,n){if("a"===r&&!n)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!n:!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)}var t;"function"==typeof SuppressedError&&SuppressedError;class r{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,t.set(this,(()=>{})),this.id=function(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}((r=>{e(this,t,"f").call(this,r)}))}set onmessage(e){!function(e,t,r,n,s){if("m"===n)throw new TypeError("Private method is not writable");if("a"===n&&!s)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!s:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");"a"===n?s.call(e,r):s?s.value=r:t.set(e,r)}(this,t,e,"f")}get onmessage(){return e(this,t,"f")}toJSON(){return`__CHANNEL__:${this.id}`}}async function n(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}t=new WeakMap;class s{constructor(e,t){this.id=e,this.listeners=t}static async connect(e,t){const i=[],a=new r;return a.onmessage=e=>{i.forEach((t=>t(e)))},await n("plugin:websocket|connect",{url:e,onMessage:a,config:t}).then((e=>new s(e,i)))}addListener(e){this.listeners.push(e)}async send(e){let t;if("string"==typeof e)t={type:"Text",data:e};else if("object"==typeof e&&"type"in e)t=e;else{if(!Array.isArray(e))throw new Error("invalid `message` type, expected a `{ type: string, data: any }` object, a string or a numeric array");t={type:"Binary",data:e}}return await n("plugin:websocket|send",{id:this.id,message:t})}async disconnect(){return await this.send({type:"Close",data:{code:1e3,reason:"Disconnected by client"}})}}return s}();Object.defineProperty(window.__TAURI__,"websocket",{value:__TAURI_WEBSOCKET__})}
if("__TAURI__"in window){var __TAURI_WEBSOCKET__=function(){"use strict";function e(e,t,r,n){if("a"===r&&!n)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!n:!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)}var t;"function"==typeof SuppressedError&&SuppressedError;class r{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,t.set(this,(()=>{})),this.id=function(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}((r=>{e(this,t,"f").call(this,r)}))}set onmessage(e){!function(e,t,r,n,s){if("m"===n)throw new TypeError("Private method is not writable");if("a"===n&&!s)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!s:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");"a"===n?s.call(e,r):s?s.value=r:t.set(e,r)}(this,t,e,"f")}get onmessage(){return e(this,t,"f")}toJSON(){return`__CHANNEL__:${this.id}`}}async function n(e,t={},r){return window.__TAURI_INTERNALS__.invoke(e,t,r)}t=new WeakMap;class s{constructor(e,t){this.id=e,this.listeners=t}static async connect(e,t){const a=[],i=new r;return i.onmessage=e=>{a.forEach((t=>t(e)))},(null==t?void 0:t.headers)&&(t.headers=Array.from(new Headers(t.headers).entries())),await n("plugin:websocket|connect",{url:e,onMessage:i,config:t}).then((e=>new s(e,a)))}addListener(e){this.listeners.push(e)}async send(e){let t;if("string"==typeof e)t={type:"Text",data:e};else if("object"==typeof e&&"type"in e)t=e;else{if(!Array.isArray(e))throw new Error("invalid `message` type, expected a `{ type: string, data: any }` object, a string or a numeric array");t={type:"Binary",data:e}}return await n("plugin:websocket|send",{id:this.id,message:t})}async disconnect(){return await this.send({type:"Close",data:{code:1e3,reason:"Disconnected by client"}})}}return s}();Object.defineProperty(window.__TAURI__,"websocket",{value:__TAURI_WEBSOCKET__})}

@ -29,6 +29,9 @@ use tokio_tungstenite::{
};
use std::collections::HashMap;
use std::str::FromStr;
use tauri::http::header::{HeaderName, HeaderValue};
use tokio_tungstenite::tungstenite::client::IntoClientRequest;
type Id = u32;
type WebSocket = WebSocketStream<MaybeTlsStream<TcpStream>>;
@ -41,6 +44,10 @@ enum Error {
Websocket(#[from] tokio_tungstenite::tungstenite::Error),
#[error("connection not found for the given id: {0}")]
ConnectionNotFound(Id),
#[error(transparent)]
InvalidHeaderValue(#[from] tokio_tungstenite::tungstenite::http::header::InvalidHeaderValue),
#[error(transparent)]
InvalidHeaderName(#[from] tokio_tungstenite::tungstenite::http::header::InvalidHeaderName),
}
impl Serialize for Error {
@ -108,7 +115,17 @@ async fn connect<R: Runtime>(
config: Option<ConnectionConfig>,
) -> Result<Id> {
let id = rand::random();
let (ws_stream, _) = connect_async_with_config(url, config.map(Into::into), false).await?;
let mut request = url.into_client_request()?;
if let Some(headers) = config.as_ref().and_then(|c| c.headers.as_ref()) {
for (k, v) in headers {
let header_name = HeaderName::from_str(k.as_str())?;
let header_value = HeaderValue::from_str(v.as_str())?;
request.headers_mut().insert(header_name, header_value);
}
}
let (ws_stream, _) = connect_async_with_config(request, config.map(Into::into), false).await?;
tauri::async_runtime::spawn(async move {
let (write, read) = ws_stream.split();

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT
import { invoke } from "@tauri-apps/api/primitives";
import { getCurrent } from "@tauri-apps/api/window";
import { WindowLabel, getCurrent } from "@tauri-apps/api/window";
export enum StateFlags {
SIZE = 1 << 0,
@ -19,19 +19,17 @@ export enum StateFlags {
* Save the state of all open windows to disk.
*/
async function saveWindowState(flags: StateFlags): Promise<void> {
return invoke("plugin:window-state|save_window_state", {
flags,
});
return invoke("plugin:window-state|save_window_state", { flags });
}
/**
* Restore the state for the specified window from disk.
*/
async function restoreState(label: string, flags: StateFlags): Promise<void> {
return invoke("plugin:window-state|restore_state", {
label,
flags,
});
async function restoreState(
label: WindowLabel,
flags: StateFlags,
): Promise<void> {
return invoke("plugin:window-state|restore_state", { label, flags });
}
/**

@ -25,7 +25,7 @@
"LICENSE"
],
"devDependencies": {
"tslib": "2.6.0"
"tslib": "2.6.2"
},
"dependencies": {
"@tauri-apps/api": "2.0.0-alpha.11"

File diff suppressed because it is too large Load Diff

@ -24,7 +24,7 @@
"LICENSE"
],
"devDependencies": {
"tslib": "2.6.0"
"tslib": "2.6.2"
},
"dependencies": {
"@tauri-apps/api": "2.0.0-alpha.11"

@ -1,4 +0,0 @@
{
"extends": "../../tsconfig.base.json",
"include": ["guest-js/*.ts"]
}

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.base.json",
"include": ["guest-js/*.ts"]
}
Loading…
Cancel
Save